Project import generated by Copybara.

GitOrigin-RevId: 78e7a95d84f5b4995453d7894b4d840621db767f
Change-Id: I4c6a044aa2e1bde47bdf57d3d0726f55505171a3
diff --git a/sg3_utils/AUTHORS b/sg3_utils/AUTHORS
new file mode 100644
index 0000000..f72f863
--- /dev/null
+++ b/sg3_utils/AUTHORS
@@ -0,0 +1,3 @@
+Douglas Gilbert <dgilbert at interlog dot com>
+
+See the CREDITS file for the names of those who have contributed.
diff --git a/sg3_utils/BSD_LICENSE b/sg3_utils/BSD_LICENSE
new file mode 100644
index 0000000..70d5c6d
--- /dev/null
+++ b/sg3_utils/BSD_LICENSE
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 1999-2013 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/sg3_utils/COPYING b/sg3_utils/COPYING
new file mode 100644
index 0000000..3ef2d53
--- /dev/null
+++ b/sg3_utils/COPYING
@@ -0,0 +1,32 @@
+
+Upstream Authors: Douglas Gilbert <dgilbert at interlog dot com>, 
+                  Bruce Allen  <ballen at gravity dot phys dot uwm dot edu>,
+                  Peter Allworth <linsol at zeta dot org dot au>,
+                  James Bottomley  <jejb at parisc-linux dot org>,
+                  Lars Marowsky-Bree <lmb at suse dot de>,
+                  Kurt Garloff <garloff at suse dot de>,
+                  Grant Grundler  <grundler at parisc-linux dot org>,
+                  Christophe Varoqui <christophe dot varoqui at free dot fr>,
+                  Michael Weller <eowmob at exp-math dot uni-essen dot de>,
+                  Eric Youngdale <eric at andante dot org>
+
+Copyright:
+
+This software is copyright(c) 1994-2012 by the authors
+
+Most of the code in this package is covered by a BSD license.
+On Debian systems, the complete text of the BSD License
+can be found in `/usr/share/common-licenses/BSD'. All the code
+in the library (usually called libsgutils) is covered by a
+BSD license.
+
+Some of the older utilities are covered by the GPL. More precisely:
+You are free to distribute this software under the terms of the
+GNU General Public License either version 2, or (at your option)
+any later version. On Debian systems, the complete text of the GNU
+General Public License can be found in /usr/share/common-licenses/GPL-2
+file. The later GPL-3 is found in /usr/share/common-licenses/GPL-3
+file but no code in this package refers to that license.
+
+Douglas Gilbert
+10th April 2012
diff --git a/sg3_utils/COVERAGE b/sg3_utils/COVERAGE
new file mode 100644
index 0000000..68f516b
--- /dev/null
+++ b/sg3_utils/COVERAGE
@@ -0,0 +1,136 @@
+                        Command coverage
+                        ================
+The following table lists SCSI commands in alphabetical order on the
+left and the sg3_utils (or related) utilities that implement invocations
+of them on the right. The second table lists supported ATA commands.
+
+SCSI command        sg3_utils utilities that use this SCSI command
+------------        -------------------------------------------------
+ATA COMMAND PASS-THROUGH(16)  sg_sat_identify, sg_sat_set_features,
+                    sg_sat_phy_event, sg_sat_read_gplog ++
+                    [sg_sat_chk_power, sg__sat_identify,
+                     sg__sat_set_features, sg_sat_smart_rd_data
+                     (previous four in the examples directory)]
+ATA COMMAND PASS-THROUGH(12)  sg_sat_identify, ++
+CLOSE ZONE          sg_zone
+COMPARE AND WRITE   sg_compare_and_write
+COPY OPERATION ABORT    ddptctl, ++
+EXTENDED COPY(LID1)    sg_xcopy, ddpt, ++
+GET CONFIGURATION   sg_get_config, ++
+GET LBA STATUS      sg_get_lba_status, ++
+INQUIRY             sg_dd, sg_format, sg_inq, sginfo,
+                    sg_logs, sg_map('-i'), sg_modes, sg_opcodes,
+                    sg_persist, sg_scan, sg_ses, sg_vpd ++
+FINISH ZONE         sg_zone
+FORMAT MEDIUM       sg_format, ++ [SSC]
+FORMAT UNIT         sg_format, ++ [SBC]
+LOG SELECT          sg_logs('-r' or '-select'), ++
+LOG SENSE           sg_logs, ++
+MODE SELECT(6)      sdparm, sg_wr_mode, sginfo, sg_format,
+                    sg_emc_trespass, sg_rdac, ++
+MODE SELECT(10)     sdparm, sg_wr_mode, sginfo, sg_format,
+                    sg_emc_trespass, sg_rdac, ++
+MODE SENSE(6)       sdparm, sg_modes, sg_wr_mode, sginfo, sg_format,
+                    sg_senddiag('-e'), sg_rdac, ++
+MODE SENSE(10)      sdparm, sg_modes, sg_wr_mode, sginfo, sg_format,
+                    sg_senddiag('-e'), sg_rdac, ++
+OPEN ZONE           sg_zone
+PERSISTENT RESERVE IN       sg_persist, ++
+PERSISTENT RESERVE OUT      sg_persist, ++
+POPULATE TOKEN      ddpt, ddptctl, ++
+PREVENT ALLOW MEDIUM REMOVAL        sg_prevent, ++
+READ(6)             sg_dd, sgm_dd, sgp_dd, sg_read
+READ(10)            sg_dd, sgm_dd, sgp_dd, sg_read
+READ(12)            sg_dd, sgm_dd, sgp_dd, sg_read
+READ(16)            sg_dd, sgm_dd, sgp_dd, sg_read
+READ ATTRUBUTE      sg_read_attr
+READ BLOCK LIMITS   sg_read_block_limits, ++
+READ BUFFER(10)     sg_rbuf, sg_test_rwbuf, sg_read_buffer, sg_safte, ++
+READ BUFFER(16)     sg_read_buffer
+READ CAPACITY(10)   sg_readcap, sg_dd, sgm_dd, sgp_dd, sg_format, ++
+READ CAPACITY(16)   sg_readcap, sg_dd, sgm_dd, sgp_dd, sg_format, ++
+READ DEFECT(10)     sginfo('-d' or '-G'), sg_reassign('-g'), smartmontools, ++
+READ DEFECT(12)     sginfo('-d' or '-G'), smartmontools
+READ LONG(10)       sg_read_long, sg_dd, ++
+READ LONG(16)       sg_read_long, ++
+READ MEDIA SERIAL NUMBER     sg_rmsn, ++
+REASSIGN BLOCKS     sg_reassign, ++
+RECEIVE COPY DATA(LID1)    sg_copy_results, ++
+RECEIVE COPY FAILURE DETAILS(LID1)    sg_copy_results, ++
+RECEIVE COPY OPERATING PARAMETERS    ddpt, sg_copy_results, sg_xcopy, ++
+RECEIVE COPY STATUS(LID1)    sg_copy_results, ++
+RECEIVE DIAGNOSTIC RESULTS  sg_senddiag, sg_ses, sg_ses_microcode ++
+RECEIVE ROD TOKEN INFORMATION    ddpt, ddptctl ++
+REPORT ALL ROD TOKENS    ddptctl ++
+REPORT IDENTIFYING INFORMATION  sg_ident, ++ (2)
+REPORT LUNS         sg_luns, ++
+REPORT REFERRALS    sg_referrals, ++
+REPORT SUPPORTED OPERATION CODES              sg_opcodes
+REPORT SUPPORTED TASK MANAGEMENT FUNCTIONS    sg_opcodes
+REPORT TARGET PORT GROUPS       sg_rtpg, sg_stpg ++
+REPORT TIMESTAMP    sg_timestamp
+REPORT ZONES        sg_rep_zones
+REQUEST SENSE       sg_requests, ++
+RESET WRITE POINTER sg_reset_wp
+SANITIZE            sg_sanitize
+SEND DIAGNOSTIC     sg_senddiag, sg_ses, sg_ses_microcode ++
+SET IDENTIFYING INFORMATION  sg_ident, ++ (3)
+SET TARGET PORT GROUPS       sg_stpg, ++
+SET TIMESTAMP       sg_timestamp
+START STOP          sg_start, ++
+SYNCHRONIZE CACHE(10)   sg_sync, sg_dd, sgm_dd, sgp_dd, ++
+SYNCHRONIZE CACHE(16)   sg_sync++
+TEST UNIT READY     sg_turs, sg_format, ++
+UNMAP               sg_unmap, ++
+VERIFY(10)          sg_verify, ++
+VERIFY(16)          sg_verify, ++
+WRITE(6)            sg_dd, sgm_dd, sgp_dd
+WRITE(10)           sg_dd, sgm_dd, sgp_dd
+WRITE(12)           sg_dd, sgm_dd, sgp_dd
+WRITE(16)           sg_dd, sgm_dd, sgp_dd
+WRITE AND VERIFY(10)      sg_write_verify
+WRITE AND VERIFY(16)      sg_write_verify
+WRITE ATOMIC(16)    ddpt
+WRITE BUFFER        sg_test_rwbuf, sg_write_buffer, ++
+WRITE LONG(10)      sg_write_long, ++
+WRITE LONG(16)      sg_write_long, ++
+WRITE SAME(10)      sg_write_same
+WRITE SAME(16)      sg_write_same
+WRITE SAME(32)      sg_write_same
+WRITE USING TOKEN   ddpt, ddptctl, ++
+<most commands>     sg_raw
+
+
+
+ATA command         sg3_utils utilities that use this SCSI command
+-----------         ----------------------------------------------
+CHECK POWER MODE    examples/sg_sat_chk_power
+IDENTIFY DEVICE     sg_inq, sg_scan, sg_sat_identify,
+                    examples/sg__sat_identify
+IDENTIFY PACKET DEVICE     sg_inq, sg_sat_identify,
+                    examples/sg__sat_identify
+READ LOG EXT        sg_sat_phy_event, examples/sg__sat_phy_event
+                    sg_sat_read_gplog
+READ LOG DMA EXT    sg_sat_read_gplog
+SET FEATURES        sg_sat_set_features
+                    examples/sg__sat_set_features
+SMART READ DATA     examples/sg_sat_smart_rd_data
+
+
+++  command wrapper found in sg_cmds_basic.c, sg_cmds_mmc.c  or
+    sg_cmds_extra.c for this command
+(2) this command was known as REPORT DEVICE IDENTIFIER prior to spc4r07
+(3) this command was known as SET DEVICE IDENTIFIER prior to spc4r07
+
+Note that any SCSI command, including bi-directional and variable length
+commands (whose cdb size is > 16 bytes) can be issued by the sg_raw utility.
+
+The RECEIVE COPY * commands in SPC-4 were grouped as one command name
+with 4 service actions in SPC-3 and earlier. The single SPC-3 command
+name is RECEIVE COPY RESULTS. The two opcodes associated with all
+EXTENDED COPY commands are now known as THIRD PARTY COPY IN (0x84) and
+THIRD PARTY COPY IN (0x83).
+
+
+Douglas Gilbert
+8th February 2016
diff --git a/sg3_utils/CREDITS b/sg3_utils/CREDITS
new file mode 100644
index 0000000..a0082e2
--- /dev/null
+++ b/sg3_utils/CREDITS
@@ -0,0 +1,138 @@
+The author of sg3_utils would like to thank the following people who
+have made contributions:
+
+
+Andries Brouwer <aebr at win dot tue dot nl> rewrite of isosize (original
+        written by Joerg Schilling). isosize is now found in the util-linux
+        package and in the archive directory of this package.
+
+Brian Bunker <Brian dot Bunker at netapp dot com> contributed
+        sg_read_block_limits and the target reset addition to sg_reset
+        [20090615]
+
+Christophe Varoqui <christophe dot varoqui at free dot fr> original sg_rtpg
+        [20041229]
+
+Clayton Weaver <cgweav at email dot com> contributed safe_strerror().
+
+Dan Horak <dhorak at redhat dot com> website support for this package and
+        others. Lot of fixes, recently man pages [20140128]
+
+Dave Johnson <ddj at ccv dot brown dot edu> improved disk defect list
+        handling [20051218]
+
+Dave Williams <dave at opensourcesolutions dot co dot uk> help with
+        sgp_dd especially and "> 0x7fffffff" with sg*_dd [20060303]
+
+Eric Schwartz <emschwar at debian dot org> who wrote these man pages:
+        sg_readcap, sg_reset, sg_scan, sg_start, sg_test_rwbuf,
+        sg_turs and sginfo
+
+Eric Seppanen <eric @ purestorage dot com> borrowed ideas from alternate
+        implementation of sg_compare_and_write [20130823]
+
+Eric Youngdale <eric at andante dot org> author of scsi_info on which sginfo
+        is based.
+
+Frank Jansen <fjansen at egenera dot com>: additions to sg_scan; contributed
+        code for '--alloc-length=' option in sg_persist [20090402]
+
+Grant Grundler <grundler at parisc-linux dot org> co-author of blk512-linux
+        that has become sg_format [20050201]
+
+Greg Inozemtsev <greg at purestorage dot com>
+        extensions to sg_xcopy [20130207+20130816]
+
+Hannes Reinecke <hare at suse dot de>
+        contributed sg_rdac, (and some corresponding VPD entries to
+        sg_vpd_vendor), sg_stpg and sg_safte [20071013+20130110]
+        sg_referrals [20100906]
+        sg_inq --export option [20120220+20130109]
+        sg_xcopy+sg_copy_results [20120322]
+        rescan-scsi-bus.sh patches to Kurt Garloff's v1.57 [20130715]
+        55-scsi-sg3_id.rules + 58-scsi-sg3_symlink.rules [20140527]
+        sg_sat_read_gplog [20141107]
+
+Hayashi Naoyuki <titan at culzean dot org>
+        port to Tru64 [20060127]
+
+Heiko Eissfeldt <heiko at colossus dot escape dot de> sg based example
+        programs for the original sg driver
+
+Ilan Steinberg <ilan dot steinberg at kaminario dot com>
+        sg_xcopy: contributed --on_src and --on_dst options [20130505]
+
+Ingo van Lil <inguin at gmx dot de>
+        contributed sg_raw [20070331]
+
+James Bottomley <jejb at parisc-linux dot org> co-author of blk512-linux
+        that has become sg_format [20050201]
+
+Jan Engelhardt <jengelh at inai dot de>
+        autotools clean-up [20150216]
+
+Joe Krahn <krahn at niehs dot nih dot gov> help with int64_t cleanup
+        [20071219]
+
+Kai Makisara <Kai dot Makisara at kolumbus dot fi> help with tape
+        minor numbers in lk 2.6 plus earlier advice [20081008]
+
+Kurt Garloff <garloff at suse dot de> original sg_start and sg_test_rwbuf.
+        Additions to sginfo and sg_map. Author of rescan-scsi-bus.sh with
+        latest update to v1.57 [20130331]
+
+Lars Marowsky-Brée <lmb at suse dot de> contributed Unit Path Report VPD
+        page decoding in sg_inq (vendor specific: EMC) and sg_emc_trespass
+        utility
+
+Luben Tuikov <ltuikov at yahoo dot com>
+        help with documentation and other suggestions [20061014]
+        contribution sg_read_buffer and sg_write_buffer [20061103]
+
+Marius Konitzer <marius dot konitzer at ruhr-uni-bochum dot de
+        log pages on IBM LTO Ultrium drives [20100225]
+
+Mark Knibbs <markk at clara dot co dot uk>
+        suggested and tested oflag=sparse for sg_dd
+
+Martin Schwenke <martin at meltin dot net> added the raw switch "-r" to sg_inq
+
+Nate Dailey < Nate dot Dailey at stratus dot com > extended sg_map for sparse
+        disk node names (e.g. /dev/sdaaa) [20050511]
+
+Pat LaVarre <p.lavarre at ieee dot org> pointed out danger of negative bpt
+        values in sg_dd (and friends); also problems when reading /dev/null
+
+Peter Allworth <linsol at zeta dot org dot au> original dd clone design used
+        by sg3_utils's dd variants (e.g. sg_dd).
+
+Roland Dreier <roland at purestorage dot com>
+        extension and correction to sg_xcopy [20120205]
+
+Ronnie Sahlberg <ronniesahlberg at gmail dot com> has written libiscsi and a
+        set of external patches to add direct iSCSI support to this package.
+        See README.iscsi [20110518]
+
+Saeed Bishara contributed sg_write_long
+
+Sean Stewart <Sean dot Stewart at netapp dot com> various improvements
+        to rescan-scsi-bush.sh script [20130827]
+
+Shahar Salzman <shahar dot salzman at kaminario dot com> contributed
+        sg_compare_and_write [20121205]
+
+Thomas Kolbe <tkolbe at partnersdata dot com>
+        Solaris port help and testing [20070503]
+
+Tim Hunt <tim at timhunt dot net> increased number of (sd and sg) devices
+        that sginfo could detect.
+
+Tom Steudten <steudten at gmx dot ch> sginfo addition: add '-Fhead' option
+        to sort defect list by head.
+
+Trent Piepho <xyzzy at speakeasy dot org> print out some "sense key specific"
+        data and "-6" switch for sg_modes
+
+
+Douglas Gilbert
+17th February 2015
diff --git a/sg3_utils/ChangeLog b/sg3_utils/ChangeLog
new file mode 100644
index 0000000..4d5cfd5
--- /dev/null
+++ b/sg3_utils/ChangeLog
@@ -0,0 +1,1361 @@
+Each utility has its own version number, date of last change and
+some description at the top of its ".c" file. All utilities in the main
+directory have their own "man" pages. There is also a sg3_utils man page.
+
+Changelog for sg3_utils-1.42 [20160217] [svn: r663]
+  - sg_timestamp: new, to report or set timestamp
+  - sg_read_attr: new, supported by tape drives
+  - sg_stpg: fix truncation of target port field
+  - sg_inq: cope with unicode strings, udev fixes
+    - update version descriptor list to 20160125
+    - '--export': new entries for UUID descriptor
+  - sg_ses: add more field acronyms (ses3r11)
+  - sg_logs: add Utilization lpage (sbc4r07)
+    - add Background operation lpage
+    - add Pending defects lpage
+    - add LPS misalignment lpage (sbc4r10)
+    - document '--All' ('-A') option
+    - rework lto tape vendor lpages
+  - sg_vpd: add Block limits extension VPD page
+    - add Device constituents VPD page
+    - add LB Protection VPD page (ssc 15-296r1)
+    - LB provisioning VPD page: expand LBPRZ, add
+      Minimum and Threshold percentage fields
+    - rework lto tape vendor VPD pages
+  - sg_inq+sg_vpd+sg_xcopy: add support for locally
+    assigned UUIDs in VPD page 0x83 (15-267r2)
+  - sg_sanitize: add --znr option (sbc4r07)
+  - sg_rep_zones: add --partial option (zbc-r04)
+  - sg_format: add ffmt option (sbc4r10)
+    - add support for FORMAT MEDIUM (for tape)
+  - sg_raw: document length relationships
+  - rescan-scsi-bus.sh: updates from Suse
+  - sg_lib_data: sync asc/ascq codes with T10 20151126
+  - sg_lib: add 'sense' categories for SCSI statuses:
+    condition met, busy, task set full, ACA active and
+    task aborted
+    - add pr2serr() extern
+    - change sg_get_sense_str() and dStrHexStr(), return
+      chars written (returned void previously)
+    - add sg_get_sense_descriptors_str() function
+    - add sg_get_designation_descriptor_str() function
+    - sg_get_desig_type_str()+sg_get_desig_assoc_str()
+      and sg_get_desig_code_set_str() added
+    - sg_get_opcode_sa_name() break out zoning in/out,
+      read attribute and read position service actions
+  - sg_cmds_extra: add sg_ll_format_unit2() for FFMT
+  - sg_pr2serr.h: new, to shorten fprintf(stderr, ...)
+  - sg_io_linux, sg_pt_linux: drop SUGGEST_* decoding
+  - sg_unaligned.h: add 48 bit support and gets for
+    variable length unsigned integers
+
+Changelog for sg3_utils-1.41 [20150511] [svn: r644]
+  - sg_zone: new utility for open, close and finish
+    zone commands introduced in zbc-r02
+  - sg_rep_zones and sg_reset_wp: change opcodes as
+    indicated in zbc-r02
+  - sg_read_buffer: add READ BUFFER(16) support (spc5r02)
+  - sg_logs: add --enumerate and acronyms
+    - allow decode from hex or binary in file
+    - decode environmental reporting + limits lpages
+  - sg_write_buffer: add --timeout=TO option
+  - sg_lib interface: add sg_lib_pdt_decay(), TPROTO_PCIE
+    plus support for zoning service actions
+  - sg_lib: in Linux blocked devices yield ENXIO from
+    ioctl(SG_IO), map to SG_LIB_CAT_NOT_READY
+    - clean up sg_warnings_stream handling
+  - sg_inq+sg_vpd: fix SCSI name string decoding in
+    device identification VPD page (0x83)
+    - increase sanity on Unit Serial number VPD page
+    - improve rdac vpd page reporting (vendor)
+  - sg_inq: improve NAA handling in dev_id VPD page
+    - update version descriptor list to 20150126
+  - sg_vpd: add atomic boundary values (sbc4r04)
+    - block limits VPD page: fix unmap granularity
+      alignment value; spc5r02 additions
+  - sg_readcap: add support for ZBC's rc_basis field
+  - sg_senddiag: fix bug with --raw option
+    - add support for -HHH for output suitable for --raw
+  - sg_ses: enclosure element: add failure and warning
+    acronyms, fix warning indication output
+    - additional element status dpage: add PCIe/NVMe
+    - handle element descriptor names that count
+      multiple trailing NULLs
+  - rescan-scsi-bus.sh: add --issue-lip-wait option and
+    improve error handling
+    - improve dm-multipath handling
+  - sg_modes: make '-HHH' output suitable as input to
+    'sdparm --inhex='
+  - sg_rdac: add '-6' option for mode sense/select(6)
+    - add support for reporting more SCSI transports
+      and accessing rdac extended mode page 0x2c
+  - sg_write_same: cleanup, mainly man page
+  - scsi_logging_level: replace use of tr command
+  - examples/sg_tst_async: cleanup
+  - examples/sg-simple_aio.c: remove
+  - sg_lib_data: sync asc/ascq codes with T10 20150423
+  - Makefile cleanup
+  - autogen.sh: upgrade to buildconf 20091223 version
+
+Changelog for sg3_utils-1.40 [20141110] [svn: r620]
+  - sg_write_verify: new utility for WRITE AND VERIFY
+  - sg_ses_microcode: new utility
+  - sg_sat_read_gplog: new utility
+  - sg_senddiag: add --maxlen= option
+  - sg_copy_results: correct response length calculations
+  - sg_format: make '-FFF' bypass mode sense/select
+    - add --mode=MP to supply alternate mode page,
+      default remains read-write error recovery mpage
+    - output unit serial number and LU name prior to
+  - sg_inq: expand Block limits VPD page output
+    - fix --cmddt output if not supported by device
+    - more sanity checks on vendor supplied fields
+  - sg_vpd: add --all option
+    - more TPC VPD page decoding
+    - add zoned block device characteristics page
+    - more sanity checks on vendor supplied fields
+  - sg_ses: fix problem with --index=sse (and ssc)
+    - mask status element before using as control
+    - defeat previous item with --mask (ignore) option
+    - SAS connector status element: add overcurrent bit
+    - handle element descriptor names that count a
+      trailing NULL
+    - add --warn option mainly for broken joins
+    - add optional descriptions to -ee output
+    - sync with ses3r07
+  - sg_sanitize: add --desc and --zero options
+    - output unit serial number and LU name prior to
+  - sg_rep_zones: corrections, sync with zbc-r01c
+  - sg_persist: split help into two pages, '-hh' for 2nd
+  - sg_logs: refine tape drive output
+  - sg_raw: with -vvv decode T10 CDB name
+    - do not output/print data-in if error
+  - sg_opcodes: add --compact field
+  - sg_senddiag: add --page=PG option
+  - sg_reset: add words for EAGAIN from reset ioctl
+  - sg_sat_*: mention t_type and multiple_count fields
+  - win32: sg_scan: handle larger configurations
+  - sg_lib: trim trailing spaces in dStrHex() and friends
+  - sg_lib_data: sync asc/ascq codes with T10 20140924
+    - clean up service action string functions
+    - sg_ll_unmap_v2(): fix group number
+    - sg_ll_inquiry(), sg_ll_mode_sense*(),
+      sg_ll_log_sense(): use resid to clear unfilled
+      data-in buffer
+  - sg_unaligned.h: add header for building parameters
+  - examples/sg_tst_async: new Linux sg test utility
+
+Changelog for sg3_utils-1.39 [20140612] [svn: r588]
+  - sg_rep_zones: new utility for ZBC REPORT ZONES
+  - sg_reset_wp: new utility, ZBC RESET WRITE POINTER
+  - sg_ses: add --eiioe=auto|force option
+    - fix AES dpage element indexing problems
+    - add --readonly option
+  - sg_write_buffer: add --bpw=CS option to call
+    write buffer multiple times for big blobs
+  - sg_format: add --ip_def option to fully provision
+  - sg_opcodes: add --mask option
+  - sg_logs: add --in=FN option for log select params
+    - add --filter=PARC (parameter code)
+    - add --no_inq for suppress initial INQUIRY call
+    - add --readonly option
+  - sg_persist: add --readonly option, environment
+    variable SG_PERSIST_IN_RDONLY sets ro on prin cmds
+  - sg_inq: sync version descriptors dated 20105176
+    - suppress dev-id VPD messages so they only appear
+      when --verbose is given
+    - add new SCSI_IDENT_*_ATA pair to --export output
+  - sg_luns: add decoding for conglomerate LUNS
+    - add --lu_cong option to simulate the LU_CONG bit
+  - sg_vpd: add --vendor=VP option, re-order vendor
+    specific pages, split lto into lto5 and lto6
+    - add Supported block lengths and protection types
+      page (sbc4r01)
+    - add Block device characteristics extension
+      page (sbc4r02)
+  - sg_copy_results, sg_get_lba_status, sg_luns,
+    sg_read_buffer, sg_readcap, sg_referrals, sg_rtpg,
+    sg_sat_set_features, sg_sat_identify:
+    add --readonly option
+  - sginfo: strip trailing spaces from INQUIRY text
+  - sg_rbuf: add --echo option (to use echo buffer)
+  - sg_lib: add sanitize command service action names
+    - add 'sense' categories for reservation conflict,
+      data protect and protection information violations
+    - add sg_get_category_sense_str() to API
+    - change struct sg_simple_inquiry_resp::rmb to byte_1
+    - add initial zbc service actions
+    - dStrHex(Err): fix output truncation error
+    - linux, sg: support SCSI_PT_FLAGS_QUEUE_AT_TAIL and
+      SCSI_PT_FLAGS_QUEUE_AT_HEAD (block layer queueing)
+  - sg_lib_data: sync asc/ascq codes with T10 20140516
+    - sync operation code with T10 20140515
+    - add id string for SPC-5
+  - scripts/59-scsi-sg3_utils.rules: removed
+    - functionality split into two scripts:
+      55-scsi-sg3_id.rules + 58-scsi-sg3_symlink.rules
+  - examples/sg_persist_tst.sh: add --exclusive option
+  - win32: sg_scan, sg_ses and sg_log fixes
+  - examples/sgq_dd: re-add old utility as example
+
+Changelog for sg3_utils-1.38 [20140401] [svn: r563]
+  - sg_ses: add --dev-slot-num= and --sas-addr=
+    - fix --data=- problem with large buffers
+    - new --data=@FN to read hex data from file FN
+    - error and warning message cleanup
+    - add --maxlen= option
+  - sg_inq: add --block=0|1 option to control opens
+    - add --inhex=FN to read response in ASCII hex from
+      a file; --inhex=FN --raw reads response in binary
+    - make -HHH (-HHHH for '-p ai') output suitable for
+      another sg_inq invocation to use --inhex to decode
+    - add LU_CONG to standard inquiry response output
+    - decode ASCII information VPD pages
+    - add HAW_ZBC in block dev char. VPD page (sbc4r01)
+    - sync version descriptors dated 20131126
+    - allow --page=-1 to force std INQUIRY decoding
+    - fix overflow in encode_whitespaces
+    - improve unit serial number display (VPD page 0x80)
+  - sg_vpd: add LU_CONG to standard inquiry response output
+    - add --inhex=FN to read response in ASCII hex from
+      a file; --inhex=FN --raw reads response in binary
+    - decode Third Party Copy (tpc) page
+    - add HAW_ZBC in block dev char. VPD page (sbc4r01)
+    - add LTO and DDS vendor pages
+    - allow --page=num to restrict --enumerate output
+  - sg_persist: add PROUT: Replace Lost Reservation (spc4r36)
+    - add --transport-id= for SOP: 'sop,<routing_id_in_hex>'
+  - sg_readcap: for --16 show physical block size if
+    different from logical block size
+  - sg_xcopy: environment variables: XCOPY_TO_SRC and
+    XCOPY_TO_DST indicate where xcopy command is sent
+    - change default to send xcopy to dst (was src)
+    - improve CL handling of short options (e.g. '-vv')
+  - sg_luns: guard against garbage response
+  - sg_decode_sense: with --nospace ignore spaces on
+    command line, so multiple arguments are concatenated
+  - sg_write_same: repeat if unit attention
+  - sg_rtpg: fix indexing bug with --extended option
+  - sg_logs: placeholder for pending defects lpage
+  - sg_unmap: fix another problem with --grpnum= option
+  - sg_lib.h: add PDT_ZBC define (spc4r36p)
+  - sg_lib_data: sync asc/ascq codes with T10 dated 20140320
+    - add pdt string for ZBC (spc4r36p)
+  - sg_lib: extensions to sg_get_num() and sg_get_llnum()
+  - sg_cmds_extra: fix sa bug in sg_ll_3party_copy_out()
+  - scripts/rescan-scsi-bus.sh: check if FC driver exports
+    issue_lip before using it
+    - man page added (Linux only)
+  - scripts/59-scsi-sg3_utils.rules: linux specific udev rules
+  - examples: add sg_tst_excl3 for testing O_EXCL
+    - improve sg_tst_excl and sg_tst_excl2
+    - add sg_tst_context for testing file handle contexts
+  - upgrade automake to version 1.13.3
+  - add suse directory and 'spec' file to facilitate builds
+
+Changelog for sg3_utils-1.37 [20131014] [svn: r522]
+  - sg_compare_and_write: fix wrprotect setting
+    - add --quiet option to suppress miscompare report
+    - merge features from another implementation
+  - sg_inq: fix referrals VPD page
+    - dev_id VPD: T10 vendor id designator clean up
+  - sg_logs: improve for tape drives, general cleanup
+  - sg_persist: fix core dump on -Q option
+  - sg_unmap: fix core dump on -g option
+  - sg_vpd: dev_id VPD: T10 vendor id designator clean up
+    - cleanup up dev_id NAA-3: locally assigned
+  - sg_ses: add --nickname and --nickid options
+    - eiioe added to additional element status page (ses3r6)
+    - multiple --filter options to prune output
+  - sg_verify: improve miscompare handling
+    - rename --btychk=ndo option to --ndo=ndo (hide former)
+    - add --quiet option
+  - sg_xcopy: allow sg and bsg devices
+    - fix for bpt going negative
+    - limit each XCOPY(LID1) command to 65535 blocks
+    - fix for seek in multi-segment copies
+  - sg_sanitize: skip 15 second safety delay with --fail
+  - sg_libs: extended copy opcode renamed (spc4r34)
+    - sg_ll_receive_copy_results(): expand for all sa_s
+    - add sg_get_sense_key()
+    - add sg_ll_3party_copy_out()
+    - add dStrHexErr(): ascii hex to stderr
+    - add dStrHexStr(): ascii hex to string
+    - add SG_LIB_CAT_MISCOMPARE to categories
+    - clean header files
+  - sg_pt_freebsd: sanity check on sense_resid; fix leaks
+  - scripts/rescan-scsi-bus.sh KG's v1.57 + HR patch
+    - improve wlun handling, detect updated and resized
+      devices, better multipath support
+  - Makefile.am cleanup
+  - examples: add sg_tst_excl and sg_tst_excl2
+
+Changelog for sg3_utils-1.36 [20130531] [svn: r497]
+  - sg_vpd: Protocol-specific port information VPD page
+    for SAS SSP, persistent connection (spl3r2), power
+    disable (spl3r3)
+    - block device characteristics: add FUAB bit
+  - sg_xcopy: handle more descriptor types; handle zero
+    maximum segment length; allow list IDs to be disabled;
+    improve skip/seek handling; allow xcopy on destination
+  - sg_reset: and --no-esc option to stop reset escalation
+    - clean up cli, add long option names
+  - sg_luns: add --test=ALUN option for decoding LUNs
+    - decoded luns output in decimal or hex (if -HH given)
+    - add '--linux' option to show Linux LUN after T10
+      representation, can map one to the other
+  - sg_inq: add --vendor option to show standard inquiry's
+    vendor specific fields in ASCII
+    - take resid into account with response output
+  - sg_sync: add --16 (for 16 byte command) and --timeout=
+  - sg_logs: add data compression page (ssc4)
+  - sg_sat_set_features: increase --lba from 1 to 4 bytes
+  - sg_write_same: add --ndob option (sbc3r35d)
+  - sg_map: mark as deprecated
+  - sginfo: mark as deprecated, especially -l (list)
+  - sg_lib: improve snprintf handling
+  - sg_lib_data: sync asc/ascq codes with T10 20130117
+  - sg_cmds (lib): if noisy given, give more UA info
+  - make code more C++ friendly
+
+Changelog for sg3_utils-1.35 [20130117] [svn: r476]
+  - sg_compare_and_write: new utility
+  - sg_inq+sg_vpd: block device characteristics VPD page:
+    add product_type, WABEREQ, WACEREQ and VBULS fields
+  - sg_inq: more --export option changes for udev
+  - sg_vpd: add more rdac vendor specific vpd pages
+  - sg_verify: add --ebytchk option for sbc3r34 changes
+  - sg_stpg: --offline option: fix 'Invalid state 0xe'
+  - sg_ses: Door Lock element changed to Door element and
+    abbreviation changed from 'dl' to 'do' (ses3r05)
+  - archive/rescan-scsi-bus.sh: upgrade to version 1.53hr
+    - move rescan-scsi-bus.sh to scripts directory
+  - sync to sbc3r34
+  - sg_lib: sg_ll_verify10+16 expand BYTCHK to 2 bit field
+  - sg_pt_win32, sg_scan(win32): changes for cygwin 1.7.17
+  - clean up man page summary lines
+
+Changelog for sg3_utils-1.34 [20121013] [svn: r461]
+  - sg_xcopy: new dd like utility for extended copy command
+  - sg_copy_results: new utility for receive copy results
+  - sg_verify: add 16 byte cdb, bytchk (data-out buffer)
+    and group number support
+  - sync to spc4r36 and sbc3r32
+  - sg_inq: add --export so sg_inq can replace udev's scsi_id
+    - decode old EMC Symmetrix abuse of VPD page 0x83
+  - sg_vpd: decode old EMC Symmetrix abuse of VPD page 0x83
+  - sg_ses: increase max dpage response size to 64 KB
+    - allow ident,locate on enclosure controller
+    - more sanity for additional element status descriptor
+  - sg_sanitize: add --ause, --fail and --test=
+  - sg_luns: add long extended flat space addressing format
+  - sg_logs: add ATA pass-through results lpage (SAT-2)
+  - sg_rtpg: add --extended option
+  - sg_senddiag: list rebuild assist diag page name
+  - sg_pt_linux: expand DID_ (host_byte) codes
+    - cope with a transport error plus sense data
+    - prefer major() over MAJOR() macro
+  - sg_lib: fix sg_get_command_name() service actions
+    - report sdat_ovfl bit (if set) in sense data
+    - decode extended_copy and receive_copy service actions
+    - decode read_buffer and write_buffer modes
+    - decode ATA PT fixed format sense (SAT-2)
+  - sg_cmds_extra: add sg_ll_report_tgt_prt_grp2()
+  - ./configure options:
+    - change --enable-no-linux-bsg to --disable-linuxbsg
+    - add --disable-scsistrings to reduce utility sizes
+
+Changelog for sg3_utils-1.33 [20120118] [svn: r435]
+  - sg_ses: major rework of indexes (again), now two level
+  - sg_write_buffer: new --specific option for mode specific
+    field; new mode 13 (spc4r32)
+  - sg_vpd: add hp3par volume info vendor VPD page
+    - fix 'scsi ports' [0x88] page problem
+    - add 'sinq' pseudo page for standard inquiry response
+    - add power consumption page
+  - sg_format: add --poll= option for request sense polling
+    - improve handling of disks > 2 TB and DIF (protection)
+  - sg_logs: LB provision lpage extra (sbc3r28)
+  - sg_modes: application tag mpage subcode 0xf0->0x2
+  - sg_write_same: no prot fields when wrprotect=0
+  - sg_get_lba_status: reflect change in sbc3r25 to Parameter
+    Data Length response field (offset reduced from 8 to 4)
+  - sg_inq, sg_vpd: sync with spc4r33
+  - win32: change DataBufferOffset type per MSDN; caused
+    problem with 64 bit machines (with buffered interface)
+  - sg_luns: tweak documentation for vendor specific reports
+  - add man pages for scsi_loging_level, scsi_mandat,
+    scsi_satl and scsi_temperature
+
+Changelog for sg3_utils-1.32 [20110730] [svn: r410]
+  - sg_sanitize: new utility for command added in sb3r27
+  - sg_sat_identify: add '--ident' to output WWN
+  - sg_ses: major rework of descriptor output
+    - add --index, --descriptor, --join, --clear, --get,
+      and --set options
+  - sg_raw: exit status corrections
+  - sg_decode_sense: add --nospace and --hex options
+  - sg_logs: fix bug with large --maxlen
+    - zero response length when resid implies it is invalid
+    - add scope field to lb provisioning lpage (sb3r27)
+  - sg_inq: sync version descriptors with spc4r31
+  - sg_lib_data: sync asc/ascq codes with spc4r31
+  - sg_vpd: add LBPRZ field in LB provisioning VPD page
+  - sg_format: allow format of pdt 7 (some MO drives)
+  - sg_cmds_basic: sg_cmds_process_resp() handle status good
+    with a sense key other than no_sense (e.g. completed)
+  - add README.iscsi
+
+Changelog for sg3_utils-1.31 [20110216] [svn: r386]
+  - sg_decode_sense: new utility to decode sense data
+  - sg_vpd: LB provisioning + Block limits pages (sbc3r26)
+  - sync asc/ascq and version descriptors with spc4r28
+  - sg_get_config, sg_rmsn, sg_verify: add --readonly option
+  - sg_lib: implement forwarded sense data descriptor
+    - decode user data segment referral sense data descriptor
+  - sg_lib, sg_turs, sg_format: more precision for progress
+    indication (two places after decimal point)
+  - sg_lib(win32): add runtime selection of SPT direct or
+    indirect interface
+    - sg_read_buffer+sg_write_buffer: set SPT direct
+  - add examples/forwarded_sense.txt + examples/ref_sense.txt
+
+Changelog for sg3_utils-1.30 [20101111] [svn: r363]
+  - sg_referrals: new utility for REPORT REFERRALS
+  - sbc3r25 renames 'thin' provisioning' to 'logical block
+    provisioning': changes in sg_format, sg_inq, sg_logs,
+    sg_modes, sg_readcap, sg_vpd
+  - sg_inq: update version descriptor list to spc4r27
+    - extended inquiry vpd page add extended self test
+      completion minutes field
+  - sg_lib: sync asc/ascq list to spc4r27
+    - dStrHex(): trim excess trailing spaces
+  - sg_read_long: add --readonly option (open() is rw)
+  - sg_raw: add --readonly option (open() is rw)
+    - allow bidirectional commands
+  - sg_vpd: rdac vendor page [0xc8] parse corrections
+    - extended inquiry vpd page add extended self test
+      completion minutes field
+  - sg_ses: expand --data (in) buffer to 2048 bytes
+  - sg_opcodes: add extended parameter data for TMFs (spc4r26)
+  - sg_dd: clean count calculation, document nocache flag
+    - treat bsg devices as implicit sg_io
+    - add more conversions
+  - sg_write_same: if READ CAPACITY(16) fails try 10 byte variant
+    - anticipate approval of proposal to allow UNMAP and ANCHOR
+      bits to be set on WRITE SAME(10) with '--10' option
+  - sg3_utils man page: sections added for OS device names
+
+Changelog for sg3_utils-1.29 [20100406] [svn: r334]
+  - sg_rtpg: new logical block dependent state and bit (spc4r23)
+  - sg_start: add '--readonly' option for ATA disks
+  - sg_lib: update asc/ascq list to spc4r23
+  - sg_inq: update version descriptor list to spc4r23
+  - sg_vpd: block device characteristics page: fix form factor
+    - update Extended Inquiry VPD page to spc4r23
+    - update Block Limits VPD page to sbc3r22
+    - update Thin Provisioning VPD page to sbc3r22
+    - Automation device serial number and Data transfer device
+      element VPD pages (ssc4r01)
+    - add Referrals VPD page (sbc3r22)
+  - sg_logs: add thin provisioning and solid state media log pages
+    - addition of IBM LTO specific log pages
+  - sg_modes: new page names from ssc4r01
+  - sg_ses: sync with ses3r02 (SAS-2.1 connector types)
+  - sg_unmap: add '--anchor' option (sbc3r22)
+  - sg_write_same: add '--anchor' option (sbc3r22)
+  - sg_pt interface: add set_scsi_pt_flags() to permit passing
+    through SCSI_PT_FLAGS_QUEUE_AT_TAIL and AT_HEAD flags
+  - add examples/sg_queue_tst+bsg_queue_tst for SG_FLAG_Q_AT_TAIL
+  - add AM_MAINTAINER_MODE to configure.ac to lessen build issues
+  - add BSD_LICENSE file to this and lib directories, refer to
+    it from source and header files. Some source has GPL license
+
+Changelog for sg3_utils-1.28 [20091002] [svn: r315]
+  - sg_unmap: new utility for thin provisioning
+    - add examples/sg_unmap_example.txt
+  - sg_get_lba_status: new utility for thin provisioning
+  - sg_read_block_limits: new utility for tape drives
+  - sg_logs: add cache memory statistics log (sub)page
+  - sg_vpd, sg_inq: extend Block limits VPD page (sbc3r19)
+  - sg_vpd: add Thin provisioning VPD page (sbc3r20) and
+            TapeAlert supported flags VPD page
+  - sg_inq: note VPD page support better in sg_vpd
+  - sg_persist: add transport specific transportID format
+    - allow transportIDs to be read from named file
+  - sg_opcodes: allow --opcode= option to take OP and SA
+    values (comma seperated)
+    - tweak print format, remove test code
+  - sg_requests: remove test code in progress calculation
+  - sg_reset: add target reset option
+  - sg_luns: reduce default maxlen to 8192 (for FreeBSD)
+  - sg_raw: extend max cdb length from 16 to 256 bytes
+    - align heap allocs to page boundaries
+  - sg_lib: sg_set_binary_mode() needs config.h included
+    - add progress indication sense data descriptor (0xa)
+    - change SG3_UTILS_* constants to SG_LIB_*
+    - decode service actions within persistent reserve in/out
+    - sync with spc4r21
+  - sg_cmds_extra: add sg_ll_unmap() and sg_ll_get_lba_status()
+  - sg_pt_linux: fix check condition but empty sense buffer; occurred
+    when sg v3 node used and /usr/include/linux/bsg.h visible
+    - major() macro grief, if present include <linux/kdev_t.h> and
+      use MAJOR() instead
+  - scripts/sas_disk_blink: moved from this package to sdparm
+  - utils/hxascdmp: in Windows set binary mode on read files
+  - examples/sg_persist_tst.sh: add PRIN read full status command
+  - sg_raw,sg_write_buffer,sg_write_long,sg_write_same: in Windows
+    set binary mode on read files
+  - sg_pt_win32: default to non-direct variant of SPT interface
+    - use './configure --enable-win32-spt-direct' to override
+    - non-direct data length set to 16 KB, extended if required
+  - debian: incorporate patch from debian sid
+
+Changelog for sg3_utils-1.27 [20090411] [svn: r250]
+  - sg_write_same: new utility: 10, 16 and 32 byte cdb variants
+  - sg_inq: sync version descriptors with spc4r18
+    - add power condition VPD page
+    - expand block limits VPD page (sbc3r18)
+  - sg_vpd: add power condition VPD page
+    - expand block limits VPD page (sbc3r18)
+  - sg_map26: fix for lk 2.6.26 when CONFIG_SYSFS_DEPRECATED_V2
+    is not defined
+    - output cdb when verbose option given
+    - correct tape minors >= 32
+  - sg_dd: flock flag (does LOCK_EX|LOCK_NB)
+    - switch open on input for sg device nodes: first open
+      read-write and if that fails try opening read-only
+    - experiment with of2=OFILE2; add conv=sparse
+    - use posix_fadvise() to defeat caching of normal+block files
+      when new 'nocache' flag given
+    - sg_dd copied to own package called ddpt
+  - sg_dd, sgm_dd, sgp_dd: accept 'count=-1' for calculate count,
+    accept '-V' for version string
+  - sg_get_config: add OSSC feature [mmc6r02]
+  - sg_modes: add ATA power condition mode page
+  - sg_logs: protocol specific (SAS) lpage sync to sas2r15
+    - power condition transitions lpage (added in spc4r18)
+    - extra parameters for start-stop cycle counter lpage
+  - sg_format: add '--fmtpinfo=' and '--pie=' options (sbc3r18)
+  - sg_readcap: more protection + thin provisioning (sbc3r18)
+    - add a '--16' option for 16 byte cdb version
+  - sg_persist: code clean up
+    - allow '--transport-id=' argument to use space as separator
+    - add '--alloc-length=' argument
+  - sg_scan: (win32) new format, scsi adapter scan optional
+  - sginfo: fix crash when 1024 sg device nodes (or more)
+  - sg_ses: allow '--data=' argument to use space as separator
+  - sg_senddiag: allow '--raw=' argument to use space as separator
+  - sg_reassign: allow '--address=' argument to use space as
+    separator
+  - sg_wr_mode: allow '--contents=' and '--mask=' arguments to
+    use space as separator
+  - sg3_utils.spec: correction to configure call
+  - sg_pt: add scsi_pt_open_device_flags() call
+    - add scsi_pt_version() and clear_scsi_pt_obj() calls
+    - clear os_err at start of do_scsi_pt()
+    - add linux bsg support via runtime detection
+  - sg_cmds: add sg_cmds_open_device_flags()
+  - sg_cmds_extra: sg_ll_format_unit: remove rto_req argument,
+    the expanded fmtpinfo argument subsumes it.
+  - clearer split between Linux and Windows only code and doc
+  - automake tools: change to what Ubuntu 8.10 provides
+    - Ubuntu 8.10 libtool problems -> Debian 4.0
+
+Changelog for sg3_utils-1.26 [20080625] [svn: r183]
+  - sg_sat_phy_event: new utility; copied from examples
+    directory and enhanced, rename original to sg__sat_phy_event
+  - sg_ses: sync with ses2r19b, many nomenclature changes
+  - sg_get_config: sync with mmc6r01
+    - allow Microcode upgrade and DVD read feature descriptors
+      to be 4 bytes long
+    - add '--raw' option
+  - sg_verify: add --vrprotect= option
+  - sg_vpd: add nominal form factor to block dev. char. VPD page
+    - add --maxlen= option to set allocation length in cdb
+  - sg_inq: add --maxlen= option that does same as --len=
+    - move version descriptors (spc4r15) to sg_inq_data.c file
+  - sg_inq+sg_vpd: logic for "NAA-3 Locally assigned" identifier
+    - update extended inquiry VPD page
+  - sg_modes: add --maxlen= option to specify allocation length
+  - sg_start: add '--noflush' and '--mod=PC_MOD' options (sbc3r14)
+  - sg_request: add a '--progress' option (similar to sg_turs)
+    - add --maxlen= option to set allocation length in cdb
+  - sg_luns: add --maxlen= option to specify allocation length
+  - sg_dd: improve MMC handling of 'illegal mode for this track'
+    read errors (with ILI and info field)
+  - sg_dd, sgm_dd, sgp_dd, sginfo, sg_rbuf, sg_read: replace
+    "%lld" and friends with PRI macros
+  - sg_opcodes: tmf name change in spc4r15 (async event)
+  - sg_turs: add more to man page about '--progress' indication
+  - sg_write_long: add examples section to man page
+  - '--raw' option: modify utilities that can send binary output
+    to call sg_set_binary_mode(). For MingGW port CR problem.
+  - sg_lib: update asc/ascq and command name strings to spc4r15
+    - split sg_lib into sg_lib_data.[hc] and sg_lib.[hc]
+    - split sg_cmds_extra into sg_cmds_extra and sg_cmds_mmc
+    - add osd2r03 service actions (all different from osd-r10)
+    - add sg_get_trans_proto_str()
+    - add sg_get_sense_filemark_eom_ili() function (MMC uses ILI)
+    - add sense key specific unit attention condition queue
+      overflow decoding (added in spc4r13)
+    - add sg_set_text_mode() and sg_set_binary_mode() functions
+      for non-Unix OSes
+  - sg_cmds_mmc: add sg_ll_set_streaming() function
+  - sg_cmds_extra: add vrprotect argument to sg_ll_verify10()
+    - add sg_ll_get_performance() and sg_ll_set_cd_speed()
+  - change 'long long' to int64_t and 'unsigned long long' to
+    uint64_t to stress that 64 bit integer wanted, not larger
+  - audit of dangerous 'u64 = uch[24] << 24' code, replace most
+    'unsigned long's
+  - multiple documentation corrections provided by Dan Horak
+  - win32/MinGW: define SG3_UTILS_MINGW when detected
+  - remove archive/pre_configure subdirectory
+  - move sg_io_linux.c into the lib subdirectory
+  - utils/hxascdmp: add hxascdmp(1) man page
+  - switch primary build to ubuntu environment, rename
+    library to libsgutils2 to avoid clash
+
+Changelog for sg3_utils-1.25 [20071016] [svn: r115]
+  - sg_stpg: new utility to Set Target Port Groups
+  - sg_safte: new utility to query SAF-TE processor (SES like)
+  - sg_sat_set_features: new utility (actually copied from examples
+    directory); renamed examples version to: sg__sat_set_features
+  - sg_read_buffer: restore (had fallen out of build scripts)
+  - sg_dd: add oflag=sparse to step over bs*bpt number of zeros;
+    - with oflag=sparse, write last bs*bpt segment at end or after
+      error so file length of OFILE is appropriate
+    - when coe>1 then SCSI READ LONG logic remembers extended block
+      length of first encountered error
+  - sg_dd, sgm_dd, sgp_dd: allow iflag=null and oflag=null both of
+    which do nothing (placeholders)
+  - sg_ses: sync with ses2r17 then r18
+  - sg_vpd, sg_inq: add block device characteristics VPD page
+  - sg_inq: add '--vpd' option (or '-e') for backward compatibility
+  - sg_vpd: decode protocol specific lu information page for SAS
+    - add more RDAC vendor VPD pages
+  - sg_logs: update background scan results log page, sbc3r11
+    - add generation code to protocol specific page for SAS SSP
+    - add media changer diagnostic data log page
+  - sg_raw: fix error message when do_scsi_pt() fails
+  - sg_lib: sync asc/ascq codes with spc4r11
+    - add sg_get_num_nomult()
+    - add TPROTO_* protocol identifier constants to sg_lib.h
+  - sg_cmds_extra: add sg_ll_set_tgt_prt_grp()
+  - place source in subversion repository
+  - split code into src/ lib/ and include/ directories
+  - sync debian directory with their 1.24 version (sid unstable)
+  - convert build logic to use autotools (i.e. './configure ; make')
+    - rename this file from CHANGELOG to ChangeLog
+    - note: only code in lib/ and src/ directories built by
+      autotools; some other subdirectories still use hand-crafted
+      Makefiles
+
+Changelog for sg3_utils-1.24 [20070507] [svn: r77]
+  - sg_raw: new utility to send arbirary SCSI commands
+  - sg_luns: increase number of luns that can be fetched
+    - fix length of raw and hex output
+    - add '--quiet' option to output only ASCII hex
+      representation of each lun
+  - sg_rtpg: update for changes in spc4r09
+  - sg_persist: update documentation, spc-4 references
+    - fix exit status values
+  - sg_inq: update version descriptors per spc4r09
+    - fix '--id' and '--extended'
+    - extend block limits VPD page (sbc3r09)
+  - sg_vpd: extend block limits VPD page (sbc3r09)
+    - append relative target port identifier to SAS target
+      port address with '-iq' option
+  - sg_logs: add decoding for stats+performance log pages
+    - fix showing of page names for pdt > 0
+    - implement '-HH' for single and all pages, fix '-r'
+    - when '--maxlen=' given, only do single fetch
+    - add Tape Alert (ssc), Media and Element statistics (smc) pages
+    - add '--brief' option
+  - sg_ses: sync with ses2r16
+    - fix bay number for SAS
+  - sg_format: add '--dcrt' and '--security' options
+  - sgm_dd: add 'smmap' oflag for shared_mmap_io testing
+    - add 'dio' oflag
+  - sg_dd, sgp_dd: add 'dio' iflag and oflag
+  - sg_modes: change SAS mode page names per sas2r09
+    - check validity of block descriptors length
+  - sg_pt: change opaque context object from 'void *'
+    to 'struct sg_pt_base *'
+  - sg_opcodes: anticipate extra tmfs from 07-159r0
+  - sg_sat_set_features: add more usage information
+    - add man page
+  - sg_sat_phy_event: add to examples directory
+  - sg_lib: sync asc/ascq codes with spc4r10
+  - Solaris port: using uscsi interface
+  - various .html files removed from doc directory
+
+Changelog for sg3_utils-1.23 [20070131] [svn: 75]
+  - sg_read_buffer: new utility
+  - sg_write_buffer: new utility
+  - sg_opcodes, sg_senddiag, sg_logs, sg_modes, sg_start, sg_inq,
+    sg_turs, sg_readcap, sg_rbuf: add getopt_long() based cli;
+    old and new cli selectable, new getopt_long cli is default
+  - scripts: new subdirectory containing some bash scripts
+    - add scripts/README file
+  - sg_reassign: add '--hex' option for grown and primary lists
+  - sg_rtpg: add '--raw' option
+  - sg_lib.h, sg_cmds_basic.h + sg_cmds_extra.h: add C++
+    'extern "C" ' wrappers
+    - cleanup C code so it will compile as C++
+  - sg_lib: sync with spc4r08
+    - include <inttypes.h>, use PRId64 instead of %lld form
+    - fix sg_get_sense_str() when empty sense buffer
+  - win32 port: add Makefile.mingw + related support for MinGW
+  - sg_cmds_extra: add sg_ll_read_buffer() and sg_ll_write_buffer()
+  - sg_dd, sgp_dd, sgm_dd, sg_read: use lseek64() instead of llseek.c
+  - sgm_dd: accept coe=<n> for interworking with sg_dd
+  - sg_rdac: fix on non-linux ports
+  - sg_ses: fix spurious warning in additional element status page
+    - '-rr' option outputs a diagnostic page in binary to stdout
+  - sg_opcodes: add command timeout descriptor support (spc4r08)
+    - change linux specific pass through to generic pass through
+  - sg_logs: add 'name=value' decoding for SAS specific lpage
+  - examples+utils subdirectories: remove symlinks
+  - synchronize man pages with usage messages
+  - sg3_utils.spec: rework
+
+Changelog for sg3_utils-1.22 [20061016] [svn: 72]
+  - sgp_dd: accept verbose=<n> as well as deb=<n> to ease
+    interworking with sg_dd and sgm_dd
+  - sg_sat_set_features: added to examples directory
+  - sg_lib: sync asc/ascq text with spc4r06
+    - move SG_LIB_CAT_NO_SENSE and SG_LIB_CAT_RECOVERED to
+      20 and 21 respectively; add SG_LIB_CAT_ABORTED_COMMAND
+      at 11 (its sense key value)
+  - sg_vpd: tweak '--page=sp --quiet' output
+    - change '-HHH' so same as '-rr' (prepares ATA Information
+      (ai) response for hdparm)
+  - sg_requests: add '-s' option to set exit status from
+    parameter data
+  - sg_modes: exit quickly from '-e' if device not ready
+  - sg_logs: sync sas log pages with sas2r05a
+    - expand background scan results log page
+    - add '-m=<max_len>' to limit response length
+    - drop '-scum' and '-sthr' options and add '-select'
+  - sg_write_long: add '--16' option to send 16 byte cdb
+    - add '--wr_uncor' and '--pblock' options
+  - sg_senddiag: cleanup and add sdiag_sas_p1_stop.txt
+    to examples directory
+  - sg_format: add '--cmplst=<n>' option (default: 1)
+    - add '--pfu=<n>' option
+    - expand man page to discuss P/D/C/GLISTs
+  - sg_reassign: add '--primary' option to fetch primary
+    defect list (PLIST) length
+  - sg_readcap: add '-H' option to output response in hex
+    and '-r' to output response in binary to stdout
+    - add logical blocks per physical block (sbc3r07)
+  - sginfo: add PLIST and GLIST designation to defect lists
+  - sg_cmds: split this support file into sg_cmds_basic.[hc]
+    and sg_cmds_extra.[hc]
+    - add sg_ll_ata_pt() (SATL ATA pass) to sg_cmds_extra.[hc]
+  - sg_rdac: fix includes for FreeBSD
+  - sg_dd: add 'coe_limit=' option to exit after <n>
+    consecutive 'coe' type read errors
+  - sgm_dd: print out throughput information when signal arrives
+    if time=1 (like sg_dd does already)
+  - sg_inq: change '-HHH' so same as '-rr'. Now sg_inq, sg_vpd
+    and sdparm output for hdparm with '-HHH'
+    -add '-l=<resp_len>' option
+  - sg_read_long: add '--pblock' option for physical blocks
+  - sg_luns: add '--hex' and '--raw' options
+  - sg_requests: add '--hex' and '--raw' options
+  - sg_scan: windows version added (was previously linux only)
+    - 2 man pages: sg_scan.8l and sg_scan.8w that are installed
+      as sg_scan.8
+  - archive directory: removed all but rescan-scsi-bus.sh
+      - README points to previous version in that directory
+  - sg_sat_identify: add to main directory
+      - rename earlier version to examples/sg__sat_identify.c
+  - sg_ident: rework as spc4r07 changed command names and
+    expanded functionality
+
+Changelog for sg3_utils-1.21 [20060706] [svn: 70]
+  - sg_vpd: new utility for decoding VPD pages. sg_inq's cli is
+    cluttered; also borrows from sdparm's VPD handling
+  - sg_rdac: new utility for vendor specific work
+  - sg_lib: add sg_vpd_dev_id_iter() to iterate over di VPD page
+    - add sg_ata_get_chars() to fetch chars from ATA words
+    - sync additional sense code strings with spc4r05a
+    - add SG_LIB_CAT_NOT_READY category when sense_key is NOT READY
+    - add SG_LIB_FILE_ERROR category for open problems
+    - add SG_LIB_SYNTAX_ERROR category for command line problems
+    - broaden SG_LIB_CAT_MEDIA_CHANGED to SG_LIB_CAT_UNIT_ATTENTION
+    - add SG_LIB_CAT_MALFORMED for bad responses
+    - BEWARE: these changes cause confusion if an executable from this
+      version is run with a libsgutils library from 1.20 or earlier
+  - sg_cmds: add SG_LIB_CAT_NOT_READY return to most "ll" functions
+    - alter many utilities to report SG_LIB_CAT_NOT_READY
+  - sg_dd: add retries=<n> option for sg_io
+  - sg_logs: add '-T' option to output protocol specific port log page
+    - add support for log subpages (new in spc4r05)
+    - more sanity checks in Start Stop Cycle Counter page
+  - sg_cmds: add sg_ll_read_long16()
+    - add page_code and subpage_code to sg_ll_log_select()
+    - add subpage_code to sg_ll_log_sense()
+  - sg_read_long: do READ LONG(16) when '--16' given
+  - sg_read: accept and ignore 'of=' arguments
+  - sg_dd: expand medium/hardware error "coe' processing to include
+    the "blank check" sense key (for optical devices)
+  - sg_ses: expand display element (per 05-011r2)
+  - sg_format: clear 'cmplst' bit (for MO disks)
+    - add '--six' ('-6') option for mode sense/select(6)
+  - sg_format + sg_test_rwbuf: fix for when char is unsigned
+  - sg_inq: VPD page 0x89: output ATA IDENTIFY DEVICE strings
+    - for IDENTIFY (PACKET) DEVICE response use sg_ata_get_chars()
+  - sg3_utils.html : new name, was previously u_index.html. Copy
+    placed in doc subdirectory
+  - tools.html : SCSI and storage tools reference, copy placed in
+    doc subdirectory
+  - sg3_utils.8 : add a new man page containing general information
+    especially common exit status values
+  - sg_sat_identify: added to examples directory (SAT passthrough test)
+    - extend to pass through IDENTIFY PACKET DEVICE with '-p' option
+  - sg_sat_chk_power: added to examples directory
+  - sg_sat_smart_rd_data: added to examples directory
+  - sg_chk_asc: added to utils directory to check asc_ascq codes
+  - debian: stop placing archive directory under examples
+    - add build_debian.sh script
+
+Changelog for sg3_utils-1.20 [20060418] [svn: 68]
+  - sg_logs: decode phy event descriptors in SAS port specific
+    log page (sas2r03)
+    - new parameter control byte format (spc4r03), subpages to come
+  - update Makefile (linux) to install sg_io_linux.h + sg_linux_inc.h
+  - sg_map26: fix for block device mapping in lk 2.6.16-rc1 and beyond
+    - cope with sysfs removal of 'generic' symlink post lk 2.6.16,
+      anticipate removal of 'tape' symlink
+  - sg_dd, sgm_dd, sgp_dd: fix problem around 0x7fffffff blocks
+  - sg_dd: fix read_long processing error (when 'coe=2' or 3)
+    - expand 'coe=' to take 0...3 (invokes read long with 2 or 3)
+    - allow for SG_GET_RESERVED_SIZE yielding 0, lk 2.6.16 feature
+  - sgp_dd: add 'iflag=' and 'oflag=' arguments; signals (like sg_dd)
+  - sgm_dd: add 'iflag=' and 'oflag=' arguments; signals (like sg_dd)
+  - sg_get_config: double->dual renaming (mmc5r03)
+  - sg_read: add 'dpo=' and 'fua=' options
+    - allow 'count' < 0 (or 'bpt=0') for issuing zero block READs
+    - allow for SG_GET_RESERVED_SIZE yielding 0, lk 2.6.16 feature
+    - add 'no_dxfer=0|1' option
+  - sg_modes: fix exit value when MODE SENSE fails
+    - add '-e' to examine presence of page codes from 0x0 to 0x3e
+  - sg_requests: add '--num=' and '--time' options for timing multiple
+    invocations
+  - sg_inq: fix vpd 0x83 designator code 8 name: "scsi name string"
+  - sg_scan: if lk 2.6, use sysfs to find active sg device nodes
+  - sg_map: if lk 2.6, use sysfs to find active sg device nodes
+  - sg_ses: expand display element (per 05-011r1)
+  - sg_start: add an '-i' option which is equivalent to '--imm=1'
+  - sg_senddiag: update man page showing use of two scripts in
+    examples directory (sdiag_sas_p0_cjtpat.txt and _p1_)
+  - sg_lib: fix sg_get_sense_descriptors_str() case 9 (ATA Return)
+
+Changelog for sg3_utils-1.19 [20060127] [svn: 66]
+  - sg_start: accept '--' options (e.g. 'sg_start --stop')
+    - add '--fl=<n>' option for jump to format layer (mmc5)
+  - sg_logs: background scan log page, resync with sbc3r03
+    - add '-scum' and '-sthr' for setting defaults
+    - add device statistics log page (ssc + adc)
+    - fix "last n" deferred errors/error events incrementing
+    - partial addition of log subpages (spc4r03)
+  - sg_get_config: sync features with mmc5 rev 02b
+  - sg_wr_mode: mask out dpofua bit in mode select header
+  - sg_inq: try harder with '-A' to identify ATA device
+    - broaden meaning of '-d' option to decode ...
+    - decode software interface id VPD page ('-p=84 -d')
+    - decode device capabilities (ssc) VPD page ('-p=b0 -d')
+  - sginfo: correct defect list handling ('-d' and '-G')
+  - sg_verify: improve error processing (e.g. medium errors)
+  - sg_ses: scsi target_initiator port additional element
+     status (ses2r14)
+  - many: arguments that currently accept '0x' or '0X' to
+     indicate a hex number may alternatively take a trailing
+     'h' or 'H' to indicate hex
+  - sg_lib: update asc/ascq strings (spc4r03)
+  - sg_lib+sg_cmds: make independent of linux via
+    sg_pt.h function based interface.
+    - linux pass through code placed in sg_pt_linux.c
+    - rename sg_include.h to sg_linux_inc.h
+    - linux specific code in sg_lib.[hc] moved to
+      sg_io_linux.[hc]
+  - port to FreeBSD: using sg_pt_freebsd.c
+  - port to Tru64: using sg_pt_osf1.c
+  - sg_cmds: add sg_ll_get_config(), sg_ll_format_unit(),
+     sg_ll_reassign_blocks(), sg_ll_persistent_reserve_in+out(),
+     sg_ll_read_long10(), sg_ll_verify10(), sg_ll_write_long10()
+  - sg_persist: add "allow commands" to report capabilities
+  - sg_persist_tst: (examples) takes device node as argument
+  - sg_luns: add "security protocol" wlun
+
+Changelog for sg3_utils-1.18 [20051118] [svn:63]
+  - sg_map26: new utility to map sg devices in lk 2.6
+  - sg_luns: luns > 16,384 (sam-4 rev 4)
+  - sg_ses: bump fan speed field to 11 bits
+    - SAS connector names (ses2r13)
+  - sg_inq: add '-rr' option for "hdparm --Istdin"
+  - sg_get_config: tracking mmc-5
+  - sg_write_long: add support for COR_DIS bit
+  - sg_cmds: add sg_ll_test_unit_ready_progress()
+  - sg_turs: '-p' option shows progress
+  - sg_dd: add 'iflag=' and 'oflag=' options
+    - remove output of mode page info when verbose > 0
+    - add control of DPO bit via iflag/oflag
+  - sg_lib: add sg_get_pdt_str()
+    - update asc/ascq strings
+  - sg_modes + sginfo: add SAS(2) SSP shared mode subpage
+  - doc: rename "html" directory to "doc"
+  - Makefile: add 'libtool --finish' to install
+
+Changelog for sg3_utils-1.17 [20050922] [svn: 60]
+  - sg_inq: add '-a' option for ATA information VPD page
+    - add '-b' option for Block limits VPD page (SBC)
+    - add '-A' option for probing ATA or ATAPI device
+    - increase raw ('-r') and verbose ('-v') output for
+      ATA(PI) devices to 512 bytes (was 256 bytes)
+    - output hex ('-H') and verbose response for ATA(PI)
+      devices in 16 bit words (corrected for endianness)
+      - output bytes if '-HH' option given
+    - sync with spc4 rev 02
+  - sg_lib: add dWordHex() and sg_is_big_endian()
+    - sync asc/ascq with spc4 rev 02
+  - sg_cmds: defensive prefill for inquiry commands
+  - sg_opcodes: sync with spc4 rev 02 (add tmf I_T nexus reset)
+  - sginfo: add EBACKERR in Informational exception mode page
+    - add Background control mode page (SBC-3)
+  - sgm_dd: add 'verbose=<n>' option
+
+Changelog for sg3_utils-1.16 [20050810] [svn: 58]
+  - sg_ident: new utility to report+set device identifier
+  - sg_map: increase MAX_SG_DEVS from 256 to 2048
+  - debian: new directory to support deb package builds
+  - sg_get_config: add '--current' option, same as '--rt=1'
+    - update for DVD+RW Dual Layer
+  - sg_inq: add notes in source about use of SCSI INQUIRY
+     - decode Management network addresses VPD page ('-m')
+     - decode Mode page policy VPD page ('-M')
+  - sginfo: increase device mapping capability (> 78 disks)
+     - add '-r' option to scan /dev/raw* device nodes [Tim Hunt]
+  - sg_dd: change bpt default value to 32 when bs >= 2048 bytes
+  - sg_ses: mention SAF-TE in man page
+  - sg_readcap: add '-b' option for brief output (2 hex numbers)
+  - sg_cmds: add sg_ll_start_stop_unit(), sg_ll_prevent_allow(),
+      sg_ll_report_dev_id() and sg_ll_set_dev_id()
+  - sg_lib: add extra argument to sense print functions to enable
+      the suppression of the raw output of the sense buffer
+      - resid > 0 warnings now includes number actually fetched
+  - sg_start: add '-load' and '-eject' options
+    - default to start action when no other indication given
+    - change -imm=0|1 option default to 0 (was 1)
+  - gcc 4.0: cleanup warnings (apart from sgp_dd: revisit later)
+
+Changelog for sg3_utils-1.15 [20050605] [svn: 56]
+  - sg_cmds: sg_get_mode_page_controls(): improve error processing,
+     add double fetch
+  - sg_turs, sg_rbuf, sg_requests, sg_test_rwbuf, sg_format,
+    sg_dd and sgm_dd: add O_NONBLOCK to open()
+  - sgm_dd: switch to use SG_IO ioctl (that leaves only
+     sgp_dd using the asynchronous sg write()/read() sequence)
+  - sg_ses: sync with rev 12 changes
+  - sg_map: extend to cope with sparse disk device names with
+     up to 3 letters (e.g. /dev/sdaaa) [Nate Dailey]
+  - sg_modes: add '-f' option to flexibly decode broken mode
+     sense responses.
+     - zero prefill response; stop decoding response after 3
+       unit attention mode pages seen (i.e. malformed)
+     - add '-L' option for LLBAA bit in msense 10 cdb
+  - sg_reset: update man page
+  - sg_inq: VPD page 0x83: output eui addresses in hex as well
+  - Makefile: fix bug in rules for sgp_dd (when 'make dep' used)
+  - sg_format: expand explanations in its man page
+  - sg_inq, sg_logs, sg_modes, sg_opcodes, sg_rbuf, sg_readcap,
+    sg_scan, sg_senddiag, sg_start and sg_turs: allow command
+    line to take concaternated options
+  - sg_start: add -start and -stop to parallel "1" and "0"
+  - sg_senddiag: set pf bit with '-l' option
+
+Changelog for sg3_utils-1.14 [20050506] [svn: 54]
+  - sg_rmsn: new utility to read media serial number
+  - sg_rtpg: add T_SUP bit report
+  - sg_ses: ses-2 rev 11 changes (mainly to additional element status)
+        - add 'bay number' to SAS additional element status
+  - sg_modes: recognise attached enclosure and medium changer
+  - sg_inq: spell out non-zero peripheral qualifiers
+        - note VS bit preceding MultiP(ort) when latter set
+        - VPD page 0x83: output naa addresses in hex as well
+  - sginfo: recognise attached enclosure and medium changer
+        - increase device mapping capability (to 78 disks)
+          [Tim Hunt]
+  - sg_senddiag: add option to send raw diagnostic page
+  - sg_get_config: update some BD information
+  - sg_reasign: add '-g' option to give grown defect list length
+  - sg_dd: note default bpt value (128) may be too high for cd/dvds
+  - sg_lib: sync with SPC-3 rev 22a [opcodes + asc/q]
+        - add DID_IMM_RETRY and DID_REQUEUE [linux specific "host" bytes]
+  - sg_cmds: add send+receive diagnostic, read defect data commands
+        - add duration output on some commands when verbose > 2
+  - spec: change to produce libsgutils (and -devel variant) as well as
+          sg3_utils binary rpms
+  - sdparm: new utility like hdparm but for SCSI disks (or other devices)
+        - moved to its own package called: sdparm
+
+Changelog for sg3_utils-1.13 [20050313] [svn: 52]
+  - sg_format: new utility to format disks (perhaps change block size)
+  - sg_ses: rename "device element" to "additional element" [SES-2 rev 10]
+        - add SAS expander and connector elements; add download
+          microcode and subenclosure nickname diagnostic pages
+        - fix additional element descriptor for SAS
+        - off by 1 error when no type descriptor text in config page
+          <David dot Baldwin at anu dot edu dot au>
+  - sg_logs: log page for background media scan results
+  - sginfo: add "-flba64" option for outputting 64 bit lba defect lists
+  - sg_get_config: additions for BD from MMC-5 rev 1b
+  - sg_lib: add SG_LIB_CAT_ILLEGAL_REQ sense category
+        - add sg_get_sense_progress_fld()
+        - SPC-3 rev21d updates: report + set timestamp
+        - sg_get_num() + sg_get_llnum(): switch to multipliers that
+          are compatible with SI and with IEC 60027-2. Used modern
+          GNU's dd command as guide.
+        - report field replaceable unit code in fixed format
+  - sg_dd: add logic to use read_long on unrecovered read errors when
+           'coe' set, read just prior to error if 'coe' clear
+        - both 'odir' and 'blk_sgio" are honoured on block devices
+        - add 'verbose' switch, output some mode page info when verbose
+        - print out elapsed time/throughput when signal received
+        - add new web page discussing sg_dd, copy in html subdirectory
+  - sg_read: add 'blk_sgio' and 'odir' options
+  - sg_wr_mode: clear mode data length in mode select(10)
+  - sg_test_rwbuf: add long options, allow test to run multiple times
+  - sg_cmds: add sg_get_mode_page_types() [get current, changeable, etc]
+  - llseek.c: add Makefile rule without "-std=c99", breaks on some archs
+
+Changelog for sg3_utils-1.12 [20050121] [svn: 50]
+  - sg_wr_mode: new utility to modify (i.e. write to) mode pages
+  - sg_reassign: new utility: issues Reassign Blocks command
+  - sg_rtpg: new utility: issues Report Target Port Groups command
+             [Christophe Varoqui]
+  - ATA IDENTIFY command misspelt as "IDENTITY" in several places
+  - sginfo: tweak SAS mode pages to match sas 1.1 rev 07
+        - add NV_DIS bit to disk caching mode page
+  - sg_map: open /dev/nst* rather than /dev/st* (to stop spurious rewinds)
+  - sg_lib: ATA return sense descriptor
+        - add sg_get_sense_info_fld() to fetch info field from sense data
+        - fix bug in sg_scsi_sense_desc_find()
+        - add sense key specific decoding for fixed format sense data
+  - sg_modes: extend '-p' option to allow '-p=<page_code>,<subpage_code>'
+        - add '-A' option to output all mode pages and subpages
+        - extend '-l' option to show subpages, selected command set pages
+  - sg_inq: fix LUN WWN output in unit path report VPD page (emc)
+            [Hergen Lange]
+  - sg_get_config: some additions for DVD-R dual layer
+  - sg_modes: show write protect (WP) and DpoFua flags for disks
+  - sg_cmds: add llbaa argument to sg_ll_mode_sense10()
+
+Changelog for sg3_utils-1.11 [20041126] [svn: 48]
+  - sg_sync: new utility: invokes the synchronize cache command
+  - sg_prevent: new utility: invokes the prevent allow medium removal command
+  - sg_get_config: new utility: get configuration command for dvds and cds
+  - sg_request: fix, allocation length wasn't set
+  - sg_start: remove '-s', as start_stop_unit implicitly syncs caches
+  - sg_ses: add SAS expander element type
+  - sg_inq: add sanity check to unit serial number (VPD page 0x80)
+        - output ANSI version string (e.g. "SPC-2", previously was number)
+        - add '-s' option to decode SCSI Ports VPD page
+  - sg_logs: decoding of format status and non-volatile cache log
+        pages (0x8 and 0x17 in sbc-2)
+  - sg_dd: handle compile error when O_DIRECT not defined
+  - sginfo: tighten sanity checks around Unit serial number VPD page
+
+Changelog for sg3_utils-1.10 [20041030] [svn: 46]
+  - sg_readcap, sg_dd, sgm_dd, sgp_dd: fix sg_ll_readcap_10+16 (sg_cmds.c)
+  - sg_luns: new utility to report luns
+  - sg_logs: with '-t' (show temperature) ignore extra parameters in
+        temperature log page (still show them with '-p=d')
+  - sg_ses: clean argument sanity checks
+  - sg_cmds: add more common command wrappers
+
+Changelog for sg3_utils-1.09 [20041022] [svn: 44]
+  - sg_ses: new utility to get status and set control on SES devices
+  - sg_verify: new utility to verify block devices
+  - sg_emc_trespass: new utility for EMC specific trespass mode page
+  - sg_request: new utility that sends a REQUEST SENSE command
+  - sg_logs: '-r' to reset to manufacturer's defaults
+      - decode last n error events and last n deferred errors pages
+      - add names of ADC log pages
+  - sg_inq: update to SPC-3 rev 21
+      - decode Extended INQUIRY data VPD page [0x86] {'-x'}
+      - decode Unit Path Report VPD page [0xc0] (EMC) {'-P'}
+  - sginfo: decode SAS protocol specific lu mode page
+  - sg_err: convert to sg_lib + update to SPC-3 rev 21
+      - change GPL to FreeBSD license
+      - flag vendor specific asc/ascq ranges
+  - libsgutils: library made from sg_lib.c and sg_cmds.c
+    - rpm "spec" file additionally builds a "-devel" rpm with
+      static libsgutils.a and sg_lib.h and sg_cmds.h
+  - utils/hxascdmp.c: add FreeBSD license
+  - sg_persist: additions to man page
+      - add sg_persist_tst.sh example script to examples directory
+  - sg_turs: add '-v' and '-V' options
+  - sg_senddiag: add '-v' option
+
+Changelog for sg3_utils-1.08 [20040831] [svn: 42]
+  - sg_inq: fix noisy message when EVPD and raw modes set
+      - for VPD page 0, list supported page names if known {'-e'}
+      - add '-d' option to list version descriptors
+  - sg_opcodes: numerically sort list of opcodes unless '-u' given
+      - add '-a' to sort alphabetically list of opcode names
+      - add '-t' to report supported task management functions
+  - sg_persist: add 'register and move" PROUT service action
+      - add transportId support, document in sg_persist.8 and example
+  - sg_modes: handle subpage code for known pages (e.g. control extension)
+      - clean up sense buffer handling (allow for descriptor format)
+      - SPC-3 draft revision 20a updates
+  - sg_write_long: new utility to exercise WRITE LONG command
+  - sg_read_long: new utility to exercise READ LONG command
+  - sg_err.c: fix compile errors when SG_KERNEL_INCLUDES defined in lk 2.6
+      - sg_includes.h typedef of u64 for BLKGETSIZE64 ioctl in lk 2.4
+      - add safe_strerror(), sg_scsi_normalize_sense(), sg_normalize_sense()
+        and sg_scsi_sense_desc_find() functions
+      - add more sense data descriptor format decoding
+      - move multiple implementations of dStrHex() into sg_err.c
+  - sg_logs: exit if SCSI INQUIRY fails (e.g. when applied to ATA disk)
+  - sginfo: bug fixes and SPC-3 revision 20a updates
+      - add '-E' option to access Control Extension mode (sub)page
+  - sg_start: change '-d' switch to '-v' (verbose) switch for consistency
+      - document extra power condition states in man page
+  - sg_readcap: output rto_en and prot_en bits from long read capacity data
+  - add COVERAGE file to list SCSI command coverage
+
+Changelog for sg3_utils-1.07 [20040708] [svn: 40]
+  - sginfo: clean up inquiry vendor,product,revision strings
+      - '-Fhead': sort defect list by head
+        Tom Steudten <steudten at mx dot ch>
+  - rework sg_err for better command name coverage: service actions,
+    variable length and peripheral device type
+      - update asc,ascq codes to SPC-3 revision 19
+  - move scsi_devfs_scan to archive directory
+  - add sg_opcodes utility to list supported operation codes
+  - add sg_persist utility to support persistent reservations
+  - add '-i' option to sg_inq to decode device identification VPD page (0x83)
+  - sg_inq tries an ATA IDENTIFY if SCSI INQUIRY fails
+  - sg_dd, sgm_dd and sgp_dd calculate block device sizes (if count not given)
+  - drop SG_GET_VERSION_NUM ioctl guard in most utilities
+
+Changelog for sg3_utils-1.06 [20040426] [svn: 37]
+  - sg_logs: some HBAs don't like odd transfer lengths so increment
+      - do INQUIRY and output product strings
+      - add ASCII rendering of the Protocol specific SAS page
+      - add '-v' verbose option to output cdb
+  - sg_scan: optionally take device file names (e.g. /dev/hdc and /dev/sda)
+      - only request 36 byte INQUIRY responses
+  - sg_err: add sg_decode_sense() function
+  - sg_inq: update output (ref: SPC-3 t10/1416-d rev 17, 28 January 2004)
+      - remove '-p' option to print out PCI address of host
+      - add '-v' verbose option to output cdb
+  - sginfo: allow '-u' to take hex arguments (prefixed by '0x'),
+            when subpage value is 255 show multiple subpages
+      - accept /dev/hd? ATAPI devices directly in lk 2.6
+      - add '-t <pn>[,<spn>]' argument; like '-u' but decodes page
+        if it recognizes it
+      - drop '-L' argument
+      - add cd/dvd, tape, SES, more disk and more SPC-3 decoded mode pages
+      - add transport protocol decoded mode pages for SPI-4, FCP and SAS
+  - sg_modes: print all subpages when '-subp=ff' is selected
+      - do INQUIRY and output product strings
+      - add '-v' verbose option to output cdb
+  - Makefile: add -W compile flag and fix exposed warnings
+  - .spec file: change to build on Mandrake without errors
+
+Changelog for sg3_utils-1.05 [20031112] [svn: 35]
+  - sginfo: major rework; add IE page, clean up control, cache +
+        disconnect pages (as per SPC-3 and SBC-2).
+      - when storing, update saved page (change from previous version)
+      - use 10 byte mode sense and select by default (override with '-6')
+      - mode subpage support
+  - sg_dd, sgm_dd + sgp_dd:
+      - 64 bit capable (read capacity; count, skip and seek values).
+      - numerical arguments accept hex (prefixed by '0x' or '0X')
+      - require bpt > 0
+      - fix problem when reading /dev/null
+  - sg_dd: Treat SIGUSR1 properly: print stats and continue;
+  - sgp_dd: reduce READ CAPACITY response size to 8 bytes
+  - sg_read: require bpt > 0
+  - sg_test_rwbuf: switch from sg_header to sg_io_hdr interface
+    N.B. After these changes no sg3_utils utilities (in the main directory)
+    use the sg_header interface
+  - sg_scan: switch from sg_header to sg_io_hdr interface
+  - sg_senddiag: increase extended foreground timeout to 60 minutes
+  - sg_inq: add names of peripheral device types
+  - sg_readcap: show total size in bytes, MB, GB
+  - sg_logs: read log pages twice (first time to get response length), for
+    fragile HBAs; decode Seagate 0x37 + 0x3e pages; display pcbs
+  - sg_modes: fix core dump when corrupted response, don't print extra pages
+  - sg_map: increase sg device scanning from 128 to 256
+  - change man page references from lk 2.5 to lk 2.6
+  - examples/sg_iovec_tst: added testing sg_iovec (sg_io_hdr iovec's)
+    [retrospective addition to this log: "#define __user" added into
+     sg_include.h so user space programs aren't broken if they choose
+     to include kernel header.]
+  - utils/hxascdmp: add utility for displaying ASCII hex
+
+Changelog for sg3_utils-1.04 [20030513] [svn: 33]
+  - all remaining utilities in the main directory have man pages [thanks
+    to Eric Schwartz <emschwar at debian dot org> for 7 man pages]
+  - add CREDITS file
+  - sg_simple1, sg_simple2, sg_simple3, sg_simple4, sg_simple16 and
+    scsi_inquiry: moved to the examples directory
+  - sg_debug: moved to the archive directory
+  - sg_modes: add '-subp=<n>' for sub page code, suggests 6/10 byte
+    alternative if bad opcode sense received, flip -cpf flag to -pf,
+    add page names for most peripheral types
+  - sg_turs: default '-n=' argument to 1, only when '-n=1' print error
+    message in full
+  - sg_logs: print temperature "<not available>" for 255, '-t' switch
+    for temperature (from either temperature or IE log page)
+  - sg_dd: add '-odir=0|1' switch for O_DIRECT on block devices
+  - sg_start: add '-imm', '-loej' and 'pc=<n>' switches plus man page
+  - sg_readcap: add '-pmi' and 'lba=<n>' switches
+  - open files O_NONBLOCK in sg_inq, sg_modes and sg_logs so they
+    can be used on cd/dvd drivers when there is no disk present
+
+Changelog for sg3_utils-1.03 [20030402] [svn: 30]
+  - sg_senddiag: added, allows self tests and listing of diag pages
+  - sg_start: changed to use SG_IO so works on block devices
+  - sg_err: print out some "sense key specific" data [Trent Piepho]
+  - sg_modes: add "-6" switch for force 6 byte MODE SENSE [Trent Piepho]
+  - sg_modes: more information on page codes and controls
+  - sg_inq, sg_modes, sg_logs, sg_senddiag: add man pages
+  - sg_dd: add "append=0|1" switch for glueing together large files
+  - note in README about utilities offered by scsirastools package
+
+Changelog for sg3_utils-1.02 [20030101] [svn: 28]
+  - sg_inq: check if cmddt or evpd bits ignored
+  - sg_inq: warn -o=<n> not used for standard INQUIRY
+  - sg_turs: add -t option to time Test Unit Ready commands
+  - sg_errs: (used by most utilities) warn if sense buffer empty
+  - sg_modes: make safe with block SG_IO (bypass SG_GET_SCSI_ID ioctl)
+  - sg_logs: make safe with block SG_IO, self-test page work
+  - sg_dd: add "blksg_io=" switch to experiment with block SG_IO
+  - sg_read: now use SG_IO ioctl rather than sg write/read
+  - sginfo: fix writing parameters, check for block devices that answer
+    sg's ioctls, recognize "scd<n>" device names
+  - sg_map: stop close error report on tape devices
+  - sg_readcap: make safe with block SG_IO
+  - sg_start: make safe with block SG_IO
+  - sg_test_rwbuf: make safe with block SG_IO
+
+Changelog for sg3_utils-1.01 [20020814] [svn: 27]
+----------------------------
+  - add raw switch ("-r") to sg_inq [Martin Schwenke]
+
+Changelog for sg3_utils-1.00 [20020728] [svn: 26]
+----------------------------
+  - update sg_err [to SPC-3 T10/1416-D Rev 07 3 May 2002]
+  - "sg_inq -cl" now outputs opcode names
+  - add "continue on error" option to sg_dd
+  - add _LARGEFILE64_SOURCE _FILE_OFFSET_BITS=64 defines to Makefile
+  - drop 'gen' argument from sg_dd and friends, allow any file types
+    except scsi tape device file names
+  - treat of=/dev/null as special (skip write). Accept of=. as alias
+    for of=/dev/null
+  - decode various log pages in sg_logs
+  - add 'dio' argument to sgm_dd for testing "zero copy" copies
+
+Changelog for sg3_utils-0.99 [20020317]
+----------------------------
+  - add 'fua' and 'sync' arguments to sg_dd, sgp_dd and sgm_dd
+  - improve sg_inq, add "-cl" and "-36" arguments
+  - add sg_modes + sg_logs for MODE SENSE and LOG SENSE queries
+  - add rescan-scsi-bus.sh [Kurt Garloff] to archive directory
+
+Changelog for sg3_utils-0.98 [20020216]
+----------------------------
+  - move sg_reset back from archive to main directory + build
+  - sprintf() to snprintf() changes
+  - add "time=<n>" argument to sg_dd, sgp_dd and sgm_dd to time transfer
+  - add man pages for sgm_dd and sg_read, update sg_dd and sgp_dd man pages
+  - add "cdbsz=" argument to sg_dd, sgp_dd, sgm_dd + sg_read
+  - add "gen=0|1" argument to sg_dd
+
+Changelog for sg3_utils-0.97 [20011223]
+----------------------------
+  - move isosize to archive since introduced into util-linux-2.10s
+
+Changelog for sg3_utils-0.96 [20011221]
+----------------------------
+  - add '-p' switch to sg_inq to provide PCI slot_name
+  - add '-n' switch to scsi_inquiry for non-blocking open
+  - new sgm_dd (dd variant) using mmap-ed IO
+  - sg_rbuf now has a '-m' argument to select mmap-ed IO
+  - sg_rbuf now has a '-t' switch to do timing + throughput calculation
+  - add sg_simple4 to demonstrate mmap-ed IO on an INQUIRY response
+  - add sg_simple16 to do a READ_16 (16 byte SCSI READ command)
+  - mmap-ed IO requires sg version 3.1.22 or later
+  - add sg_read to read multiple times starting at same offset
+
+Changelog for sg3_utils-0.95 [20010915]
+----------------------------
+  - make sg_dd, sgp_dd and archive/sgq_dd warn if dio selected but
+    /proc/scsi/sg/allow_dio is '0'
+  - sg_map can now do any INQUIRY (when '-i' argument given)
+  - expand example in scsi_inquiry
+
+Changelog for sg3_utils-0.94 [20010419]
+----------------------------
+  - add sg_start, documented in README.sg_start [Kurt Garloff]
+  - add osst suport in sg_map [Kurt Garloff]
+  - improvements to sginfo [Kurt Garloff]
+
+Changelog for sg3_utils-0.93 [20010415]
+----------------------------
+  - more include file fine tuning
+  - some "dio" work sg_rbuf
+  - extend sgp_dd so "continue on error" works on normal files
+  - introduce sg_include.h to encapsulate sg include problems
+  - add scsi_devfs_scan
+  - add sg_bus_xfer to archive directory
+  - more error info in sginfo
+
+Changelog for sg3_utils-0.92 [20010116]
+----------------------------
+  - change sg_err.c output from stdout to stderr
+  - change sg_debug to call system("cat /proc/scsi/sg/debug");
+  - fix in+out totals in sg_dd and sgp_dd when partial transfers
+  - lower include dependencies in sg_err.h
+  - add sgq_dd + Makefile to archive directory
+
+Changelog for sg3_utils-0.91 [20001221]
+----------------------------
+  - signalling handling added to sg_dd (and documented in sg_dd.8)
+  - add man page for sg_rbuf (and a small change to its code)
+  - add "-d" switch to isosize and cope with > 2 GB (and man page)
+
+Changelog for sg3_utils-0.90
+----------------------------
+  - switch from dated versioning. Previous version was sg3_utils001012.
+    Arbitrarily start at package version 0.90 . Start Changelog.
+  - incorporate Kurt Garloff's patches from Suse scsi.spm source rpm
+    compilation:
+    - add Kurt Garloff's sg_test_rwbuf utility to read and write to disk
+      buffer
+    - clean up Makefile to include a "make install" (and also add a
+      "make uninstall").
+    - add "-uno" switch to sginfo
+  - make raw and sg devices equally acceptable to sg_dd and sgp_dd.
+    [Raw devices still not as fast as sg devices doing disk to disk
+    copies in sgp_dd but this may be improved soon. Still faster than
+    using dd!]
+  - change lseek() in sg_dd and sgp_dd to _llseek() [using code borrowed
+    from fdisk] so big disks can be properly offset with 'skip' and
+    'seek' arguments. [This change is significant for raw devices and
+    normal files since sg devices already use 31 bit block addressing.]
+  - rename sg_s3_inq to sg_inq. This utility allows the INQUIRY response
+    to be decoded as per SCSI 3 and 4. Also can probe VPD and CmdDt pages.
+  - change multiplier suffixes on sg_dd, sgp_dd and sg_turs so lower case
+    "k, m, g" are powers of 2 while "K, M, G" are powers of 10. This idea
+    borrowed from lmdd (lmbench suite)
+  - retire a few more less used utilities into the archive directory.
+  - add man pages for sg_dd, sgp_dd and sg_map
diff --git a/sg3_utils/INSTALL b/sg3_utils/INSTALL
new file mode 100644
index 0000000..dc471d1
--- /dev/null
+++ b/sg3_utils/INSTALL
@@ -0,0 +1,240 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands './configure; make; make install' should
+configure, build, and install this package. If that fails try
+doing './autogen.sh' first and then repeat the above sequence. The
+autogen.sh script may require some autotools packages to be loaded.
+
+The following more detailed instructions are generic; see the `README'
+file for instructions specific to this package.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.
+
+     Running `configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+  6. Often, you can also type `make uninstall' to remove the installed
+     files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug.  Until the bug is fixed you can use this workaround:
+
+     CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/sg3_utils/Makefile.am b/sg3_utils/Makefile.am
new file mode 100644
index 0000000..0417ee9
--- /dev/null
+++ b/sg3_utils/Makefile.am
@@ -0,0 +1,13 @@
+
+SUBDIRS = include \
+	  lib \
+	  src \
+	  doc \
+	  scripts
+
+EXTRA_DIST=autogen.sh COVERAGE CREDITS
+
+distclean-local:
+	rm -rf autom4te.cache
+	rm -f build-stamp configure-stamp
+
diff --git a/sg3_utils/Makefile.in b/sg3_utils/Makefile.in
new file mode 100644
index 0000000..500ea60
--- /dev/null
+++ b/sg3_utils/Makefile.in
@@ -0,0 +1,819 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
+	$(am__configure_deps) $(am__DIST_COMMON)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	cscope distdir dist dist-all distcheck
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
+	$(LISP)config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+CSCOPE = cscope
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in AUTHORS \
+	COPYING ChangeLog INSTALL NEWS README compile config.guess \
+	config.sub depcomp install-sh ltmain.sh missing
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+  if test -d "$(distdir)"; then \
+    find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+      && rm -rf "$(distdir)" \
+      || { sleep 5 && rm -rf "$(distdir)"; }; \
+  else :; fi
+am__post_remove_distdir = $(am__remove_distdir)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+DIST_TARGETS = dist-gzip
+distuninstallcheck_listfiles = find . -type f -print
+am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
+  | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETOPT_O_FILES = @GETOPT_O_FILES@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = include \
+	  lib \
+	  src \
+	  doc \
+	  scripts
+
+EXTRA_DIST = autogen.sh COVERAGE CREDITS
+all: config.h
+	$(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+am--refresh: Makefile
+	@:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \
+	      $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \
+		&& exit 0; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    echo ' $(SHELL) ./config.status'; \
+	    $(SHELL) ./config.status;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	$(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	$(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+	@test -f $@ || rm -f stamp-h1
+	@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+	@rm -f stamp-h1
+	cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
+	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+	rm -f stamp-h1
+	touch $@
+
+distclean-hdr:
+	-rm -f config.h stamp-h1
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+	-rm -f libtool config.lt
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscope: cscope.files
+	test ! -s cscope.files \
+	  || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
+clean-cscope:
+	-rm -f cscope.files
+cscope.files: clean-cscope cscopelist
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+	-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
+
+distdir: $(DISTFILES)
+	$(am__remove_distdir)
+	test -d "$(distdir)" || mkdir "$(distdir)"
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+	-test -n "$(am__skip_mode_fix)" \
+	|| find "$(distdir)" -type d ! -perm -755 \
+		-exec chmod u+rwx,go+rx {} \; -o \
+	  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+	  ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+	|| chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+	$(am__post_remove_distdir)
+
+dist-bzip2: distdir
+	tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
+	$(am__post_remove_distdir)
+
+dist-lzip: distdir
+	tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
+	$(am__post_remove_distdir)
+
+dist-xz: distdir
+	tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
+	$(am__post_remove_distdir)
+
+dist-tarZ: distdir
+	@echo WARNING: "Support for distribution archives compressed with" \
+		       "legacy program 'compress' is deprecated." >&2
+	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+	$(am__post_remove_distdir)
+
+dist-shar: distdir
+	@echo WARNING: "Support for shar distribution archives is" \
+	               "deprecated." >&2
+	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+	shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+	$(am__post_remove_distdir)
+
+dist-zip: distdir
+	-rm -f $(distdir).zip
+	zip -rq $(distdir).zip $(distdir)
+	$(am__post_remove_distdir)
+
+dist dist-all:
+	$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
+	$(am__post_remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+	case '$(DIST_ARCHIVES)' in \
+	*.tar.gz*) \
+	  GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+	*.tar.bz2*) \
+	  bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+	*.tar.lz*) \
+	  lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
+	*.tar.xz*) \
+	  xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+	*.tar.Z*) \
+	  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+	*.shar.gz*) \
+	  GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+	*.zip*) \
+	  unzip $(distdir).zip ;;\
+	esac
+	chmod -R a-w $(distdir)
+	chmod u+w $(distdir)
+	mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
+	chmod a-w $(distdir)
+	test -d $(distdir)/_build || exit 0; \
+	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+	  && am__cwd=`pwd` \
+	  && $(am__cd) $(distdir)/_build/sub \
+	  && ../../configure \
+	    $(AM_DISTCHECK_CONFIGURE_FLAGS) \
+	    $(DISTCHECK_CONFIGURE_FLAGS) \
+	    --srcdir=../.. --prefix="$$dc_install_base" \
+	  && $(MAKE) $(AM_MAKEFLAGS) \
+	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
+	  && $(MAKE) $(AM_MAKEFLAGS) check \
+	  && $(MAKE) $(AM_MAKEFLAGS) install \
+	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+	  && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+	  && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+	        distuninstallcheck \
+	  && chmod -R a-w "$$dc_install_base" \
+	  && ({ \
+	       (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+	            distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+	      } || { rm -rf "$$dc_destdir"; exit 1; }) \
+	  && rm -rf "$$dc_destdir" \
+	  && $(MAKE) $(AM_MAKEFLAGS) dist \
+	  && rm -rf $(DIST_ARCHIVES) \
+	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+	  && cd "$$am__cwd" \
+	  || exit 1
+	$(am__post_remove_distdir)
+	@(echo "$(distdir) archives ready for distribution: "; \
+	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+	  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+	@test -n '$(distuninstallcheck_dir)' || { \
+	  echo 'ERROR: trying to run $@ with an empty' \
+	       '$$(distuninstallcheck_dir)' >&2; \
+	  exit 1; \
+	}; \
+	$(am__cd) '$(distuninstallcheck_dir)' || { \
+	  echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
+	  exit 1; \
+	}; \
+	test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
+	   || { echo "ERROR: files left after uninstall:" ; \
+	        if test -n "$(DESTDIR)"; then \
+	          echo "  (check DESTDIR support)"; \
+	        fi ; \
+	        $(distuninstallcheck_listfiles) ; \
+	        exit 1; } >&2
+distcleancheck: distclean
+	@if test '$(srcdir)' = . ; then \
+	  echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+	  exit 1 ; \
+	fi
+	@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+	  || { echo "ERROR: files left in build directory after distclean:" ; \
+	       $(distcleancheck_listfiles) ; \
+	       exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile config.h
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr \
+	distclean-libtool distclean-local distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
+	-rm -rf $(top_srcdir)/autom4te.cache
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) all install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+	am--refresh check check-am clean clean-cscope clean-generic \
+	clean-libtool cscope cscopelist-am ctags ctags-am dist \
+	dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
+	dist-xz dist-zip distcheck distclean distclean-generic \
+	distclean-hdr distclean-libtool distclean-local distclean-tags \
+	distcleancheck distdir distuninstallcheck dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am
+
+.PRECIOUS: Makefile
+
+
+distclean-local:
+	rm -rf autom4te.cache
+	rm -f build-stamp configure-stamp
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/sg3_utils/NEWS b/sg3_utils/NEWS
new file mode 100644
index 0000000..6a95914
--- /dev/null
+++ b/sg3_utils/NEWS
@@ -0,0 +1 @@
+See the ChangeLog file.
diff --git a/sg3_utils/README b/sg3_utils/README
new file mode 100644
index 0000000..cee7a65
--- /dev/null
+++ b/sg3_utils/README
@@ -0,0 +1,417 @@
+                      README for sg3_utils
+                      ====================
+Introduction
+============
+This package contains low level utilities for devices that use the SCSI
+command set. Originally the SCSI command set was associated exclusively
+with the SCSI Parallel Interface (SPI) transport. SPI has now almost
+been completely replaced by the Serial Attached SCSI (SAS) transport which
+also accepts the SCSI command set. Additionally many other storage related
+transports use the SCSI command set (amongst others); examples are ATAPI
+devices (CD/DVDs and tapes), USB mass storage devices (including those
+using the newer UAS[P]), Fibre Channel disks, IEEE 1394 storage devices (SBP
+protocol), iSCSI, FCoE and SOP devices.
+
+This package originally targeted the Linux SCSI subsystem. Since most
+operating systems contain a SCSI command pass-through mechanism, many
+utilities within this package have been ported. This README mainly
+concentrates on Linux: see the README.freebsd file for the FreeBSD port,
+README.solaris for the Solaris port, the README.tru64 file for the Tru64
+(OSF) port and README.win32 for the Windows ports (of which there are two
+variants).
+
+Most utilities within the sg3_utils package work at the SCSI command level.
+For example the sg_inq utility issues a SCSI INQUIRY command and decodes the
+response. The COVERAGE file has a table containing a row for each SCSI
+command issued by this package; to the right of each row is the utility
+(sometimes more than one) that issue that SCSI command. The COVERAGE file
+has a second table for ATA commands usage.
+
+Some utilities interface at a slightly higher level, for example: sg_dd,
+sgm_dd and sgp_dd. These are closely related to the Unix dd command and
+typically issue a sequence of SCSI READ and WRITE commands to copy data.
+These utilities are relatively tightly bound to Linux and are not ported to
+other Operating Systems. A new utility called ddpt (in a package of the same
+name) is more generic while still allowing a copy to be done in terms of
+SCSI READ and WRITE commands. ddpt has been ported to other OSes.
+
+Description
+===========
+A web site supporting the sg3_utils package can be found at
+http://sg.danny.cz/sg/sg3_utils.html . That page has a table of released
+versions for download. The most recent release or beta of sg3_utils may
+be found on this page: http://sg.danny.cz/sg in the News section.
+
+The predecessor to this package was called sg_utils. It is described in
+http://sg.danny.cz/sg/uu_index.html and old versions can be downloaded
+from the Downloads section of http://sg.danny.cz/sg .
+
+In the Linux 2.4 kernel series these utilities need to use the SCSI generic
+(sg) driver to access SCSI devices. The name of this package (i.e. sg3_utils)
+refers to version 3 of the SCSI generic (sg) driver which was introduced at
+the beginning of the 2.4 Linux kernel series. Significantly this added a new
+SCSI command interface structure (i.e. struct sg_io_hdr) that is more
+flexible than the older "sg_header" structure found in the sg driver in the
+2.2 and earlier Linux kernel series. The sg_io_hdr structure is also more
+flexible than the awkward (and limiting) interface to the
+SCSI_IOCTL_SEND_COMMAND ioctl supported by the Linux SCSI mid level. The
+version 3 sg driver also added the SG_IO ioctl that is synchronous (i.e. it
+issues the requested SCSI command and waits for the response (or a timeout)
+before the ioctl returns to the user space program that invoked it). The
+SG_IO ioctl is now supported in other parts of the Linux kernel in the 2.6
+series.
+
+In sg3_utils version 1.27 support has been added for the Linux bsg driver
+which use the sg version 4 interface. There seems no point in renaming
+this package sg4_utils. The existing utilities just silently support either.
+Currently the source build must be able to see the /usr/include/linux/bsg.h
+file. Then at run time the /proc/devices pseudo file needs to have an entry
+for the bsg driver (appeared around lk 2.6.28). With this in place each
+utility at run time checks the device it has been given and if it is a char
+device whose major number matches the bsg entry in /proc/devices then the
+sg v4 interface is used. Otherwise the sg v3 interface is used.
+
+Utilities that wish to use the asynchronous SCSI command interface (i.e. via
+a write() read() sequence) or issue special "commands" (e.g. bus and device
+resets) still need to use the Linux sg driver. Note that various
+drivers (e.g. cdrom/sr) have different open() flag and permissions policies
+that the user may need to take into account.
+
+If users have problems or questions about them please contact the author.
+Documentation for the Linux sg device driver can be found at:
+http://sg.danny.cz/sg/p/sg_v3_ho.html . This is written in DocBook and the
+original xml can be found in the same directory with the ".xml" extension.
+Postscript and pdf renderings are also in that directory. Older documentation
+for the sg version 3 driver can be found at:
+http://sg.danny.cz/sg/p/scsi_generic_v3.txt .
+
+All utilities are either "GPL"-ed or have a FreeBSD license. The author's
+intention is that users may incorporate all or part of the code in their work
+as they please. Attribution is encouraged. Please check the code as other
+contributors (apart from the author) may also have copyright notices. For a
+list of contributors see the CREDITS file.
+
+To save the repetition of common code (e.g. SCSI error processing) and
+reduce the size of the executable files, a shared library called
+libsgutils<num>.so (its Linux name) is created during the build process.
+That library is built from the contents of the include and lib
+subdirectories. The header files in the include subdirectory can be seen
+as the API of libsgutils and are commented with that in mind. The SCSI
+pass-through code for the supported operating systems is found in the lib
+subdirectory with names like sg_pt_linux.c and sg_pt_win32.c .
+
+Various distributions (of Linux mainly) distribute sg3_utils as 3
+installable packages. One is a package containing the shared library
+discussed above (e.g. libsgutils2-2_1.33-0.1_i386.deb). A second package
+contains the utilities (e.g. sg3-utils_1.33-0.1_i386.deb) and depends on the
+first package). Finally there is an optional package that contains header
+files and a static library (e.g. libsgutils2-dev_1.33-0.1_i386.deb). This
+final package is only needed to build other packages (e.g. sdparm) that
+wish to use the sg3_utils shared library.
+
+All the utilities in the src subdirectory have "man" pages that are
+placed in the doc subdirectory. There is also a sg3_utils (8) man page that
+summarizes common facilities including exit statuses. Additional
+information (including each utility's version number) can be found towards
+the top of each ".c" file corresponding to the utility name.
+
+The sg driver in Linux can be seen as having 3 distinct versions:
+
+   v1   lk < 2.2.6     sg_header based relatively unchanged since 1992
+   v2   lk >= 2.2.6    enhanced sg_header interface structure [1999/4/16]
+   v3   lk >= 2.4      additional sg_io_hdr interface structure [2001/1/4]
+   v3   lk >= 2.6      same interface as found in lk 2.4 [2.6.0: 2003/12/18]
+
+and the bsg driver supports the sg v4 interface and was added around
+lk 2.6.28 . This package is targeted at "v3" and "v4". Another package called
+"sg_utils" is targeted at "v2" and to a lesser extent "v1". The "sg_utils"
+package has a subset of the utilities found in this package.
+
+In Linux some sg driver ioctls (notably SG_IO) are defined for many block
+devices in lk 2.6 series. In practice this means all SCSI block devices,
+ATAPI block devices (mainly CD, DVD and BD optical devices) but _not_ ATA
+disks, depending on which kernel configuration options, can be accessed by
+the utilities in this package. SATA disks that use the libata kernel library
+(or some other SCSI to ATA Translation (SAT) Layer (SATL)) accept SCSI
+commands and thus are supported. Support for the SG_IO as been added to the
+scsi tape driver (st) in lk 2.6.6 .
+
+In the src directory the bulk of the utilities are written in relatively
+clean POSIX compliant C code with Linux specific system calls and structures
+removed and placed in Linux specific files in the lib directory. A small
+number of utilities in the src directory do contain Linux specific logic
+and are not ported to other OSes (e.g. sg_dd). One utility, sg_scan, has
+two separate implementations, one for Linux (sg_scan_linux.c) and one for
+Windows (sg_scan_win32.c). The src-lib directory split approach allows
+FreeBSD, Solaris, Tru64 and Windows specific code to be isolated to a few
+files in the lib directory whose interfaces match those of the Linux
+specific code.
+
+Darwin is not supported because the Apple folks do not want to give their
+users a pass-through SCSI interface. The author has read about creative
+hackers using a VM containing a real OS to circumvent the Apple restriction.
+
+C standard is C99
+==================
+The C code in this package is written for portability rather than speed.
+It assumes a level of C99 compliance and favours POSIX system and library
+calls over OS specific calls.
+
+The C code is written in a C++ friendly way and is checked from time to
+time that it compiles clean with C++. To accommodate C++ certain C99
+constructs such as designated initializers cannot be used.
+
+Building
+========
+This package is designed to be built with the usual:
+    "./configure ; make ; make install"
+sequence. In some situations that may need to be prefixed by a call to
+the "./autogen.sh" script which invokes autoconf and automake. That in turn
+may require packages containing those utilities to be installed. The
+libtool utility is also required. Naturally a C compiler is required
+and due to the vagaries of libtool a C++ compiler also.
+
+The "./configure" takes many command line options with the defaults
+being usually sufficient to start with. One quirk is that the location
+of the installation is under the /usr/local directory. So the sg_inq
+utility will be installed at /usr/local/bin/sg_inq . This is controlled
+by the "--prefix=<directory>" option which defaults to
+"--prefix=/usr/local". As an example to install the executables in /usr/bin
+and disable the creation of the shared library (libsgutils<num>.so) this
+invocation could be used: "./configure --prefix=/usr --disable-shared".
+To reduce the size of an executable as well try this:
+"./configure --prefix=/usr --disable-shared --disable-scsistrings".
+
+In Linux there are package build files for "rpm" based and for "deb" based
+systems. The 'sg3_utils.spec' file in the main directory can be used like
+this: 'rpmbuild -ba sg3_utils.spec' in a rpmbuild tree SPECS directory.
+To cross build or make a more widely distributable package then the --target
+option may be useful: 'rpmbuild --target=i386 -ba sg3_utils.spec' or
+'rpmbuild --target=x86_64 -ba sg3_utils.spec' . The sg3_utils.spec file
+in the main directory targets Red Hat systems, an alternative "spec" file
+for Suse systems has been placed under the 'suse' directory.
+
+The 'build_debian.sh' script should build several "deb" packages and place
+them in the parent directory. In debian based systems doing
+a 'apt-get install build-essential' is one way to get most of build
+environment needed if it has not already been loaded. There are now some
+problems with this script and the superseded Debian 4.0 ("etch"). See
+debian/README.debian4 for a workaround. Amongst other things debian
+builds are sensitive to the value in the debian/compat file. If it
+contains "7" then it works on lenny and gives warning on squeeze (but
+fails on the earlier etch).
+
+Warning
+=======
+Many devices use SCSI command sets over transport protocols not normally
+associated with SCSI (as defined at http://www.t10.org ). Some of these
+devices react poorly (e.g. lock up) when sent SCSI commands that they don't
+support. Even sending a supported SCSI command with a field set to an
+unexpected value can cause problems. [The author is talking about billions
+of USB devices with horrible SCSI implementations.]
+
+For example, all "SCSI" devices must support the INQUIRY command which the
+SCSI-2 standard says should request a 36 byte response. However later SCSI
+standards (e.g. SPC-2) have increased that length but some SCSI devices lock
+up when they receive a request for anything other than a 36 byte response.
+
+Any well implemented "SCSI" device should react sensibly when a utility in
+sg3_utils sends a SCSI command that it doesn't support. Unfortunately this
+cannot be guaranteed.
+
+Prior to lk 2.6.29 USB mass storage limited sense data to 18 bytes which
+caused problems for certain types of descriptor based sense data. An
+example of this is the SCSI ATA PASS-THROUGH command with the CK_COND bit
+set.
+
+
+Utilities
+=========
+Here is list in alphabetical order of utilities found in the 'src'
+subdirectory of the sg3_utils package:
+    sginfo, sg_compare_and_write, sg_copy_results, sgm_dd, sgp_dd, sg_dd,
+    sg_decode_sense, sg_emc_trespass, sg_format, sg_get_config,
+    sg_get_lba_status, sg_ident, sg_inq, sg_logs, sg_luns, sg_map, sg_map26,
+    sg_modes, sg_opcodes, sg_persist, sg_prevent, sg_raw, sg_rbuf, sg_rdac,
+    sg_read, sg_read_attr, sg_readcap, sg_read_block_limits, sg_read_buffer,
+    sg_read_long, sg_reassign, sg_referrals, sg_request, sg_reset, sg_rmsn,
+    sg_rtpg, sg_safte, sg_sanitize, sg_sat_identify, sg_sat_phy_event,
+    sg_sat_read_gplog, sg_sat_set_features, sg_scan, sg_senddiag, sg_ses,
+    sg_ses_microcode, sg_start, sg_stpg, sg_sync, sg_test_rwbuff,
+    sg_timestamp, sg_turs, sg_unmap, sg_verify, sg_vpd, sg_write_buffer,
+    sg_write_long, sg_write_same, sg_write_verify sg_wr_mode, sg_xcopy,
+    sg_zone
+
+Each of the above utilities depends on header files found in the 'include'
+subdirectory and library code found in the 'lib' subdirectory. Associated
+man pages are found in the 'doc' subdirectory. Additional programs found
+in the 'archive', 'examples' and 'utils' subdirectories in not build by the
+top level build infrastructure. Linux binary distributions of the sg3_utils
+package (e.g. "rpm" and debian packages) typically contain the shared
+library, the utilities found in the 'src' subdirectory, their associated man
+pages and some documentation files (e.g. README, INSTALL, CREDITS, COPYING
+and COVERAGE). See the INSTALL file for generic instructions about building
+with autotools (e.g. ./configure ).
+
+Man pages can be read (without building and installing the package) by
+going to the 'doc' subdirectory and executing something like this:
+ $ man ./sg_dd.8
+
+To see which SCSI commands (and ATA commands) are used by these utilities
+refer to the COVERAGE file.
+
+Here is a list in alphabetical order of utilities found in the 'examples'
+subdirectory:
+  - bsg_queue_tst, sg_excl, scsi_inquiry, sg_iovec_tst, sg_queue_tst,
+    sg_sat_chk_power, sg__sat_identify, sg__sat_phy_event,
+    sg__sat_set_features, sg_sat_smart_rd_data, sg_simple1, sg_simple2,
+    sg_simple3, sg_simple4, sg_simple5, sg_simple16, sg_tst_excl,
+    sg_tst_excl2, sg_tst_excl3, sg_tst_context and sg_tst_async
+
+Also in that subdirectory is a script to test sg_persist, an example data
+file for sg_persist (called "transport_ids.txt") and an example data file for
+sg_reassign (called "reassign_addr.txt"). There are several scripts
+for 'sg_senddiag -pf -raw=-' that will put some SAS disk phys into
+a "compliant jitter tolerance pattern" (CJTPAT).
+
+The 'utils' subdirectory contains source and a Makefile to build "hxascdmp"
+which accepts binary data from stdin (or a file on the command line) and
+outputs an ASCII-HEX and ASCII representation of it. It is similar to the
+Unix od command. There is also code to sg_chk_asc.c which checks a given
+text file (typically a copy of http://www.t10.org/lists/asc-num.txt ) and
+checks it against the asc/ascq text strings held in sg_lib_data.c .
+
+The 'doc' subdirectory contains a README file containing the urls of
+various related documents.
+
+The 'scripts' subdirectory contains some Bourne (bash) shell scripts that
+rely on utilities in the main directory. One script uses the sdparm utility.
+These scripts are described in the scripts/README file and have usage
+messages.
+
+
+Notes for utilities without man pages
+=====================================
+These utils are found in the 'examples' subdirectory.
+
+The "scsi_inquiry" program shows the use of the SCSI_IOCTL_SEND_COMMAND
+ioctl to send a SCSI INQUIRY command. That ioctl() is supported by the
+SCSI sub system mid level and so is common to all sd, sr, st and sg devices.
+That ioctl is deprecated in the lk 2.6 series. This program has been placed
+in the "examples" subdirectory.
+
+"sg_simple1" and "sg_simple2" are example programs demonstrating calls
+to the SCSI INQUIRY and TEST UNIT READY commands. They only differ in their
+error processing: sg_simple1 uses sg_lib.[hc] for error processing while
+sg_simple2 does its own more primitive checks.
+
+"sg_simple3" tests out user space scatter gather added to the version 3
+sg driver.
+
+"sg_simple4" shows the INQUIRY command using mmap-ed IO to obtain its
+response buffer.
+
+"sg_simple5" also sends and INQUIRY and TEST UNIT READY commands. It
+uses the generic pass through mechanism based on sg_pt.h . It will
+currently build in Linux and FreeBSD (with "make -f Makefile.freebsd").
+It has extensive error checking code.
+
+"sg_simple16" attempts to send a 16 byte SCSI command, READ_16, to the
+scsi device. This is only supported for lk >= 2.4.15 and for adapter
+drivers that indicate that they have 16 byte CDB capability (otherwise
+DID_ABORT will appear in the host_status).
+
+"sg_sat_chk_power" attempts to push an ATA CHECK POWER MODE command
+through the SAT-defined ATA PASS_THROUGH (16) SCSI command. That
+ATA command needs to read the "FIS" registers after the command is
+completed which involves using the ATA Status Return (sense data)
+descriptor (as defined in SAT).
+
+"sg_sat_smart_rd_data" attempts to push an ATA SMART/READ DATA command
+through the SAT-defined ATA PASS_THROUGH (16) SCSI command. If
+successful, the 256 word (512 byte) response is output.
+
+"sg_tst_excl" and "sg_tst_excl2" use multiple threads to bombard the
+given device with O_EXCL open flags, so only one should succeed at a
+time. While holding O_EXCL control a thread attempts a double increment
+on an integer in the given LBA. If the integer starts even (after the
+first read) then it should remain even if the O_EXCL flag is doing its job.
+The "sg_tst_excl" variant uses the Linux SG_IO v3 interface while the
+"sg_tst_excl2" uses the more generic sg_pt infrastructure.
+
+"sg_tst_excl3" is a variant of "sg_tst_excl2". "sg_tst_excl3" only does
+the double increment from the first thread, each time using O_EXCL on
+open. The remaining threads check the value is even, each time doing
+an open without the O_EXCL flag.
+
+"bsg_queue_tst" sends an INQUIRY command via the Linux SG_IO v4 interface
+which is used by the bsg driver. So it will take device names like
+"/dev/bsg/6:0:0:0". It tests if sending repeated INQUIRYs with
+the BSG_FLAG_Q_AT_HEAD or BSG_FLAG_Q_AT_TAIL flag makes any difference.
+
+"sg_tst_async" is a test harness for the Linux sg driver. It is multi
+threaded, submitting either TEST UNIT READY, READ(16) or WRITE(16) SCSI
+commands asynchronously. Each thread opens a file descriptor and submits
+those commands up to the queue limit (sg driver has a per file descriptor
+queue limit of 16). Multiple threads doing the same thing act as a
+multiplier to that queue limit.
+
+
+Command line processing
+=======================
+These utilities can be divided into 3 groups when their handling of command
+line arguments is considered:
+  - ad hoc, typically in a short form only, sometimes longer (e.g.
+    "sg_logs -pcb /dev/sdc")
+  - inspired by the dd Unix command (e.g. sg_dd, sgm_dd, sgp_dd, sg_read)
+  - recent utilities use "getopt_long" (see "man getopt_long")
+    type command lines. These have short form (starting with "-")
+    and corresponding longer form (starting with "--") options.
+
+The older utilities that use ad hoc options, in alphabetical order:
+  - sg_emc_trespass, sginfo(1/2), sg_inq, sg_logs, sg_map, sg_modes,
+    sg_opcodes, sg_rbuf, sg_rdac, sg_readcap, sg_reset, sg_scan (Linux),
+    sg_senddiag, sg_start, sg_test_rwbuf, sg_turs
+In sg3_utils version 1.23 the following utilities from this group were
+converted to have a dual getopt_long/ad_hoc interface, defaulting to
+the getop_long interface:
+  - sg_inq, sg_logs, sg_modes, sg_opcodes, sg_rbuf, sg_readcap,
+    sg_senddiag, sg_start, sg_turs
+These can be switched back to the older (backward compatible) ad hoc
+interface by defining the SG3_UTILS_OLD_OPTS environment variable
+or using '-O' as the first command line option.
+
+The more recent utilities that use "getopt_long" only are:
+  - sg_compare_and_write, sg_decode_sense, sg_format, sg_get_config,
+    sg_get_lba_status, sg_ident, sg_luns, sg_map26, sg_persist, sg_prevent,
+    sg_raw, sg_read_attr, sg_read_block_limits, sg_read_buffer, sg_read_long,
+    sg_reassign, sg_referrals, sg_requests, sg_rmsn, sg_rtpg, sg_safte,
+    sg_sanitize, sg_sat_identify, sg_sat_phy_event, sg_sat_read_gplog,
+    sg_sat_set_features, sg_scan(w), sg_ses, sg_ses_microcode, sg_stpg,
+    sg_sync, sg_test_rwbuf, sg_timestamp, sg_unmap, sg_verify, sg_vpd,
+    sg_write_buffer, sg_write_long, sg_write_same, sg_write_verify,
+    sg_wr_mode, sg_zone
+
+
+Dangerous code
+==============
+This C code snippet:
+    unsigned char uc = 0x80;
+    uint64_t ull;
+    ull = (uc << 24);
+Somewhat surprisingly sets ull to:
+    ull: 0xffffffff80000000
+This result is due to the 'unary conversion' of uc to a (32 bit signed)
+'int' before the shift. The resultant type from the shift is also an int
+and it has its top bit set so there is sign extension when it is assigned
+into a 64 bit unsigned integer. Making sure there is no conversion to 'int'
+solves the problem. In this case if uc is declared as unsigned int the
+result will be as expected (i.e. 0x80000000).
+
+Other SCSI and storage tools
+============================
+See http://sg.danny.cz/sg/tools.html
+
+
+Douglas Gilbert
+17th February 2016
diff --git a/sg3_utils/README.freebsd b/sg3_utils/README.freebsd
new file mode 100644
index 0000000..ab1a7ed
--- /dev/null
+++ b/sg3_utils/README.freebsd
@@ -0,0 +1,138 @@
+Introduction
+============
+The FreeBSD port of sg3_utils contains those utilities that are _not_
+specific to Linux. In some cases the FreeBSD camcontrol command supplies
+similar functionality; for example 'sg_map' is similar to
+'camcontrol devlist'.
+
+The dd variants from the sg3_utils package (e.g. sg_dd) rely on too many
+Linux idiosyncrasies to be easily ported. A new package called 'ddpt'
+contains a utility with similar functionality to sg_dd and ddpt is available
+for FreeBSD.
+
+Supported Utilities
+===================
+Here is a list of utilities that have been ported:
+    sg_compare_and_write
+    sg_decode_sense
+    sg_format
+    sg_get_config
+    sg_get_lba_status
+    sg_ident
+    sg_inq          [dropped ATA IDENTIFY DEVICE capability]
+    sg_logs
+    sg_luns
+    sg_modes
+    sg_opcodes
+    sg_persist
+    sg_prevent
+    sg_raw
+    sg_rdac
+    sg_read_block_limits
+    sg_read_buffer
+    sg_read_long
+    sg_readcap
+    sg_reassign
+    sg_referrals
+    sg_requests
+    sg_rmsn
+    sg_rtpg
+    sg_safte
+    sg_sanitize
+    sg_sat_identify
+    sg_sat_phy_event
+    sg_sat_set_features
+    sg_senddiag
+    sg_ses
+    sg_start
+    sg_stpg
+    sg_sync
+    sg_turs
+    sg_verify
+    sg_unmap
+    sg_vpd
+    sg_wr_mode
+    sg_write_buffer
+    sg_write_long
+    sg_write_same
+
+Most utility names are indicative of the main SCSI command
+that they execute.  Some utilities are slightly higher level, for
+example sg_ses fetches SCSI Enclosure Services (SES) status pages and
+can send control pages. Each utility has a man page (placed in
+section 8). An overview of sg3_utils can be found at:
+http://sg.danny.cz/sg/sg3_utils.html .
+A copy of the "sg3_utils.html" file is in the "doc" subdirectory.
+
+
+The executables and library can be built from the source code in
+the tarball and installed with the familiar
+"./configure ; make ; make install" sequence. If this fails try
+running the "./autogen.sh" script prior to that sequence. There
+are generic instruction on configure and friend in the INSTALL file.
+
+Some man pages have examples which use Linux device names which
+hopefully will not confuse the FreeBSD users.
+
+Device naming
+=============
+In FreeBSD disks have block names like '/dev/da0' with a corresponding
+pass-through device name like '/dev/pass0'. Use this command
+"camcontrol devlist" to see that SCSI devices available.
+
+FreeBSD installation
+====================
+The traditional './configure ; make ; make install' sequence from the
+top level of the unpacked tarball will work on FreeBSD. But the man pages
+will be placed under the /usr/local/share/man directory which unfortunately
+is not on the standard manpath. One solution is to add this path by
+creating a file with a name like local_share.conf in the
+/usr/local/etc/man.d/ directory and placing this line in it:
+    MANPATH /usr/local/share/man
+
+FreeBSD 9.0 has a "ports" entry for sg3_utils under the
+/usr/ports/sysutils directory. It points to version 1.28 of sg3_utils
+which is now a bit dated. It could be used as a template to point
+to more recent versions.
+
+kFreeBSD
+========
+sg3_utils can be built into a Debian package for kFreeBSD using the
+./build_debian.sh script in the top level directory. This has been tested
+with Debian 6.0 release.
+
+Details
+=======
+Most of the ported utilities listed above use SCSI command functions
+declared in sg_cmds_*.h headers . Those SCSI command functions are
+implemented in the corresponding ".c" files. The ".c" files pass SCSI
+commands to the host operating system via an interface declared in sg_pt.h .
+There are currently five implementations of that interface depending on
+the host operating system:
+  - sg_pt_linux.c
+  - sg_pt_freebsd.c
+  - sg_pt_osf1.c  [Tru64]
+  - sg_pt_win32.c
+  - sg_pt_solaris.c
+
+The sg_pt_freebsd.c file uses the FreeBSD CAM SCSI pass through mechanism.
+Hence only FreeBSD device nodes that support CAM can be used. These can be
+viewed with the "camcontrol devlist" command. To access ATAPI devices (e.g.
+ATAPI DVD drives) the kernel may need to be configured with the "atapicam"
+device.
+
+Attempts to send SCSI commands with data-in or data-out buffers around 64 KB
+and larger failed on a FreeBSD 7.0 with an "argument list too long" error
+message. There is an associated kernel message (viewable with dmesg) that an
+attempt has been made to map <n> bytes which is greater than
+DFLTPHYS(65536). Still a problem in FreeBSD 8.1 . Due to CAM overhead the
+largest power of 2 that can fit through with one command is 32768 bytes (32
+KB).
+
+FreeBSD 9.0 is the most recent version of FreeBSD tested with these
+utilities.
+
+
+
+Douglas Gilbert
+14th January 2013
diff --git a/sg3_utils/README.iscsi b/sg3_utils/README.iscsi
new file mode 100644
index 0000000..917190a
--- /dev/null
+++ b/sg3_utils/README.iscsi
@@ -0,0 +1,37 @@
+iSCSI support for sg3-utils is available from external patches.
+
+To build sg3-utils from sources and activate built-in iSCSI support
+you need both sg3-utils and the external user-space iSCSI library hosted at :
+
+https://github.com/sahlberg/libiscsi
+
+This library provides a client library for accessing remote iSCSI
+devices and also comes with patches to the sg3-utils source code
+distribution to compile a special version of sg3-utils with iSCSI
+support.
+
+No support for iSCSI is provided by the sg3-utils maintainer.
+
+
+
+Once sg3-utils is compiler and installed with libiscsi support, you
+can specify remote iSCSI devices through a special URL format instead
+of the normal /dev/* syntax.
+
+Example:
+
+sg_inq iscsi://ronnie%password@10.1.1.27/iqn.ronnie.test/1
+standard INQUIRY:
+ PQual=0  Device_type=0  RMB=0  version=0x05  [SPC-3]
+ [AERC=0]  [TrmTsk=1]  NormACA=0  HiSUP=0  Resp_data_format=2
+ SCCS=0  ACC=0  TPGS=0  3PC=0  Protect=0  BQue=0
+ EncServ=0  MultiP=0  [MChngr=0]  [ACKREQQ=0]  Addr16=0
+ [RelAdr=0]  WBus16=0  Sync=0  Linked=0  [TranDis=0]  CmdQue=1
+ [SPI: Clocking=0x0  QAS=0  IUS=0]
+   length=66 (0x42)   Peripheral device type: disk
+ Vendor identification: IET
+ Product identification: VIRTUAL-DISK
+ Product revision level: 0001
+ Unit serial number:                           beaf11
+
+
diff --git a/sg3_utils/README.sg_start b/sg3_utils/README.sg_start
new file mode 100644
index 0000000..c4b663f
--- /dev/null
+++ b/sg3_utils/README.sg_start
@@ -0,0 +1,38 @@
+Hi,
+
+you can use sg_start to start (spin-up, 1) and stop (spin-down, 0) devices.
+I also offers a parameter (-s) to send a synchronize cache command to a
+device, so it should write back its internal buffers to the medium.
+
+Be aware that the Linux SCSI subsystem at this time does not automatically
+starts stopped devices, so stopping a device which is in use may have fatal
+results for you.
+
+So, you should apply with care.
+I use it in my shutdown script at the end (before the poweroff command):
+
+# SG_SHUG_NOS is set in my config file rc.config
+# SG_SHUT_NOS="0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"
+if test -x /bin/sg_start; then
+    if test "`basename $command`" = "reboot"; then
+        for no in $SG_SHUT_NOS;
+	      do /bin/sg_start /dev/sg$no -s >/dev/null 2>&1; 
+	 done
+    else
+        for no in $SG_SHUT_NOS;
+	    do /bin/sg_start /dev/sg$no -s 0 >/dev/null 2>&1;
+        done
+    fi
+fi
+
+Enjoy!
+					Kurt Garloff <garloff at suse dot de>
+
+
+Postscript
+==========
+sg_start has been reworked to allow a block device (e.g. /dev/sda) in
+addition to the sg device name (e.g. /dev/sg0) in the lk 2.6 series.
+sg_start now has more command line options, see its man page.
+
+	Douglas Gilbert <dgilbert at interlog dot com> 2004/5/8
diff --git a/sg3_utils/README.solaris b/sg3_utils/README.solaris
new file mode 100644
index 0000000..8548f18
--- /dev/null
+++ b/sg3_utils/README.solaris
@@ -0,0 +1,159 @@
+Please Note:
+>>> Up to and including sg3_utils-1.33 the Solaris code was built
+>>> and tested on an OpenSolaris VM run with VirtualBox on Ubuntu
+>>> 11.10 . Now with Ubuntu 12.04 those VMs crash immediately when
+>>> started with VirtualBox. Further, Oracle (who owns SUN and thus
+>>> Solaris) no longer supports OpenSolaris and its package
+>>> repository has been withdrawn. The author can find no generic VMs
+>>> for Oracle Solaris 11 that run on VirtualBox or VMWare. The author
+>>> is also displeased with the withdrawal of the Open Software
+>>> OS and is disinclined to build a Solaris 11 system just to
+>>> virtualize it.
+>>> So as of sg3_utils-1.34 the Solaris port is provided "as-is" without
+>>> testing on a Solaris platform.
+
+Douglas Gilbert
+13th October 2012
+
+
+
+Introduction
+============
+The Solaris port of sg3_utils contains those utilities that are
+_not_ specific to Linux.
+
+The dd variants from the sg3_utils package (e.g. sg_dd) rely on too many
+Linux idiosyncrasies to be easily ported. A new package called 'ddpt'
+contains a utility with similar functionality to sg_dd and is available
+for Solaris.
+
+Supported Utilities
+===================
+Here is a list of utilities that have been ported:
+    sg_compare_and_write
+    sg_decode_sense
+    sg_format
+    sg_get_config
+    sg_get_lba_status
+    sg_ident
+    sg_inq          [dropped ATA IDENTIFY DEVICE capability]
+    sg_logs
+    sg_luns
+    sg_modes
+    sg_persist
+    sg_opcodes
+    sg_prevent
+    sg_raw
+    sg_rdac
+    sg_read_block_limts
+    sg_read_buffer
+    sg_read_long
+    sg_readcap
+    sg_reassign
+    sg_referrals
+    sg_requests
+    sg_rmsn
+    sg_rtpg
+    sg_safte
+    sg_sanitize
+    sg_sat_identify
+    sg_sat_phy_event
+    sg_sat_set_features
+    sg_senddiag
+    sg_ses
+    sg_start
+    sg_stpg
+    sg_sync
+    sg_turs
+    sg_unmap
+    sg_verify
+    sg_vpd
+    sg_wr_mode
+    sg_write_buffer
+    sg_write_long
+    sg_write_same
+
+Most utility names are indicative of the main SCSI command
+that they execute.  Some utilities are slightly higher level, for
+example sg_ses fetches SCSI Enclosure Services (SES) status pages and
+can send control pages. Each utility has a man page (placed in
+section 8). An overview of sg3_utils can be found at:
+http://sg.danny.cz/sg/sg3_utils.html .
+A copy of the "sg3_utils.html" file is in the "doc" subdirectory.
+
+
+The executables and library can be built from the source code in
+the tarball and installed with the familiar
+"./configure ; make ; make install" sequence. If this fails try
+running the "./autogen.sh" script prior to that sequence. There
+are generic instruction on configure and friend in the INSTALL file.
+
+Some man pages have examples which use Linux device names which
+hopefully will not confuse the Solaris users.
+
+Device naming
+=============
+In Solaris, SCSI device names below the '/dev' directory have a
+form like: c5t4d3s2 where the number following "c" is the controller
+(HBA) number, the number following "t" is the target number (from
+the SCSI parallel interface days) and the number following "d" is
+the LUN. Following the "s" is the slice number which is related to
+a partition and by convention "s2" is the whole disk.
+
+OpenSolaris also has a c5t4d3p2 form where the number following
+the "p" is the partition number apart from "p0" which is the whole
+disk. So a whole disk may be referred to as either:
+  - c5t4d3
+  - c5t4d3s2
+  - c5t4d3p0
+
+And these device names are duplicated in the /dev/dsk and /dev/rdsk
+directories. The former is the block device name and the latter
+is for "raw" (or char device) access which is what sg3_utils needs.
+So in OpenSolaris something of the form:
+   sg_inq /dev/rdsk/c5t4d3p0
+should work. If it doesn't add a '-vvv' option. If that is attempted
+on the /dev/dsk/c5t4d3p0 variant an inappropriate ioctl for device
+error will result.
+
+The device names within the /dev directory are typically symbolic
+links to much longer topological names in the /device directory.
+
+In Solaris cd/dvd/bd players seem to be treated as disks and so are
+found in the /dev/rdsk directory. Tape drives appear in the /dev/rmt
+directory.
+
+There is also a sgen (SCSI generic) driver which by default does not
+attach to any device. See the /kernel/drv/sgen.conf file to control
+what is attached. Any attached device will have a device name of
+the form /dev/scsi/c5t4d3 .
+
+Listing available SCSI devices in Solaris seems to be a challenge.
+"Use the 'format' command" advice works but seems a very dangerous
+way to list devices. [It does prompt again before doing any damage.]
+'devfsadm -Cv' cleans out the clutter in the /dev/rdsk directory,
+only leaving what is "live". The "cfgadm -v" command looks promising.
+
+Details
+=======
+The ported utilities listed above, all use SCSI command functions
+declared in sg_cmds_basic.h and sg_cmds_extra.h . Those SCSI command
+functions are implemented in the corresponding ".c" files. The ".c"
+files pass SCSI commands to the host operating system via an interface
+declared in sg_pt.h . There are currently five implementations of that
+interface depending on the host operating system:
+  - sg_pt_linux.c
+  - sg_pt_freebsd.c
+  - sg_pt_osf1.c  [Tru64]
+  - sg_pt_solaris.c
+  - sg_pt_win32.c
+
+The sg_pt_solaris.c file uses the "uscsi" SCSI pass through mechanism. There
+seems to be no corresponding ATA pass through and recent SATA disks do not
+seem to have a SAT layer in front of them (within Solaris). If SAT is
+present (perhaps externally or within a HBA) then that would allow SATA
+disks to accept SCSI commands including the SCSI ATA PASS THROUGH commands.
+
+
+Douglas Gilbert
+14th January 2013
diff --git a/sg3_utils/README.tru64 b/sg3_utils/README.tru64
new file mode 100644
index 0000000..6ebd026
--- /dev/null
+++ b/sg3_utils/README.tru64
@@ -0,0 +1,97 @@
+Introduction
+============
+The Tru64 port of sg3_utils contains those utilities that are _not_
+specific to Linux. In some cases a utility could be ported but
+requires more work. An example is sg_dd which needs more work
+beyond the SCSI command pass through mechanism.
+
+Supported Utilities
+===================
+Here is a list of utilities that have been ported:
+    sg_compare_and_write
+    sg_decode_sense
+    sg_format
+    sg_get_config
+    sg_get_lba_status
+    sg_ident
+    sg_inq          [dropped ATA IDENTIFY DEVICE capability]
+    sg_logs
+    sg_luns
+    sg_modes
+    sg_opcodes
+    sg_persist
+    sg_prevent
+    sg_raw
+    sg_rdac
+    sg_read_block_limits
+    sg_read_buffer
+    sg_read_long
+    sg_readcap
+    sg_reassign
+    sg_referrals
+    sg_requests
+    sg_rmsn
+    sg_rtpg
+    sg_safte
+    sg_sanitize
+    sg_sat_identify
+    sg_sat_phy_event
+    sg_sat_set_features
+    sg_senddiag
+    sg_ses
+    sg_start
+    sg_stpg
+    sg_sync
+    sg_turs
+    sg_unmap
+    sg_verify
+    sg_vpd
+    sg_wr_mode
+    sg_write_buffer
+    sg_write_long
+    sg_write_same
+
+Most utility names are indicative of the main SCSI command
+that they execute.  Some utilities are slightly higher level, for
+example sg_ses fetches SCSI Enclosure Services (SES) status pages and
+can send control pages. Each utility has a man page (placed in
+section 8). An overview of sg3_utils can be found at:
+http://sg.danny.cz/sg/sg3_utils.html .
+A copy of the "sg3_utils.html" file is in the "doc" subdirectory.
+
+This package uses autotools infrastructure with the now common
+"./configure ; make ; make install" sequence needed to build and install
+from the source found in the tarball. If the "./configure" sequence
+fails try using the ./autogen.sh prior to that sequence.
+
+Some man pages have examples which use Linux device names which hopefully
+will not confuse Tru64 users.
+
+
+Details
+=======
+Most of the ported utilities listed above use SCSI command functions
+declared in sg_cmds_*.h headers . Those SCSI command functions are
+implemented in the corresponding ".c" files. The ".c" files pass SCSI
+commands to the host operating system via an interface declared in sg_pt.h .
+There are currently five implementations of that interface depending on
+the host operating system:
+system:
+  - sg_pt_linux.c
+  - sg_pt_osf1.c  [Tru64]
+  - sg_pt_freebsd.c
+  - sg_pt_solaris.c
+  - sg_pt_win32.c
+
+The sg_pt_osf1.c file uses the Tru64 CAM SCSI pass through mechanism.
+
+Tru64 does not have general library support for "long" options
+(e.g. "--verbose") which are used extensively by most of the
+utilities in this package. Rather than change all the utilities
+and their man/web pages a local implementation of the missing
+function "getopt_long()" has been placed in the "getopt_long"
+subdirectory. Currently only the Tru64 port uses it.
+
+
+Douglas Gilbert
+14th January 2013
diff --git a/sg3_utils/README.win32 b/sg3_utils/README.win32
new file mode 100644
index 0000000..cfce3ed
--- /dev/null
+++ b/sg3_utils/README.win32
@@ -0,0 +1,233 @@
+Introduction
+============
+The win32 port of sg3_utils contains those utilities that are _not_ specific
+to Linux. One utility for listing available devices, sg_scan, has a
+Windows-specific version for this port.
+
+The dd variants from the sg3_utils package (e.g. sg_dd) rely on too many
+Linux idiosyncrasies to be easily ported. A new package called 'ddpt'
+contains a utility with similar functionality to sg_dd and is available
+for Windows.
+
+The Windows port uses the Microsoft SCSI Pass Through (SPT) interface.
+It has two variants: "SPT" where data is double buffered; and "SPTD"
+where data pointers to the user space are passed to the OS. Only Windows
+2000 and later (i.e. not 95, 98 or ME) support SPT.
+
+Two build environments are catered for: cygwin (see www.cygwin.com) and
+MinGW ("Minimalist GNU for Windows", see www.mingw.org). Both are based in
+the gcc compiler (although other C compilers should have little problem with
+the source code). Cygwin is a more sophisticated, commercial product that
+results in executables that depend on cygwin1.dll . No licensing is required
+since sg3_utils is open source (with either BSD or GPL licenses) but users
+will need to fetch that dll. On the other hand MinGW (and its companion MSYS
+shell) builds freestanding console executables. The Unix library support is
+not as advanced with MinGW which has led to some timing functions being
+compiled out when sg3_utils is built for MinGW.
+
+In later versions of Windows these utilities may need to be "run as
+Administrator" for disks and other devices to be seen. If not those devices
+will simply not be found as calls to query them fail with access permission
+problems.
+
+Supported Utilities
+===================
+Here is a list of utilities that have been ported:
+    sg_compare_and_write
+    sg_decode_sense
+    sg_format
+    sg_get_config
+    sg_get_lba_status
+    sg_ident
+    sg_inq          [dropped ATA IDENTIFY DEVICE capability]
+    sg_logs
+    sg_luns
+    sg_modes
+    sg_persist
+    sg_opcodes
+    sg_prevent
+    sg_raw
+    sg_rdac
+    sg_read_block_limits
+    sg_read_buffer
+    sg_read_long
+    sg_readcap
+    sg_reassign
+    sg_referrals
+    sg_requests
+    sg_rmsn
+    sg_rtpg
+    sg_safte
+    sg_sanitize
+    sg_sat_identify
+    sg_sat_phy_event
+    sg_sat_set_features
+    sg_scan         [this is Windows specific]
+    sg_senddiag
+    sg_ses
+    sg_start
+    sg_stpg
+    sg_sync
+    sg_turs
+    sg_unmap
+    sg_verify
+    sg_vpd
+    sg_wr_mode
+    sg_write_buffer
+    sg_write_long
+    sg_write_same
+
+Most utility names are indicative of the main SCSI command that they execute.
+Some utilities are slightly higher level, for example sg_ses fetches SCSI
+Enclosure Services (SES) status pages and can send control pages. Each
+utility has a man page (placed in section 8). There is summary of the mapping
+between utility names and the SCSI commands they execute in the COVERAGE
+file. An overview of sg3_utils can be found at:
+http://sg.danny.cz/sg/sg3_utils.html .
+A copy of the "sg3_utils.html" file is in the "doc" subdirectory.
+
+Some man pages have examples which use Linux device names which hopefully
+will not confuse Windows users.
+
+Two pass-through variants
+=========================
+The sg_pt_win32.c file uses the Windows SCSI Pass Through interface.
+That is often shortened to SPT or SPTI. There are two DeviceIoControl()
+ioctl variants provided: IOCTL_SCSI_PASS_THROUGH and
+IOCTL_SCSI_PASS_THROUGH_DIRECT. The former involves double handling of
+data (and perhaps an upper limit on the data length associated with
+one SCSI command; MS documentation mentions 16 KB). The "direct"
+variant passes a pointer from the user space and to be faster looks
+and more versatile.
+
+However the "direct" variant has potentially (unquantified) alignment
+requirements and may not be (well) implemented by the hardware driver.
+In practice some users have reported errors (e.g. 1117: a non-descript
+IO error) when the direct variant is used.
+
+Hence the non-direct variant is the default. The default size limit
+on the data buffer is set at 16 KB but if the user asks for more
+the data buffer will be extended. The OS or the hardware drivers
+may reject the extended data buffer but we tried.
+
+The package can be built using the direct variant with:
+   ./configure --enable-win32-spt-direct
+rather than:
+   ./configure
+prior to the 'make' call.
+
+In sg3_utils version 1.31 run-time selection of the direct or indirect
+interface was added with the scsi_pt_win32_direct(int state_direct)
+function declared in sg_pt.h. The default is indirect unless
+'./configure --enable-win32-spt-direct' was used in the build. If
+'state_direct' is 1 then the direct interface is used and if it is 0
+the indirect interface is used.
+
+Both sg_read_buffer and sg_write_buffer can transfer buffers larger
+than 16 KB. So in sg3_utils version 1.31, they use this new function
+to set direct interface mode. This is regardless of whether or
+not "--enable-win32-spt-direct" is given to ./configure .
+
+Details
+=======
+Most of the ported utilities listed above use SCSI command functions
+declared in sg_cmds_*.h headers . Those SCSI command functions are
+implemented in the corresponding ".c" files. The ".c" files pass SCSI
+commands to the host operating system via an interface declared in sg_pt.h .
+There are currently five implementations of that interface depending on
+the host operating system:
+  - sg_pt_linux.c
+  - sg_pt_freebsd.c
+  - sg_pt_osf1.c  [Tru64]
+  - sg_pt_solaris.c
+  - sg_pt_win32.c
+
+The ASPI32 interface which requires a dll from Adaptec is not supported.
+
+The sg_scan utility is a special version for Windows and it attempts to show
+the various available storage device names, one per line. Here is an example
+of sg_scan's output:
+
+# sg_scan
+PD0     [C]     FUJITSU   MHY2160BH         0000
+PD1     [DF]    WD        2500BEV External  1.05  WD-WXE90
+CDROM0  [E]     MATSHITA DVD/CDRW UJDA775  CB03
+
+Here is an example with added bus type:
+
+# sg_scan -b
+PD0     [C]     <Ata  >  FUJITSU   MHY2160BH         0000
+PD1     [DF]    <Usb  >  WD        2500BEV External  1.05  WD-WXE90
+CDROM0  [E]     <Atapi>  MATSHITA DVD/CDRW UJDA775  CB03
+
+Here is an example with added SCSI adapter scan:
+
+# sg_scan -b -s
+PD0     [C]     <Ata  >  ST380011A  8.01
+PD1             <Scsi >  SEAGATE   ST373455SS        2189
+PD2             <Scsi >  ATA       ST3160812AS       D
+PD3             <Scsi >  SEAGATE   ST336754SS        0003
+CDROM0  [F]     <Atapi>  HL-DT-ST DVDRAM GSA-4163B  A103
+TAPE0           <Scsi >  SONY      SDT-7000          0192
+
+SCSI0:0,0,0   claimed=1 pdt=0h dubious  ST380011  A                 8.01
+SCSI1:0,0,0   claimed=1 pdt=5h          HL-DT-ST  DVDRAM GSA-4163B  A103
+SCSI2:0,6,0   claimed=1 pdt=1h          SONY      SDT-7000          0192
+SCSI5:0,17,0  claimed=1 pdt=0h          SEAGATE   ST373455SS        2189
+SCSI5:0,19,0  claimed=1 pdt=0h          ATA       ST3160812AS       D
+SCSI5:0,21,0  claimed=1 pdt=0h          SEAGATE   ST336754SS        0003
+SCSI5:0,112,0 claimed=0 pdt=10h         LSI       PSEUDO DEVICE     2.34
+
+The storage devices scanned are PhysicalDrive<n> (shortened form PD<n> used),
+CDROM<n> (which includes DVD and BD drives) and TAPE<n>. There is also an
+optional SCSI adapter scan with device names of the form SCSI<n>:<b>:<t>:<l> .
+These only come into play for devices that are not claimed by one of the
+storage class drivers. The "LSI PSEUDO DEVICE" device above is an example
+of an unclaimed device. The SCSI adapter scan does not show USB and IEEE
+1394 connected devices.
+
+Volume names (e.g. "C:") that match a storage device (or perhaps a
+partition within that device) are shown in brackets. Notice there can be
+zero, one or more volume names for each storage device. Up to four volume
+names are listed in brackets, if there are more a "+" is added after the
+fourth.
+
+Several utilities have conditional compilation sections based on
+the SG_LIB_MINGW define. For those who want to try native C compilers
+on Windows setting the SG_LIB_MINGW define may help.
+
+Build environments
+==================
+This package uses autotools infrastructure with the now common
+"./configure ; make ; make install" sequence needed to build and install
+from the source found in the tarball. Two Windows environments for building
+Unix code are supported: cygwin and MinGW. If the "./configure" sequence
+fails try using the ./autogen.sh prior to that sequence. The executables
+produced are console applications that can be executed in either a cygwin,
+MSYS or "cmd" shell. Various build options are available by giving
+command line options to "./configure", see the INSTALL file for generic
+information about the build infrastructure.
+
+MinGW can be used to cross built on some Redhat (Linux) platforms. After
+loading the cross build packages, the ./configure call in the normal
+autotools sequence should be replaced by either mingw32-configure or
+mingw64-configure. These scripts will set up the environment for
+the cross build and then call ./configure (so this invocation should be
+made in the top level of the untarred source). Options given to either
+script (e.g. --enable-win32-spt-direct) will be passed through to
+./configure .
+
+Binary and Text files
+=====================
+A problem has been reported with binary output being written in a MinGW
+environment (or executables build by MinGW). Windows has a concept of text
+and binary files which is not found in Unix. Recent versions of MinGW
+default to opening files in text mode. This can lead to binary output
+(such as when the '--raw' option is given) having 0xa (i.e. LF) translated
+to 0xd,0xa (i.e. CR,LF). sg3_utils version 1.26 attempts to fix this
+problem by changing what it knows to be binary output files to "binary
+mode" with the setmode() Windows command.
+
+
+Douglas Gilbert
+24th September 2014
diff --git a/sg3_utils/aclocal.m4 b/sg3_utils/aclocal.m4
new file mode 100644
index 0000000..c6fd25a
--- /dev/null
+++ b/sg3_utils/aclocal.m4
@@ -0,0 +1,9819 @@
+# generated automatically by aclocal 1.15 -*- Autoconf -*-
+
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
+[m4_warning([this file was generated for autoconf 2.69.
+You have another version of autoconf.  It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically 'autoreconf'.])])
+
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+#   This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+])
+
+# serial 57 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+       [m4_default([$3],
+		   [m4_fatal([Libtool version $1 or higher is required],
+		             63)])],
+       [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+  *\ * | *\	*)
+    AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}])
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
+m4_defun([_LT_CC_BASENAME],
+[for cc_temp in $1""; do
+  case $cc_temp in
+    compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+    distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+
+_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl
+dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_WITH_SYSROOT])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    _LT_PATH_MAGIC
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PREPARE_SED_QUOTE_VARS
+# --------------------------
+# Define a few sed substitution that help us do robust quoting.
+m4_defun([_LT_PREPARE_SED_QUOTE_VARS],
+[# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+])
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from `configure', and `config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool.  Notably,
+# `config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain="$ac_aux_dir/ltmain.sh"
+])# _LT_PROG_LTMAIN
+
+
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the `libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+              [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME.  Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+    [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+	[m4_ifval([$1], [$1], [$2])])
+    lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+    m4_ifval([$4],
+	[lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+    lt_dict_add_subkey([lt_decl_dict], [$2],
+	[tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+  [0], [m4_fatal([$0: too few arguments: $#])],
+  [1], [m4_fatal([$0: too few arguments: $#: $1])],
+  [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+  [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+  [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+    m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+    m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+     m4_if([$2], [],
+	   m4_quote(lt_decl_varnames),
+	m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+			lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to `config.status' so that its
+# declaration there will have the same value as in `configure'.  VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly.  In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+#    <var>='`$ECHO "$<var>" | $SED "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+    [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags="_LT_TAGS"dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+#    # Some comment about what VAR is for.
+#    visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+					   [description])))[]dnl
+m4_pushdef([_libtool_name],
+    m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+    [0], [_libtool_name=[$]$1],
+    [1], [_libtool_name=$lt_[]$1],
+    [2], [_libtool_name=$lt_[]$1],
+    [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
+# script.  Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+    m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS.  Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into `config.status', and then the shell code to quote escape them in
+# for loops in `config.status'.  Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+	dnl If the libtool generation code has been placed in $CONFIG_LT,
+	dnl instead of duplicating it all over again into config.status,
+	dnl then we will have config.status run $CONFIG_LT later, so it
+	dnl needs to know what name is stored there:
+        [AC_CONFIG_COMMANDS([libtool],
+            [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+    dnl If the libtool generation code is destined for config.status,
+    dnl expand the accumulated commands and init code now:
+    [AC_CONFIG_COMMANDS([libtool],
+        [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$[]1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+# _LT_GENERATED_FILE_INIT(FILE, [COMMENT])
+# ------------------------------------
+# Generate a child script FILE with all initialization necessary to
+# reuse the environment learned by the parent script, and make the
+# file executable.  If COMMENT is supplied, it is inserted after the
+# `#!' sequence but before initialization text begins.  After this
+# macro, additional text can be appended to FILE to form the body of
+# the child script.  The macro ends with non-zero status if the
+# file could not be fully written (such as if the disk is full).
+m4_ifdef([AS_INIT_GENERATED],
+[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])],
+[m4_defun([_LT_GENERATED_FILE_INIT],
+[m4_require([AS_PREPARE])]dnl
+[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl
+[lt_write_fail=0
+cat >$1 <<_ASEOF || lt_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+$2
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$1 <<\_ASEOF || lt_write_fail=1
+AS_SHELL_SANITIZE
+_AS_PREPARE
+exec AS_MESSAGE_FD>&1
+_ASEOF
+test $lt_write_fail = 0 && chmod +x $1[]dnl
+m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+_LT_GENERATED_FILE_INIT(["$CONFIG_LT"],
+[# Run this file to recreate a libtool stub with the current configuration.])
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+lt_cl_silent=false
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+  echo
+  AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+  -h, --help      print this help, then exit
+  -V, --version   print version number, then exit
+  -q, --quiet     do not print progress messages
+  -d, --debug     don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2011 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+  case $[1] in
+    --version | --v* | -V )
+      echo "$lt_cl_version"; exit 0 ;;
+    --help | --h* | -h )
+      echo "$lt_cl_help"; exit 0 ;;
+    --debug | --d* | -d )
+      debug=: ;;
+    --quiet | --q* | --silent | --s* | -q )
+      lt_cl_silent=: ;;
+
+    -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+    *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+  esac
+  shift
+done
+
+if $lt_cl_silent; then
+  exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure.  Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+lt_cl_success=:
+test "$silent" = yes &&
+  lt_config_lt_args="$lt_config_lt_args --quiet"
+exec AS_MESSAGE_LOG_FD>/dev/null
+$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+exec AS_MESSAGE_LOG_FD>>config.log
+$lt_cl_success || AS_EXIT(1)
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars.  Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+  m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+  m4_if(_LT_TAG, [C], [
+    # See if we are running on zsh, and set the options which allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}" ; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile="${ofile}T"
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+  _LT_PROG_LTMAIN
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" \
+     || (rm -f "$cfgfile"; exit 1)
+
+  _LT_PROG_REPLACE_SHELLFNS
+
+   mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    TIMESTAMP='$TIMESTAMP'
+    RM='$RM'
+    ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+#    autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+  [C],			[_LT_LANG(C)],
+  [C++],		[_LT_LANG(CXX)],
+  [Go],			[_LT_LANG(GO)],
+  [Java],		[_LT_LANG(GCJ)],
+  [Fortran 77],		[_LT_LANG(F77)],
+  [Fortran],		[_LT_LANG(FC)],
+  [Windows Resource],	[_LT_LANG(RC)],
+  [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+    [_LT_LANG($1)],
+    [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+  [LT_SUPPORTED_TAG([$1])dnl
+  m4_append([_LT_TAGS], [$1 ])dnl
+  m4_define([_LT_LANG_]$1[_enabled], [])dnl
+  _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+m4_ifndef([AC_PROG_GO], [
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_GO.  When it is available in    #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+m4_defun([AC_PROG_GO],
+[AC_LANG_PUSH(Go)dnl
+AC_ARG_VAR([GOC],     [Go compiler command])dnl
+AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl
+_AC_ARG_VAR_LDFLAGS()dnl
+AC_CHECK_TOOL(GOC, gccgo)
+if test -z "$GOC"; then
+  if test -n "$ac_tool_prefix"; then
+    AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo])
+  fi
+fi
+if test -z "$GOC"; then
+  AC_CHECK_PROG(GOC, gccgo, gccgo, false)
+fi
+])#m4_defun
+])#m4_ifndef
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+  [LT_LANG(CXX)],
+  [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+  [LT_LANG(F77)],
+  [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+  [LT_LANG(FC)],
+  [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+  [LT_LANG(GCJ)],
+  [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+    [LT_LANG(GCJ)],
+    [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+      [LT_LANG(GCJ)],
+      [m4_ifdef([AC_PROG_GCJ],
+	[m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([A][M_PROG_GCJ],
+	[m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([LT_PROG_GCJ],
+	[m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([AC_PROG_GO],
+  [LT_LANG(GO)],
+  [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+  [LT_LANG(RC)],
+  [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+dnl AC_DEFUN([AC_LIBTOOL_RC], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+  case $host_os in
+    rhapsody* | darwin*)
+    AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+    AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+    AC_CHECK_TOOL([LIPO], [lipo], [:])
+    AC_CHECK_TOOL([OTOOL], [otool], [:])
+    AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+    _LT_DECL([], [DSYMUTIL], [1],
+      [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+    _LT_DECL([], [NMEDIT], [1],
+      [Tool to change global to local symbols on Mac OS X])
+    _LT_DECL([], [LIPO], [1],
+      [Tool to manipulate fat objects and archives on Mac OS X])
+    _LT_DECL([], [OTOOL], [1],
+      [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+    _LT_DECL([], [OTOOL64], [1],
+      [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+    AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+      [lt_cv_apple_cc_single_mod=no
+      if test -z "${LT_MULTI_MODULE}"; then
+	# By default we will add the -single_module flag. You can override
+	# by either setting the environment variable LT_MULTI_MODULE
+	# non-empty at configure time, or by adding -multi_module to the
+	# link flags.
+	rm -rf libconftest.dylib*
+	echo "int foo(void){return 1;}" > conftest.c
+	echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+	$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+	  -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+	# If there is a non-empty error log, and "single_module"
+	# appears in it, assume the flag caused a linker warning
+        if test -s conftest.err && $GREP single_module conftest.err; then
+	  cat conftest.err >&AS_MESSAGE_LOG_FD
+	# Otherwise, if the output was created with a 0 exit code from
+	# the compiler, it worked.
+	elif test -f libconftest.dylib && test $_lt_result -eq 0; then
+	  lt_cv_apple_cc_single_mod=yes
+	else
+	  cat conftest.err >&AS_MESSAGE_LOG_FD
+	fi
+	rm -rf libconftest.dylib*
+	rm -f conftest.*
+      fi])
+
+    AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+      [lt_cv_ld_exported_symbols_list],
+      [lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+	[lt_cv_ld_exported_symbols_list=yes],
+	[lt_cv_ld_exported_symbols_list=no])
+	LDFLAGS="$save_LDFLAGS"
+    ])
+
+    AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load],
+      [lt_cv_ld_force_load=no
+      cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
+      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
+      echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
+      $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
+      echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
+      $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
+      cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD
+      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+      _lt_result=$?
+      if test -s conftest.err && $GREP force_load conftest.err; then
+	cat conftest.err >&AS_MESSAGE_LOG_FD
+      elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then
+	lt_cv_ld_force_load=yes
+      else
+	cat conftest.err >&AS_MESSAGE_LOG_FD
+      fi
+        rm -f conftest.err libconftest.a conftest conftest.c
+        rm -rf conftest.dSYM
+    ])
+    case $host_os in
+    rhapsody* | darwin1.[[012]])
+      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+	10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+	  _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+	10.[[012]]*)
+	  _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+	10.*)
+	  _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES([TAG])
+# ---------------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+  m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_automatic, $1)=yes
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  if test "$lt_cv_ld_force_load" = "yes"; then
+    _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+    m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes],
+                  [FC],  [_LT_TAGVAR(compiler_needs_object, $1)=yes])
+  else
+    _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+  fi
+  _LT_TAGVAR(link_all_deplibs, $1)=yes
+  _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=func_echo_all
+    _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+    m4_if([$1], [CXX],
+[   if test "$lt_cv_apple_cc_single_mod" != "yes"; then
+      _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
+      _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+    fi
+],[])
+  else
+  _LT_TAGVAR(ld_shlibs, $1)=no
+  fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX([TAGNAME])
+# ----------------------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+# Store the results from the different compilers for each TAGNAME.
+# Allow to override them for all tags through lt_cv_aix_libpath.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])],
+  [AC_LINK_IFELSE([AC_LANG_PROGRAM],[
+  lt_aix_libpath_sed='[
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }]'
+  _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+    _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi],[])
+  if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+    _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib"
+  fi
+  ])
+  aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])
+fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[m4_divert_text([M4SH-INIT], [$1
+])])# _LT_SHELL_INIT
+
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Find how we can fake an echo command that does not interpret backslash.
+# In particular, with Autoconf 2.60 or later we add some code to the start
+# of the generated configure script which will find a shell with a builtin
+# printf (which we can use as an echo command).
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+AC_MSG_CHECKING([how to print strings])
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
+else
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$[]1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO "$*" 
+}
+
+case "$ECHO" in
+  printf*) AC_MSG_RESULT([printf]) ;;
+  print*) AC_MSG_RESULT([print -r]) ;;
+  *) AC_MSG_RESULT([cat]) ;;
+esac
+
+m4_ifdef([_AS_DETECT_SUGGESTED],
+[_AS_DETECT_SUGGESTED([
+  test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || (
+    ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+    ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+    ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+    PATH=/empty FPATH=/empty; export PATH FPATH
+    test "X`printf %s $ECHO`" = "X$ECHO" \
+      || test "X`print -r -- $ECHO`" = "X$ECHO" )])])
+
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_WITH_SYSROOT
+# ----------------
+AC_DEFUN([_LT_WITH_SYSROOT],
+[AC_MSG_CHECKING([for sysroot])
+AC_ARG_WITH([sysroot],
+[  --with-sysroot[=DIR] Search for dependent libraries within DIR
+                        (or the compiler's sysroot if not specified).],
+[], [with_sysroot=no])
+
+dnl lt_sysroot will always be passed unquoted.  We quote it here
+dnl in case the user passed a directory name.
+lt_sysroot=
+case ${with_sysroot} in #(
+ yes)
+   if test "$GCC" = yes; then
+     lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+   fi
+   ;; #(
+ /*)
+   lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+   ;; #(
+ no|'')
+   ;; #(
+ *)
+   AC_MSG_RESULT([${with_sysroot}])
+   AC_MSG_ERROR([The sysroot must be an absolute path.])
+   ;;
+esac
+
+ AC_MSG_RESULT([${lt_sysroot:-no}])
+_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl
+[dependent libraries, and in which our libraries should be installed.])])
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+  [AS_HELP_STRING([--disable-libtool-lock],
+    [avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+	HPUX_IA64_MODE="32"
+	;;
+      *ELF-64*)
+	HPUX_IA64_MODE="64"
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    if test "$lt_cv_prog_gnu_ld" = yes; then
+      case `/usr/bin/file conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -melf32bsmip"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -melf32bmipn32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -melf64bmip"
+	;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -32"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -n32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -64"
+	  ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_i386_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    case `/usr/bin/file conftest.o` in
+	      *x86-64*)
+		LD="${LD-ld} -m elf32_x86_64"
+		;;
+	      *)
+		LD="${LD-ld} -m elf_i386"
+		;;
+	    esac
+	    ;;
+	  powerpc64le-*)
+	    LD="${LD-ld} -m elf32lppclinux"
+	    ;;
+	  powerpc64-*)
+	    LD="${LD-ld} -m elf32ppclinux"
+	    ;;
+	  s390x-*linux*)
+	    LD="${LD-ld} -m elf_s390"
+	    ;;
+	  sparc64-*linux*)
+	    LD="${LD-ld} -m elf32_sparc"
+	    ;;
+	esac
+	;;
+      *64-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_x86_64_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    LD="${LD-ld} -m elf_x86_64"
+	    ;;
+	  powerpcle-*)
+	    LD="${LD-ld} -m elf64lppc"
+	    ;;
+	  powerpc-*)
+	    LD="${LD-ld} -m elf64ppc"
+	    ;;
+	  s390*-*linux*|s390*-*tpf*)
+	    LD="${LD-ld} -m elf64_s390"
+	    ;;
+	  sparc*-*linux*)
+	    LD="${LD-ld} -m elf64_sparc"
+	    ;;
+	esac
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+    [AC_LANG_PUSH(C)
+     AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+     AC_LANG_POP])
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+*-*solaris*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*)
+        case $host in
+        i?86-*-solaris*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        sparc*-*-solaris*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+        esac
+        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.
+        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+          LD="${LD-ld}_sol2"
+        fi
+        ;;
+      *)
+	if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+	  LD="${LD-ld} -64"
+	fi
+	;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks="$enable_libtool_lock"
+])# _LT_ENABLE_LOCK
+
+
+# _LT_PROG_AR
+# -----------
+m4_defun([_LT_PROG_AR],
+[AC_CHECK_TOOLS(AR, [ar], false)
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
+
+AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
+  [lt_cv_ar_at_file=no
+   AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
+     [echo conftest.$ac_objext > conftest.lst
+      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD'
+      AC_TRY_EVAL([lt_ar_try])
+      if test "$ac_status" -eq 0; then
+	# Ensure the archiver fails upon bogus file names.
+	rm -f conftest.$ac_objext libconftest.a
+	AC_TRY_EVAL([lt_ar_try])
+	if test "$ac_status" -ne 0; then
+          lt_cv_ar_at_file=@
+        fi
+      fi
+      rm -f conftest.* libconftest.a
+     ])
+  ])
+
+if test "x$lt_cv_ar_at_file" = xno; then
+  archiver_list_spec=
+else
+  archiver_list_spec=$lt_cv_ar_at_file
+fi
+_LT_DECL([], [archiver_list_spec], [1],
+  [How to feed a file listing to the archiver])
+])# _LT_PROG_AR
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[_LT_PROG_AR
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+    [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+  darwin*)
+    lock_old_archive_extraction=yes ;;
+  *)
+    lock_old_archive_extraction=no ;;
+esac
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+    [Commands used to build an old-style archive])
+_LT_DECL([], [lock_old_archive_extraction], [0],
+    [Whether to use a lock for old archive extraction])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#		[OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$3"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       $2=yes
+     fi
+   fi
+   $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+    m4_if([$5], , :, [$5])
+else
+    m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#                  [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $3"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&AS_MESSAGE_LOG_FD
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         $2=yes
+       fi
+     else
+       $2=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+    m4_if([$4], , :, [$4])
+else
+    m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+  i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  mint*)
+    # On MiNT this can take a long time and run out of memory.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536	# usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  os2*)
+    # The test takes a long time on OS/2.
+    lt_cv_sys_max_cmd_len=8192
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[	 ]]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len" && \
+	test undefined != "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8 ; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \
+	         = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+	      test $i != 17 # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+  AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+  AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+    [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+#                      ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "$cross_compiling" = yes; then :
+  [$4]
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+[#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+	  if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+	}
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}]
+_LT_EOF
+  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) $1 ;;
+      x$lt_dlneed_uscore) $2 ;;
+      x$lt_dlunknown|x*) $3 ;;
+    esac
+  else :
+    # compilation failed
+    $3
+  fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    AC_CHECK_LIB([dl], [dlopen],
+		[lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ])
+    ;;
+
+  *)
+    AC_CHECK_FUNC([shl_load],
+	  [lt_cv_dlopen="shl_load"],
+      [AC_CHECK_LIB([dld], [shl_load],
+	    [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+	[AC_CHECK_FUNC([dlopen],
+	      [lt_cv_dlopen="dlopen"],
+	  [AC_CHECK_LIB([dl], [dlopen],
+		[lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+	    [AC_CHECK_LIB([svld], [dlopen],
+		  [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+	      [AC_CHECK_LIB([dld], [dld_link],
+		    [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+	      ])
+	    ])
+	  ])
+	])
+      ])
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    AC_CACHE_CHECK([whether a program can dlopen itself],
+	  lt_cv_dlopen_self, [dnl
+	  _LT_TRY_DLOPEN_SELF(
+	    lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+	    lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+    ])
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+	  lt_cv_dlopen_self_static, [dnl
+	  _LT_TRY_DLOPEN_SELF(
+	    lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+	    lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
+      ])
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+	 [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+	 [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+	 [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+     fi
+   fi
+   chmod u+w . 2>&AS_MESSAGE_LOG_FD
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+	[Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links="nottested"
+if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  AC_MSG_CHECKING([if we can lock with hard links])
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  AC_MSG_RESULT([$hard_links])
+  if test "$hard_links" = no; then
+    AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+         [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
+  [Define to the sub-directory in which libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+   test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+   test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
+     test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
+    # Linking always hardcodes the temporary library directory.
+    _LT_TAGVAR(hardcode_action, $1)=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    _LT_TAGVAR(hardcode_action, $1)=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
+   test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+    [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP" ; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      AC_MSG_RESULT([yes])
+    else
+      AC_MSG_RESULT([no])
+    fi
+    ;;
+  *)
+    AC_MSG_RESULT([no])
+    ;;
+  esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+	[], [
+if test "$GCC" = yes; then
+  case $host_os in
+    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+    *) lt_awk_arg="/^libraries:/" ;;
+  esac
+  case $host_os in
+    mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;;
+    *) lt_sed_strip_eq="s,=/,/,g" ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+  case $lt_search_path_spec in
+  *\;*)
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+    ;;
+  *)
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+    ;;
+  esac
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary.
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+    else
+      test -d "$lt_sys_path" && \
+	lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+  lt_foo="";
+  lt_count=0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo="/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+  if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+  # AWK program above erroneously prepends '/' to C:/dos/paths
+  # for these hosts.
+  case $host_os in
+    mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+      $SED 's,/\([[A-Za-z]]:\),\1,g'` ;;
+  esac
+  sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[[4-9]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[[01]] | aix4.[[01]].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[[45]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+m4_if([$1], [],[
+      sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl*)
+    # Native MSVC
+    libname_spec='$name'
+    soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+    library_names_spec='${libname}.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec="$LIB"
+      if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC wrapper
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[[23]].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+  freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[[3-9]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux # correct to gnu/linux during the next big refactor
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath],
+    [lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+	 LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+      [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+	 [lt_cv_shlibpath_overrides_runpath=yes])])
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+    ])
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*)	need_version=yes ;;
+    *)				need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[[89]] | openbsd2.[[89]].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+	;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+    [Variables whose values should be saved in libtool wrapper scripts and
+    restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+    [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0],  [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+    [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+    [[List of archive names.  First name is the real one, the rest are links.
+    The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+    [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [install_override_mode], [1],
+    [Permission mode override for installation of shared libraries])
+_LT_DECL([], [postinstall_cmds], [2],
+    [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+    [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+    [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+    [[As "finish_cmds", except a single script fragment to be evaled but
+    not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+    [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+    [Compile-time system search path for libraries])
+_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
+    [Run-time system search path for libraries])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program which can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] |  ?:[\\/]*])
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word.  This closes a longstanding sh security hole.
+  ac_dummy="m4_if([$2], , $PATH, [$2])"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$1; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  AC_MSG_RESULT($MAGIC_CMD)
+else
+  AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+	 [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program which can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+  else
+    MAGIC_CMD=:
+  fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PROG_ECHO_BACKSLASH])dnl
+
+AC_ARG_WITH([gnu-ld],
+    [AS_HELP_STRING([--with-gnu-ld],
+	[assume the C compiler uses GNU ld @<:@default=no@:>@])],
+    [test "$withval" = no || with_gnu_ld=yes],
+    [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by $CC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]]* | ?:[[\\/]]*)
+      re_direlt='/[[^/]][[^/]]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test "$with_gnu_ld" != no && break
+	;;
+      *)
+	test "$with_gnu_ld" != yes && break
+	;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+#   -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+  lt_cv_ld_reload_flag,
+  [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    if test "$GCC" != yes; then
+      reload_cmds=false
+    fi
+    ;;
+  darwin*)
+    if test "$GCC" = yes; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+_LT_TAGDECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_TAGDECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[[45]]*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
+  if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    # Keep this pattern in sync with the one in func_win32_libid.
+    lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc*)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+haiku*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]']
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[[3-9]]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+])
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+  case $host_os in
+  mingw* | pw32*)
+    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+      want_nocaseglob=yes
+    else
+      file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"`
+    fi
+    ;;
+  esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+    [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+    [Command to use when deplibs_check_method = "file_magic"])
+_LT_DECL([], [file_magic_glob], [1],
+    [How to find potential files when deplibs_check_method = "file_magic"])
+_LT_DECL([], [want_nocaseglob], [1],
+    [Find potential files using nocaseglob when deplibs_check_method = "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_nm_to_check="${ac_tool_prefix}nm"
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS="$lt_save_ifs"
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm="$ac_dir/$lt_tmp_nm"
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+	# Check to see if the nm accepts a BSD-compat flag.
+	# Adding the `sed 1q' prevents false positives on HP-UX, which says:
+	#   nm: unknown option "B" ignored
+	# Tru64's nm complains that /dev/null is an invalid object file
+	case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+	*/dev/null* | *'Invalid file or object type'*)
+	  lt_cv_path_NM="$tmp_nm -B"
+	  break
+	  ;;
+	*)
+	  case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+	  */dev/null*)
+	    lt_cv_path_NM="$tmp_nm -p"
+	    break
+	    ;;
+	  *)
+	    lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	    continue # so that we can try to find one that supports BSD flags
+	    ;;
+	  esac
+	  ;;
+	esac
+      fi
+    done
+    IFS="$lt_save_ifs"
+  done
+  : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+  NM="$lt_cv_path_NM"
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$DUMPBIN"; then :
+    # Let the user override the test.
+  else
+    AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :)
+    case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+    *COFF*)
+      DUMPBIN="$DUMPBIN -symbols"
+      ;;
+    *)
+      DUMPBIN=:
+      ;;
+    esac
+  fi
+  AC_SUBST([DUMPBIN])
+  if test "$DUMPBIN" != ":"; then
+    NM="$DUMPBIN"
+  fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+  [lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD)
+  cat conftest.out >&AS_MESSAGE_LOG_FD
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+# --------------------------------
+# how to determine the name of the shared library
+# associated with a specific link library.
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+m4_require([_LT_DECL_DLLTOOL])
+AC_CACHE_CHECK([how to associate runtime and link libraries],
+lt_cv_sharedlib_from_linklib_cmd,
+[lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+  # two different shell functions defined in ltmain.sh
+  # decide which to use based on capabilities of $DLLTOOL
+  case `$DLLTOOL --help 2>&1` in
+  *--identify-strict*)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+    ;;
+  *)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+    ;;
+  esac
+  ;;
+*)
+  # fallback: assume linklib IS sharedlib
+  lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+  ;;
+esac
+])
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+_LT_DECL([], [sharedlib_from_linklib_cmd], [1],
+    [Command to associate shared and link libraries])
+])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+
+
+# _LT_PATH_MANIFEST_TOOL
+# ----------------------
+# locate the manifest tool
+m4_defun([_LT_PATH_MANIFEST_TOOL],
+[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :)
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool],
+  [lt_cv_path_mainfest_tool=no
+  echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD
+  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+    lt_cv_path_mainfest_tool=yes
+  fi
+  rm -f conftest*])
+if test "x$lt_cv_path_mainfest_tool" != xyes; then
+  MANIFEST_TOOL=:
+fi
+_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl
+])# _LT_PATH_MANIFEST_TOOL
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+  # These system don't have libm, or don't need it
+  ;;
+*-ncr-sysv4.3*)
+  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+  AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+  ;;
+*)
+  AC_CHECK_LIB(m, cos, LIBM="-lm")
+  ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test "$GCC" = yes; then
+  case $cc_basename in
+  nvcc*)
+    _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;;
+  esac
+
+  _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+    lt_cv_prog_compiler_rtti_exceptions,
+    [-fno-rtti -fno-exceptions], [],
+    [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+	[Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[[BCDT]]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[[ABCDGISTW]]'
+  ;;
+hpux*)
+  if test "$host_cpu" = ia64; then
+    symcode='[[ABCDEGRST]]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[[BCDEGRST]]'
+  ;;
+osf*)
+  symcode='[[BCDEGQRST]]'
+  ;;
+solaris*)
+  symcode='[[BDRT]]'
+  ;;
+sco3.2v5*)
+  symcode='[[DT]]'
+  ;;
+sysv4.2uw2*)
+  symcode='[[DT]]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[[ABDT]]'
+  ;;
+sysv4)
+  symcode='[[DFNSTU]]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function
+    # and D for any global variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK ['"\
+"     {last_section=section; section=\$ 3};"\
+"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx]"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[	 ]]\($symcode$symcode*\)[[	 ]][[	 ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+  lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if AC_TRY_EVAL(ac_compile); then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+	if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT@&t@_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT@&t@_DLSYM_CONST
+#else
+# define LT@&t@_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+	  cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+LT@&t@_DLSYM_CONST struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+	  $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+	  cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  lt_globsym_save_LIBS=$LIBS
+	  lt_globsym_save_CFLAGS=$CFLAGS
+	  LIBS="conftstm.$ac_objext"
+	  CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+	  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+	    pipe_works=yes
+	  fi
+	  LIBS=$lt_globsym_save_LIBS
+	  CFLAGS=$lt_globsym_save_CFLAGS
+	else
+	  echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+    fi
+  else
+    echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  AC_MSG_RESULT(failed)
+else
+  AC_MSG_RESULT(ok)
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then
+  nm_file_list_spec='@'
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+    [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+    [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_c_name_address],
+    [lt_cv_sys_global_symbol_to_c_name_address], [1],
+    [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+    [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+    [Transform the output of nm in a C name address pair when lib prefix is needed])
+_LT_DECL([], [nm_file_list_spec], [1],
+    [Specify filename containing input files for $NM])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+m4_if([$1], [CXX], [
+  # C++ specific cases for pic, static, wl, etc.
+  if test "$GXX" = yes; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+    aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    mingw* | cygwin* | os2* | pw32* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+	[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+    *djgpp*)
+      # DJGPP does not support shared libraries at all
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+      ;;
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)=
+      ;;
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+	;;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	;;
+      esac
+      ;;
+    *qnx* | *nto*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+  else
+    case $host_os in
+      aix[[4-9]]*)
+	# All AIX code is PIC.
+	if test "$host_cpu" = ia64; then
+	  # AIX 5 now supports IA64 processor
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	else
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+	fi
+	;;
+      chorus*)
+	case $cc_basename in
+	cxch68*)
+	  # Green Hills C++ Compiler
+	  # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+	  ;;
+	esac
+	;;
+      mingw* | cygwin* | os2* | pw32* | cegcc*)
+	# This hack is so that the source file can tell whether it is being
+	# built for inclusion in a dll (and should export symbols for example).
+	m4_if([$1], [GCJ], [],
+	  [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+	;;
+      dgux*)
+	case $cc_basename in
+	  ec++*)
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    ;;
+	  ghcx*)
+	    # Green Hills C++ Compiler
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      freebsd* | dragonfly*)
+	# FreeBSD uses GNU C++
+	;;
+      hpux9* | hpux10* | hpux11*)
+	case $cc_basename in
+	  CC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+	    if test "$host_cpu" != ia64; then
+	      _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	    fi
+	    ;;
+	  aCC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+	    case $host_cpu in
+	    hppa*64*|ia64*)
+	      # +Z the default
+	      ;;
+	    *)
+	      _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	      ;;
+	    esac
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      interix*)
+	# This is c89, which is MS Visual C++ (no shared libs)
+	# Anyone wants to do a port?
+	;;
+      irix5* | irix6* | nonstopux*)
+	case $cc_basename in
+	  CC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    # CC pic flag -KPIC is the default.
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+	case $cc_basename in
+	  KCC*)
+	    # KAI C++ Compiler
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	    ;;
+	  ecpc* )
+	    # old Intel C++ for x86_64 which still supported -KPIC.
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+	    ;;
+	  icpc* )
+	    # Intel C++, used to be incompatible with GCC.
+	    # ICC 10 doesn't accept -KPIC any more.
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+	    ;;
+	  pgCC* | pgcpp*)
+	    # Portland Group C++ compiler
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    ;;
+	  cxx*)
+	    # Compaq C++
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    ;;
+	  xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*)
+	    # IBM XL 8.0, 9.0 on PPC and BlueGene
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+	    ;;
+	  *)
+	    case `$CC -V 2>&1 | sed 5q` in
+	    *Sun\ C*)
+	      # Sun C++ 5.9
+	      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+	      ;;
+	    esac
+	    ;;
+	esac
+	;;
+      lynxos*)
+	;;
+      m88k*)
+	;;
+      mvs*)
+	case $cc_basename in
+	  cxx*)
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      netbsd* | netbsdelf*-gnu)
+	;;
+      *qnx* | *nto*)
+        # QNX uses GNU C++, but need to define -shared option too, otherwise
+        # it will coredump.
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+        ;;
+      osf3* | osf4* | osf5*)
+	case $cc_basename in
+	  KCC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+	    ;;
+	  RCC*)
+	    # Rational C++ 2.4.1
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  cxx*)
+	    # Digital/Compaq C++
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    # Make sure the PIC flag is empty.  It appears that all Alpha
+	    # Linux and Compaq Tru64 Unix objects are PIC.
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      psos*)
+	;;
+      solaris*)
+	case $cc_basename in
+	  CC* | sunCC*)
+	    # Sun C++ 4.2, 5.x and Centerline C++
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+	    ;;
+	  gcx*)
+	    # Green Hills C++ Compiler
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      sunos4*)
+	case $cc_basename in
+	  CC*)
+	    # Sun C++ 4.x
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    ;;
+	  lcc*)
+	    # Lucid
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+	case $cc_basename in
+	  CC*)
+	    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	    ;;
+	esac
+	;;
+      tandem*)
+	case $cc_basename in
+	  NCC*)
+	    # NonStop-UX NCC 3.20
+	    _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	    ;;
+	  *)
+	    ;;
+	esac
+	;;
+      vxworks*)
+	;;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+	;;
+    esac
+  fi
+],
+[
+  if test "$GCC" = yes; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+	[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)=
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+	# +Z the default
+	;;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	;;
+      esac
+      ;;
+
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+
+    case $cc_basename in
+    nvcc*) # Cuda Compiler Driver 2.2
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker '
+      if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)"
+      fi
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      else
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      m4_if([$1], [GCJ], [],
+	[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC (with -KPIC) is the default.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+      case $cc_basename in
+      # old Intel for x86_64 which still supported -KPIC.
+      ecc*)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+	;;
+      nagfor*)
+	# NAG Fortran compiler
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	;;
+      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+	# which looks to be a dead project)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+        ;;
+      ccc*)
+        _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+        # All Alpha code is PIC.
+        _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+        ;;
+      xl* | bgxl* | bgf* | mpixl*)
+	# IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+	;;
+      *)
+	case `$CC -V 2>&1 | sed 5q` in
+	*Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*)
+	  # Sun Fortran 8.3 passes all unrecognized flags to the linker
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+	  ;;
+	*Sun\ F* | *Sun*Fortran*)
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+	  ;;
+	*Sun\ C*)
+	  # Sun C 5.9
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	  ;;
+        *Intel*\ [[CF]]*Compiler*)
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+	  ;;
+	*Portland\ Group*)
+	  _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+	  _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+	  _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+	  ;;
+	esac
+	;;
+      esac
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # All OSF/1 code is PIC.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    rdos*)
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+      *)
+	_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	_LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+	_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    unicos*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+    esac
+  fi
+])
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+    ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+    ;;
+esac
+
+AC_CACHE_CHECK([for $compiler option to produce PIC],
+  [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+  _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+    [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+    [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+    [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+     "" | " "*) ;;
+     *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+     esac],
+    [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+     _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+	[Additional compiler flags for building library objects])
+
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+	[How to pass a linker flag through the compiler])
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+  _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+  $lt_tmp_static_flag,
+  [],
+  [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+	[Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  case $host_os in
+  aix[[4-9]]*)
+    # If we're using GNU nm, then we don't want the "-C" option.
+    # -C means demangle to AIX nm, but means don't demangle with GNU nm
+    # Also, AIX nm treats weak defined symbols like other global defined
+    # symbols, whereas GNU nm marks them as "W".
+    if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    else
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    fi
+    ;;
+  pw32*)
+    _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+    ;;
+  cygwin* | mingw* | cegcc*)
+    case $cc_basename in
+    cl*)
+      _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+      ;;
+    *)
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+      ;;
+    esac
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    _LT_TAGVAR(link_all_deplibs, $1)=no
+    ;;
+  *)
+    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+    ;;
+  esac
+], [
+  runpath_var=
+  _LT_TAGVAR(allow_undefined_flag, $1)=
+  _LT_TAGVAR(always_export_symbols, $1)=no
+  _LT_TAGVAR(archive_cmds, $1)=
+  _LT_TAGVAR(archive_expsym_cmds, $1)=
+  _LT_TAGVAR(compiler_needs_object, $1)=no
+  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+  _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(hardcode_automatic, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+  _LT_TAGVAR(hardcode_libdir_separator, $1)=
+  _LT_TAGVAR(hardcode_minus_L, $1)=no
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  _LT_TAGVAR(inherit_rpath, $1)=no
+  _LT_TAGVAR(link_all_deplibs, $1)=unknown
+  _LT_TAGVAR(module_cmds, $1)=
+  _LT_TAGVAR(module_expsym_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+  _LT_TAGVAR(thread_safe_flag_spec, $1)=
+  _LT_TAGVAR(whole_archive_flag_spec, $1)=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  _LT_TAGVAR(include_expsyms, $1)=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    _LT_TAGVAR(link_all_deplibs, $1)=no
+    ;;
+  esac
+
+  _LT_TAGVAR(ld_shlibs, $1)=yes
+
+  # On some targets, GNU ld is compatible enough with the native linker
+  # that we're better off using the native interface for both.
+  lt_use_gnu_ld_interface=no
+  if test "$with_gnu_ld" = yes; then
+    case $host_os in
+      aix*)
+	# The AIX port of GNU ld has always aspired to compatibility
+	# with the native linker.  However, as the warning in the GNU ld
+	# block says, versions before 2.19.5* couldn't really create working
+	# shared libraries, regardless of the interface used.
+	case `$LD -v 2>&1` in
+	  *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+	  *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;;
+	  *\ \(GNU\ Binutils\)\ [[3-9]]*) ;;
+	  *)
+	    lt_use_gnu_ld_interface=yes
+	    ;;
+	esac
+	;;
+      *)
+	lt_use_gnu_ld_interface=yes
+	;;
+    esac
+  fi
+
+  if test "$lt_use_gnu_ld_interface" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v 2>&1` in
+      *GNU\ gold*) supports_anon_versioning=yes ;;
+      *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[[3-9]]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	_LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+      # as there is no search path for DLLs.
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols'
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=no
+      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	# If the export-symbols file already is a .def file (1st line
+	# is EXPORTS), use it as is; otherwise, prepend...
+	_LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	  cp $export_symbols $output_objdir/$soname.def;
+	else
+	  echo EXPORTS > $output_objdir/$soname.def;
+	  cat $export_symbols >> $output_objdir/$soname.def;
+	fi~
+	$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    haiku*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    interix[[3-9]]*)
+      _LT_TAGVAR(hardcode_direct, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+      tmp_diet=no
+      if test "$host_os" = linux-dietlibc; then
+	case $cc_basename in
+	  diet\ *) tmp_diet=yes;;	# linux-dietlibc with static linking (!diet-dyn)
+	esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+	 && test "$tmp_diet" = no
+      then
+	tmp_addflag=' $pic_flag'
+	tmp_sharedflag='-shared'
+	case $cc_basename,$host_cpu in
+        pgcc*)				# Portland Group C compiler
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  tmp_addflag=' $pic_flag'
+	  ;;
+	pgf77* | pgf90* | pgf95* | pgfortran*)
+					# Portland Group f77 and f90 compilers
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  tmp_addflag=' $pic_flag -Mnomain' ;;
+	ecc*,ia64* | icc*,ia64*)	# Intel C compiler on ia64
+	  tmp_addflag=' -i_dynamic' ;;
+	efc*,ia64* | ifort*,ia64*)	# Intel Fortran compiler on ia64
+	  tmp_addflag=' -i_dynamic -nofor_main' ;;
+	ifc* | ifort*)			# Intel Fortran compiler
+	  tmp_addflag=' -nofor_main' ;;
+	lf95*)				# Lahey Fortran 8.1
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)=
+	  tmp_sharedflag='--shared' ;;
+	xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+	  tmp_sharedflag='-qmkshrobj'
+	  tmp_addflag= ;;
+	nvcc*)	# Cuda Compiler Driver 2.2
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  _LT_TAGVAR(compiler_needs_object, $1)=yes
+	  ;;
+	esac
+	case `$CC -V 2>&1 | sed 5q` in
+	*Sun\ C*)			# Sun C 5.9
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  _LT_TAGVAR(compiler_needs_object, $1)=yes
+	  tmp_sharedflag='-G' ;;
+	*Sun\ F*)			# Sun Fortran 8.3
+	  tmp_sharedflag='-G' ;;
+	esac
+	_LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+        if test "x$supports_anon_versioning" = xyes; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+	    cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+	    echo "local: *; };" >> $output_objdir/$libname.ver~
+	    $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+        fi
+
+	case $cc_basename in
+	xlf* | bgf* | bgxlf* | mpixlf*)
+	  # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+	  if test "x$supports_anon_versioning" = xyes; then
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+	      cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+	      echo "local: *; };" >> $output_objdir/$libname.ver~
+	      $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+	  fi
+	  ;;
+	esac
+      else
+        _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+	;;
+	*)
+	  # For security reasons, it is highly recommended that you always
+	  # use absolute paths for naming shared libraries, and exclude the
+	  # DT_RUNPATH tag from executables and libraries.  But doing so
+	  # requires that you compile everything twice, which is a pain.
+	  if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+	  else
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	  fi
+	;;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+    esac
+
+    if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
+      runpath_var=
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	_LT_TAGVAR(hardcode_direct, $1)=unsupported
+      fi
+      ;;
+
+    aix[[4-9]]*)
+      if test "$host_cpu" = ia64; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=""
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to AIX nm, but means don't demangle with GNU nm
+	# Also, AIX nm treats weak defined symbols like other global
+	# defined symbols, whereas GNU nm marks them as "W".
+	if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+	  _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+	else
+	  _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# need to do runtime linking.
+	case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+	  for ld_flag in $LDFLAGS; do
+	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+	    aix_use_runtimelinking=yes
+	    break
+	  fi
+	  done
+	  ;;
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      _LT_TAGVAR(archive_cmds, $1)=''
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+      if test "$GCC" = yes; then
+	case $host_os in aix4.[[012]]|aix4.[[012]].*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" &&
+	   strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+	  then
+	  # We have reworked collect2
+	  :
+	  else
+	  # We have old collect2
+	  _LT_TAGVAR(hardcode_direct, $1)=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  _LT_TAGVAR(hardcode_minus_L, $1)=yes
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+	  _LT_TAGVAR(hardcode_libdir_separator, $1)=
+	  fi
+	  ;;
+	esac
+	shared_flag='-shared'
+	if test "$aix_use_runtimelinking" = yes; then
+	  shared_flag="$shared_flag "'${wl}-G'
+	fi
+	_LT_TAGVAR(link_all_deplibs, $1)=no
+      else
+	# not using gcc
+	if test "$host_cpu" = ia64; then
+	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+	  if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag='${wl}-G'
+	  else
+	    shared_flag='${wl}-bM:SRE'
+	  fi
+	fi
+      fi
+
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      if test "$aix_use_runtimelinking" = yes; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	_LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        _LT_SYS_MODULE_PATH_AIX([$1])
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+      else
+	if test "$host_cpu" = ia64; then
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+	  _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+	  _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an
+	 # empty executable.
+	 _LT_SYS_MODULE_PATH_AIX([$1])
+	 _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+	  _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+	  if test "$with_gnu_ld" = yes; then
+	    # We only use this code for GNU lds that support --whole-archive.
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	  else
+	    # Exported symbols can be pulled into shared objects from archives
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+	  fi
+	  _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+	  # This is similar to how AIX traditionally builds its shared libraries.
+	  _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[[45]]*)
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      case $cc_basename in
+      cl*)
+	# Native MSVC
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+	_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	_LT_TAGVAR(always_export_symbols, $1)=yes
+	_LT_TAGVAR(file_list_spec, $1)='@'
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=".dll"
+	# FIXME: Setting linknames here is a bad hack.
+	_LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+	_LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	    sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+	  else
+	    sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+	  fi~
+	  $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+	  linknames='
+	# The linker will not automatically build a static lib if we build a DLL.
+	# _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+	_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+	_LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+	_LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+	# Don't use ranlib
+	_LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+	_LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+	  lt_tool_outputfile="@TOOL_OUTPUT@"~
+	  case $lt_outputfile in
+	    *.exe|*.EXE) ;;
+	    *)
+	      lt_outputfile="$lt_outputfile.exe"
+	      lt_tool_outputfile="$lt_tool_outputfile.exe"
+	      ;;
+	  esac~
+	  if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+	    $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+	    $RM "$lt_outputfile.manifest";
+	  fi'
+	;;
+      *)
+	# Assume MSVC wrapper
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+	_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=".dll"
+	# FIXME: Setting linknames here is a bad hack.
+	_LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+	# The linker will automatically build a .lib file if we build a DLL.
+	_LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+	# FIXME: Should let the user specify the lib program.
+	_LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+	_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+	;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+      _LT_DARWIN_LINKER_FEATURES($1)
+      ;;
+
+    dgux*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2.*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      ;;
+
+    hpux10*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test "$with_gnu_ld" = no; then
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	_LT_TAGVAR(hardcode_libdir_separator, $1)=:
+	_LT_TAGVAR(hardcode_direct, $1)=yes
+	_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+	_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+	# hardcode_minus_L: Not really in the search PATH,
+	# but as the default location of the library.
+	_LT_TAGVAR(hardcode_minus_L, $1)=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+	case $host_cpu in
+	hppa*64*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case $host_cpu in
+	hppa*64*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	m4_if($1, [], [
+	  # Older versions of the 11.00 compiler do not understand -b yet
+	  # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+	  _LT_LINKER_OPTION([if $CC understands -b],
+	    _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b],
+	    [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
+	    [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])],
+	  [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
+	  ;;
+	esac
+      fi
+      if test "$with_gnu_ld" = no; then
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	_LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	case $host_cpu in
+	hppa*64*|ia64*)
+	  _LT_TAGVAR(hardcode_direct, $1)=no
+	  _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	  ;;
+	*)
+	  _LT_TAGVAR(hardcode_direct, $1)=yes
+	  _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+	  _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  _LT_TAGVAR(hardcode_minus_L, $1)=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	# Try to use the -exported_symbol ld option, if it does not
+	# work, assume that -exports_file does not work either and
+	# implicitly export all symbols.
+	# This should be the same for all languages, so no per-tag cache variable.
+	AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol],
+	  [lt_cv_irix_exported_symbol],
+	  [save_LDFLAGS="$LDFLAGS"
+	   LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+	   AC_LINK_IFELSE(
+	     [AC_LANG_SOURCE(
+	        [AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
+			      [C++], [[int foo (void) { return 0; }]],
+			      [Fortran 77], [[
+      subroutine foo
+      end]],
+			      [Fortran], [[
+      subroutine foo
+      end]])])],
+	      [lt_cv_irix_exported_symbol=yes],
+	      [lt_cv_irix_exported_symbol=no])
+           LDFLAGS="$save_LDFLAGS"])
+	if test "$lt_cv_irix_exported_symbol" = yes; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+	fi
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(inherit_rpath, $1)=yes
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+	_LT_TAGVAR(hardcode_direct, $1)=yes
+	_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+	if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	  _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+	else
+	  case $host_os in
+	   openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+	     _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	     _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	     ;;
+	   *)
+	     _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	     _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	     ;;
+	  esac
+	fi
+      else
+	_LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    os2*)
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	_LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      else
+	_LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+	$CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+      if test "$GCC" = yes; then
+	wlarc='${wl}'
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+	case `$CC -V 2>&1` in
+	*"Compilers 5.0"*)
+	  wlarc=''
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+	  ;;
+	*)
+	  wlarc='${wl}'
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+	  ;;
+	esac
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      case $host_os in
+      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+      *)
+	# The compiler driver will combine and reorder linker options,
+	# but understands `-z linker_flag'.  GCC discards it without `$wl',
+	# but is careful enough not to reorder.
+	# Supported since Solaris 2.6 (maybe 2.5.1?)
+	if test "$GCC" = yes; then
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+	else
+	  _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+	fi
+	;;
+      esac
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	_LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+	  _LT_TAGVAR(hardcode_direct, $1)=no
+        ;;
+	motorola)
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4.3*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	_LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	_LT_TAGVAR(ld_shlibs, $1)=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We can NOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	_LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      _LT_TAGVAR(ld_shlibs, $1)=no
+      ;;
+    esac
+
+    if test x$host_vendor = xsni; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+	_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
+	;;
+      esac
+    fi
+  fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+    [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+  # Assume -lc should be added
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $_LT_TAGVAR(archive_cmds, $1) in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      AC_CACHE_CHECK([whether -lc should be explicitly linked in],
+	[lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1),
+	[$RM conftest*
+	echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+	if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+	  soname=conftest
+	  lib=conftest
+	  libobjs=conftest.$ac_objext
+	  deplibs=
+	  wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+	  pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+	  compiler_flags=-v
+	  linker_flags=-v
+	  verstring=
+	  output_objdir=.
+	  libname=conftest
+	  lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+	  _LT_TAGVAR(allow_undefined_flag, $1)=
+	  if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+	  then
+	    lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+	  else
+	    lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+	  fi
+	  _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+	else
+	  cat conftest.err 1>&5
+	fi
+	$RM conftest*
+	])
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+    [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+    [enable_shared_with_static_runtimes], [0],
+    [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+    [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+    [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+    [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+    [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+    [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+    [Commands used to build a loadable module if different from building
+    a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+    [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+    [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+    [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+    [Flag to hardcode $libdir into a binary during linking.
+    This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+    [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+    DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+    DIR into the resulting binary and the resulting library dependency is
+    "absolute", i.e impossible to change by setting ${shlibpath_var} if the
+    library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+    [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+    [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+    [Set to "yes" if building a shared library automatically hardcodes DIR
+    into the library and all subsequent libraries and executables linked
+    against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+    [Set to yes if linker adds runtime paths of dependent libraries
+    to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+    [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [always_export_symbols], [0],
+    [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+    [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+    [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+    [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+    [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [postlink_cmds], [2],
+    [Commands necessary for finishing linking programs])
+_LT_TAGDECL([], [file_list_spec], [1],
+    [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl    [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC="$CC"
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_SYS_DYNAMIC_LINKER($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+  LT_SYS_DLOPEN_SELF
+  _LT_CMD_STRIPLIB
+
+  # Report which library types will actually be built
+  AC_MSG_CHECKING([if libtool supports shared libraries])
+  AC_MSG_RESULT([$can_build_shared])
+
+  AC_MSG_CHECKING([whether to build shared libraries])
+  test "$can_build_shared" = "no" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test "$enable_shared" = yes && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[[4-9]]*)
+    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+      test "$enable_shared" = yes && enable_static=no
+    fi
+    ;;
+  esac
+  AC_MSG_RESULT([$enable_shared])
+
+  AC_MSG_CHECKING([whether to build static libraries])
+  # Make sure either enable_shared or enable_static is yes.
+  test "$enable_shared" = yes || enable_static=yes
+  AC_MSG_RESULT([$enable_static])
+
+  _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC="$lt_save_CC"
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
+    ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
+    (test "X$CXX" != "Xg++"))) ; then
+  AC_PROG_CXXCPP
+else
+  _lt_caught_CXX_error=yes
+fi
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_caught_CXX_error" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="int some_variable = 0;"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC=$CC
+  lt_save_CFLAGS=$CFLAGS
+  lt_save_LD=$LD
+  lt_save_GCC=$GCC
+  GCC=$GXX
+  lt_save_with_gnu_ld=$with_gnu_ld
+  lt_save_path_LD=$lt_cv_path_LD
+  if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+    lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+  else
+    $as_unset lt_cv_prog_gnu_ld
+  fi
+  if test -n "${lt_cv_path_LDCXX+set}"; then
+    lt_cv_path_LD=$lt_cv_path_LDCXX
+  else
+    $as_unset lt_cv_path_LD
+  fi
+  test -z "${LDCXX+set}" || LD=$LDCXX
+  CC=${CXX-"c++"}
+  CFLAGS=$CXXFLAGS
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    # We don't want -fno-exception when compiling C++ code, so set the
+    # no_builtin_flag separately
+    if test "$GXX" = yes; then
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+    else
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+    fi
+
+    if test "$GXX" = yes; then
+      # Set up default GNU C++ configuration
+
+      LT_PATH_LD
+
+      # Check if GNU C++ uses GNU ld as the underlying linker, since the
+      # archiving commands below assume that GNU ld is being used.
+      if test "$with_gnu_ld" = yes; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+        # If archive_cmds runs LD, not CC, wlarc should be empty
+        # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+        #     investigate it a little bit more. (MM)
+        wlarc='${wl}'
+
+        # ancient GNU ld didn't support --whole-archive et. al.
+        if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+	  $GREP 'no-whole-archive' > /dev/null; then
+          _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+        else
+          _LT_TAGVAR(whole_archive_flag_spec, $1)=
+        fi
+      else
+        with_gnu_ld=no
+        wlarc=
+
+        # A generic and very simple default shared library creation
+        # command for GNU C++ for the case where it uses the native
+        # linker, instead of GNU ld.  If possible, this setting should
+        # overridden to take advantage of the native linker features on
+        # the platform it is being used on.
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+      fi
+
+      # Commands to make compiler produce verbose output that lists
+      # what "hidden" libraries, object files and flags are used when
+      # linking a shared library.
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+    else
+      GXX=no
+      with_gnu_ld=no
+      wlarc=
+    fi
+
+    # PORTME: fill in a description of your system's C++ link characteristics
+    AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+    _LT_TAGVAR(ld_shlibs, $1)=yes
+    case $host_os in
+      aix3*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+      aix[[4-9]]*)
+        if test "$host_cpu" = ia64; then
+          # On IA64, the linker does run time linking by default, so we don't
+          # have to do anything special.
+          aix_use_runtimelinking=no
+          exp_sym_flag='-Bexport'
+          no_entry_flag=""
+        else
+          aix_use_runtimelinking=no
+
+          # Test if we are trying to use run time linking or normal
+          # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+          # need to do runtime linking.
+          case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+	    for ld_flag in $LDFLAGS; do
+	      case $ld_flag in
+	      *-brtl*)
+	        aix_use_runtimelinking=yes
+	        break
+	        ;;
+	      esac
+	    done
+	    ;;
+          esac
+
+          exp_sym_flag='-bexport'
+          no_entry_flag='-bnoentry'
+        fi
+
+        # When large executables or shared objects are built, AIX ld can
+        # have problems creating the table of contents.  If linking a library
+        # or program results in "error TOC overflow" add -mminimal-toc to
+        # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+        # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+        _LT_TAGVAR(archive_cmds, $1)=''
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+        if test "$GXX" = yes; then
+          case $host_os in aix4.[[012]]|aix4.[[012]].*)
+          # We only want to do this on AIX 4.2 and lower, the check
+          # below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" &&
+	     strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+	  then
+	    # We have reworked collect2
+	    :
+	  else
+	    # We have old collect2
+	    _LT_TAGVAR(hardcode_direct, $1)=unsupported
+	    # It fails to find uninstalled libraries when the uninstalled
+	    # path is not listed in the libpath.  Setting hardcode_minus_L
+	    # to unsupported forces relinking
+	    _LT_TAGVAR(hardcode_minus_L, $1)=yes
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+	    _LT_TAGVAR(hardcode_libdir_separator, $1)=
+	  fi
+          esac
+          shared_flag='-shared'
+	  if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag="$shared_flag "'${wl}-G'
+	  fi
+        else
+          # not using gcc
+          if test "$host_cpu" = ia64; then
+	  # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	  # chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+          else
+	    if test "$aix_use_runtimelinking" = yes; then
+	      shared_flag='${wl}-G'
+	    else
+	      shared_flag='${wl}-bM:SRE'
+	    fi
+          fi
+        fi
+
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+        # It seems that -bexpall does not export symbols beginning with
+        # underscore (_), so it is better to generate a list of symbols to
+	# export.
+        _LT_TAGVAR(always_export_symbols, $1)=yes
+        if test "$aix_use_runtimelinking" = yes; then
+          # Warning - without using the other runtime loading flags (-brtl),
+          # -berok will link without error, but may produce a broken library.
+          _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+          # Determine the default libpath from the value encoded in an empty
+          # executable.
+          _LT_SYS_MODULE_PATH_AIX([$1])
+          _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+        else
+          if test "$host_cpu" = ia64; then
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+	    _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+	    _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+          else
+	    # Determine the default libpath from the value encoded in an
+	    # empty executable.
+	    _LT_SYS_MODULE_PATH_AIX([$1])
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+	    # Warning - without using the other run time loading flags,
+	    # -berok will link without error, but may produce a broken library.
+	    _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+	    _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+	    if test "$with_gnu_ld" = yes; then
+	      # We only use this code for GNU lds that support --whole-archive.
+	      _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	    else
+	      # Exported symbols can be pulled into shared objects from archives
+	      _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+	    fi
+	    _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+	    # This is similar to how AIX traditionally builds its shared
+	    # libraries.
+	    _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+          fi
+        fi
+        ;;
+
+      beos*)
+	if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	  _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	  # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	  # support --undefined.  This deserves some investigation.  FIXME
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	else
+	  _LT_TAGVAR(ld_shlibs, $1)=no
+	fi
+	;;
+
+      chorus*)
+        case $cc_basename in
+          *)
+	  # FIXME: insert proper C++ library support
+	  _LT_TAGVAR(ld_shlibs, $1)=no
+	  ;;
+        esac
+        ;;
+
+      cygwin* | mingw* | pw32* | cegcc*)
+	case $GXX,$cc_basename in
+	,cl* | no,cl*)
+	  # Native MSVC
+	  # hardcode_libdir_flag_spec is actually meaningless, as there is
+	  # no search path for DLLs.
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+	  _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	  _LT_TAGVAR(always_export_symbols, $1)=yes
+	  _LT_TAGVAR(file_list_spec, $1)='@'
+	  # Tell ltmain to make .lib files, not .a files.
+	  libext=lib
+	  # Tell ltmain to make .dll files, not .so files.
+	  shrext_cmds=".dll"
+	  # FIXME: Setting linknames here is a bad hack.
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	      $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+	    else
+	      $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+	    fi~
+	    $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+	    linknames='
+	  # The linker will not automatically build a static lib if we build a DLL.
+	  # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+	  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+	  # Don't use ranlib
+	  _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+	  _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+	    lt_tool_outputfile="@TOOL_OUTPUT@"~
+	    case $lt_outputfile in
+	      *.exe|*.EXE) ;;
+	      *)
+		lt_outputfile="$lt_outputfile.exe"
+		lt_tool_outputfile="$lt_tool_outputfile.exe"
+		;;
+	    esac~
+	    func_to_tool_file "$lt_outputfile"~
+	    if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+	      $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+	      $RM "$lt_outputfile.manifest";
+	    fi'
+	  ;;
+	*)
+	  # g++
+	  # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+	  # as there is no search path for DLLs.
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+	  _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols'
+	  _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+	  _LT_TAGVAR(always_export_symbols, $1)=no
+	  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+	  if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	    # If the export-symbols file already is a .def file (1st line
+	    # is EXPORTS), use it as is; otherwise, prepend...
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	      cp $export_symbols $output_objdir/$soname.def;
+	    else
+	      echo EXPORTS > $output_objdir/$soname.def;
+	      cat $export_symbols >> $output_objdir/$soname.def;
+	    fi~
+	    $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	  else
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	  fi
+	  ;;
+	esac
+	;;
+      darwin* | rhapsody*)
+        _LT_DARWIN_LINKER_FEATURES($1)
+	;;
+
+      dgux*)
+        case $cc_basename in
+          ec++*)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          ghcx*)
+	    # Green Hills C++ Compiler
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+        esac
+        ;;
+
+      freebsd2.*)
+        # C++ shared libraries reported to be fairly broken before
+	# switch to ELF
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      freebsd-elf*)
+        _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+        ;;
+
+      freebsd* | dragonfly*)
+        # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+        # conventions
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+        ;;
+
+      haiku*)
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        ;;
+
+      hpux9*)
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+				             # but as the default
+				             # location of the library.
+
+        case $cc_basename in
+          CC*)
+            # FIXME: insert proper C++ library support
+            _LT_TAGVAR(ld_shlibs, $1)=no
+            ;;
+          aCC*)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            # Commands to make compiler produce verbose output that lists
+            # what "hidden" libraries, object files and flags are used when
+            # linking a shared library.
+            #
+            # There doesn't appear to be a way to prevent this compiler from
+            # explicitly linking system object files so we need to strip them
+            # from the output so that they don't get included in the library
+            # dependencies.
+            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+            ;;
+          *)
+            if test "$GXX" = yes; then
+              _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            else
+              # FIXME: insert proper C++ library support
+              _LT_TAGVAR(ld_shlibs, $1)=no
+            fi
+            ;;
+        esac
+        ;;
+
+      hpux10*|hpux11*)
+        if test $with_gnu_ld = no; then
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+	  _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+          case $host_cpu in
+            hppa*64*|ia64*)
+              ;;
+            *)
+	      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+              ;;
+          esac
+        fi
+        case $host_cpu in
+          hppa*64*|ia64*)
+            _LT_TAGVAR(hardcode_direct, $1)=no
+            _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+            ;;
+          *)
+            _LT_TAGVAR(hardcode_direct, $1)=yes
+            _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+					         # but as the default
+					         # location of the library.
+            ;;
+        esac
+
+        case $cc_basename in
+          CC*)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          aCC*)
+	    case $host_cpu in
+	      hppa*64*)
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	      ia64*)
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	      *)
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	        ;;
+	    esac
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+	    ;;
+          *)
+	    if test "$GXX" = yes; then
+	      if test $with_gnu_ld = no; then
+	        case $host_cpu in
+	          hppa*64*)
+	            _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	          ia64*)
+	            _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	          *)
+	            _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	            ;;
+	        esac
+	      fi
+	    else
+	      # FIXME: insert proper C++ library support
+	      _LT_TAGVAR(ld_shlibs, $1)=no
+	    fi
+	    ;;
+        esac
+        ;;
+
+      interix[[3-9]]*)
+	_LT_TAGVAR(hardcode_direct, $1)=no
+	_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+	# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+	# Instead, shared libraries are loaded at an image base (0x10000000 by
+	# default) and relocated if they conflict, which is a slow very memory
+	# consuming and fragmenting process.  To avoid this, we pick a random,
+	# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+	# time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+	_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+	_LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+	;;
+      irix5* | irix6*)
+        case $cc_basename in
+          CC*)
+	    # SGI C++
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+
+	    # Archives containing C++ object files must be created using
+	    # "CC -ar", where "CC" is the IRIX C++ compiler.  This is
+	    # necessary to make sure instantiated templates are included
+	    # in the archive.
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+	    ;;
+          *)
+	    if test "$GXX" = yes; then
+	      if test "$with_gnu_ld" = no; then
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	      else
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib'
+	      fi
+	    fi
+	    _LT_TAGVAR(link_all_deplibs, $1)=yes
+	    ;;
+        esac
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(inherit_rpath, $1)=yes
+        ;;
+
+      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+        case $cc_basename in
+          KCC*)
+	    # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	    # KCC will only create a shared library if the output file
+	    # ends with ".so" (or ".sl" for HP-UX), so rename the library
+	    # to its proper name (with version) after linking.
+	    _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+	    # Archives containing C++ object files must be created using
+	    # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+	    ;;
+	  icpc* | ecpc* )
+	    # Intel C++
+	    with_gnu_ld=yes
+	    # version 8.0 and above of icpc choke on multiply defined symbols
+	    # if we add $predep_objects and $postdep_objects, however 7.1 and
+	    # earlier do not add the objects themselves.
+	    case `$CC -V 2>&1` in
+	      *"Version 7."*)
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+		_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+		;;
+	      *)  # Version 8.0 or newer
+	        tmp_idyn=
+	        case $host_cpu in
+		  ia64*) tmp_idyn=' -i_dynamic';;
+		esac
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+		_LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+		;;
+	    esac
+	    _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	    ;;
+          pgCC* | pgcpp*)
+            # Portland Group C++ compiler
+	    case `$CC -V` in
+	    *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*)
+	      _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+		compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+	      _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+		$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+		$RANLIB $oldlib'
+	      _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+		$CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+	      _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+		rm -rf $tpldir~
+		$CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+		$CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+	      ;;
+	    *) # Version 6 and above use weak symbols
+	      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+	      _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+	      ;;
+	    esac
+
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+            ;;
+	  cxx*)
+	    # Compaq C++
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+	    runpath_var=LD_RUN_PATH
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+	    _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+	    ;;
+	  xl* | mpixl* | bgxl*)
+	    # IBM XL 8.0 on PPC, with GNU ld
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    if test "x$supports_anon_versioning" = xyes; then
+	      _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+		cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+		echo "local: *; };" >> $output_objdir/$libname.ver~
+		$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+	    fi
+	    ;;
+	  *)
+	    case `$CC -V 2>&1 | sed 5q` in
+	    *Sun\ C*)
+	      # Sun C++ 5.9
+	      _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+	      _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	      _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+	      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	      _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	      _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+	      # Not sure whether something based on
+	      # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+	      # would be better.
+	      output_verbose_link_cmd='func_echo_all'
+
+	      # Archives containing C++ object files must be created using
+	      # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+	      # necessary to make sure instantiated templates are included
+	      # in the archive.
+	      _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+	      ;;
+	    esac
+	    ;;
+	esac
+	;;
+
+      lynxos*)
+        # FIXME: insert proper C++ library support
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	;;
+
+      m88k*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+	;;
+
+      mvs*)
+        case $cc_basename in
+          cxx*)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+	  *)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+	esac
+	;;
+
+      netbsd*)
+        if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	  _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+	  wlarc=
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	  _LT_TAGVAR(hardcode_direct, $1)=yes
+	  _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	fi
+	# Workaround some broken pre-1.5 toolchains
+	output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+	;;
+
+      *nto* | *qnx*)
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+	;;
+
+      openbsd2*)
+        # C++ shared libraries are fairly broken
+	_LT_TAGVAR(ld_shlibs, $1)=no
+	;;
+
+      openbsd*)
+	if test -f /usr/libexec/ld.so; then
+	  _LT_TAGVAR(hardcode_direct, $1)=yes
+	  _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	  _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+	  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	  if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
+	    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+	    _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+	  fi
+	  output_verbose_link_cmd=func_echo_all
+	else
+	  _LT_TAGVAR(ld_shlibs, $1)=no
+	fi
+	;;
+
+      osf3* | osf4* | osf5*)
+        case $cc_basename in
+          KCC*)
+	    # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+	    # KCC will only create a shared library if the output file
+	    # ends with ".so" (or ".sl" for HP-UX), so rename the library
+	    # to its proper name (with version) after linking.
+	    _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+	    _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	    # Archives containing C++ object files must be created using
+	    # the KAI C++ compiler.
+	    case $host in
+	      osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+	      *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+	    esac
+	    ;;
+          RCC*)
+	    # Rational C++ 2.4.1
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          cxx*)
+	    case $host in
+	      osf3*)
+	        _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+		;;
+	      *)
+	        _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	        _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+	          echo "-hidden">> $lib.exp~
+	          $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp  `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~
+	          $RM $lib.exp'
+	        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+		;;
+	    esac
+
+	    _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	    # Commands to make compiler produce verbose output that lists
+	    # what "hidden" libraries, object files and flags are used when
+	    # linking a shared library.
+	    #
+	    # There doesn't appear to be a way to prevent this compiler from
+	    # explicitly linking system object files so we need to strip them
+	    # from the output so that they don't get included in the library
+	    # dependencies.
+	    output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+	    ;;
+	  *)
+	    if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	      _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+	      case $host in
+	        osf3*)
+	          _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+		  ;;
+	        *)
+	          _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+		  ;;
+	      esac
+
+	      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+	      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+	      # Commands to make compiler produce verbose output that lists
+	      # what "hidden" libraries, object files and flags are used when
+	      # linking a shared library.
+	      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+	    else
+	      # FIXME: insert proper C++ library support
+	      _LT_TAGVAR(ld_shlibs, $1)=no
+	    fi
+	    ;;
+        esac
+        ;;
+
+      psos*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      sunos4*)
+        case $cc_basename in
+          CC*)
+	    # Sun C++ 4.x
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          lcc*)
+	    # Lucid
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+        esac
+        ;;
+
+      solaris*)
+        case $cc_basename in
+          CC* | sunCC*)
+	    # Sun C++ 4.2, 5.x and Centerline C++
+            _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+	    _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag}  -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	      $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+	    _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	    case $host_os in
+	      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+	      *)
+		# The compiler driver will combine and reorder linker options,
+		# but understands `-z linker_flag'.
+	        # Supported since Solaris 2.6 (maybe 2.5.1?)
+		_LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+	        ;;
+	    esac
+	    _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+	    output_verbose_link_cmd='func_echo_all'
+
+	    # Archives containing C++ object files must be created using
+	    # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+	    # necessary to make sure instantiated templates are included
+	    # in the archive.
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+	    ;;
+          gcx*)
+	    # Green Hills C++ Compiler
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+	    # The C++ compiler must be used to create the archive.
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+	    ;;
+          *)
+	    # GNU C++ compiler with Solaris linker
+	    if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+	      _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+	      if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	        _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+		  $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	        # Commands to make compiler produce verbose output that lists
+	        # what "hidden" libraries, object files and flags are used when
+	        # linking a shared library.
+	        output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+	      else
+	        # g++ 2.7 appears to require `-G' NOT `-shared' on this
+	        # platform.
+	        _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+	        _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+		  $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+	        # Commands to make compiler produce verbose output that lists
+	        # what "hidden" libraries, object files and flags are used when
+	        # linking a shared library.
+	        output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+	      fi
+
+	      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+	      case $host_os in
+		solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+		*)
+		  _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+		  ;;
+	      esac
+	    fi
+	    ;;
+        esac
+        ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      case $cc_basename in
+        CC*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+      esac
+      ;;
+
+      sysv5* | sco3.2v5* | sco5v6*)
+	# Note: We can NOT use -z defs as we might desire, because we do not
+	# link with -lc, and that would cause any symbols used from libc to
+	# always be unresolved, which means just about no library would
+	# ever link correctly.  If we're not using GNU ld we use -z text
+	# though, which does catch some bad symbols but isn't as heavy-handed
+	# as -z defs.
+	_LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+	_LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+	_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+	_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+	_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+	_LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+	_LT_TAGVAR(link_all_deplibs, $1)=yes
+	_LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+	runpath_var='LD_RUN_PATH'
+
+	case $cc_basename in
+          CC*)
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~
+	      '"$_LT_TAGVAR(old_archive_cmds, $1)"
+	    _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~
+	      '"$_LT_TAGVAR(reload_cmds, $1)"
+	    ;;
+	  *)
+	    _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	    ;;
+	esac
+      ;;
+
+      tandem*)
+        case $cc_basename in
+          NCC*)
+	    # NonStop-UX NCC 3.20
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+          *)
+	    # FIXME: insert proper C++ library support
+	    _LT_TAGVAR(ld_shlibs, $1)=no
+	    ;;
+        esac
+        ;;
+
+      vxworks*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      *)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+    esac
+
+    AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+    test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+    _LT_TAGVAR(GCC, $1)="$GXX"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+  LDCXX=$LD
+  LD=$lt_save_LD
+  GCC=$lt_save_GCC
+  with_gnu_ld=$lt_save_with_gnu_ld
+  lt_cv_path_LDCXX=$lt_cv_path_LD
+  lt_cv_path_LD=$lt_save_path_LD
+  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test "$_lt_caught_CXX_error" != yes
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_FUNC_STRIPNAME_CNF
+# ----------------------
+# func_stripname_cnf prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+#
+# This function is identical to the (non-XSI) version of func_stripname,
+# except this one can be used by m4 code that may be executed by configure,
+# rather than the libtool script.
+m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl
+AC_REQUIRE([_LT_DECL_SED])
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])
+func_stripname_cnf ()
+{
+  case ${2} in
+  .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+  *)  func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+  esac
+} # func_stripname_cnf
+])# _LT_FUNC_STRIPNAME_CNF
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library.  It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+  Foo (void) { a = 0; }
+private:
+  int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer*4 a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+  private int a;
+  public void bar (void) {
+    a = 0;
+  }
+};
+_LT_EOF
+], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF
+package foo
+func foo() {
+}
+_LT_EOF
+])
+
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
+
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+  # Parse the compiler output and extract the necessary
+  # objects, libraries and library flags.
+
+  # Sentinel used to keep track of whether or not we are before
+  # the conftest object file.
+  pre_test_object_deps_done=no
+
+  for p in `eval "$output_verbose_link_cmd"`; do
+    case ${prev}${p} in
+
+    -L* | -R* | -l*)
+       # Some compilers place space between "-{L,R}" and the path.
+       # Remove the space.
+       if test $p = "-L" ||
+          test $p = "-R"; then
+	 prev=$p
+	 continue
+       fi
+
+       # Expand the sysroot to ease extracting the directories later.
+       if test -z "$prev"; then
+         case $p in
+         -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
+         -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
+         -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
+         esac
+       fi
+       case $p in
+       =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
+       esac
+       if test "$pre_test_object_deps_done" = no; then
+	 case ${prev} in
+	 -L | -R)
+	   # Internal compiler library paths should come after those
+	   # provided the user.  The postdeps already come after the
+	   # user supplied libs so there is no need to process them.
+	   if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+	     _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+	   else
+	     _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+	   fi
+	   ;;
+	 # The "-l" case would never come before the object being
+	 # linked, so don't bother handling this case.
+	 esac
+       else
+	 if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+	   _LT_TAGVAR(postdeps, $1)="${prev}${p}"
+	 else
+	   _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
+	 fi
+       fi
+       prev=
+       ;;
+
+    *.lto.$objext) ;; # Ignore GCC LTO objects
+    *.$objext)
+       # This assumes that the test object file only shows up
+       # once in the compiler output.
+       if test "$p" = "conftest.$objext"; then
+	 pre_test_object_deps_done=yes
+	 continue
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+	 if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+	   _LT_TAGVAR(predep_objects, $1)="$p"
+	 else
+	   _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+	 fi
+       else
+	 if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+	   _LT_TAGVAR(postdep_objects, $1)="$p"
+	 else
+	   _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+	 fi
+       fi
+       ;;
+
+    *) ;; # Ignore the rest.
+
+    esac
+  done
+
+  # Clean up.
+  rm -f a.out a.exe
+else
+  echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+  # Interix 3.5 installs completely hosed .la files for C++, so rather than
+  # hack all around it, let's just trust "g++" to DTRT.
+  _LT_TAGVAR(predep_objects,$1)=
+  _LT_TAGVAR(postdep_objects,$1)=
+  _LT_TAGVAR(postdeps,$1)=
+  ;;
+
+linux*)
+  case `$CC -V 2>&1 | sed 5q` in
+  *Sun\ C*)
+    # Sun C++ 5.9
+
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    if test "$solaris_use_stlport4" != yes; then
+      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
+  ;;
+
+solaris*)
+  case $cc_basename in
+  CC* | sunCC*)
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    # Adding this requires a known-good setup of shared libraries for
+    # Sun compiler versions before 5.6, else PIC objects from an old
+    # archive will be linked into the output, leading to subtle bugs.
+    if test "$solaris_use_stlport4" != yes; then
+      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
+  ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+    [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+    [Dependencies to place before and after the objects being linked to
+    create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+    [The library search path used internally by the compiler when linking
+    a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_LANG_PUSH(Fortran 77)
+if test -z "$F77" || test "X$F77" = "Xno"; then
+  _lt_disable_F77=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_F77" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC="$CC"
+  lt_save_GCC=$GCC
+  lt_save_CFLAGS=$CFLAGS
+  CC=${F77-"f77"}
+  CFLAGS=$FFLAGS
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+  GCC=$G77
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test "$can_build_shared" = "no" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test "$enable_shared" = yes && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+	if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+	  test "$enable_shared" = yes && enable_static=no
+	fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test "$enable_shared" = yes || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)="$G77"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC="$lt_save_CC"
+  CFLAGS="$lt_save_CFLAGS"
+fi # test "$_lt_disable_F77" != yes
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_LANG_PUSH(Fortran)
+
+if test -z "$FC" || test "X$FC" = "Xno"; then
+  _lt_disable_FC=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_FC" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC="$CC"
+  lt_save_GCC=$GCC
+  lt_save_CFLAGS=$CFLAGS
+  CC=${FC-"f95"}
+  CFLAGS=$FCFLAGS
+  compiler=$CC
+  GCC=$ac_cv_fc_compiler_gnu
+
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test "$can_build_shared" = "no" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test "$enable_shared" = yes && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+	if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+	  test "$enable_shared" = yes && enable_static=no
+	fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test "$enable_shared" = yes || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+fi # test "$_lt_disable_FC" != yes
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+CFLAGS=$GCJFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_GO_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Go compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GO_CONFIG],
+[AC_REQUIRE([LT_PROG_GO])dnl
+AC_LANG_SAVE
+
+# Source file extension for Go test sources.
+ac_ext=go
+
+# Object file extension for compiled Go test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="package main; func main() { }"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='package main; func main() { }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GOC-"gccgo"}
+CFLAGS=$GOFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# Go did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GO_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+CFLAGS=
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+  :
+  _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+  [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+    [AC_CHECK_TOOL(GCJ, gcj,)
+      test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+      AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_GO
+# ----------
+AC_DEFUN([LT_PROG_GO],
+[AC_CHECK_TOOL(GOC, gccgo,)
+])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+# _LT_DECL_DLLTOOL
+# ----------------
+# Ensure DLLTOOL variable is set.
+m4_defun([_LT_DECL_DLLTOOL],
+[AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])
+AC_SUBST([DLLTOOL])
+])
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible.  Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+    [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for lt_ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+  test ! -f $lt_ac_sed && continue
+  cat /dev/null > conftest.in
+  lt_ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+  # Check for GNU sed and select it if it is found.
+  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+    lt_cv_path_SED=$lt_ac_sed
+    break
+  fi
+  while true; do
+    cat conftest.in conftest.in >conftest.tmp
+    mv conftest.tmp conftest.in
+    cp conftest.in conftest.nl
+    echo >>conftest.nl
+    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+    cmp -s conftest.out conftest.nl || break
+    # 10000 chars as input seems more than enough
+    test $lt_ac_count -gt 10 && break
+    lt_ac_count=`expr $lt_ac_count + 1`
+    if test $lt_ac_count -gt $lt_ac_max; then
+      lt_ac_max=$lt_ac_count
+      lt_cv_path_SED=$lt_ac_sed
+    fi
+  done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+  test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
+      = c,a/b,b/c, \
+    && eval 'test $(( 1 + 1 )) -eq 2 \
+    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+  && xsi_shell=yes
+AC_MSG_RESULT([$xsi_shell])
+_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
+
+AC_MSG_CHECKING([whether the shell understands "+="])
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
+    >/dev/null 2>&1 \
+  && lt_shell_append=yes
+AC_MSG_RESULT([$lt_shell_append])
+_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY)
+# ------------------------------------------------------
+# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and
+# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY.
+m4_defun([_LT_PROG_FUNCTION_REPLACE],
+[dnl {
+sed -e '/^$1 ()$/,/^} # $1 /c\
+$1 ()\
+{\
+m4_bpatsubsts([$2], [$], [\\], [^\([	 ]\)], [\\\1])
+} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+])
+
+
+# _LT_PROG_REPLACE_SHELLFNS
+# -------------------------
+# Replace existing portable implementations of several shell functions with
+# equivalent extended shell implementations where those features are available..
+m4_defun([_LT_PROG_REPLACE_SHELLFNS],
+[if test x"$xsi_shell" = xyes; then
+  _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl
+    case ${1} in
+      */*) func_dirname_result="${1%/*}${2}" ;;
+      *  ) func_dirname_result="${3}" ;;
+    esac])
+
+  _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl
+    func_basename_result="${1##*/}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl
+    case ${1} in
+      */*) func_dirname_result="${1%/*}${2}" ;;
+      *  ) func_dirname_result="${3}" ;;
+    esac
+    func_basename_result="${1##*/}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl
+    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+    # positional parameters, so assign one to ordinary parameter first.
+    func_stripname_result=${3}
+    func_stripname_result=${func_stripname_result#"${1}"}
+    func_stripname_result=${func_stripname_result%"${2}"}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl
+    func_split_long_opt_name=${1%%=*}
+    func_split_long_opt_arg=${1#*=}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl
+    func_split_short_opt_arg=${1#??}
+    func_split_short_opt_name=${1%"$func_split_short_opt_arg"}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl
+    case ${1} in
+      *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+      *)    func_lo2o_result=${1} ;;
+    esac])
+
+  _LT_PROG_FUNCTION_REPLACE([func_xform], [    func_xform_result=${1%.*}.lo])
+
+  _LT_PROG_FUNCTION_REPLACE([func_arith], [    func_arith_result=$(( $[*] ))])
+
+  _LT_PROG_FUNCTION_REPLACE([func_len], [    func_len_result=${#1}])
+fi
+
+if test x"$lt_shell_append" = xyes; then
+  _LT_PROG_FUNCTION_REPLACE([func_append], [    eval "${1}+=\\${2}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl
+    func_quote_for_eval "${2}"
+dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \
+    eval "${1}+=\\\\ \\$func_quote_for_eval_result"])
+
+  # Save a `func_append' function call where possible by direct use of '+='
+  sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+else
+  # Save a `func_append' function call even when '+=' is not available
+  sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+fi
+
+if test x"$_lt_function_replace_fail" = x":"; then
+  AC_MSG_WARN([Unable to substitute extended shell functions in $ofile])
+fi
+])
+
+# _LT_PATH_CONVERSION_FUNCTIONS
+# -----------------------------
+# Determine which file name conversion functions should be used by
+# func_to_host_file (and, implicitly, by func_to_host_path).  These are needed
+# for certain cross-compile configurations and native mingw.
+m4_defun([_LT_PATH_CONVERSION_FUNCTIONS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_MSG_CHECKING([how to convert $build file names to $host format])
+AC_CACHE_VAL(lt_cv_to_host_file_cmd,
+[case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+        ;;
+    esac
+    ;;
+  *-*-cygwin* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_noop
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+        ;;
+    esac
+    ;;
+  * ) # unhandled hosts (and "normal" native builds)
+    lt_cv_to_host_file_cmd=func_convert_file_noop
+    ;;
+esac
+])
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+AC_MSG_RESULT([$lt_cv_to_host_file_cmd])
+_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd],
+         [0], [convert $build file names to $host format])dnl
+
+AC_MSG_CHECKING([how to convert $build file names to toolchain format])
+AC_CACHE_VAL(lt_cv_to_tool_file_cmd,
+[#assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+        ;;
+    esac
+    ;;
+esac
+])
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+AC_MSG_RESULT([$lt_cv_to_tool_file_cmd])
+_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd],
+         [0], [convert $build files to toolchain format])dnl
+])# _LT_PATH_CONVERSION_FUNCTIONS
+
+# Helper functions for option handling.                    -*- Autoconf -*-
+#
+#   Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation,
+#   Inc.
+#   Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 7 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it.  Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+        _LT_MANGLE_DEFUN([$1], [$2]),
+    [m4_warning([Unknown $1 option `$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+	    [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+		      [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME.  If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+    [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+  dnl
+  dnl Simply set some default values (i.e off) if boolean options were not
+  dnl specified:
+  _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+  ])
+  _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+  ])
+  dnl
+  dnl If no reference was made to various pairs of opposing options, then
+  dnl we run the default mode handler for the pair.  For example, if neither
+  dnl `shared' nor `disable-shared' was passed, we enable building of shared
+  dnl archives by default:
+  _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+  _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+  		   [_LT_ENABLE_FAST_INSTALL])
+  ])
+])# _LT_SET_OPTIONS
+
+
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+  AC_CHECK_TOOL(AS, as, false)
+  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+  AC_CHECK_TOOL(OBJDUMP, objdump, false)
+  ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS],      [1], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the `shared' and
+# `disable-shared' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+    [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+	[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_shared=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+    _LT_DECL([build_libtool_libs], [enable_shared], [0],
+	[Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the `static' and
+# `disable-static' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+    [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+	[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_static=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+    _LT_DECL([build_old_libs], [enable_static], [0],
+	[Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the `fast-install'
+# and `disable-fast-install' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+    [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+    [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_fast_install=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+	 [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# LT_INIT options.
+# MODE is either `yes' or `no'.  If omitted, it defaults to `both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+    [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
+	[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+    [lt_p=${PACKAGE-default}
+    case $withval in
+    yes|no) pic_mode=$withval ;;
+    *)
+      pic_mode=default
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for lt_pkg in $withval; do
+	IFS="$lt_save_ifs"
+	if test "X$lt_pkg" = "X$lt_p"; then
+	  pic_mode=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [pic_mode=default])
+
+test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+		 [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+		 [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+		 [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+		 [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+		 [m4_define([_LTDL_TYPE], [convenience])])
+
+# ltsugar.m4 -- libtool m4 base layer.                         -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+       [$#], [2], [[$2]],
+       [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+       [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59 which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+       [$#], 1, [],
+       [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+	   m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn.  Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+       [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+	     [m4_foreach([_Lt_suffix],
+		]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+	[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+	  [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+		 [lt_append([$1], [$2], [$3])$4],
+		 [$5])],
+	  [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+	m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+    m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+	[$5],
+    [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+  [lt_join(m4_quote(m4_default([$4], [[, ]])),
+           lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+		      [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
+
+# ltversion.m4 -- version numbers			-*- Autoconf -*-
+#
+#   Copyright (C) 2004 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# @configure_input@
+
+# serial 3337 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.4.2])
+m4_define([LT_PACKAGE_REVISION], [1.3337])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.4.2'
+macro_revision='1.3337'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions.    -*-Autoconf-*-
+#
+#   Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 5 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick.  It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else.  This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. 
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION],	[AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP],		[AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH],	[AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT],		[AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX],	[AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN],		[AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR],		[AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL],	[AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN],		[AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER],	[AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK],		[AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE],	[AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF],	[AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O],	[AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR],		[AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR],		[AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP],	[AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC],		[AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU],		[AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG],	[AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD],	[AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS],	[AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP],	[AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP],		[AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED],		[AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME],		[AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE],	[AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE],	[AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL],		[AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP],		[AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN],		[AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER],	[AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG],		[AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL],	[AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX],		[AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77],		[AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ],		[AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG],	[AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG],	[AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG],	[AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG],	[AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG],	[AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG],		[AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C],	[AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS],	[AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
+m4_ifndef([_LT_AC_PROG_CXXCPP],		[AC_DEFUN([_LT_AC_PROG_CXXCPP])])
+m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS],	[AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
+m4_ifndef([_LT_PROG_ECHO_BACKSLASH],	[AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_PROG_F77],		[AC_DEFUN([_LT_PROG_F77])])
+m4_ifndef([_LT_PROG_FC],		[AC_DEFUN([_LT_PROG_FC])])
+m4_ifndef([_LT_PROG_CXX],		[AC_DEFUN([_LT_PROG_CXX])])
+
+# Copyright (C) 2002-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.15'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version.  Point them to the right macro.
+m4_if([$1], [1.15], [],
+      [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too.  Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.15])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
+
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to '$srcdir/foo'.  In other projects, it is set to
+# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is '.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+])
+
+# AM_CONDITIONAL                                            -*- Autoconf -*-
+
+# Copyright (C) 1997-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ([2.52])dnl
+ m4_if([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+  AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+m4_if([$1], [CC],   [depcc="$CC"   am_compiler_list=],
+      [$1], [CXX],  [depcc="$CXX"  am_compiler_list=],
+      [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+      [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
+      [$1], [UPC],  [depcc="$UPC"  am_compiler_list=],
+      [$1], [GCJ],  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                    [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  am__universal=false
+  m4_case([$1], [CC],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac],
+    [CXX],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac])
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_$1_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE([dependency-tracking], [dnl
+AS_HELP_STRING(
+  [--enable-dependency-tracking],
+  [do not reject slow dependency extractors])
+AS_HELP_STRING(
+  [--disable-dependency-tracking],
+  [speeds up one-time build])])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+AC_SUBST([am__nodep])dnl
+_AM_SUBST_NOTMAKE([am__nodep])dnl
+])
+
+# Generate code to set up dependency tracking.              -*- Autoconf -*-
+
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+  # Older Autoconf quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  case $CONFIG_FILES in
+  *\'*) eval set x "$CONFIG_FILES" ;;
+  *)   set x $CONFIG_FILES ;;
+  esac
+  shift
+  for mf
+  do
+    # Strip MF so we end up with the name of the file.
+    mf=`echo "$mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile or not.
+    # We used to match only the files named 'Makefile.in', but
+    # some people rename them; so instead we look at the file content.
+    # Grep'ing the first line is not enough: some people post-process
+    # each Makefile.in and add a new line on top of each file to say so.
+    # Grep'ing the whole file is not good either: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+      dirpart=`AS_DIRNAME("$mf")`
+    else
+      continue
+    fi
+    # Extract the definition of DEPDIR, am__include, and am__quote
+    # from the Makefile without running 'make'.
+    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+    test -z "$DEPDIR" && continue
+    am__include=`sed -n 's/^am__include = //p' < "$mf"`
+    test -z "$am__include" && continue
+    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+    # Find all dependency output files, they are included files with
+    # $(DEPDIR) in their names.  We invoke sed twice because it is the
+    # simplest approach to changing $(DEPDIR) to its actual value in the
+    # expansion.
+    for file in `sed -n "
+      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+	 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
+      # Make sure the directory exists.
+      test -f "$dirpart/$file" && continue
+      fdir=`AS_DIRNAME(["$file"])`
+      AS_MKDIR_P([$dirpart/$fdir])
+      # echo "creating $dirpart/$file"
+      echo '# dummy' > "$dirpart/$file"
+    done
+  done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled.  FIXME.  This creates each '.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+     [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Do all the work for Automake.                             -*- Autoconf -*-
+
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This macro actually does too much.  Some checks are only needed if
+# your package does certain things.  But this isn't really a big deal.
+
+dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
+m4_define([AC_PROG_CC],
+m4_defn([AC_PROG_CC])
+[_AM_PROG_CC_C_O
+])
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out.  PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition.  After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.65])dnl
+dnl Autoconf wants to disallow AM_ names.  We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[AC_DIAGNOSE([obsolete],
+             [$0: two- and three-arguments forms are deprecated.])
+m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(
+  m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+  [ok:ok],,
+  [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+AM_MISSING_PROG([AUTOCONF], [autoconf])
+AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+AM_MISSING_PROG([AUTOHEADER], [autoheader])
+AM_MISSING_PROG([MAKEINFO], [makeinfo])
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
+# We need awk for the "check" target (and possibly the TAP driver).  The
+# system "awk" is bad on some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+	      [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+			     [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+		  [_AM_DEPENDENCIES([CC])],
+		  [m4_define([AC_PROG_CC],
+			     m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+		  [_AM_DEPENDENCIES([CXX])],
+		  [m4_define([AC_PROG_CXX],
+			     m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+		  [_AM_DEPENDENCIES([OBJC])],
+		  [m4_define([AC_PROG_OBJC],
+			     m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
+		  [_AM_DEPENDENCIES([OBJCXX])],
+		  [m4_define([AC_PROG_OBJCXX],
+			     m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
+])
+AC_REQUIRE([AM_SILENT_RULES])dnl
+dnl The testsuite driver may need to know about EXEEXT, so add the
+dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This
+dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+  [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes.  So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+  cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present.  This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message.  This
+can help us improve future automake versions.
+
+END
+  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+    echo 'Configuration will proceed anyway, since you have set the' >&2
+    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+    echo >&2
+  else
+    cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <http://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+    AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
+  fi
+fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
+])
+
+dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+AC_SUBST([install_sh])])
+
+# Copyright (C) 2003-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot.  For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Add --enable-maintainer-mode option to configure.         -*- Autoconf -*-
+# From Jim Meyering
+
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MAINTAINER_MODE([DEFAULT-MODE])
+# ----------------------------------
+# Control maintainer-specific portions of Makefiles.
+# Default is to disable them, unless 'enable' is passed literally.
+# For symmetry, 'disable' may be passed as well.  Anyway, the user
+# can override the default with the --enable/--disable switch.
+AC_DEFUN([AM_MAINTAINER_MODE],
+[m4_case(m4_default([$1], [disable]),
+       [enable], [m4_define([am_maintainer_other], [disable])],
+       [disable], [m4_define([am_maintainer_other], [enable])],
+       [m4_define([am_maintainer_other], [enable])
+        m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
+  dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+  AC_ARG_ENABLE([maintainer-mode],
+    [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode],
+      am_maintainer_other[ make rules and dependencies not useful
+      (and sometimes confusing) to the casual installer])],
+    [USE_MAINTAINER_MODE=$enableval],
+    [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+  AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST([MAINT])dnl
+]
+)
+
+# Check to see how 'make' treats includes.	            -*- Autoconf -*-
+
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+	@echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from 'make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+  am__include=include
+  am__quote=
+  _am_result=GNU
+  ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   case `$am_make -s -f confmf 2> /dev/null` in #(
+   *the\ am__doit\ target*)
+     am__include=.include
+     am__quote="\""
+     _am_result=BSD
+     ;;
+   esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
+
+# Copyright (C) 1997-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it is modern enough.
+# If it is, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+  *)
+    MISSING="\${SHELL} $am_aux_dir/missing" ;;
+  esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  AC_MSG_WARN(['missing' script is too old or missing])
+fi
+])
+
+#  -*- Autoconf -*-
+# Obsolete and "removed" macros, that must however still report explicit
+# error messages when used, to smooth transition.
+#
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([AM_CONFIG_HEADER],
+[AC_DIAGNOSE([obsolete],
+['$0': this macro is obsolete.
+You should use the 'AC][_CONFIG_HEADERS' macro instead.])dnl
+AC_CONFIG_HEADERS($@)])
+
+AC_DEFUN([AM_PROG_CC_STDC],
+[AC_PROG_CC
+am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc
+AC_DIAGNOSE([obsolete],
+['$0': this macro is obsolete.
+You should simply use the 'AC][_PROG_CC' macro instead.
+Also, your code should no longer depend upon 'am_cv_prog_cc_stdc',
+but upon 'ac_cv_prog_cc_stdc'.])])
+
+AC_DEFUN([AM_C_PROTOTYPES],
+         [AC_FATAL([automatic de-ANSI-fication support has been removed])])
+AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES])
+
+# Helper functions for option handling.                     -*- Autoconf -*-
+
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# --------------------
+# Set option NAME.  Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_CC_C_O
+# ---------------
+# Like AC_PROG_CC_C_O, but changed for automake.  We rewrite AC_PROG_CC
+# to automatically call this.
+AC_DEFUN([_AM_PROG_CC_C_O],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+AC_LANG_PUSH([C])dnl
+AC_CACHE_CHECK(
+  [whether $CC understands -c and -o together],
+  [am_cv_prog_cc_c_o],
+  [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i])
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+AC_LANG_POP([C])])
+
+# For backward compatibility.
+AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
+
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+   ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   (exit $ac_status); }])
+
+# Check to make sure that the build environment is sane.    -*- Autoconf -*-
+
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[[\\\"\#\$\&\'\`$am_lf]]*)
+    AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+  *[[\\\"\#\$\&\'\`$am_lf\ \	]]*)
+    AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$[*]" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$[*]" != "X $srcdir/configure conftest.file" \
+	&& test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment])
+     fi
+     if test "$[2]" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT([yes])
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+AC_CONFIG_COMMANDS_PRE(
+  [AC_MSG_CHECKING([that generated files are newer than configure])
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   AC_MSG_RESULT([done])])
+rm -f conftest.file
+])
+
+# Copyright (C) 2009-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# ("yes" being less verbose, "no" or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+  [--enable-silent-rules],
+  [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+  [--disable-silent-rules],
+  [verbose build output (undo: "make V=0")])dnl
+])
+case $enable_silent_rules in @%:@ (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+dnl
+dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+   [am_cv_make_support_nested_variables],
+   [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+  dnl Using '$V' instead of '$(V)' breaks IRIX make.
+  AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor 'install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in "make install-strip", and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
+if test "$cross_compiling" != no; then
+  AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# --------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball.                            -*- Autoconf -*-
+
+# Copyright (C) 2004-2014 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of 'v7', 'ustar', or 'pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+#     tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+#     $(am__untar) < result.tar
+#
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+
+m4_if([$1], [v7],
+  [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+
+  [m4_case([$1],
+    [ustar],
+     [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
+      # There is notably a 21 bits limit for the UID and the GID.  In fact,
+      # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
+      # and bug#13588).
+      am_max_uid=2097151 # 2^21 - 1
+      am_max_gid=$am_max_uid
+      # The $UID and $GID variables are not portable, so we need to resort
+      # to the POSIX-mandated id(1) utility.  Errors in the 'id' calls
+      # below are definitely unexpected, so allow the users to see them
+      # (that is, avoid stderr redirection).
+      am_uid=`id -u || echo unknown`
+      am_gid=`id -g || echo unknown`
+      AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
+      if test $am_uid -le $am_max_uid; then
+         AC_MSG_RESULT([yes])
+      else
+         AC_MSG_RESULT([no])
+         _am_tools=none
+      fi
+      AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
+      if test $am_gid -le $am_max_gid; then
+         AC_MSG_RESULT([yes])
+      else
+        AC_MSG_RESULT([no])
+        _am_tools=none
+      fi],
+
+  [pax],
+    [],
+
+  [m4_fatal([Unknown tar format])])
+
+  AC_MSG_CHECKING([how to create a $1 tar archive])
+
+  # Go ahead even if we have the value already cached.  We do so because we
+  # need to set the values for the 'am__tar' and 'am__untar' variables.
+  _am_tools=${am_cv_prog_tar_$1-$_am_tools}
+
+  for _am_tool in $_am_tools; do
+    case $_am_tool in
+    gnutar)
+      for _am_tar in tar gnutar gtar; do
+        AM_RUN_LOG([$_am_tar --version]) && break
+      done
+      am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+      am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+      am__untar="$_am_tar -xf -"
+      ;;
+    plaintar)
+      # Must skip GNU tar: if it does not support --format= it doesn't create
+      # ustar tarball either.
+      (tar --version) >/dev/null 2>&1 && continue
+      am__tar='tar chf - "$$tardir"'
+      am__tar_='tar chf - "$tardir"'
+      am__untar='tar xf -'
+      ;;
+    pax)
+      am__tar='pax -L -x $1 -w "$$tardir"'
+      am__tar_='pax -L -x $1 -w "$tardir"'
+      am__untar='pax -r'
+      ;;
+    cpio)
+      am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+      am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+      am__untar='cpio -i -H $1 -d'
+      ;;
+    none)
+      am__tar=false
+      am__tar_=false
+      am__untar=false
+      ;;
+    esac
+
+    # If the value was cached, stop now.  We just wanted to have am__tar
+    # and am__untar set.
+    test -n "${am_cv_prog_tar_$1}" && break
+
+    # tar/untar a dummy directory, and stop if the command works.
+    rm -rf conftest.dir
+    mkdir conftest.dir
+    echo GrepMe > conftest.dir/file
+    AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+    rm -rf conftest.dir
+    if test -s conftest.tar; then
+      AM_RUN_LOG([$am__untar <conftest.tar])
+      AM_RUN_LOG([cat conftest.dir/file])
+      grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+    fi
+  done
+  rm -rf conftest.dir
+
+  AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+  AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
diff --git a/sg3_utils/archive/README b/sg3_utils/archive/README
new file mode 100644
index 0000000..e0e3220
--- /dev/null
+++ b/sg3_utils/archive/README
@@ -0,0 +1,16 @@
+The code and scripts in this directory may be removed at some later
+date. The last cleanup (i.e. purge of unused files) of this
+directory occurred between sg3_utils version 1.22 and 1.23 .
+No other code or script in this package currently uses the contents
+of this directory. The contents of this directory are not
+maintained by the author.
+
+The rescan-scsi-bus.sh script was copied long ago from
+http://www.garloff.de/kurt/linux (under the "Rescan SCSI bus"
+heading) and was later placed in this directory. Since others
+do use the version of this script found in this package then
+rescan-scsi-bus.sh was updated in sg3_utils version 1.35 and
+was moved to the scripts directory.
+
+Douglas Gilbert
+9th January 2013
diff --git a/sg3_utils/archive/llseek.c b/sg3_utils/archive/llseek.c
new file mode 100644
index 0000000..fcc53fa
--- /dev/null
+++ b/sg3_utils/archive/llseek.c
@@ -0,0 +1,128 @@
+/*
+ * llseek.c -- stub calling the llseek system call
+ *
+ * Copyright (C) 1994 Remy Card.  This file may be redistributed
+ * under the terms of the GNU Public License.
+ *
+ * This file is borrowed from the util-linux-2.11z tarball's implementation
+ * of fdisk. It allows seeks to 64 bit offsets, if supported.
+ * Changed "ext2_" prefix to "llse".
+ */
+
+#include "config.h"
+
+#define _XOPEN_SOURCE 500
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <unistd.h>
+
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef int64_t       llse_loff_t;
+#else
+typedef long            llse_loff_t;
+#endif
+
+extern llse_loff_t llse_llseek (unsigned int, llse_loff_t, unsigned int);
+
+#ifdef __linux__
+
+#ifdef HAVE_LLSEEK
+#include <syscall.h>
+
+#else   /* HAVE_LLSEEK */
+
+#if defined(__alpha__) || defined(__ia64__)  || defined(__s390x__) || defined (__x86_64__) || defined (__powerpc64__)
+
+#define my_llseek lseek
+
+#else
+#include <linux/unistd.h>       /* for __NR__llseek */
+
+static int _llseek (unsigned int, unsigned long,
+                   unsigned long, llse_loff_t *, unsigned int);
+
+#ifdef __NR__llseek
+
+static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high,
+                 unsigned long, offset_low,llse_loff_t *,result,
+                 unsigned int, origin)
+
+#else
+
+/* no __NR__llseek on compilation machine - might give it explicitly */
+static int _llseek (unsigned int fd, unsigned long oh,
+                    unsigned long ol, llse_loff_t *result,
+                    unsigned int origin) {
+        errno = ENOSYS;
+        return -1;
+}
+
+#endif
+
+static llse_loff_t my_llseek (unsigned int fd, llse_loff_t offset,
+                unsigned int origin)
+{
+        llse_loff_t result;
+        int retval;
+
+#ifdef HAVE_LSEEK64
+        return lseek64 (fd, offset, origin);
+#else
+        retval = _llseek (fd, ((uint64_t) offset) >> 32,
+                        ((uint64_t) offset) & 0xffffffff,
+                        &result, origin);
+        return (retval == -1 ? (llse_loff_t) retval : result);
+#endif
+}
+
+#endif /* __alpha__ */
+
+#endif  /* HAVE_LLSEEK */
+
+llse_loff_t llse_llseek (unsigned int fd, llse_loff_t offset,
+                         unsigned int origin)
+{
+        llse_loff_t result;
+        static int do_compat = 0;
+
+        if (!do_compat) {
+                result = my_llseek (fd, offset, origin);
+                if (!(result == -1 && errno == ENOSYS))
+                        return result;
+
+                /*
+                 * Just in case this code runs on top of an old kernel
+                 * which does not support the llseek system call
+                 */
+                do_compat = 1;
+                /*
+                 * Now try ordinary lseek.
+                 */
+        }
+
+        if ((sizeof(off_t) >= sizeof(llse_loff_t)) ||
+            (offset < ((llse_loff_t) 1 << ((sizeof(off_t)*8) -1))))
+                return lseek(fd, (off_t) offset, origin);
+
+        errno = EINVAL;
+        return -1;
+}
+
+#else /* !linux */
+
+llse_loff_t llse_llseek (unsigned int fd, llse_loff_t offset,
+                         unsigned int origin)
+{
+        if ((sizeof(off_t) < sizeof(llse_loff_t)) &&
+            (offset >= ((llse_loff_t) 1 << ((sizeof(off_t)*8) -1)))) {
+                errno = EINVAL;
+                return -1;
+        }
+        return lseek (fd, (off_t) offset, origin);
+}
+
+#endif  /* linux */
+
diff --git a/sg3_utils/archive/llseek.h b/sg3_utils/archive/llseek.h
new file mode 100644
index 0000000..61c12e4
--- /dev/null
+++ b/sg3_utils/archive/llseek.h
@@ -0,0 +1,14 @@
+#ifndef LLSEEK_H
+#define LLSEEK_H
+
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef int64_t llse_loff_t;
+#else
+typedef long      llse_loff_t;
+#endif
+
+extern llse_loff_t llse_llseek(unsigned int fd,
+                               llse_loff_t offset,
+                               unsigned int origin);
+
+#endif
diff --git a/sg3_utils/archive/o_scsi_logging_level b/sg3_utils/archive/o_scsi_logging_level
new file mode 100755
index 0000000..ecbc827
--- /dev/null
+++ b/sg3_utils/archive/o_scsi_logging_level
@@ -0,0 +1,295 @@
+#! /bin/bash
+###############################################################################
+# Conveniently create and set scsi logging level, show SCSI_LOG fields in human
+# readable form.
+#
+# Copyright (C) IBM Corp. 2006
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+###############################################################################
+
+# Contributed by Andreas Herrmann <aherrman@de.ibm.com> 2006/08/18
+
+SCRIPTNAME="scsi_logging_level"
+
+declare -i LOG_ERROR=0
+declare -i LOG_TIMEOUT=0
+declare -i LOG_SCAN=0
+declare -i LOG_MLQUEUE=0
+declare -i LOG_MLCOMPLETE=0
+declare -i LOG_LLQUEUE=0
+declare -i LOG_LLCOMPLETE=0
+declare -i LOG_HLQUEUE=0
+declare -i LOG_HLCOMPLETE=0
+declare -i LOG_IOCTL=0
+
+declare -i LEVEL=0
+
+_ERROR_SHIFT=0
+_TIMEOUT_SHIFT=3
+_SCAN_SHIFT=6
+_MLQUEUE_SHIFT=9
+_MLCOMPLETE_SHIFT=12
+_LLQUEUE_SHIFT=15
+_LLCOMPLETE_SHIFT=18
+_HLQUEUE_SHIFT=21
+_HLCOMPLETE_SHIFT=24
+_IOCTL_SHIFT=27
+
+SET=0
+GET=0
+CREATE=0
+
+OPTS=`getopt -o hvcgsa:E:T:S:I:M:L:H: --long \
+help,version,create,get,set,all:,error:,timeout:,scan:,ioctl:,\
+midlevel:,mlqueue:,mlcomplete:,lowlevel:,llqueue:,llcomplete:,\
+highlevel:,hlqueue:,hlcomplete: -n \'$SCRIPTNAME\' -- "$@"`
+eval set -- "$OPTS"
+
+# print version info
+printversion()
+{
+    cat <<EOF
+$SCRIPTNAME (s390-tools) %S390_TOOLS_VERSION%
+(C) Copyright IBM Corp. 2006
+EOF
+}
+
+# print usage and help
+printhelp()
+{
+    cat <<EOF
+Usage: $SCRIPTNAME [OPTIONS]
+
+Create, get or set scsi logging level.
+
+Options:
+
+        -h, --help       print this help
+        -v, --version    print version information
+        -s, --set        create and set logging level as specified on
+                         command line
+        -g, --get        get current logging level and display it
+        -c, --create     create logging level as specified on command line
+        -a, --all        specify value for all SCSI_LOG fields
+        -E, --error      specify SCSI_LOG_ERROR
+        -T, --timeout    specify SCSI_LOG_TIMEOUT
+        -S, --scan       specify SCSI_LOG_SCAN
+        -M, --midlevel   specify SCSI_LOG_MLQUEUE and SCSI_LOG_MLCOMPLETE
+            --mlqueue    specify SCSI_LOG_MLQUEUE
+            --mlcomplete specify SCSI_LOG_MLCOMPLETE
+        -L, --lowlevel   specify SCSI_LOG_LLQUEUE and SCSI_LOG_LLCOMPLETE
+            --llqueue    specify SCSI_LOG_LLQUEUE
+            --llcomplete specify SCSI_LOG_LLCOMPLETE
+        -H, --highlevel  specify SCSI_LOG_HLQUEUE and SCSI_LOG_HLCOMPLETE
+            --hlqueue    specify SCSI_LOG_HLQUEUE
+            --hlcomplete specify SCSI_LOG_HLCOMPLETE
+        -I, --ioctl      specify SCSI_LOG_IOCTL
+
+Exactly one of the options "-c", "-g" and "-s" has to be specified.
+Valid values for SCSI_LOG fields are integers from 0 to 7.
+
+Note: Several SCSI_LOG fields can be specified using several options.
+When multiple options specify same SCSI_LOG field the most specific
+option has precedence.
+
+Example: "scsi_logging_level --hlqueue 3 --hlcomplete 2 --all 1 -s" sets
+SCSI_LOG_HLQUEUE=3, SCSI_LOG_HLCOMPLETE=2 and assigns all other SCSI_LOG
+fields the value 1.
+EOF
+}
+
+check_level()
+{
+# something is wrong with the following if ... dpg 20061027
+#    if [ `echo -n $1 | tr --complement [:digit:] 'a' | grep -s 'a'` ]
+#    then
+#	invalid_cmdline "log level '$1' out of range [0, 7]"
+#    fi
+	
+    if [ $1 -lt 0 -o $1 -gt 7 ]
+    then
+	invalid_cmdline "log level '$1' out of range [0, 7]"
+    fi
+}
+
+# check cmd line arguments
+check_cmdline()
+{
+    while true ; do
+	case "$1" in
+	    -a|--all)	_ALL=$2; check_level $2
+			shift 2;;
+	    -c|--create) CREATE=1;
+			shift 1;;
+	    -g|--get)	GET=1
+			shift 1;;
+	    -h|--help) printhelp
+			exit 0;;
+	    -s|--set)	SET=1
+			shift 1;;
+	    -v|--version) printversion
+			exit 0;;
+	    -E|--error)	_ERROR=$2; check_level $2
+			shift 2;;
+	    -T|--timeout) _TIMEOUT=$2; check_level $2
+			shift 2;;
+	    -S|--scan)	_SCAN=$2; check_level $2
+			shift 2;;
+	    -M|--midlevel) _ML=$2; check_level $2
+			shift 2;;
+	    --mlqueue)	_MLQUEUE=$2; check_level $2
+			shift 2;;
+	    --mlcomplete) _MLCOMPLETE=$2; check_level $2
+			shift 2;;
+	    -L|--lowlevel) _LL=$2; check_level $2
+			shift 2;;
+	    --llqueue)	_LLQUEUE=$2; check_level $2
+			shift 2;;
+	    --llcomplete) _LLCOMPLETE=$2; check_level $2
+			shift 2;;
+	    -H|--highlevel) _HL=$2; check_level $2
+			shift 2;;
+	    --hlqueue)	_HLQUEUE=$2; check_level $2
+			shift 2;;
+	    --hlcomplete) _HLCOMPLETE=$2; check_level $2
+			shift 2;;
+	    -I|--ioctl) _IOCTL=$2; check_level $2
+			shift 2;;
+	    --) shift; break;;
+	    *) echo "Internal error!" ; exit 1;;
+	esac
+    done
+
+    if [ -n "$*" ]
+    then
+	invalid_cmdline invalid parameter $*
+    fi
+
+    if [ $GET = "1" -a $SET = "1" ]
+    then
+        invalid_cmdline options \'-c\', \'-g\' and \'-s\' are mutual exclusive
+    elif [ $GET = "1" -a $CREATE = "1" ]
+    then
+        invalid_cmdline options \'-c\', \'-g\' and \'-s\' are mutual exclusive
+    elif [ $SET = "1" -a $CREATE = "1" ]
+    then
+        invalid_cmdline options \'-c\', \'-g\' and \'-s\' are mutual exclusive
+    fi
+    
+    LOG_ERROR=${_ERROR:-${_ALL:-0}}
+    LOG_TIMEOUT=${_TIMEOUT:-${_ALL:-0}}
+    LOG_SCAN=${_SCAN:-${_ALL:-0}}
+    LOG_MLQUEUE=${_MLQUEUE:-${_ML:-${_ALL:-0}}}
+    LOG_MLCOMPLETE=${_MLCOMPLETE:-${_ML:-${_ALL:-0}}}
+    LOG_LLQUEUE=${_LLQUEUE:-${_LL:-${_ALL:-0}}}
+    LOG_LLCOMPLETE=${_LLCOMPLETE:-${_LL:-${_ALL:-0}}}
+    LOG_HLQUEUE=${_HLQUEUE:-${_HL:-${_ALL:-0}}}
+    LOG_HLCOMPLETE=${_HLCOMPLETE:-${_HL:-${_ALL:-0}}}
+    LOG_IOCTL=${_IOCTL:-${_ALL:-0}}
+}
+
+invalid_cmdline()
+{
+        echo "$SCRIPTNAME: $*"
+	echo "$SCRIPTNAME: Try '$SCRIPTNAME --help' for more information."
+	exit 1
+}
+
+get_logging_level()
+{
+    echo "Current scsi logging level:"
+    LEVEL=`sysctl -n dev.scsi.logging_level`
+    if [ $? != 0 ]
+    then
+ 	echo "$SCRIPTNAME: could not read scsi logging level" \
+             "(kernel probably without SCSI_LOGGING support)"
+ 	exit 1
+    fi
+}
+
+show_logging_level()
+{
+    echo "dev.scsi.logging_level = $LEVEL"
+
+    LOG_ERROR=$((($LEVEL>>$_ERROR_SHIFT) & 7))
+    LOG_TIMEOUT=$((($LEVEL>>$_TIMEOUT_SHIFT) & 7))
+    LOG_SCAN=$((($LEVEL>>$_SCAN_SHIFT) & 7))
+    LOG_MLQUEUE=$((($LEVEL>>$_MLQUEUE_SHIFT) & 7))
+    LOG_MLCOMPLETE=$((($LEVEL>>$_MLCOMPLETE_SHIFT) & 7))
+    LOG_LLQUEUE=$((($LEVEL>>$_LLQUEUE_SHIFT) & 7))
+    LOG_LLCOMPLETE=$((($LEVEL>>$_LLCOMPLETE_SHIFT) & 7))
+    LOG_HLQUEUE=$((($LEVEL>>$_HLQUEUE_SHIFT) & 7))
+    LOG_HLCOMPLETE=$((($LEVEL>>$_HLCOMPLETE_SHIFT) & 7))
+    LOG_IOCTL=$((($LEVEL>>$_IOCTL_SHIFT) & 7))
+
+    echo "SCSI_LOG_ERROR=$LOG_ERROR"
+    echo "SCSI_LOG_TIMEOUT=$LOG_TIMEOUT"
+    echo "SCSI_LOG_SCAN=$LOG_SCAN"
+    echo "SCSI_LOG_MLQUEUE=$LOG_MLQUEUE"
+    echo "SCSI_LOG_MLCOMPLETE=$LOG_MLCOMPLETE"
+    echo "SCSI_LOG_LLQUEUE=$LOG_LLQUEUE"
+    echo "SCSI_LOG_LLCOMPLETE=$LOG_LLCOMPLETE"
+    echo "SCSI_LOG_HLQUEUE=$LOG_HLQUEUE"
+    echo "SCSI_LOG_HLCOMPLETE=$LOG_HLCOMPLETE"
+    echo "SCSI_LOG_IOCTL=$LOG_IOCTL"
+}
+
+set_logging_level()
+{
+    echo "New scsi logging level:"
+    sysctl -q -w dev.scsi.logging_level=$LEVEL
+    if [ $? != 0 ]
+    then
+ 	echo "$SCRIPTNAME: could not write scsi logging level" \
+ 	     "(kernel probably without SCSI_LOGGING support)"
+ 	exit 1
+    fi
+}
+
+create_logging_level()
+{
+    LEVEL=$((($LOG_ERROR & 7)<<$_ERROR_SHIFT))
+    LEVEL=$(($LEVEL|(($LOG_TIMEOUT & 7)<<$_TIMEOUT_SHIFT)))
+    LEVEL=$(($LEVEL|(($LOG_SCAN & 7)<<$_SCAN_SHIFT)))
+    LEVEL=$(($LEVEL|(($LOG_MLQUEUE & 7)<<$_MLQUEUE_SHIFT)))
+    LEVEL=$(($LEVEL|(($LOG_MLCOMPLETE & 7)<<$_MLCOMPLETE_SHIFT)))
+    LEVEL=$(($LEVEL|(($LOG_LLQUEUE & 7)<<$_LLQUEUE_SHIFT)))
+    LEVEL=$(($LEVEL|(($LOG_LLCOMPLETE & 7)<<$_LLCOMPLETE_SHIFT)))
+    LEVEL=$(($LEVEL|(($LOG_HLQUEUE & 7)<<$_HLQUEUE_SHIFT)))
+    LEVEL=$(($LEVEL|(($LOG_HLCOMPLETE & 7)<<$_HLCOMPLETE_SHIFT)))
+    LEVEL=$(($LEVEL|(($LOG_IOCTL & 7)<<$_IOCTL_SHIFT)))
+}
+
+check_cmdline $*
+
+if [ $SET = "1" ]
+then
+    create_logging_level
+    set_logging_level
+    show_logging_level
+elif [ $GET = "1" ]
+then
+    get_logging_level
+    show_logging_level
+elif [ $CREATE = "1" ]
+then
+    create_logging_level
+    show_logging_level
+else
+    invalid_cmdline missing option \'-g\', \'-s\' or \'-c\'
+fi
+
diff --git a/sg3_utils/autogen.sh b/sg3_utils/autogen.sh
new file mode 100755
index 0000000..5bce9ba
--- /dev/null
+++ b/sg3_utils/autogen.sh
@@ -0,0 +1,1578 @@
+#!/bin/sh
+#                        a u t o g e n . s h
+#
+# Copyright (c) 2005-2009 United States Government as represented by
+# the U.S. Army Research Laboratory.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+###
+#
+# Script for automatically preparing the sources for compilation by
+# performing the myriad of necessary steps.  The script attempts to
+# detect proper version support, and outputs warnings about particular
+# systems that have autotool peculiarities.
+#
+# Basically, if everything is set up and installed correctly, the
+# script will validate that minimum versions of the GNU Build System
+# tools are installed, account for several common configuration
+# issues, and then simply run autoreconf for you.
+#
+# If autoreconf fails, which can happen for many valid configurations,
+# this script proceeds to run manual preparation steps effectively
+# providing a POSIX shell script (mostly complete) reimplementation of
+# autoreconf.
+#
+# The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER
+# environment variables and corresponding _OPTIONS variables (e.g.
+# AUTORECONF_OPTIONS) may be used to override the default automatic
+# detection behaviors.  Similarly the _VERSION variables will override
+# the minimum required version numbers.
+#
+# Examples:
+#
+#   To obtain help on usage:
+#     ./autogen.sh --help
+#
+#   To obtain verbose output:
+#     ./autogen.sh --verbose
+#
+#   To skip autoreconf and prepare manually:
+#     AUTORECONF=false ./autogen.sh
+#
+#   To verbosely try running with an older (unsupported) autoconf:
+#     AUTOCONF_VERSION=2.50 ./autogen.sh --verbose
+#
+# Author:
+#   Christopher Sean Morrison <morrison@brlcad.org>
+#
+# Patches:
+#   Sebastian Pipping <sebastian@pipping.org>
+#
+######################################################################
+
+# set to minimum acceptable version of autoconf
+if [ "x$AUTOCONF_VERSION" = "x" ] ; then
+    AUTOCONF_VERSION=2.52
+fi
+# set to minimum acceptable version of automake
+if [ "x$AUTOMAKE_VERSION" = "x" ] ; then
+    AUTOMAKE_VERSION=1.6.0
+fi
+# set to minimum acceptable version of libtool
+if [ "x$LIBTOOL_VERSION" = "x" ] ; then
+    LIBTOOL_VERSION=1.4.2
+fi
+
+
+##################
+# ident function #
+##################
+ident ( ) {
+    # extract copyright from header
+    __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`"
+    if [ "x$__copyright" = "x" ] ; then
+	__copyright="`date +%Y`"
+    fi
+
+    # extract version from CVS Id string
+    __id="$Id: autogen.sh 33925 2009-03-01 23:27:06Z brlcad $"
+    __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`"
+    if [ "x$__version" = "x" ] ; then
+	__version=""
+    fi
+
+    echo "autogen.sh build preparation script by Christopher Sean Morrison"
+    echo "  + config.guess download patch by Sebastian Pipping (2008-12-03)"
+    echo "revised 3-clause BSD-style license, copyright (c) $__copyright"
+    echo "script version $__version, ISO/IEC 9945 POSIX shell script"
+}
+
+
+##################
+# USAGE FUNCTION #
+##################
+usage ( ) {
+    echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]"
+    echo "    --help      Help on $NAME_OF_AUTOGEN usage"
+    echo "    --verbose   Verbose progress output"
+    echo "    --quiet     Quiet suppressed progress output"
+    echo "    --download  Download the latest config.guess from gnulib"
+    echo "    --version   Only perform GNU Build System version checks"
+    echo
+    echo "Description: This script will validate that minimum versions of the"
+    echo "GNU Build System tools are installed and then run autoreconf for you."
+    echo "Should autoreconf fail, manual preparation steps will be run"
+    echo "potentially accounting for several common preparation issues.  The"
+
+    echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER,"
+    echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS"
+    echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the"
+    echo "default automatic detection behavior."
+    echo
+
+    ident
+
+    return 0
+}
+
+
+##########################
+# VERSION_ERROR FUNCTION #
+##########################
+version_error ( ) {
+    if [ "x$1" = "x" ] ; then
+	echo "INTERNAL ERROR: version_error was not provided a version"
+	exit 1
+    fi
+    if [ "x$2" = "x" ] ; then
+	echo "INTERNAL ERROR: version_error was not provided an application name"
+	exit 1
+    fi
+    $ECHO
+    $ECHO "ERROR:  To prepare the ${PROJECT} build system from scratch,"
+    $ECHO "        at least version $1 of $2 must be installed."
+    $ECHO
+    $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will"
+    $ECHO "run configure or make.  Either the GNU Autotools will need to be installed"
+    $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source"
+    $ECHO "code on another system and then transferred to here. -- Cheers!"
+    $ECHO
+}
+
+##########################
+# VERSION_CHECK FUNCTION #
+##########################
+version_check ( ) {
+    if [ "x$1" = "x" ] ; then
+	echo "INTERNAL ERROR: version_check was not provided a minimum version"
+	exit 1
+    fi
+    _min="$1"
+    if [ "x$2" = "x" ] ; then
+	echo "INTERNAL ERROR: version check was not provided a comparison version"
+	exit 1
+    fi
+    _cur="$2"
+
+    # needed to handle versions like 1.10 and 1.4-p6
+    _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+    _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+
+    _min_major="`echo $_min | cut -d. -f1`"
+    _min_minor="`echo $_min | cut -d. -f2`"
+    _min_patch="`echo $_min | cut -d. -f3`"
+
+    _cur_major="`echo $_cur | cut -d. -f1`"
+    _cur_minor="`echo $_cur | cut -d. -f2`"
+    _cur_patch="`echo $_cur | cut -d. -f3`"
+
+    if [ "x$_min_major" = "x" ] ; then
+	_min_major=0
+    fi
+    if [ "x$_min_minor" = "x" ] ; then
+	_min_minor=0
+    fi
+    if [ "x$_min_patch" = "x" ] ; then
+	_min_patch=0
+    fi
+    if [ "x$_cur_minor" = "x" ] ; then
+	_cur_major=0
+    fi
+    if [ "x$_cur_minor" = "x" ] ; then
+	_cur_minor=0
+    fi
+    if [ "x$_cur_patch" = "x" ] ; then
+	_cur_patch=0
+    fi
+
+    $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}"
+
+    if [ $_min_major -lt $_cur_major ] ; then
+	return 0
+    elif [ $_min_major -eq $_cur_major ] ; then
+	if [ $_min_minor -lt $_cur_minor ] ; then
+	    return 0
+	elif [ $_min_minor -eq $_cur_minor ] ; then
+	    if [ $_min_patch -lt $_cur_patch ] ; then
+		return 0
+	    elif [ $_min_patch -eq $_cur_patch ] ; then
+		return 0
+	    fi
+	fi
+    fi
+    return 1
+}
+
+
+######################################
+# LOCATE_CONFIGURE_TEMPLATE FUNCTION #
+######################################
+locate_configure_template ( ) {
+    _pwd="`pwd`"
+    if test -f "./configure.ac" ; then
+	echo "./configure.ac"
+    elif test -f "./configure.in" ; then
+	echo "./configure.in"
+    elif test -f "$_pwd/configure.ac" ; then
+	echo "$_pwd/configure.ac"
+    elif test -f "$_pwd/configure.in" ; then
+	echo "$_pwd/configure.in"
+    elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then
+	echo "$PATH_TO_AUTOGEN/configure.ac"
+    elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then
+	echo "$PATH_TO_AUTOGEN/configure.in"
+    fi
+}
+
+
+##################
+# argument check #
+##################
+ARGS="$*"
+PATH_TO_AUTOGEN="`dirname $0`"
+NAME_OF_AUTOGEN="`basename $0`"
+AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN"
+
+LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4"
+
+if [ "x$HELP" = "x" ] ; then
+    HELP=no
+fi
+if [ "x$QUIET" = "x" ] ; then
+    QUIET=no
+fi
+if [ "x$VERBOSE" = "x" ] ; then
+    VERBOSE=no
+fi
+if [ "x$VERSION_ONLY" = "x" ] ; then
+    VERSION_ONLY=no
+fi
+if [ "x$DOWNLOAD" = "x" ] ; then
+    DOWNLOAD=no
+fi
+if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then
+    AUTORECONF_OPTIONS="-i -f"
+fi
+if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then
+    AUTOCONF_OPTIONS="-f"
+fi
+if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then
+    AUTOMAKE_OPTIONS="-a -c -f"
+fi
+ALT_AUTOMAKE_OPTIONS="-a -c"
+if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then
+    LIBTOOLIZE_OPTIONS="--automake -c -f"
+fi
+ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force"
+if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then
+    ACLOCAL_OPTIONS=""
+fi
+if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then
+    AUTOHEADER_OPTIONS=""
+fi
+if [ "x$CONFIG_GUESS_URL" = "x" ] ; then
+    CONFIG_GUESS_URL="http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD"
+fi
+for arg in $ARGS ; do
+    case "x$arg" in
+	x--help) HELP=yes ;;
+	x-[hH]) HELP=yes ;;
+	x--quiet) QUIET=yes ;;
+	x-[qQ]) QUIET=yes ;;
+	x--verbose) VERBOSE=yes ;;
+	x-[dD]) DOWNLOAD=yes ;;
+	x--download) DOWNLOAD=yes ;;
+	x-[vV]) VERBOSE=yes ;;
+	x--version) VERSION_ONLY=yes ;;
+	*)
+	    echo "Unknown option: $arg"
+	    echo
+	    usage
+	    exit 1
+	    ;;
+    esac
+done
+
+
+#####################
+# environment check #
+#####################
+
+# sanity check before recursions potentially begin
+if [ ! -f "$AUTOGEN_SH" ] ; then
+    echo "INTERNAL ERROR: $AUTOGEN_SH does not exist"
+    if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then
+	echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH"
+    fi
+    exit 1
+fi
+
+# force locale setting to C so things like date output as expected
+LC_ALL=C
+
+# commands that this script expects
+for __cmd in echo head tail pwd ; do
+    echo "test" | $__cmd > /dev/null 2>&1
+    if [ $? != 0 ] ; then
+	echo "INTERNAL ERROR: '${__cmd}' command is required"
+	exit 2
+    fi
+done
+echo "test" | grep "test" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+    echo "INTERNAL ERROR: grep command is required"
+    exit 1
+fi
+echo "test" | sed "s/test/test/" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+    echo "INTERNAL ERROR: sed command is required"
+    exit 1
+fi
+
+
+# determine the behavior of echo
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+    *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='	' ;;
+    *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+    *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+# determine the behavior of head
+case "x`echo 'head' | head -n 1 2>&1`" in
+    *xhead*) HEAD_N="n " ;;
+    *) HEAD_N="" ;;
+esac
+
+# determine the behavior of tail
+case "x`echo 'tail' | tail -n 1 2>&1`" in
+    *xtail*) TAIL_N="n " ;;
+    *) TAIL_N="" ;;
+esac
+
+VERBOSE_ECHO=:
+ECHO=:
+if [ "x$QUIET" = "xyes" ] ; then
+    if [ "x$VERBOSE" = "xyes" ] ; then
+	echo "Verbose output quelled by quiet option.  Further output disabled."
+    fi
+else
+    ECHO=echo
+    if [ "x$VERBOSE" = "xyes" ] ; then
+	echo "Verbose output enabled"
+	VERBOSE_ECHO=echo
+    fi
+fi
+
+
+# allow a recursive run to disable further recursions
+if [ "x$RUN_RECURSIVE" = "x" ] ; then
+    RUN_RECURSIVE=yes
+fi
+
+
+################################################
+# check for help arg and bypass version checks #
+################################################
+if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then
+    HELP=yes
+fi
+if [ "x$HELP" = "xyes" ] ; then
+    usage
+    $ECHO "---"
+    $ECHO "Help was requested.  No preparation or configuration will be performed."
+    exit 0
+fi
+
+
+#######################
+# set up signal traps #
+#######################
+untrap_abnormal ( ) {
+    for sig in 1 2 13 15; do
+	trap - $sig
+    done
+}
+
+# do this cleanup whenever we exit.
+trap '
+    # start from the root
+    if test -d "$START_PATH" ; then
+	cd "$START_PATH"
+    fi
+
+    # restore/delete backup files
+    if test "x$PFC_INIT" = "x1" ; then
+	recursive_restore
+    fi
+' 0
+
+# trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15)
+for sig in 1 2 13 15; do
+    trap '
+	$ECHO ""
+	$ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'"
+
+	# start from the root
+	if test -d "$START_PATH" ; then
+	    cd "$START_PATH"
+	fi
+
+	# clean up on abnormal exit
+	$VERBOSE_ECHO "rm -rf autom4te.cache"
+	rm -rf autom4te.cache
+
+	if test -f "acinclude.m4.$$.backup" ; then
+	    $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4"
+	    chmod u+w acinclude.m4
+	    cat acinclude.m4.$$.backup > acinclude.m4
+
+	    $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup"
+	    rm -f acinclude.m4.$$.backup
+        fi
+
+	{ (exit 1); exit 1; }
+' $sig
+done
+
+
+#############################
+# look for a configure file #
+#############################
+if [ "x$CONFIGURE" = "x" ] ; then
+    CONFIGURE="`locate_configure_template`"
+    if [ ! "x$CONFIGURE" = "x" ] ; then
+	$VERBOSE_ECHO "Found a configure template: $CONFIGURE"
+    fi
+else
+    $ECHO "Using CONFIGURE environment variable override: $CONFIGURE"
+fi
+if [ "x$CONFIGURE" = "x" ] ; then
+    if [ "x$VERSION_ONLY" = "xyes" ] ; then
+	CONFIGURE=/dev/null
+    else
+	$ECHO
+	$ECHO "A configure.ac or configure.in file could not be located implying"
+	$ECHO "that the GNU Build System is at least not used in this directory.  In"
+	$ECHO "any case, there is nothing to do here without one of those files."
+	$ECHO
+	$ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+	exit 1
+    fi
+fi
+
+####################
+# get project name #
+####################
+if [ "x$PROJECT" = "x" ] ; then
+    PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ 	]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    if [ "x$PROJECT" = "xAC_INIT" ] ; then
+	# projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead
+	PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ 	]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    fi
+    if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then
+	PROJECT="project"
+    fi
+    if [ "x$PROJECT" = "x" ] ; then
+	PROJECT="project"
+    fi
+else
+    $ECHO "Using PROJECT environment variable override: $PROJECT"
+fi
+$ECHO "Preparing the $PROJECT build system...please wait"
+$ECHO
+
+
+########################
+# check for autoreconf #
+########################
+HAVE_AUTORECONF=no
+if [ "x$AUTORECONF" = "x" ] ; then
+    for AUTORECONF in autoreconf ; do
+	$VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version"
+	$AUTORECONF --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    HAVE_AUTORECONF=yes
+	    break
+	fi
+    done
+else
+    HAVE_AUTORECONF=yes
+    $ECHO "Using AUTORECONF environment variable override: $AUTORECONF"
+fi
+
+
+##########################
+# autoconf version check #
+##########################
+_acfound=no
+if [ "x$AUTOCONF" = "x" ] ; then
+    for AUTOCONF in autoconf ; do
+	$VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version"
+	$AUTOCONF --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    _acfound=yes
+	    break
+	fi
+    done
+else
+    _acfound=yes
+    $ECHO "Using AUTOCONF environment variable override: $AUTOCONF"
+fi
+
+_report_error=no
+if [ ! "x$_acfound" = "xyes" ] ; then
+    $ECHO "ERROR:  Unable to locate GNU Autoconf."
+    _report_error=yes
+else
+    _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+    if [ "x$_version" = "x" ] ; then
+	_version="0.0.0"
+    fi
+    $ECHO "Found GNU Autoconf version $_version"
+    version_check "$AUTOCONF_VERSION" "$_version"
+    if [ $? -ne 0 ] ; then
+	_report_error=yes
+    fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+    version_error "$AUTOCONF_VERSION" "GNU Autoconf"
+    exit 1
+fi
+
+
+##########################
+# automake version check #
+##########################
+_amfound=no
+if [ "x$AUTOMAKE" = "x" ] ; then
+    for AUTOMAKE in automake ; do
+	$VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version"
+	$AUTOMAKE --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    _amfound=yes
+	    break
+	fi
+    done
+else
+    _amfound=yes
+    $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE"
+fi
+
+
+_report_error=no
+if [ ! "x$_amfound" = "xyes" ] ; then
+    $ECHO
+    $ECHO "ERROR: Unable to locate GNU Automake."
+    _report_error=yes
+else
+    _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+    if [ "x$_version" = "x" ] ; then
+	_version="0.0.0"
+    fi
+    $ECHO "Found GNU Automake version $_version"
+    version_check "$AUTOMAKE_VERSION" "$_version"
+    if [ $? -ne 0 ] ; then
+	_report_error=yes
+    fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+    version_error "$AUTOMAKE_VERSION" "GNU Automake"
+    exit 1
+fi
+
+
+########################
+# check for libtoolize #
+########################
+HAVE_LIBTOOLIZE=yes
+HAVE_ALT_LIBTOOLIZE=no
+_ltfound=no
+if [ "x$LIBTOOLIZE" = "x" ] ; then
+    LIBTOOLIZE=libtoolize
+    $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version"
+    $LIBTOOLIZE --version > /dev/null 2>&1
+    if [ ! $? = 0 ] ; then
+	HAVE_LIBTOOLIZE=no
+	$ECHO
+	if [ "x$HAVE_AUTORECONF" = "xno" ] ; then
+	    $ECHO "Warning:  libtoolize does not appear to be available."
+	else
+	    $ECHO "Warning:  libtoolize does not appear to be available.  This means that"
+	    $ECHO "the automatic build preparation via autoreconf will probably not work."
+	    $ECHO "Preparing the build by running each step individually, however, should"
+	    $ECHO "work and will be done automatically for you if autoreconf fails."
+	fi
+
+	# look for some alternates
+	for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do
+	    $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version"
+	    _glibtoolize="`$tool --version > /dev/null 2>&1`"
+	    if [ $? = 0 ] ; then
+		$VERBOSE_ECHO "Found $tool --version"
+		_glti="`which $tool`"
+		if [ "x$_glti" = "x" ] ; then
+		    $VERBOSE_ECHO "Cannot find $tool with which"
+		    continue;
+		fi
+		if test ! -f "$_glti" ; then
+		    $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file"
+		    continue;
+		fi
+		_gltidir="`dirname $_glti`"
+		if [ "x$_gltidir" = "x" ] ; then
+		    $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti"
+		    continue;
+		fi
+		if test ! -d "$_gltidir" ; then
+		    $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory"
+		    continue;
+		fi
+		HAVE_ALT_LIBTOOLIZE=yes
+		LIBTOOLIZE="$tool"
+		$ECHO
+		$ECHO "Fortunately, $tool was found which means that your system may simply"
+		$ECHO "have a non-standard or incomplete GNU Autotools install.  If you have"
+		$ECHO "sufficient system access, it may be possible to quell this warning by"
+		$ECHO "running:"
+		$ECHO
+		sudo -V > /dev/null 2>&1
+		if [ $? = 0 ] ; then
+		    $ECHO "   sudo ln -s $_glti $_gltidir/libtoolize"
+		    $ECHO
+		else
+		    $ECHO "   ln -s $_glti $_gltidir/libtoolize"
+		    $ECHO
+		    $ECHO "Run that as root or with proper permissions to the $_gltidir directory"
+		    $ECHO
+		fi
+		_ltfound=yes
+		break
+	    fi
+	done
+    else
+	_ltfound=yes
+    fi
+else
+    _ltfound=yes
+    $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE"
+fi
+
+
+############################
+# libtoolize version check #
+############################
+_report_error=no
+if [ ! "x$_ltfound" = "xyes" ] ; then
+    $ECHO
+    $ECHO "ERROR: Unable to locate GNU Libtool."
+    _report_error=yes
+else
+    _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+    if [ "x$_version" = "x" ] ; then
+	_version="0.0.0"
+    fi
+    $ECHO "Found GNU Libtool version $_version"
+    version_check "$LIBTOOL_VERSION" "$_version"
+    if [ $? -ne 0 ] ; then
+	_report_error=yes
+    fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+    version_error "$LIBTOOL_VERSION" "GNU Libtool"
+    exit 1
+fi
+
+
+#####################
+# check for aclocal #
+#####################
+if [ "x$ACLOCAL" = "x" ] ; then
+    for ACLOCAL in aclocal ; do
+	$VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version"
+	$ACLOCAL --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    break
+	fi
+    done
+else
+    $ECHO "Using ACLOCAL environment variable override: $ACLOCAL"
+fi
+
+
+########################
+# check for autoheader #
+########################
+if [ "x$AUTOHEADER" = "x" ] ; then
+    for AUTOHEADER in autoheader ; do
+	$VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version"
+	$AUTOHEADER --version > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    break
+	fi
+    done
+else
+    $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER"
+fi
+
+
+#########################
+# check if version only #
+#########################
+$VERBOSE_ECHO "Checking whether to only output version information"
+if [ "x$VERSION_ONLY" = "xyes" ] ; then
+    $ECHO
+    ident
+    $ECHO "---"
+    $ECHO "Version requested.  No preparation or configuration will be performed."
+    exit 0
+fi
+
+
+#################################
+# PROTECT_FROM_CLOBBER FUNCTION #
+#################################
+protect_from_clobber ( ) {
+    PFC_INIT=1
+
+    # protect COPYING & INSTALL from overwrite by automake.  the
+    # automake force option will (inappropriately) ignore the existing
+    # contents of a COPYING and/or INSTALL files (depending on the
+    # version) instead of just forcing *missing* files like it does
+    # for AUTHORS, NEWS, and README. this is broken but extremely
+    # prevalent behavior, so we protect against it by keeping a backup
+    # of the file that can later be restored.
+
+    for file in COPYING INSTALL ; do
+	if test -f ${file} ; then
+	    if test -f ${file}.$$.protect_from_automake.backup ; then
+		$VERBOSE_ECHO "Already backed up ${file} in `pwd`"
+	    else
+		$VERBOSE_ECHO "Backing up ${file} in `pwd`"
+		$VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup"
+		cp -p ${file} ${file}.$$.protect_from_automake.backup
+	    fi
+	fi
+    done
+}
+
+
+##############################
+# RECURSIVE_PROTECT FUNCTION #
+##############################
+recursive_protect ( ) {
+
+    # for projects using recursive configure, run the build
+    # preparation steps for the subdirectories.  this function assumes
+    # START_PATH was set to pwd before recursion begins so that
+    # relative paths work.
+
+    # git 'r done, protect COPYING and INSTALL from being clobbered
+    protect_from_clobber
+
+    if test -d autom4te.cache ; then
+	$VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+	$VERBOSE_ECHO "rm -rf autom4te.cache"
+	rm -rf autom4te.cache
+    fi
+
+    # find configure template
+    _configure="`locate_configure_template`"
+    if [ "x$_configure" = "x" ] ; then
+	return
+    fi
+    # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure"
+
+    # look for subdirs
+    # $VERBOSE_ECHO "Looking for subdirs in `pwd`"
+    _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ 	]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    CHECK_DIRS=""
+    for dir in $_det_config_subdirs ; do
+	if test -d "`pwd`/$dir" ; then
+	    CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+	fi
+    done
+
+    # process subdirs
+    if [ ! "x$CHECK_DIRS" = "x" ] ; then
+	$VERBOSE_ECHO "Recursively scanning the following directories:"
+	$VERBOSE_ECHO "  $CHECK_DIRS"
+	for dir in $CHECK_DIRS ; do
+	    $VERBOSE_ECHO "Protecting files from automake in $dir"
+	    cd "$START_PATH"
+	    eval "cd $dir"
+
+	    # recursively git 'r done
+	    recursive_protect
+	done
+    fi
+} # end of recursive_protect
+
+
+#############################
+# RESTORE_CLOBBERED FUNCION #
+#############################
+restore_clobbered ( ) {
+
+    # The automake (and autoreconf by extension) -f/--force-missing
+    # option may overwrite COPYING and INSTALL even if they do exist.
+    # Here we restore the files if necessary.
+
+    spacer=no
+
+    for file in COPYING INSTALL ; do
+	if test -f ${file}.$$.protect_from_automake.backup ; then
+	    if test -f ${file} ; then
+	    # compare entire content, restore if needed
+	    if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then
+		if test "x$spacer" = "xno" ; then
+		    $VERBOSE_ECHO
+		    spacer=yes
+		fi
+		# restore the backup
+		$VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)"
+		$VERBOSE_ECHO "rm -f ${file}"
+		rm -f ${file}
+		$VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+		mv ${file}.$$.protect_from_automake.backup ${file}
+	    fi # check contents
+	    elif test -f ${file}.$$.protect_from_automake.backup ; then
+		$VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+		mv ${file}.$$.protect_from_automake.backup ${file}
+	    fi # -f ${file}
+	
+	    # just in case
+	    $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup"
+	    rm -f ${file}.$$.protect_from_automake.backup
+	fi # -f ${file}.$$.protect_from_automake.backup
+    done
+
+    CONFIGURE="`locate_configure_template`"
+    if [ "x$CONFIGURE" = "x" ] ; then
+	return
+    fi
+
+    _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ 	]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    if test ! -d "$_aux_dir" ; then
+	_aux_dir=.
+    fi
+
+    for file in config.guess config.sub ltmain.sh ; do
+	if test -f "${_aux_dir}/${file}" ; then
+	    $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\""
+	    rm -f "${_aux_dir}/${file}.backup"
+	fi
+    done
+} # end of restore_clobbered
+
+
+##############################
+# RECURSIVE_RESTORE FUNCTION #
+##############################
+recursive_restore ( ) {
+
+    # restore COPYING and INSTALL from backup if they were clobbered
+    # for each directory recursively.
+
+    # git 'r undone
+    restore_clobbered
+
+    # find configure template
+    _configure="`locate_configure_template`"
+    if [ "x$_configure" = "x" ] ; then
+	return
+    fi
+
+    # look for subdirs
+    _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ 	]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    CHECK_DIRS=""
+    for dir in $_det_config_subdirs ; do
+	if test -d "`pwd`/$dir" ; then
+	    CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+	fi
+    done
+
+    # process subdirs
+    if [ ! "x$CHECK_DIRS" = "x" ] ; then
+	$VERBOSE_ECHO "Recursively scanning the following directories:"
+	$VERBOSE_ECHO "  $CHECK_DIRS"
+	for dir in $CHECK_DIRS ; do
+	    $VERBOSE_ECHO "Checking files for automake damage in $dir"
+	    cd "$START_PATH"
+	    eval "cd $dir"
+
+	    # recursively git 'r undone
+	    recursive_restore
+	done
+    fi
+} # end of recursive_restore
+
+
+#######################
+# INITIALIZE FUNCTION #
+#######################
+initialize ( ) {
+
+    # this routine performs a variety of directory-specific
+    # initializations.  some are sanity checks, some are preventive,
+    # and some are necessary setup detection.
+    #
+    # this function sets:
+    #   CONFIGURE
+    #   SEARCH_DIRS
+    #   CONFIG_SUBDIRS
+
+    ##################################
+    # check for a configure template #
+    ##################################
+    CONFIGURE="`locate_configure_template`"
+    if [ "x$CONFIGURE" = "x" ] ; then
+	$ECHO
+	$ECHO "A configure.ac or configure.in file could not be located implying"
+	$ECHO "that the GNU Build System is at least not used in this directory.  In"
+	$ECHO "any case, there is nothing to do here without one of those files."
+	$ECHO
+	$ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+	exit 1
+    fi
+
+    #####################
+    # detect an aux dir #
+    #####################
+    _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ 	]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    if test ! -d "$_aux_dir" ; then
+	_aux_dir=.
+    else
+	$VERBOSE_ECHO "Detected auxillary directory: $_aux_dir"
+    fi
+
+    ################################
+    # detect a recursive configure #
+    ################################
+    CONFIG_SUBDIRS=""
+    _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ 	]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+    for dir in $_det_config_subdirs ; do
+	if test -d "`pwd`/$dir" ; then
+	    $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir"
+	    CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir"
+	fi
+    done
+
+    ###########################################################
+    # make sure certain required files exist for GNU projects #
+    ###########################################################
+    _marker_found=""
+    _marker_found_message_intro='Detected non-GNU marker "'
+    _marker_found_message_mid='" in '
+    for marker in foreign cygnus ; do
+	_marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid}
+	_marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`"
+	if [ ! "x$_marker_found" = "x" ] ; then
+	    $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`"
+	    break
+	fi
+	if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+	    _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`"
+	    if [ ! "x$_marker_found" = "x" ] ; then
+		$VERBOSE_ECHO "${_marker_found_message}Makefile.am"
+		break
+	    fi
+	fi
+    done
+    if [ "x${_marker_found}" = "x" ] ; then
+	_suggest_foreign=no
+	for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do
+	    if [ ! -f $file ] ; then
+		$VERBOSE_ECHO "Touching ${file} since it does not exist"
+		_suggest_foreign=yes
+		touch $file
+	    fi
+	done
+
+	if [ "x${_suggest_foreign}" = "xyes" ] ; then
+	    $ECHO
+	    $ECHO "Warning: Several files expected of projects that conform to the GNU"
+	    $ECHO "coding standards were not found.  The files were automatically added"
+	    $ECHO "for you since you do not have a 'foreign' declaration specified."
+	    $ECHO
+	    $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`"
+	    if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+		$ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file."
+	    fi
+	    $ECHO
+	fi
+    fi
+
+    ##################################################
+    # make sure certain generated files do not exist #
+    ##################################################
+    for file in config.guess config.sub ltmain.sh ; do
+	if test -f "${_aux_dir}/${file}" ; then
+	    $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\""
+	    mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup"
+	fi
+    done
+
+    ############################
+    # search alternate m4 dirs #
+    ############################
+    SEARCH_DIRS=""
+    for dir in m4 ; do
+	if [ -d $dir ] ; then
+	    $VERBOSE_ECHO "Found extra aclocal search directory: $dir"
+	    SEARCH_DIRS="$SEARCH_DIRS -I $dir"
+	fi
+    done
+
+    ######################################
+    # remove any previous build products #
+    ######################################
+    if test -d autom4te.cache ; then
+	$VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+	$VERBOSE_ECHO "rm -rf autom4te.cache"
+	rm -rf autom4te.cache
+    fi
+# tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it
+#     if test -f aclocal.m4 ; then
+# 	$VERBOSE_ECHO "Found an aclocal.m4 file, deleting it"
+# 	$VERBOSE_ECHO "rm -f aclocal.m4"
+# 	rm -f aclocal.m4
+#     fi
+
+} # end of initialize()
+
+
+##############
+# initialize #
+##############
+
+# stash path
+START_PATH="`pwd`"
+
+# Before running autoreconf or manual steps, some prep detection work
+# is necessary or useful.  Only needs to occur once per directory, but
+# does need to traverse the entire subconfigure hierarchy to protect
+# files from being clobbered even by autoreconf.
+recursive_protect
+
+# start from where we started
+cd "$START_PATH"
+
+# get ready to process
+initialize
+
+
+#########################################
+# DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION #
+#########################################
+
+# TODO - should make sure wget/curl exist and/or work before trying to
+# use them.
+
+download_gnulib_config_guess () {
+    # abuse gitweb to download gnulib's latest config.guess via HTTP
+    config_guess_temp="config.guess.$$.download"
+    ret=1
+    for __cmd in wget curl fetch ; do
+	$VERBOSE_ECHO "Checking for command ${__cmd}"
+	${__cmd} --version > /dev/null 2>&1
+	ret=$?
+	if [ ! $ret = 0 ] ; then
+	    continue
+        fi
+
+	__cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'`
+	$VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}"
+
+	opts=""
+	case ${__cmd} in
+	    wget)
+		opts="-O" 
+		;;
+	    curl)
+		opts="-o"
+		;;
+	    fetch)
+		opts="-t 5 -f"
+		;;
+	esac
+
+	$VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\""
+	eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1
+	if [ $? = 0 ] ; then
+	    mv -f "${config_guess_temp}" ${_aux_dir}/config.guess
+	    ret=0
+	    break
+	fi
+    done
+
+    if [ ! $ret = 0 ] ; then
+	$ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL"
+	rm -f "${config_guess_temp}"
+    fi
+}
+
+
+##############################
+# LIBTOOLIZE_NEEDED FUNCTION #
+##############################
+libtoolize_needed () {
+    ret=1 # means no, don't need libtoolize
+    for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do
+	$VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+	found="`grep \"^$feature.*\" $CONFIGURE`"
+	if [ ! "x$found" = "x" ] ; then
+	    ret=0 # means yes, need to run libtoolize
+	    break
+	fi
+    done
+    return ${ret}
+}
+
+
+
+############################################
+# prepare build via autoreconf or manually #
+############################################
+reconfigure_manually=no
+if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then
+    $ECHO
+    $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C"
+
+    $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS"
+    autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`"
+    ret=$?
+    $VERBOSE_ECHO "$autoreconf_output"
+
+    if [ ! $ret = 0 ] ; then
+	if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+	    if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then
+		$ECHO
+		$ECHO "Warning: autoreconf failed but due to what is usually a common libtool"
+		$ECHO "misconfiguration issue.  This problem is encountered on systems that"
+		$ECHO "have installed libtoolize under a different name without providing a"
+		$ECHO "symbolic link or without setting the LIBTOOLIZE environment variable."
+		$ECHO
+		$ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE"
+
+		export LIBTOOLIZE
+		RUN_RECURSIVE=no
+		export RUN_RECURSIVE
+		untrap_abnormal
+
+		$VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+		sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+		exit $?
+	    fi
+	fi
+
+	$ECHO "Warning: $AUTORECONF failed"
+
+	if test -f ltmain.sh ; then
+	    $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should"
+	fi
+
+	$ECHO "Attempting to run the preparation steps individually"
+	reconfigure_manually=yes
+    else
+	if [ "x$DOWNLOAD" = "xyes" ] ; then
+	    if libtoolize_needed ; then
+		download_gnulib_config_guess
+	    fi
+	fi
+    fi
+else
+    reconfigure_manually=yes
+fi
+
+
+############################
+# LIBTOOL_FAILURE FUNCTION #
+############################
+libtool_failure ( ) {
+
+    # libtool is rather error-prone in comparison to the other
+    # autotools and this routine attempts to compensate for some
+    # common failures.  the output after a libtoolize failure is
+    # parsed for an error related to AC_PROG_LIBTOOL and if found, we
+    # attempt to inject a project-provided libtool.m4 file.
+
+    _autoconf_output="$1"
+
+    if [ "x$RUN_RECURSIVE" = "xno" ] ; then
+	# we already tried the libtool.m4, don't try again
+	return 1
+    fi
+
+    if test -f "$LIBTOOL_M4" ; then
+	found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`"
+	if test ! "x$found_libtool" = "x" ; then
+	    if test -f acinclude.m4 ; then
+		rm -f acinclude.m4.$$.backup
+		$VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup"
+		cat acinclude.m4 > acinclude.m4.$$.backup
+	    fi
+	    $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4"
+	    chmod u+w acinclude.m4
+	    cat "$LIBTOOL_M4" >> acinclude.m4
+
+	    # don't keep doing this
+	    RUN_RECURSIVE=no
+	    export RUN_RECURSIVE
+	    untrap_abnormal
+
+	    $ECHO
+	    $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4"
+	    $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+	    sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+	    exit $?
+	fi
+    fi
+}
+
+
+###########################
+# MANUAL_AUTOGEN FUNCTION #
+###########################
+manual_autogen ( ) {
+
+    ##################################################
+    # Manual preparation steps taken are as follows: #
+    #   aclocal [-I m4]                              #
+    #   libtoolize --automake -c -f                  #
+    #   aclocal [-I m4]                              #
+    #   autoconf -f                                  #
+    #   autoheader                                   #
+    #   automake -a -c -f                            #
+    ##################################################
+
+    ###########
+    # aclocal #
+    ###########
+    $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+    aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+    ret=$?
+    $VERBOSE_ECHO "$aclocal_output"
+    if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi
+
+    ##############
+    # libtoolize #
+    ##############
+    if libtoolize_needed ; then
+	if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then
+	    $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS"
+	    libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`"
+	    ret=$?
+	    $VERBOSE_ECHO "$libtoolize_output"
+
+	    if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+	else
+	    if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+		$VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS"
+		libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`"
+		ret=$?
+		$VERBOSE_ECHO "$libtoolize_output"
+
+		if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+	    fi
+	fi
+
+	###########
+	# aclocal #
+	###########
+	# re-run again as instructed by libtoolize
+	$VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+	aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+	ret=$?
+	$VERBOSE_ECHO "$aclocal_output"
+
+	# libtoolize might put ltmain.sh in the wrong place
+	if test -f ltmain.sh ; then
+	    if test ! -f "${_aux_dir}/ltmain.sh" ; then
+		$ECHO
+		$ECHO "Warning:  $LIBTOOLIZE is creating ltmain.sh in the wrong directory"
+		$ECHO
+		$ECHO "Fortunately, the problem can be worked around by simply copying the"
+		$ECHO "file to the appropriate location (${_aux_dir}/).  This has been done for you."
+		$ECHO
+		$VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\""
+		cp -p ltmain.sh "${_aux_dir}/ltmain.sh"
+		$ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+	    fi
+	fi # ltmain.sh
+
+	if [ "x$DOWNLOAD" = "xyes" ] ; then
+	    download_gnulib_config_guess
+	fi
+    fi # libtoolize_needed
+
+    ############
+    # autoconf #
+    ############
+    $VERBOSE_ECHO
+    $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS"
+    autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`"
+    ret=$?
+    $VERBOSE_ECHO "$autoconf_output"
+
+    if [ ! $ret = 0 ] ; then
+	# retry without the -f and check for usage of macros that are too new
+	ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE"
+	ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE"
+	ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T"
+
+	macros_to_search=""
+	ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`"
+	ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`"
+
+	if [ $ac_major -lt 2 ] ; then
+	    macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+	else
+	    if [ $ac_minor -lt 54 ] ; then
+		macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+	    elif [ $ac_minor -lt 55 ] ; then
+		macros_to_search="$ac2_59_macros $ac2_55_macros"
+	    elif [ $ac_minor -lt 59 ] ; then
+		macros_to_search="$ac2_59_macros"
+	    fi
+	fi
+
+	configure_ac_macros=__none__
+	for feature in $macros_to_search ; do
+	    $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+	    found="`grep \"^$feature.*\" $CONFIGURE`"
+	    if [ ! "x$found" = "x" ] ; then
+		if [ "x$configure_ac_macros" = "x__none__" ] ; then
+		    configure_ac_macros="$feature"
+		else
+		    configure_ac_macros="$feature $configure_ac_macros"
+		fi
+	    fi
+	done
+	if [ ! "x$configure_ac_macros" = "x__none__" ] ; then
+	    $ECHO
+	    $ECHO "Warning:  Unsupported macros were found in $CONFIGURE"
+	    $ECHO
+	    $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any"
+	    $ECHO "unsupported macros are used that exceed the minimum version"
+	    $ECHO "settings specified within this file.  As such, the following macros"
+	    $ECHO "should be removed from configure.ac or the version numbers in this"
+	    $ECHO "file should be increased:"
+	    $ECHO
+	    $ECHO "$configure_ac_macros"
+	    $ECHO
+	    $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C"
+	fi
+
+	###################
+	# autoconf, retry #
+	###################
+	$VERBOSE_ECHO
+	$VERBOSE_ECHO "$AUTOCONF"
+	autoconf_output="`$AUTOCONF 2>&1`"
+	ret=$?
+	$VERBOSE_ECHO "$autoconf_output"
+
+	if [ ! $ret = 0 ] ; then
+	    # test if libtool is busted
+	    libtool_failure "$autoconf_output"
+
+	    # let the user know what went wrong
+	    cat <<EOF
+$autoconf_output
+EOF
+	    $ECHO "ERROR: $AUTOCONF failed"
+	    exit 2
+	else
+	    # autoconf sans -f and possibly sans unsupported options succeed so warn verbosely
+	    $ECHO
+	    $ECHO "Warning: autoconf seems to have succeeded by removing the following options:"
+	    $ECHO "	AUTOCONF_OPTIONS=\"$AUTOCONF_OPTIONS\""
+	    $ECHO
+	    $ECHO "Removing those options should not be necessary and indicate some other"
+	    $ECHO "problem with the build system.  The build preparation is highly suspect"
+	    $ECHO "and may result in configuration or compilation errors.  Consider"
+	    if [ "x$VERBOSE_ECHO" = "x:" ] ; then
+		$ECHO "rerunning the build preparation with verbose output enabled."
+		$ECHO "	$AUTOGEN_SH --verbose"
+	    else
+		$ECHO "reviewing the minimum GNU Autotools version settings contained in"
+		$ECHO "this script along with the macros being used in your `basename \"$CONFIGURE\"` file."
+	    fi
+	    $ECHO
+	    $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+	fi # autoconf ret = 0
+    fi # autoconf ret = 0
+
+    ##############
+    # autoheader #
+    ##############
+    need_autoheader=no
+    for feature in AM_CONFIG_HEADER AC_CONFIG_HEADER ; do
+	$VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+	found="`grep \"^$feature.*\" $CONFIGURE`"
+	if [ ! "x$found" = "x" ] ; then
+	    need_autoheader=yes
+	    break
+	fi
+    done
+    if [ "x$need_autoheader" = "xyes" ] ; then
+	$VERBOSE_ECHO "$AUTOHEADER $AUTOHEADER_OPTIONS"
+	autoheader_output="`$AUTOHEADER $AUTOHEADER_OPTIONS 2>&1`"
+	ret=$?
+	$VERBOSE_ECHO "$autoheader_output"
+	if [ ! $ret = 0 ] ; then $ECHO "ERROR: $AUTOHEADER failed" && exit 2 ; fi
+    fi # need_autoheader
+
+    ############
+    # automake #
+    ############
+    need_automake=no
+    for feature in AM_INIT_AUTOMAKE ; do
+	$VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+	found="`grep \"^$feature.*\" $CONFIGURE`"
+	if [ ! "x$found" = "x" ] ; then
+	    need_automake=yes
+	    break
+	fi
+    done
+
+    if [ "x$need_automake" = "xyes" ] ; then
+	$VERBOSE_ECHO "$AUTOMAKE $AUTOMAKE_OPTIONS"
+	automake_output="`$AUTOMAKE $AUTOMAKE_OPTIONS 2>&1`"
+	ret=$?
+	$VERBOSE_ECHO "$automake_output"
+
+	if [ ! $ret = 0 ] ; then
+
+	    ###################
+	    # automake, retry #
+	    ###################
+	    $VERBOSE_ECHO
+	    $VERBOSE_ECHO "$AUTOMAKE $ALT_AUTOMAKE_OPTIONS"
+	    # retry without the -f
+	    automake_output="`$AUTOMAKE $ALT_AUTOMAKE_OPTIONS 2>&1`"
+	    ret=$?
+	    $VERBOSE_ECHO "$automake_output"
+
+	    if [ ! $ret = 0 ] ; then
+	 	# test if libtool is busted
+		libtool_failure "$automake_output"
+
+		# let the user know what went wrong
+		cat <<EOF
+$automake_output
+EOF
+		$ECHO "ERROR: $AUTOMAKE failed"
+		exit 2
+	    fi # automake retry
+	fi # automake ret = 0
+    fi # need_automake
+} # end of manual_autogen
+
+
+#####################################
+# RECURSIVE_MANUAL_AUTOGEN FUNCTION #
+#####################################
+recursive_manual_autogen ( ) {
+
+    # run the build preparation steps manually for this directory
+    manual_autogen
+
+    # for projects using recursive configure, run the build
+    # preparation steps for the subdirectories.
+    if [ ! "x$CONFIG_SUBDIRS" = "x" ] ; then
+	$VERBOSE_ECHO "Recursively configuring the following directories:"
+	$VERBOSE_ECHO "  $CONFIG_SUBDIRS"
+	for dir in $CONFIG_SUBDIRS ; do
+	    $VERBOSE_ECHO "Processing recursive configure in $dir"
+	    cd "$START_PATH"
+	    cd "$dir"
+
+	    # new directory, prepare
+	    initialize
+
+	    # run manual steps for the subdir and any others below
+	    recursive_manual_autogen
+	done
+    fi
+}
+
+
+################################
+# run manual preparation steps #
+################################
+if [ "x$reconfigure_manually" = "xyes" ] ; then
+    $ECHO
+    $ECHO $ECHO_N "Preparing build ... $ECHO_C"
+
+    recursive_manual_autogen
+fi
+
+
+#########################
+# restore and summarize #
+#########################
+cd "$START_PATH"
+
+# restore COPYING and INSTALL from backup if necessary
+recursive_restore
+
+# make sure we end up with a configure script
+config_ac="`locate_configure_template`"
+config="`echo $config_ac | sed 's/\.ac$//' | sed 's/\.in$//'`"
+if [ "x$config" = "x" ] ; then
+    $VERBOSE_ECHO "Could not locate the configure template (from `pwd`)"
+fi
+
+# summarize
+$ECHO "done"
+$ECHO
+if test "x$config" = "x" -o ! -f "$config" ; then
+    $ECHO "WARNING: The $PROJECT build system should now be prepared but there"
+    $ECHO "does not seem to be a resulting configure file.  This is unexpected"
+    $ECHO "and likely the result of an error.  You should run $NAME_OF_AUTOGEN"
+    $ECHO "with the --verbose option to get more details on a potential"
+    $ECHO "misconfiguration."
+else
+    $ECHO "The $PROJECT build system is now prepared.  To build here, run:"
+    $ECHO "  $config"
+    $ECHO "  make"
+fi
+
+
+# Local Variables:
+# mode: sh
+# tab-width: 8
+# sh-basic-offset: 4
+# sh-indentation: 4
+# indent-tabs-mode: t
+# End:
+# ex: shiftwidth=4 tabstop=8
diff --git a/sg3_utils/build_debian.sh b/sg3_utils/build_debian.sh
new file mode 100755
index 0000000..72dcfff
--- /dev/null
+++ b/sg3_utils/build_debian.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# If this script fails on a Debian 4.0 ("etch") system then read
+# the debian/README.debian4 file.
+
+echo "chmod +x debian/rules"
+chmod +x debian/rules
+
+# in some environments the '-rfakeroot' can cause a failure (e.g. when
+# building as root). If so, remove that argument from the following:
+echo "dpkg-buildpackage -b -rfakeroot -us -uc"
+dpkg-buildpackage -b -rfakeroot -us -uc
+
+# If the above succeeds then the ".deb" binary package is placed in the
+# parent directory.
diff --git a/sg3_utils/compile b/sg3_utils/compile
new file mode 100755
index 0000000..a85b723
--- /dev/null
+++ b/sg3_utils/compile
@@ -0,0 +1,347 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand '-c -o'.
+
+scriptversion=2012-10-14.11; # UTC
+
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.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.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+nl='
+'
+
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent tools from complaining about whitespace usage.
+IFS=" ""	$nl"
+
+file_conv=
+
+# func_file_conv build_file lazy
+# Convert a $build file to $host form and store it in $file
+# Currently only supports Windows hosts. If the determined conversion
+# type is listed in (the comma separated) LAZY, no conversion will
+# take place.
+func_file_conv ()
+{
+  file=$1
+  case $file in
+    / | /[!/]*) # absolute file, and not a UNC file
+      if test -z "$file_conv"; then
+	# lazily determine how to convert abs files
+	case `uname -s` in
+	  MINGW*)
+	    file_conv=mingw
+	    ;;
+	  CYGWIN*)
+	    file_conv=cygwin
+	    ;;
+	  *)
+	    file_conv=wine
+	    ;;
+	esac
+      fi
+      case $file_conv/,$2, in
+	*,$file_conv,*)
+	  ;;
+	mingw/*)
+	  file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
+	  ;;
+	cygwin/*)
+	  file=`cygpath -m "$file" || echo "$file"`
+	  ;;
+	wine/*)
+	  file=`winepath -w "$file" || echo "$file"`
+	  ;;
+      esac
+      ;;
+  esac
+}
+
+# func_cl_dashL linkdir
+# Make cl look for libraries in LINKDIR
+func_cl_dashL ()
+{
+  func_file_conv "$1"
+  if test -z "$lib_path"; then
+    lib_path=$file
+  else
+    lib_path="$lib_path;$file"
+  fi
+  linker_opts="$linker_opts -LIBPATH:$file"
+}
+
+# func_cl_dashl library
+# Do a library search-path lookup for cl
+func_cl_dashl ()
+{
+  lib=$1
+  found=no
+  save_IFS=$IFS
+  IFS=';'
+  for dir in $lib_path $LIB
+  do
+    IFS=$save_IFS
+    if $shared && test -f "$dir/$lib.dll.lib"; then
+      found=yes
+      lib=$dir/$lib.dll.lib
+      break
+    fi
+    if test -f "$dir/$lib.lib"; then
+      found=yes
+      lib=$dir/$lib.lib
+      break
+    fi
+    if test -f "$dir/lib$lib.a"; then
+      found=yes
+      lib=$dir/lib$lib.a
+      break
+    fi
+  done
+  IFS=$save_IFS
+
+  if test "$found" != yes; then
+    lib=$lib.lib
+  fi
+}
+
+# func_cl_wrapper cl arg...
+# Adjust compile command to suit cl
+func_cl_wrapper ()
+{
+  # Assume a capable shell
+  lib_path=
+  shared=:
+  linker_opts=
+  for arg
+  do
+    if test -n "$eat"; then
+      eat=
+    else
+      case $1 in
+	-o)
+	  # configure might choose to run compile as 'compile cc -o foo foo.c'.
+	  eat=1
+	  case $2 in
+	    *.o | *.[oO][bB][jJ])
+	      func_file_conv "$2"
+	      set x "$@" -Fo"$file"
+	      shift
+	      ;;
+	    *)
+	      func_file_conv "$2"
+	      set x "$@" -Fe"$file"
+	      shift
+	      ;;
+	  esac
+	  ;;
+	-I)
+	  eat=1
+	  func_file_conv "$2" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-I*)
+	  func_file_conv "${1#-I}" mingw
+	  set x "$@" -I"$file"
+	  shift
+	  ;;
+	-l)
+	  eat=1
+	  func_cl_dashl "$2"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-l*)
+	  func_cl_dashl "${1#-l}"
+	  set x "$@" "$lib"
+	  shift
+	  ;;
+	-L)
+	  eat=1
+	  func_cl_dashL "$2"
+	  ;;
+	-L*)
+	  func_cl_dashL "${1#-L}"
+	  ;;
+	-static)
+	  shared=false
+	  ;;
+	-Wl,*)
+	  arg=${1#-Wl,}
+	  save_ifs="$IFS"; IFS=','
+	  for flag in $arg; do
+	    IFS="$save_ifs"
+	    linker_opts="$linker_opts $flag"
+	  done
+	  IFS="$save_ifs"
+	  ;;
+	-Xlinker)
+	  eat=1
+	  linker_opts="$linker_opts $2"
+	  ;;
+	-*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+	*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
+	  func_file_conv "$1"
+	  set x "$@" -Tp"$file"
+	  shift
+	  ;;
+	*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
+	  func_file_conv "$1" mingw
+	  set x "$@" "$file"
+	  shift
+	  ;;
+	*)
+	  set x "$@" "$1"
+	  shift
+	  ;;
+      esac
+    fi
+    shift
+  done
+  if test -n "$linker_opts"; then
+    linker_opts="-link$linker_opts"
+  fi
+  exec "$@" $linker_opts
+  exit 1
+}
+
+eat=
+
+case $1 in
+  '')
+     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand '-c -o'.
+Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file 'INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit $?
+    ;;
+  cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
+    func_cl_wrapper "$@"      # Doesn't return...
+    ;;
+esac
+
+ofile=
+cfile=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+	# configure might choose to run compile as 'compile cc -o foo foo.c'.
+	# So we strip '-o arg' only if arg is an object.
+	eat=1
+	case $2 in
+	  *.o | *.obj)
+	    ofile=$2
+	    ;;
+	  *)
+	    set x "$@" -o "$2"
+	    shift
+	    ;;
+	esac
+	;;
+      *.c)
+	cfile=$1
+	set x "$@" "$1"
+	shift
+	;;
+      *)
+	set x "$@" "$1"
+	shift
+	;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no '-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # '.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use '[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/sg3_utils/config.guess b/sg3_utils/config.guess
new file mode 100755
index 0000000..1f5c50c
--- /dev/null
+++ b/sg3_utils/config.guess
@@ -0,0 +1,1420 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright 1992-2014 Free Software Foundation, Inc.
+
+timestamp='2014-03-23'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+#
+# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2014 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+	for c in cc gcc c89 c99 ; do
+	  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "${UNAME_SYSTEM}" in
+Linux|GNU|GNU/*)
+	# If the system lacks a compiler, then just pick glibc.
+	# We could probably try harder.
+	LIBC=gnu
+
+	eval $set_cc_for_build
+	cat <<-EOF > $dummy.c
+	#include <features.h>
+	#if defined(__UCLIBC__)
+	LIBC=uclibc
+	#elif defined(__dietlibc__)
+	LIBC=dietlibc
+	#else
+	LIBC=gnu
+	#endif
+	EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+	;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	sysctl="sysctl -n hw.machine_arch"
+	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+	case "${UNAME_MACHINE_ARCH}" in
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    sh5el) machine=sh5le-unknown ;;
+	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE_ARCH}" in
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep -q __ELF__
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+		os=netbsd
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case "${UNAME_VERSION}" in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit ;;
+    *:Bitrig:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+	echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+	exit ;;
+    *:OpenBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+	echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+	exit ;;
+    *:ekkoBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+	exit ;;
+    *:SolidBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+	exit ;;
+    macppc:MirBSD:*:*)
+	echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+	exit ;;
+    *:MirBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+	exit ;;
+    alpha:OSF1:*:*)
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case "$ALPHA_CPU_TYPE" in
+	    "EV4 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE="alphaev5" ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE="alphaev56" ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE="alphapca56" ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE="alphapca57" ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE="alphaev6" ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE="alphaev67" ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE="alphaev69" ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE="alphaev7" ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE="alphaev79" ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+	exitcode=$?
+	trap '' 0
+	exit $exitcode ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-morphos
+	exit ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit ;;
+    *:z/VM:*:*)
+	echo s390-ibm-zvmoe
+	exit ;;
+    *:OS400:*:*)
+	echo powerpc-ibm-os400
+	exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit ;;
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
+	echo arm-unknown-riscos
+	exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit ;;
+    DRS?6000:unix:4.0:6*)
+	echo sparc-icl-nx6
+	exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) echo sparc-icl-nx7; exit ;;
+	esac ;;
+    s390x:SunOS:*:*)
+	echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+	echo i386-pc-auroraux${UNAME_RELEASE}
+	exit ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+	eval $set_cc_for_build
+	SUN_ARCH="i386"
+	# If there is a compiler, see if it is configured for 64-bit objects.
+	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+	# This test works for both compilers.
+	if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+		(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		grep IS_64BIT_ARCH >/dev/null
+	    then
+		SUN_ARCH="x86_64"
+	    fi
+	fi
+	echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+	exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+	exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+	exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+	echo m68k-milan-mint${UNAME_RELEASE}
+	exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+	echo m68k-hades-mint${UNAME_RELEASE}
+	exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+	echo m68k-unknown-mint${UNAME_RELEASE}
+	exit ;;
+    m68k:machten:*:*)
+	echo m68k-apple-machten${UNAME_RELEASE}
+	exit ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c &&
+	  dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+	  SYSTEM_NAME=`$dummy $dummyarg` &&
+	    { echo "$SYSTEM_NAME"; exit; }
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit ;;
+    Motorola:*:4.3:PL8-*)
+	echo powerpc-harris-powermax
+	exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	echo powerpc-harris-powermax
+	exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit ;;
+    AViiON:dgux:*:*)
+	# DG/UX returns AViiON for all architectures
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+	exit ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+	exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		eval $set_cc_for_build
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+		then
+			echo "$SYSTEM_NAME"
+		else
+			echo rs6000-ibm-aix3.2.5
+		fi
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit ;;
+    *:AIX:*:[4567])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if [ -x /usr/bin/getconf ]; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+		    case "${sc_cpu_version}" in
+		      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+		      532)                      # CPU_PA_RISC2_0
+			case "${sc_kernel_bits}" in
+			  32) HP_ARCH="hppa2.0n" ;;
+			  64) HP_ARCH="hppa2.0w" ;;
+			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+			esac ;;
+		    esac
+		fi
+		if [ "${HP_ARCH}" = "" ]; then
+		    eval $set_cc_for_build
+		    sed 's/^		//' << EOF >$dummy.c
+
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
+
+		int main ()
+		{
+		#if defined(_SC_KERNEL_BITS)
+		    long bits = sysconf(_SC_KERNEL_BITS);
+		#endif
+		    long cpu  = sysconf (_SC_CPU_VERSION);
+
+		    switch (cpu)
+			{
+			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+			case CPU_PA_RISC2_0:
+		#if defined(_SC_KERNEL_BITS)
+			    switch (bits)
+				{
+				case 64: puts ("hppa2.0w"); break;
+				case 32: puts ("hppa2.0n"); break;
+				default: puts ("hppa2.0"); break;
+				} break;
+		#else  /* !defined(_SC_KERNEL_BITS) */
+			    puts ("hppa2.0"); break;
+		#endif
+			default: puts ("hppa1.0"); break;
+			}
+		    exit (0);
+		}
+EOF
+		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if [ ${HP_ARCH} = "hppa2.0w" ]
+	then
+	    eval $set_cc_for_build
+
+	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+	    # generating 64-bit code.  GNU and HP use different nomenclature:
+	    #
+	    # $ CC_FOR_BUILD=cc ./config.guess
+	    # => hppa2.0w-hp-hpux11.23
+	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+	    # => hppa64-hp-hpux11.23
+
+	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+		grep -q __LP64__
+	    then
+		HP_ARCH="hppa2.0w"
+	    else
+		HP_ARCH="hppa64"
+	    fi
+	fi
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit ;;
+    3050*:HI-UX:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+		{ echo "$SYSTEM_NAME"; exit; }
+	echo unknown-hitachi-hiuxwe2
+	exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+	exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+	exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+	exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+	exit ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    *:UNICOS/mp:*:*)
+	echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+	FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+	echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit ;;
+    5000:UNIX_System_V:4.*:*)
+	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+	FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+	echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit ;;
+    *:FreeBSD:*:*)
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	case ${UNAME_PROCESSOR} in
+	    amd64)
+		echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+	    *)
+		echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+	esac
+	exit ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit ;;
+    *:MINGW64*:*)
+	echo ${UNAME_MACHINE}-pc-mingw64
+	exit ;;
+    *:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit ;;
+    *:MSYS*:*)
+	echo ${UNAME_MACHINE}-pc-msys
+	exit ;;
+    i*:windows32*:*)
+	# uname -m includes "-pc" on this system.
+	echo ${UNAME_MACHINE}-mingw32
+	exit ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit ;;
+    *:Interix*:*)
+	case ${UNAME_MACHINE} in
+	    x86)
+		echo i586-pc-interix${UNAME_RELEASE}
+		exit ;;
+	    authenticamd | genuineintel | EM64T)
+		echo x86_64-unknown-interix${UNAME_RELEASE}
+		exit ;;
+	    IA64)
+		echo ia64-unknown-interix${UNAME_RELEASE}
+		exit ;;
+	esac ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+	echo i${UNAME_MACHINE}-pc-mks
+	exit ;;
+    8664:Windows_NT:*)
+	echo x86_64-pc-mks
+	exit ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i586-pc-interix
+	exit ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+	echo x86_64-unknown-cygwin
+	exit ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
+    *:GNU:*:*)
+	# the GNU system
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit ;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
+	exit ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit ;;
+    aarch64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    aarch64_be:Linux:*:*)
+	UNAME_MACHINE=aarch64_be
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+	esac
+	objdump --private-headers /bin/sh | grep -q ld.so.1
+	if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    arc:Linux:*:* | arceb:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    arm*:Linux:*:*)
+	eval $set_cc_for_build
+	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_EABI__
+	then
+	    echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	else
+	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+		| grep -q __ARM_PCS_VFP
+	    then
+		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
+	    else
+		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
+	    fi
+	fi
+	exit ;;
+    avr32*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    cris:Linux:*:*)
+	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+	exit ;;
+    crisv32:Linux:*:*)
+	echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+	exit ;;
+    frv:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    hexagon:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    i*86:Linux:*:*)
+	echo ${UNAME_MACHINE}-pc-linux-${LIBC}
+	exit ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    m32r*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#undef CPU
+	#undef ${UNAME_MACHINE}
+	#undef ${UNAME_MACHINE}el
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=${UNAME_MACHINE}el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=${UNAME_MACHINE}
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
+	;;
+    openrisc*:Linux:*:*)
+	echo or1k-unknown-linux-${LIBC}
+	exit ;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    padre:Linux:*:*)
+	echo sparc-unknown-linux-${LIBC}
+	exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-${LIBC}
+	exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
+	  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
+	  *)    echo hppa-unknown-linux-${LIBC} ;;
+	esac
+	exit ;;
+    ppc64:Linux:*:*)
+	echo powerpc64-unknown-linux-${LIBC}
+	exit ;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-${LIBC}
+	exit ;;
+    ppc64le:Linux:*:*)
+	echo powerpc64le-unknown-linux-${LIBC}
+	exit ;;
+    ppcle:Linux:*:*)
+	echo powerpcle-unknown-linux-${LIBC}
+	exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
+	exit ;;
+    sh64*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    tile*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    vax:Linux:*:*)
+	echo ${UNAME_MACHINE}-dec-linux-${LIBC}
+	exit ;;
+    x86_64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    xtensa*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+	exit ;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+	# Unixware is an offshoot of SVR4, but it has its own version
+	# number series starting with 2...
+	# I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+	# Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit ;;
+    i*86:XTS-300:*:STOP)
+	echo ${UNAME_MACHINE}-unknown-stop
+	exit ;;
+    i*86:atheos:*:*)
+	echo ${UNAME_MACHINE}-unknown-atheos
+	exit ;;
+    i*86:syllable:*:*)
+	echo ${UNAME_MACHINE}-pc-syllable
+	exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit ;;
+    i*86:*:5:[678]*)
+	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+	# uname -m prints for DJGPP always 'pc', but it prints nothing about
+	# the processor, so we play safe by assuming i586.
+	# Note: whatever this is, it MUST be the same as what config.sub
+	# prints for the "djgpp" host, or else GDB configury will decide that
+	# this is a cross-build.
+	echo i586-pc-msdosdjgpp
+	exit ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	echo m68k-convergent-sysv
+	exit ;;
+    M680?0:D-NIX:5.3:*)
+	echo m68k-diab-dnix
+	exit ;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+	OS_REL='.3'
+	test -r /etc/.relid \
+	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	    && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit ;;
+    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	echo i586-unisys-sysv4
+	exit ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit ;;
+    i*86:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo ${UNAME_MACHINE}-stratus-vos
+	exit ;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo hppa1.1-stratus-vos
+	exit ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+		echo mips-nec-sysv${UNAME_RELEASE}
+	else
+		echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+	exit ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit ;;
+    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+	echo i586-pc-haiku
+	exit ;;
+    x86_64:Haiku:*:*)
+	echo x86_64-unknown-haiku
+	exit ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-6:SUPER-UX:*:*)
+	echo sx6-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-7:SUPER-UX:*:*)
+	echo sx7-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-8:SUPER-UX:*:*)
+	echo sx8-nec-superux${UNAME_RELEASE}
+	exit ;;
+    SX-8R:SUPER-UX:*:*)
+	echo sx8r-nec-superux${UNAME_RELEASE}
+	exit ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit ;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+	eval $set_cc_for_build
+	if test "$UNAME_PROCESSOR" = unknown ; then
+	    UNAME_PROCESSOR=powerpc
+	fi
+	if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
+	    if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+		if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		    (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		    grep IS_64BIT_ARCH >/dev/null
+		then
+		    case $UNAME_PROCESSOR in
+			i386) UNAME_PROCESSOR=x86_64 ;;
+			powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		    esac
+		fi
+	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # Avoid executing cc on OS X 10.9, as it ships with a stub
+	    # that puts up a graphical alert prompting to install
+	    # developer tools.  Any system running Mac OS X 10.7 or
+	    # later (Darwin 11 and later) is required to have a 64-bit
+	    # processor. This is not true of the ARM version of Darwin
+	    # that Apple uses in portable devices.
+	    UNAME_PROCESSOR=x86_64
+	fi
+	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+	exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = "x86"; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+	exit ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit ;;
+    NEO-?:NONSTOP_KERNEL:*:*)
+	echo neo-tandem-nsk${UNAME_RELEASE}
+	exit ;;
+    NSE-*:NONSTOP_KERNEL:*:*)
+	echo nse-tandem-nsk${UNAME_RELEASE}
+	exit ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit ;;
+    SEI:*:*:SEIUX)
+	echo mips-sei-seiux${UNAME_RELEASE}
+	exit ;;
+    *:DragonFly:*:*)
+	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit ;;
+    *:*VMS:*:*)
+	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case "${UNAME_MACHINE}" in
+	    A*) echo alpha-dec-vms ; exit ;;
+	    I*) echo ia64-dec-vms ; exit ;;
+	    V*) echo vax-dec-vms ; exit ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	echo i386-pc-xenix
+	exit ;;
+    i*86:skyos:*:*)
+	echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+	exit ;;
+    i*86:rdos:*:*)
+	echo ${UNAME_MACHINE}-pc-rdos
+	exit ;;
+    i*86:AROS:*:*)
+	echo ${UNAME_MACHINE}-pc-aros
+	exit ;;
+    x86_64:VMkernel:*:*)
+	echo ${UNAME_MACHINE}-unknown-esx
+	exit ;;
+esac
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/sg3_utils/config.h.in b/sg3_utils/config.h.in
new file mode 100644
index 0000000..7b3fa50
--- /dev/null
+++ b/sg3_utils/config.h.in
@@ -0,0 +1,116 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the `getopt_long' function. */
+#undef HAVE_GETOPT_LONG
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <linux/bsg.h> header file. */
+#undef HAVE_LINUX_BSG_H
+
+/* Define to 1 if you have the <linux/kdev_t.h> header file. */
+#undef HAVE_LINUX_KDEV_T_H
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#undef HAVE_LINUX_TYPES_H
+
+/* Define to 1 if you have the `lseek64' function. */
+#undef HAVE_LSEEK64
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `posix_fadvise' function. */
+#undef HAVE_POSIX_FADVISE
+
+/* Define to 1 if you have the `posix_memalign' function. */
+#undef HAVE_POSIX_MEMALIGN
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `sysconf' function. */
+#undef HAVE_SYSCONF
+
+/* 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/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* ignore linux bsg */
+#undef IGNORE_LINUX_BSG
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* sg3_utils Build Host */
+#undef SG_LIB_BUILD_HOST
+
+/* sg3_utils on FreeBSD */
+#undef SG_LIB_FREEBSD
+
+/* assume sg3_utils on linux */
+#undef SG_LIB_LINUX
+
+/* also MinGW environment */
+#undef SG_LIB_MINGW
+
+/* sg3_utils on Tru64 UNIX */
+#undef SG_LIB_OSF1
+
+/* sg3_utils on Solaris */
+#undef SG_LIB_SOLARIS
+
+/* sg3_utils on Win32 */
+#undef SG_LIB_WIN32
+
+/* full SCSI sense strings */
+#undef SG_SCSI_STRINGS
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* enable Win32 SPT Direct */
+#undef WIN32_SPT_DIRECT
diff --git a/sg3_utils/config.sub b/sg3_utils/config.sub
new file mode 100755
index 0000000..bba4efb
--- /dev/null
+++ b/sg3_utils/config.sub
@@ -0,0 +1,1799 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright 1992-2014 Free Software Foundation, Inc.
+
+timestamp='2014-09-11'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches with a ChangeLog entry to config-patches@gnu.org.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2014 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+  linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | \
+  kopensolaris*-gnu* | \
+  storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  android-linux)
+    os=-linux-android
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis | -knuth | -cray | -microblaze*)
+		os=
+		basic_machine=$1
+		;;
+	-bluegene*)
+		os=-cnk
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+	-chorusrdb)
+		os=-chorusrdb
+		basic_machine=$1
+		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco6)
+		os=-sco5v6
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco5v6*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*178)
+		os=-lynxos178
+		;;
+	-lynx*5)
+		os=-lynxos5
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-windowsnt*)
+		os=`echo $os | sed -e 's/windowsnt/winnt/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	1750a | 580 \
+	| a29k \
+	| aarch64 | aarch64_be \
+	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+	| am33_2.0 \
+	| arc | arceb \
+	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+	| avr | avr32 \
+	| be32 | be64 \
+	| bfin \
+	| c4x | c8051 | clipper \
+	| d10v | d30v | dlx | dsp16xx \
+	| epiphany \
+	| fido | fr30 | frv \
+	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+	| hexagon \
+	| i370 | i860 | i960 | ia64 \
+	| ip2k | iq2000 \
+	| k1om \
+	| le32 | le64 \
+	| lm32 \
+	| m32c | m32r | m32rle | m68000 | m68k | m88k \
+	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
+	| mips | mipsbe | mipseb | mipsel | mipsle \
+	| mips16 \
+	| mips64 | mips64el \
+	| mips64octeon | mips64octeonel \
+	| mips64orion | mips64orionel \
+	| mips64r5900 | mips64r5900el \
+	| mips64vr | mips64vrel \
+	| mips64vr4100 | mips64vr4100el \
+	| mips64vr4300 | mips64vr4300el \
+	| mips64vr5000 | mips64vr5000el \
+	| mips64vr5900 | mips64vr5900el \
+	| mipsisa32 | mipsisa32el \
+	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa32r6 | mipsisa32r6el \
+	| mipsisa64 | mipsisa64el \
+	| mipsisa64r2 | mipsisa64r2el \
+	| mipsisa64r6 | mipsisa64r6el \
+	| mipsisa64sb1 | mipsisa64sb1el \
+	| mipsisa64sr71k | mipsisa64sr71kel \
+	| mipsr5900 | mipsr5900el \
+	| mipstx39 | mipstx39el \
+	| mn10200 | mn10300 \
+	| moxie \
+	| mt \
+	| msp430 \
+	| nds32 | nds32le | nds32be \
+	| nios | nios2 | nios2eb | nios2el \
+	| ns16k | ns32k \
+	| open8 | or1k | or1knd | or32 \
+	| pdp10 | pdp11 | pj | pjl \
+	| powerpc | powerpc64 | powerpc64le | powerpcle \
+	| pyramid \
+	| riscv32 | riscv64 \
+	| rl78 | rx \
+	| score \
+	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+	| sh64 | sh64le \
+	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+	| spu \
+	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+	| ubicom32 \
+	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+	| we32k \
+	| x86 | xc16x | xstormy16 | xtensa \
+	| z8k | z80)
+		basic_machine=$basic_machine-unknown
+		;;
+	c54x)
+		basic_machine=tic54x-unknown
+		;;
+	c55x)
+		basic_machine=tic55x-unknown
+		;;
+	c6x)
+		basic_machine=tic6x-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+		;;
+	ms1)
+		basic_machine=mt-unknown
+		;;
+
+	strongarm | thumb | xscale)
+		basic_machine=arm-unknown
+		;;
+	xgate)
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	xscaleeb)
+		basic_machine=armeb-unknown
+		;;
+
+	xscaleel)
+		basic_machine=armel-unknown
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	580-* \
+	| a29k-* \
+	| aarch64-* | aarch64_be-* \
+	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
+	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+	| avr-* | avr32-* \
+	| be32-* | be64-* \
+	| bfin-* | bs2000-* \
+	| c[123]* | c30-* | [cjt]90-* | c4x-* \
+	| c8051-* | clipper-* | craynv-* | cydra-* \
+	| d10v-* | d30v-* | dlx-* \
+	| elxsi-* \
+	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+	| h8300-* | h8500-* \
+	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+	| hexagon-* \
+	| i*86-* | i860-* | i960-* | ia64-* \
+	| ip2k-* | iq2000-* \
+	| k1om-* \
+	| le32-* | le64-* \
+	| lm32-* \
+	| m32c-* | m32r-* | m32rle-* \
+	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+	| microblaze-* | microblazeel-* \
+	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+	| mips16-* \
+	| mips64-* | mips64el-* \
+	| mips64octeon-* | mips64octeonel-* \
+	| mips64orion-* | mips64orionel-* \
+	| mips64r5900-* | mips64r5900el-* \
+	| mips64vr-* | mips64vrel-* \
+	| mips64vr4100-* | mips64vr4100el-* \
+	| mips64vr4300-* | mips64vr4300el-* \
+	| mips64vr5000-* | mips64vr5000el-* \
+	| mips64vr5900-* | mips64vr5900el-* \
+	| mipsisa32-* | mipsisa32el-* \
+	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa32r6-* | mipsisa32r6el-* \
+	| mipsisa64-* | mipsisa64el-* \
+	| mipsisa64r2-* | mipsisa64r2el-* \
+	| mipsisa64r6-* | mipsisa64r6el-* \
+	| mipsisa64sb1-* | mipsisa64sb1el-* \
+	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+	| mipsr5900-* | mipsr5900el-* \
+	| mipstx39-* | mipstx39el-* \
+	| mmix-* \
+	| mt-* \
+	| msp430-* \
+	| nds32-* | nds32le-* | nds32be-* \
+	| nios-* | nios2-* | nios2eb-* | nios2el-* \
+	| none-* | np1-* | ns16k-* | ns32k-* \
+	| open8-* \
+	| or1k*-* \
+	| orion-* \
+	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+	| pyramid-* \
+	| rl78-* | romp-* | rs6000-* | rx-* \
+	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+	| sparclite-* \
+	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+	| tahoe-* \
+	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+	| tile*-* \
+	| tron-* \
+	| ubicom32-* \
+	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+	| vax-* \
+	| we32k-* \
+	| x86-* | x86_64-* | xc16x-* | xps100-* \
+	| xstormy16-* | xtensa*-* \
+	| ymp-* \
+	| z8k-* | z80-*)
+		;;
+	# Recognize the basic CPU types without company name, with glob match.
+	xtensa*)
+		basic_machine=$basic_machine-unknown
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-unknown
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	abacus)
+		basic_machine=abacus-unknown
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amd64)
+		basic_machine=x86_64-pc
+		;;
+	amd64-*)
+		basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aros)
+		basic_machine=i386-pc
+		os=-aros
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	blackfin)
+		basic_machine=bfin-unknown
+		os=-linux
+		;;
+	blackfin-*)
+		basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	bluegene*)
+		basic_machine=powerpc-ibm
+		os=-cnk
+		;;
+	c54x-*)
+		basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	c55x-*)
+		basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	c6x-*)
+		basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	c90)
+		basic_machine=c90-cray
+		os=-unicos
+		;;
+	cegcc)
+		basic_machine=arm-unknown
+		os=-cegcc
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | j90)
+		basic_machine=j90-cray
+		os=-unicos
+		;;
+	craynv)
+		basic_machine=craynv-cray
+		os=-unicosmp
+		;;
+	cr16 | cr16-*)
+		basic_machine=cr16-unknown
+		os=-elf
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	crisv32 | crisv32-* | etraxfs*)
+		basic_machine=crisv32-axis
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	crx)
+		basic_machine=crx-unknown
+		os=-elf
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	decsystem10* | dec10*)
+		basic_machine=pdp10-dec
+		os=-tops10
+		;;
+	decsystem20* | dec20*)
+		basic_machine=pdp10-dec
+		os=-tops20
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dicos)
+		basic_machine=i686-pc
+		os=-dicos
+		;;
+	djgpp)
+		basic_machine=i586-pc
+		os=-msdosdjgpp
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2* | dpx2*-bull)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppa-next)
+		os=-nextstep3
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+	i*86v32)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	i386-vsta | vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	m68knommu)
+		basic_machine=m68k-unknown
+		os=-linux
+		;;
+	m68knommu-*)
+		basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	m88k-omron*)
+		basic_machine=m88k-omron
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	microblaze*)
+		basic_machine=microblaze-xilinx
+		;;
+	mingw64)
+		basic_machine=x86_64-pc
+		os=-mingw64
+		;;
+	mingw32)
+		basic_machine=i686-pc
+		os=-mingw32
+		;;
+	mingw32ce)
+		basic_machine=arm-unknown
+		os=-mingw32ce
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mips3*-*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	morphos)
+		basic_machine=powerpc-unknown
+		os=-morphos
+		;;
+	moxiebox)
+		basic_machine=moxie-unknown
+		os=-moxiebox
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	ms1-*)
+		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+		;;
+	msys)
+		basic_machine=i686-pc
+		os=-msys
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	nacl)
+		basic_machine=le32-unknown
+		os=-nacl
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next )
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	neo-tandem)
+		basic_machine=neo-tandem
+		;;
+	nse-tandem)
+		basic_machine=nse-tandem
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	openrisc | openrisc-*)
+		basic_machine=or32-unknown
+		;;
+	os400)
+		basic_machine=powerpc-ibm
+		os=-os400
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	parisc)
+		basic_machine=hppa-unknown
+		os=-linux
+		;;
+	parisc-*)
+		basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+		os=-linux
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+	pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pc98)
+		basic_machine=i386-pc
+		;;
+	pc98-*)
+		basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentium | p5 | k5 | k6 | nexgen | viac3)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon | athlon_*)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2 | pentiumiii | pentium3)
+		basic_machine=i686-pc
+		;;
+	pentium4)
+		basic_machine=i786-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentium4-*)
+		basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc | ppcbe)	basic_machine=powerpc-unknown
+		;;
+	ppc-* | ppcbe-*)
+		basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle | ppc-le | powerpc-little)
+		basic_machine=powerpcle-unknown
+		;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64)	basic_machine=powerpc64-unknown
+		;;
+	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+		basic_machine=powerpc64le-unknown
+		;;
+	ppc64le-* | powerpc64little-*)
+		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rdos | rdos64)
+		basic_machine=x86_64-pc
+		os=-rdos
+		;;
+	rdos32)
+		basic_machine=i386-pc
+		os=-rdos
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	s390 | s390-*)
+		basic_machine=s390-ibm
+		;;
+	s390x | s390x-*)
+		basic_machine=s390x-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sb1)
+		basic_machine=mipsisa64sb1-unknown
+		;;
+	sb1el)
+		basic_machine=mipsisa64sb1el-unknown
+		;;
+	sde)
+		basic_machine=mipsisa32-sde
+		os=-elf
+		;;
+	sei)
+		basic_machine=mips-sei
+		os=-seiux
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh)
+		basic_machine=sh-hitachi
+		os=-hms
+		;;
+	sh5el)
+		basic_machine=sh5le-unknown
+		;;
+	sh64)
+		basic_machine=sh64-unknown
+		;;
+	sparclite-wrs | simso-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	strongarm-* | thumb-*)
+		basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=alphaev5-cray
+		os=-unicos
+		;;
+	t90)
+		basic_machine=t90-cray
+		os=-unicos
+		;;
+	tile*)
+		basic_machine=$basic_machine-unknown
+		os=-linux-gnu
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	toad1)
+		basic_machine=pdp10-xkl
+		os=-tops20
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	tpf)
+		basic_machine=s390x-ibm
+		os=-tpf
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+		basic_machine=f301-fujitsu
+		;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	xbox)
+		basic_machine=i686-pc
+		os=-mingw32
+		;;
+	xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	xscale-* | xscalee[bl]-*)
+		basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+		;;
+	ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	z8k-*-coff)
+		basic_machine=z8k-unknown
+		os=-sim
+		;;
+	z80-*-coff)
+		basic_machine=z80-unknown
+		os=-sim
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	mmix)
+		basic_machine=mmix-knuth
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp10)
+		# there are many clones, so DEC is not a safe bet
+		basic_machine=pdp10-unknown
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+		basic_machine=sh-unknown
+		;;
+	sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+		basic_machine=sparc-sun
+		;;
+	cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+	# First match some system type aliases
+	# that might get confused with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-auroraux)
+		os=-auroraux
+		;;
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# First accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST END IN A *, to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+	      | -sym* | -kopensolaris* | -plan9* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* | -aros* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+	      | -bitrig* | -openbsd* | -solidbsd* \
+	      | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+	      | -chorusos* | -chorusrdb* | -cegcc* \
+	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \
+	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
+	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto-qnx*)
+		;;
+	-nto*)
+		os=`echo $os | sed -e 's|nto|nto-qnx|'`
+		;;
+	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo $os | sed -e 's|mac|macos|'`
+		;;
+	-linux-dietlibc)
+		os=-linux-dietlibc
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo $os | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo $os | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+	-os400*)
+		os=-os400
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-osfrose*)
+		os=-osfrose
+		;;
+	-osf*)
+		os=-osf
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-atheos*)
+		os=-atheos
+		;;
+	-syllable*)
+		os=-syllable
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-nova*)
+		os=-rtmk-nova
+		;;
+	-ns2 )
+		os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+	-tpf*)
+		os=-tpf
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-es1800*)
+		os=-ose
+		;;
+	-xenix)
+		os=-xenix
+		;;
+	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+		os=-mint
+		;;
+	-aros*)
+		os=-aros
+		;;
+	-zvmoe)
+		os=-zvmoe
+		;;
+	-dicos*)
+		os=-dicos
+		;;
+	-nacl*)
+		;;
+	-none)
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	score-*)
+		os=-elf
+		;;
+	spu-*)
+		os=-elf
+		;;
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+	c4x-* | tic4x-*)
+		os=-coff
+		;;
+	c8051-*)
+		os=-elf
+		;;
+	hexagon-*)
+		os=-elf
+		;;
+	tic54x-*)
+		os=-coff
+		;;
+	tic55x-*)
+		os=-coff
+		;;
+	tic6x-*)
+		os=-coff
+		;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=-tops20
+		;;
+	pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mep-*)
+		os=-elf
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	or32-*)
+		os=-coff
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-haiku)
+		os=-haiku
+		;;
+	*-ibm)
+		os=-aix
+		;;
+	*-knuth)
+		os=-mmixware
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next )
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-next)
+		os=-nextstep3
+		;;
+	*-gould)
+		os=-sysv
+		;;
+	*-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+	*-sgi)
+		os=-irix
+		;;
+	*-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-cnk*|-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-os400*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-tpf*)
+				vendor=ibm
+				;;
+			-vxsim* | -vxworks* | -windiss*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+			-vos*)
+				vendor=stratus
+				;;
+		esac
+		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/sg3_utils/configure b/sg3_utils/configure
new file mode 100755
index 0000000..1f34fc7
--- /dev/null
+++ b/sg3_utils/configure
@@ -0,0 +1,14179 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for sg3_utils 1.42.
+#
+# Report bugs to <dgilbert@interlog.com>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+
+  test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
+    ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+    ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+    ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+    PATH=/empty FPATH=/empty; export PATH FPATH
+    test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
+      || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir/$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: dgilbert@interlog.com about your system, including any
+$0: error possibly output before this message. Then install
+$0: a modern shell, or manually run the script under such a
+$0: shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='sg3_utils'
+PACKAGE_TARNAME='sg3_utils'
+PACKAGE_VERSION='1.42'
+PACKAGE_STRING='sg3_utils 1.42'
+PACKAGE_BUGREPORT='dgilbert@interlog.com'
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+OS_WIN32_CYGWIN_FALSE
+OS_WIN32_CYGWIN_TRUE
+OS_WIN32_MINGW_FALSE
+OS_WIN32_MINGW_TRUE
+OS_SOLARIS_FALSE
+OS_SOLARIS_TRUE
+OS_OSF_FALSE
+OS_OSF_TRUE
+OS_LINUX_FALSE
+OS_LINUX_TRUE
+OS_FREEBSD_FALSE
+OS_FREEBSD_TRUE
+os_libs
+os_cflags
+GETOPT_O_FILES
+CPP
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+MANIFEST_TOOL
+RANLIB
+ac_ct_AR
+AR
+DLLTOOL
+OBJDUMP
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+LIBTOOL
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+am__nodep
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_maintainer_mode
+enable_dependency_tracking
+enable_shared
+enable_static
+with_pic
+enable_fast_install
+with_gnu_ld
+with_sysroot
+enable_libtool_lock
+enable_linuxbsg
+enable_win32_spt_direct
+enable_scsistrings
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures sg3_utils 1.42 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/sg3_utils]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of sg3_utils 1.42:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-silent-rules   less verbose build output (undo: "make V=1")
+  --disable-silent-rules  verbose build output (undo: "make V=0")
+  --enable-maintainer-mode
+                          enable make rules and dependencies not useful (and
+                          sometimes confusing) to the casual installer
+  --enable-dependency-tracking
+                          do not reject slow dependency extractors
+  --disable-dependency-tracking
+                          speeds up one-time build
+  --enable-shared[=PKGS]  build shared libraries [default=yes]
+  --enable-static[=PKGS]  build static libraries [default=yes]
+  --enable-fast-install[=PKGS]
+                          optimize for fast installation [default=yes]
+  --disable-libtool-lock  avoid locking (might break parallel builds)
+  --disable-linuxbsg      ignore linux bsg (sgv4) if present
+  --enable-win32-spt-direct
+                          enable Win32 SPT Direct
+  --disable-scsistrings   Disable full SCSI sense strings
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-pic[=PKGS]       try to use only PIC/non-PIC objects [default=use
+                          both]
+  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
+  --with-sysroot=DIR Search for dependent libraries within DIR
+                        (or the compiler's sysroot if not specified).
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <dgilbert@interlog.com>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+sg3_utils configure 1.42
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* 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 $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+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 sg3_utils $as_me 1.42, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	$as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+am__api_version='1.15'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+  ./ | .// | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+	if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+	  if test $ac_prog = install &&
+	    grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # AIX install.  It has an incompatible calling convention.
+	    :
+	  elif test $ac_prog = install &&
+	    grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+	    # program-specific install script used by HP pwplus--don't use.
+	    :
+	  else
+	    rm -rf conftest.one conftest.two conftest.dir
+	    echo one > conftest.one
+	    echo two > conftest.two
+	    mkdir conftest.dir
+	    if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+	      test -s conftest.one && test -s conftest.two &&
+	      test -s conftest.dir/conftest.one &&
+	      test -s conftest.dir/conftest.two
+	    then
+	      ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+	      break 3
+	    fi
+	  fi
+	fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[\\\"\#\$\&\'\`$am_lf]*)
+    as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+  *[\\\"\#\$\&\'\`$am_lf\ \	]*)
+    as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   am_has_slept=no
+   for am_try in 1 2; do
+     echo "timestamp, slept: $am_has_slept" > conftest.file
+     set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+     if test "$*" = "X"; then
+	# -L didn't work.
+	set X `ls -t "$srcdir/configure" conftest.file`
+     fi
+     if test "$*" != "X $srcdir/configure conftest.file" \
+	&& test "$*" != "X conftest.file $srcdir/configure"; then
+
+	# If neither matched, then we have a broken ls.  This can happen
+	# if, for instance, CONFIG_SHELL is bash and it inherits a
+	# broken ls alias from the environment.  This has actually
+	# happened.  Such a system could not be considered "sane".
+	as_fn_error $? "ls -t appears to fail.  Make sure there is not a broken
+  alias in your environment" "$LINENO" 5
+     fi
+     if test "$2" = conftest.file || test $am_try -eq 2; then
+       break
+     fi
+     # Just in case.
+     sleep 1
+     am_has_slept=yes
+   done
+   test "$2" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+  ( sleep 1 ) &
+  am_sleep_pid=$!
+fi
+
+rm -f conftest.file
+
+test "$program_prefix" != NONE &&
+  program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+
+if test x"${MISSING+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+  *)
+    MISSING="\${SHELL} $am_aux_dir/missing" ;;
+  esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+  am_missing_run="$MISSING "
+else
+  am_missing_run=
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\	*)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip".  However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if ${ac_cv_path_mkdir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in mkdir gmkdir; do
+	 for ac_exec_ext in '' $ac_executable_extensions; do
+	   as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
+	   case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+	     'mkdir (GNU coreutils) '* | \
+	     'mkdir (coreutils) '* | \
+	     'mkdir (fileutils) '4.1*)
+	       ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+	       break 3;;
+	   esac
+	 done
+       done
+  done
+IFS=$as_save_IFS
+
+fi
+
+  test -d ./--version && rmdir ./--version
+  if test "${ac_cv_path_mkdir+set}" = set; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+	@echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+  SET_MAKE=
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+  yes) AM_DEFAULT_VERBOSITY=0;;
+   no) AM_DEFAULT_VERBOSITY=1;;
+    *) AM_DEFAULT_VERBOSITY=1;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+	@$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  am__isrc=' -I$(srcdir)'
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='sg3_utils'
+ VERSION='1.42'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# For better backward compatibility.  To be removed once Automake 1.9.x
+# dies out for good.  For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+mkdir_p='$(MKDIR_P)'
+
+# We need awk for the "check" target (and possibly the TAP driver).  The
+# system "awk" is bad on some platforms.
+# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
+
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar  pax cpio none'
+
+am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
+
+
+
+
+
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes.  So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+  cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present.  This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message.  This
+can help us improve future automake versions.
+
+END
+  if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+    echo 'Configuration will proceed anyway, since you have set the' >&2
+    echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+    echo >&2
+  else
+    cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <http://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+    as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
+  fi
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+    # Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then :
+  enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+  USE_MAINTAINER_MODE=no
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+   if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+
+  MAINT=$MAINTAINER_MODE_TRUE
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+	-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
+if ${am_cv_prog_cc_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+  # Make sure it works both with $CC and with simple cc.
+  # Following AC_PROG_CC_C_O, we do the test twice because some
+  # compilers refuse to overwrite an existing .o file with -o,
+  # though they will create one.
+  am_cv_prog_cc_c_o=yes
+  for am_i in 1 2; do
+    if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+   ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+   ac_status=$?
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   (exit $ac_status); } \
+         && test -f conftest2.$ac_objext; then
+      : OK
+    else
+      am_cv_prog_cc_c_o=no
+      break
+    fi
+  done
+  rm -f core conftest*
+  unset am_i
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+$as_echo "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+	@echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from 'make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+  am__include=include
+  am__quote=
+  _am_result=GNU
+  ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   case `$am_make -s -f confmf 2> /dev/null` in #(
+   *the\ am__doit\ target*)
+     am__include=.include
+     am__quote="\""
+     _am_result=BSD
+     ;;
+   esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+  enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
+else
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC"   am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named 'D' -- because '-MD' means "put the output
+  # in D".
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+      # Solaris 10 /bin/sh.
+      echo '/* dummy */' > sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with '-c' and '-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle '-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs.
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # After this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested.
+      if test "x$enable_dependency_tracking" = xyes; then
+	continue
+      else
+	break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok '-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+# AC_PROG_CXX
+
+
+# Adding libtools to the build seems to bring in C++ environment
+case `pwd` in
+  *\ * | *\	*)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.4.2'
+macro_revision='1.3337'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
+$as_echo_n "checking how to print strings... " >&6; }
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
+else
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO ""
+}
+
+case "$ECHO" in
+  printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
+$as_echo "printf" >&6; } ;;
+  print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
+$as_echo "print -r" >&6; } ;;
+  *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5
+$as_echo "cat" >&6; } ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+     for ac_i in 1 2 3 4 5 6 7; do
+       ac_script="$ac_script$as_nl$ac_script"
+     done
+     echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+     { ac_script=; unset ac_script;}
+     if test -z "$SED"; then
+  ac_path_SED_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+  # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+  ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo '' >> "conftest.nl"
+    "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_SED_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_SED="$ac_path_SED"
+      ac_path_SED_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_SED_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_SED"; then
+    as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+  fi
+else
+  ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+  rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+   then ac_cv_path_FGREP="$GREP -F"
+   else
+     if test -z "$FGREP"; then
+  ac_path_FGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in fgrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_FGREP" || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+  # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'FGREP' >> "conftest.nl"
+    "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_FGREP="$ac_path_FGREP"
+      ac_path_FGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_FGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_FGREP"; then
+    as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_FGREP=$FGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+  withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | ?:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test "$with_gnu_ld" != no && break
+	;;
+      *)
+	test "$with_gnu_ld" != yes && break
+	;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_nm_to_check="${ac_tool_prefix}nm"
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS="$lt_save_ifs"
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm="$ac_dir/$lt_tmp_nm"
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+	# Check to see if the nm accepts a BSD-compat flag.
+	# Adding the `sed 1q' prevents false positives on HP-UX, which says:
+	#   nm: unknown option "B" ignored
+	# Tru64's nm complains that /dev/null is an invalid object file
+	case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+	*/dev/null* | *'Invalid file or object type'*)
+	  lt_cv_path_NM="$tmp_nm -B"
+	  break
+	  ;;
+	*)
+	  case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+	  */dev/null*)
+	    lt_cv_path_NM="$tmp_nm -p"
+	    break
+	    ;;
+	  *)
+	    lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	    continue # so that we can try to find one that supports BSD flags
+	    ;;
+	  esac
+	  ;;
+	esac
+      fi
+    done
+    IFS="$lt_save_ifs"
+  done
+  : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+  NM="$lt_cv_path_NM"
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$DUMPBIN"; then :
+    # Let the user override the test.
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in dumpbin "link -dump"
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DUMPBIN"; then
+  ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$DUMPBIN" && break
+  done
+fi
+if test -z "$DUMPBIN"; then
+  ac_ct_DUMPBIN=$DUMPBIN
+  for ac_prog in dumpbin "link -dump"
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DUMPBIN"; then
+  ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_DUMPBIN" && break
+done
+
+  if test "x$ac_ct_DUMPBIN" = x; then
+    DUMPBIN=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DUMPBIN=$ac_ct_DUMPBIN
+  fi
+fi
+
+    case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+    *COFF*)
+      DUMPBIN="$DUMPBIN -symbols"
+      ;;
+    *)
+      DUMPBIN=:
+      ;;
+    esac
+  fi
+
+  if test "$DUMPBIN" != ":"; then
+    NM="$DUMPBIN"
+  fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:$LINENO: output\"" >&5)
+  cat conftest.out >&5
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+    i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  mint*)
+    # On MiNT this can take a long time and run out of memory.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536	# usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  os2*)
+    # The test takes a long time on OS/2.
+    lt_cv_sys_max_cmd_len=8192
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[	 ]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len" && \
+	test undefined != "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8 ; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \
+	         = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+	      test $i != 17 # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+  test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
+      = c,a/b,b/c, \
+    && eval 'test $(( 1 + 1 )) -eq 2 \
+    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+  && xsi_shell=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+    >/dev/null 2>&1 \
+  && lt_shell_append=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
+$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
+if ${lt_cv_to_host_file_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+        ;;
+    esac
+    ;;
+  *-*-cygwin* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_noop
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+        ;;
+    esac
+    ;;
+  * ) # unhandled hosts (and "normal" native builds)
+    lt_cv_to_host_file_cmd=func_convert_file_noop
+    ;;
+esac
+
+fi
+
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
+$as_echo "$lt_cv_to_host_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
+$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
+if ${lt_cv_to_tool_file_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  #assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+        ;;
+    esac
+    ;;
+esac
+
+fi
+
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
+$as_echo "$lt_cv_to_tool_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    if test "$GCC" != yes; then
+      reload_cmds=false
+    fi
+    ;;
+  darwin*)
+    if test "$GCC" = yes; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OBJDUMP"; then
+  ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+  ac_ct_OBJDUMP=$OBJDUMP
+  # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OBJDUMP"; then
+  ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OBJDUMP="objdump"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OBJDUMP" = x; then
+    OBJDUMP="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OBJDUMP=$ac_ct_OBJDUMP
+  fi
+else
+  OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[45]*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
+  if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    # Keep this pattern in sync with the one in func_win32_libid.
+    lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc*)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+haiku*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[3-9]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+  case $host_os in
+  mingw* | pw32*)
+    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+      want_nocaseglob=yes
+    else
+      file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
+    fi
+    ;;
+  esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DLLTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DLLTOOL"; then
+  ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+$as_echo "$DLLTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+  ac_ct_DLLTOOL=$DLLTOOL
+  # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DLLTOOL"; then
+  ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+$as_echo "$ac_ct_DLLTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_DLLTOOL" = x; then
+    DLLTOOL="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DLLTOOL=$ac_ct_DLLTOOL
+  fi
+else
+  DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
+$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
+if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+  # two different shell functions defined in ltmain.sh
+  # decide which to use based on capabilities of $DLLTOOL
+  case `$DLLTOOL --help 2>&1` in
+  *--identify-strict*)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+    ;;
+  *)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+    ;;
+  esac
+  ;;
+*)
+  # fallback: assume linklib IS sharedlib
+  lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+  ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
+$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in ar
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$AR" && break
+  done
+fi
+if test -z "$AR"; then
+  ac_ct_AR=$AR
+  for ac_prog in ar
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_AR="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_AR" && break
+done
+
+  if test "x$ac_ct_AR" = x; then
+    AR="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AR=$ac_ct_AR
+  fi
+fi
+
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
+$as_echo_n "checking for archiver @FILE support... " >&6; }
+if ${lt_cv_ar_at_file+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ar_at_file=no
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  echo conftest.$ac_objext > conftest.lst
+      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
+      { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+  (eval $lt_ar_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+      if test "$ac_status" -eq 0; then
+	# Ensure the archiver fails upon bogus file names.
+	rm -f conftest.$ac_objext libconftest.a
+	{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+  (eval $lt_ar_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+	if test "$ac_status" -ne 0; then
+          lt_cv_ar_at_file=@
+        fi
+      fi
+      rm -f conftest.* libconftest.a
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
+$as_echo "$lt_cv_ar_at_file" >&6; }
+
+if test "x$lt_cv_ar_at_file" = xno; then
+  archiver_list_spec=
+else
+  archiver_list_spec=$lt_cv_ar_at_file
+fi
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+  darwin*)
+    lock_old_archive_extraction=yes ;;
+  *)
+    lock_old_archive_extraction=no ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[BCDT]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[ABCDGISTW]'
+  ;;
+hpux*)
+  if test "$host_cpu" = ia64; then
+    symcode='[ABCDEGRST]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[BCDEGRST]'
+  ;;
+osf*)
+  symcode='[BCDEGQRST]'
+  ;;
+solaris*)
+  symcode='[BDRT]'
+  ;;
+sco3.2v5*)
+  symcode='[DT]'
+  ;;
+sysv4.2uw2*)
+  symcode='[DT]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[ABDT]'
+  ;;
+sysv4)
+  symcode='[DFNSTU]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function
+    # and D for any global variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK '"\
+"     {last_section=section; section=\$ 3};"\
+"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[	 ]\($symcode$symcode*\)[	 ][	 ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+  lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
+  (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+	if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+	  cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+LT_DLSYM_CONST struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+	  $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+	  cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  lt_globsym_save_LIBS=$LIBS
+	  lt_globsym_save_CFLAGS=$CFLAGS
+	  LIBS="conftstm.$ac_objext"
+	  CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+	  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+	    pipe_works=yes
+	  fi
+	  LIBS=$lt_globsym_save_LIBS
+	  CFLAGS=$lt_globsym_save_CFLAGS
+	else
+	  echo "cannot find nm_test_func in $nlist" >&5
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&5
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+    fi
+  else
+    echo "$progname: failed program was:" >&5
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
+  nm_file_list_spec='@'
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
+$as_echo_n "checking for sysroot... " >&6; }
+
+# Check whether --with-sysroot was given.
+if test "${with_sysroot+set}" = set; then :
+  withval=$with_sysroot;
+else
+  with_sysroot=no
+fi
+
+
+lt_sysroot=
+case ${with_sysroot} in #(
+ yes)
+   if test "$GCC" = yes; then
+     lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+   fi
+   ;; #(
+ /*)
+   lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+   ;; #(
+ no|'')
+   ;; #(
+ *)
+   { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5
+$as_echo "${with_sysroot}" >&6; }
+   as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
+   ;;
+esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
+$as_echo "${lt_sysroot:-no}" >&6; }
+
+
+
+
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+  enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+	HPUX_IA64_MODE="32"
+	;;
+      *ELF-64*)
+	HPUX_IA64_MODE="64"
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    if test "$lt_cv_prog_gnu_ld" = yes; then
+      case `/usr/bin/file conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -melf32bsmip"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -melf32bmipn32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -melf64bmip"
+	;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -32"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -n32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -64"
+	  ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_i386_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    case `/usr/bin/file conftest.o` in
+	      *x86-64*)
+		LD="${LD-ld} -m elf32_x86_64"
+		;;
+	      *)
+		LD="${LD-ld} -m elf_i386"
+		;;
+	    esac
+	    ;;
+	  powerpc64le-*)
+	    LD="${LD-ld} -m elf32lppclinux"
+	    ;;
+	  powerpc64-*)
+	    LD="${LD-ld} -m elf32ppclinux"
+	    ;;
+	  s390x-*linux*)
+	    LD="${LD-ld} -m elf_s390"
+	    ;;
+	  sparc64-*linux*)
+	    LD="${LD-ld} -m elf32_sparc"
+	    ;;
+	esac
+	;;
+      *64-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_x86_64_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    LD="${LD-ld} -m elf_x86_64"
+	    ;;
+	  powerpcle-*)
+	    LD="${LD-ld} -m elf64lppc"
+	    ;;
+	  powerpc-*)
+	    LD="${LD-ld} -m elf64ppc"
+	    ;;
+	  s390*-*linux*|s390*-*tpf*)
+	    LD="${LD-ld} -m elf64_s390"
+	    ;;
+	  sparc*-*linux*)
+	    LD="${LD-ld} -m elf64_sparc"
+	    ;;
+	esac
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_cc_needs_belf=yes
+else
+  lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+     ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+*-*solaris*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*)
+        case $host in
+        i?86-*-solaris*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        sparc*-*-solaris*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+        esac
+        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.
+        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+          LD="${LD-ld}_sol2"
+        fi
+        ;;
+      *)
+	if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+	  LD="${LD-ld} -64"
+	fi
+	;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
+set dummy ${ac_tool_prefix}mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$MANIFEST_TOOL"; then
+  ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
+if test -n "$MANIFEST_TOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
+$as_echo "$MANIFEST_TOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
+  ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
+  # Extract the first word of "mt", so it can be a program name with args.
+set dummy mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_MANIFEST_TOOL"; then
+  ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
+if test -n "$ac_ct_MANIFEST_TOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
+$as_echo "$ac_ct_MANIFEST_TOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_MANIFEST_TOOL" = x; then
+    MANIFEST_TOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
+  fi
+else
+  MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
+fi
+
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
+$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
+if ${lt_cv_path_mainfest_tool+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_path_mainfest_tool=no
+  echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
+  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+  cat conftest.err >&5
+  if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+    lt_cv_path_mainfest_tool=yes
+  fi
+  rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
+$as_echo "$lt_cv_path_mainfest_tool" >&6; }
+if test "x$lt_cv_path_mainfest_tool" != xyes; then
+  MANIFEST_TOOL=:
+fi
+
+
+
+
+
+
+  case $host_os in
+    rhapsody* | darwin*)
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DSYMUTIL"; then
+  ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+  ac_ct_DSYMUTIL=$DSYMUTIL
+  # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DSYMUTIL"; then
+  ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_DSYMUTIL" = x; then
+    DSYMUTIL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DSYMUTIL=$ac_ct_DSYMUTIL
+  fi
+else
+  DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$NMEDIT"; then
+  ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+  ac_ct_NMEDIT=$NMEDIT
+  # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_NMEDIT"; then
+  ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_NMEDIT="nmedit"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_NMEDIT" = x; then
+    NMEDIT=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    NMEDIT=$ac_ct_NMEDIT
+  fi
+else
+  NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$LIPO"; then
+  ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+  ac_ct_LIPO=$LIPO
+  # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_LIPO"; then
+  ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_LIPO="lipo"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_LIPO" = x; then
+    LIPO=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    LIPO=$ac_ct_LIPO
+  fi
+else
+  LIPO="$ac_cv_prog_LIPO"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OTOOL"; then
+  ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+  ac_ct_OTOOL=$OTOOL
+  # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OTOOL"; then
+  ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OTOOL="otool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL" = x; then
+    OTOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL=$ac_ct_OTOOL
+  fi
+else
+  OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OTOOL64"; then
+  ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+  ac_ct_OTOOL64=$OTOOL64
+  # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OTOOL64"; then
+  ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OTOOL64="otool64"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL64" = x; then
+    OTOOL64=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL64=$ac_ct_OTOOL64
+  fi
+else
+  OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_apple_cc_single_mod=no
+      if test -z "${LT_MULTI_MODULE}"; then
+	# By default we will add the -single_module flag. You can override
+	# by either setting the environment variable LT_MULTI_MODULE
+	# non-empty at configure time, or by adding -multi_module to the
+	# link flags.
+	rm -rf libconftest.dylib*
+	echo "int foo(void){return 1;}" > conftest.c
+	echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+	$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+	  -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+	# If there is a non-empty error log, and "single_module"
+	# appears in it, assume the flag caused a linker warning
+        if test -s conftest.err && $GREP single_module conftest.err; then
+	  cat conftest.err >&5
+	# Otherwise, if the output was created with a 0 exit code from
+	# the compiler, it worked.
+	elif test -f libconftest.dylib && test $_lt_result -eq 0; then
+	  lt_cv_apple_cc_single_mod=yes
+	else
+	  cat conftest.err >&5
+	fi
+	rm -rf libconftest.dylib*
+	rm -f conftest.*
+      fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_ld_exported_symbols_list=yes
+else
+  lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+	LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
+$as_echo_n "checking for -force_load linker flag... " >&6; }
+if ${lt_cv_ld_force_load+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_force_load=no
+      cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
+      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
+      echo "$AR cru libconftest.a conftest.o" >&5
+      $AR cru libconftest.a conftest.o 2>&5
+      echo "$RANLIB libconftest.a" >&5
+      $RANLIB libconftest.a 2>&5
+      cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
+      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+      _lt_result=$?
+      if test -s conftest.err && $GREP force_load conftest.err; then
+	cat conftest.err >&5
+      elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then
+	lt_cv_ld_force_load=yes
+      else
+	cat conftest.err >&5
+      fi
+        rm -f conftest.err libconftest.a conftest conftest.c
+        rm -rf conftest.dSYM
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
+$as_echo "$lt_cv_ld_force_load" >&6; }
+    case $host_os in
+    rhapsody* | darwin1.[012])
+      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+	10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+	  _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+	10.[012]*)
+	  _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+	10.*)
+	  _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+		  inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dlfcn.h
+do :
+  ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+# Set options
+
+
+
+        enable_dlopen=no
+
+
+  enable_win32_dll=no
+
+
+            # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+  enableval=$enable_shared; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_shared=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+  # Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+  enableval=$enable_static; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_static=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+  withval=$with_pic; lt_p=${PACKAGE-default}
+    case $withval in
+    yes|no) pic_mode=$withval ;;
+    *)
+      pic_mode=default
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for lt_pkg in $withval; do
+	IFS="$lt_save_ifs"
+	if test "X$lt_pkg" = "X$lt_p"; then
+	  pic_mode=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+  # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+  enableval=$enable_fast_install; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+	IFS="$lt_save_ifs"
+	if test "X$pkg" = "X$p"; then
+	  enable_fast_install=yes
+	fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+  case $cc_temp in
+    compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+    distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/${ac_tool_prefix}file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+	  MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  else
+    MAGIC_CMD=:
+  fi
+fi
+
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+  case $cc_basename in
+  nvcc*)
+    lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
+  *)
+    lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
+  esac
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_rtti_exceptions=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="-fno-rtti -fno-exceptions"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_rtti_exceptions=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+    lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+    :
+fi
+
+fi
+
+
+
+
+
+
+  lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+
+  if test "$GCC" = yes; then
+    lt_prog_compiler_wl='-Wl,'
+    lt_prog_compiler_static='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            lt_prog_compiler_pic='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic='-fno-common'
+      ;;
+
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      lt_prog_compiler_static=
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic='-fPIC'
+	;;
+      esac
+      ;;
+
+    interix[3-9]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      lt_prog_compiler_can_build_shared=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      lt_prog_compiler_pic='-fPIC'
+      ;;
+    esac
+
+    case $cc_basename in
+    nvcc*) # Cuda Compiler Driver 2.2
+      lt_prog_compiler_wl='-Xlinker '
+      if test -n "$lt_prog_compiler_pic"; then
+        lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
+      fi
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      lt_prog_compiler_wl='-Wl,'
+      if test "$host_cpu" = ia64; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static='-Bstatic'
+      else
+	lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      lt_prog_compiler_static='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC (with -KPIC) is the default.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+      case $cc_basename in
+      # old Intel for x86_64 which still supported -KPIC.
+      ecc*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-KPIC'
+	lt_prog_compiler_static='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-fPIC'
+	lt_prog_compiler_static='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='--shared'
+	lt_prog_compiler_static='--static'
+	;;
+      nagfor*)
+	# NAG Fortran compiler
+	lt_prog_compiler_wl='-Wl,-Wl,,'
+	lt_prog_compiler_pic='-PIC'
+	lt_prog_compiler_static='-Bstatic'
+	;;
+      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+	# which looks to be a dead project)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-fpic'
+	lt_prog_compiler_static='-Bstatic'
+        ;;
+      ccc*)
+        lt_prog_compiler_wl='-Wl,'
+        # All Alpha code is PIC.
+        lt_prog_compiler_static='-non_shared'
+        ;;
+      xl* | bgxl* | bgf* | mpixl*)
+	# IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-qpic'
+	lt_prog_compiler_static='-qstaticlink'
+	;;
+      *)
+	case `$CC -V 2>&1 | sed 5q` in
+	*Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
+	  # Sun Fortran 8.3 passes all unrecognized flags to the linker
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl=''
+	  ;;
+	*Sun\ F* | *Sun*Fortran*)
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl='-Qoption ld '
+	  ;;
+	*Sun\ C*)
+	  # Sun C 5.9
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl='-Wl,'
+	  ;;
+        *Intel*\ [CF]*Compiler*)
+	  lt_prog_compiler_wl='-Wl,'
+	  lt_prog_compiler_pic='-fPIC'
+	  lt_prog_compiler_static='-static'
+	  ;;
+	*Portland\ Group*)
+	  lt_prog_compiler_wl='-Wl,'
+	  lt_prog_compiler_pic='-fpic'
+	  lt_prog_compiler_static='-Bstatic'
+	  ;;
+	esac
+	;;
+      esac
+      ;;
+
+    newsos6)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      lt_prog_compiler_wl='-Wl,'
+      # All OSF/1 code is PIC.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    rdos*)
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    solaris*)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+	lt_prog_compiler_wl='-Qoption ld ';;
+      *)
+	lt_prog_compiler_wl='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      lt_prog_compiler_wl='-Qoption ld '
+      lt_prog_compiler_pic='-PIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+	lt_prog_compiler_pic='-Kconform_pic'
+	lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    unicos*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_can_build_shared=no
+      ;;
+
+    uts4*)
+      lt_prog_compiler_pic='-pic'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *)
+      lt_prog_compiler_can_build_shared=no
+      ;;
+    esac
+  fi
+
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic=
+    ;;
+  *)
+    lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+    ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+if ${lt_cv_prog_compiler_pic+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
+$as_echo "$lt_cv_prog_compiler_pic" >&6; }
+lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic_works=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_pic_works=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+    case $lt_prog_compiler_pic in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+     esac
+else
+    lt_prog_compiler_pic=
+     lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_static_works=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler_static_works=yes
+       fi
+     else
+       lt_cv_prog_compiler_static_works=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+    :
+else
+    lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+  if test "$hard_links" = no; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+  runpath_var=
+  allow_undefined_flag=
+  always_export_symbols=no
+  archive_cmds=
+  archive_expsym_cmds=
+  compiler_needs_object=no
+  enable_shared_with_static_runtimes=no
+  export_dynamic_flag_spec=
+  export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  hardcode_automatic=no
+  hardcode_direct=no
+  hardcode_direct_absolute=no
+  hardcode_libdir_flag_spec=
+  hardcode_libdir_separator=
+  hardcode_minus_L=no
+  hardcode_shlibpath_var=unsupported
+  inherit_rpath=no
+  link_all_deplibs=unknown
+  module_cmds=
+  module_expsym_cmds=
+  old_archive_from_new_cmds=
+  old_archive_from_expsyms_cmds=
+  thread_safe_flag_spec=
+  whole_archive_flag_spec=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  include_expsyms=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    link_all_deplibs=no
+    ;;
+  esac
+
+  ld_shlibs=yes
+
+  # On some targets, GNU ld is compatible enough with the native linker
+  # that we're better off using the native interface for both.
+  lt_use_gnu_ld_interface=no
+  if test "$with_gnu_ld" = yes; then
+    case $host_os in
+      aix*)
+	# The AIX port of GNU ld has always aspired to compatibility
+	# with the native linker.  However, as the warning in the GNU ld
+	# block says, versions before 2.19.5* couldn't really create working
+	# shared libraries, regardless of the interface used.
+	case `$LD -v 2>&1` in
+	  *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+	  *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
+	  *\ \(GNU\ Binutils\)\ [3-9]*) ;;
+	  *)
+	    lt_use_gnu_ld_interface=yes
+	    ;;
+	esac
+	;;
+      *)
+	lt_use_gnu_ld_interface=yes
+	;;
+    esac
+  fi
+
+  if test "$lt_use_gnu_ld_interface" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    export_dynamic_flag_spec='${wl}--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      whole_archive_flag_spec=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v 2>&1` in
+      *GNU\ gold*) supports_anon_versioning=yes ;;
+      *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[3-9]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	allow_undefined_flag=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+      # as there is no search path for DLLs.
+      hardcode_libdir_flag_spec='-L$libdir'
+      export_dynamic_flag_spec='${wl}--export-all-symbols'
+      allow_undefined_flag=unsupported
+      always_export_symbols=no
+      enable_shared_with_static_runtimes=yes
+      export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+      exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	# If the export-symbols file already is a .def file (1st line
+	# is EXPORTS), use it as is; otherwise, prepend...
+	archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	  cp $export_symbols $output_objdir/$soname.def;
+	else
+	  echo EXPORTS > $output_objdir/$soname.def;
+	  cat $export_symbols >> $output_objdir/$soname.def;
+	fi~
+	$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    haiku*)
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      link_all_deplibs=yes
+      ;;
+
+    interix[3-9]*)
+      hardcode_direct=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+      export_dynamic_flag_spec='${wl}-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+      tmp_diet=no
+      if test "$host_os" = linux-dietlibc; then
+	case $cc_basename in
+	  diet\ *) tmp_diet=yes;;	# linux-dietlibc with static linking (!diet-dyn)
+	esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+	 && test "$tmp_diet" = no
+      then
+	tmp_addflag=' $pic_flag'
+	tmp_sharedflag='-shared'
+	case $cc_basename,$host_cpu in
+        pgcc*)				# Portland Group C compiler
+	  whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  tmp_addflag=' $pic_flag'
+	  ;;
+	pgf77* | pgf90* | pgf95* | pgfortran*)
+					# Portland Group f77 and f90 compilers
+	  whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  tmp_addflag=' $pic_flag -Mnomain' ;;
+	ecc*,ia64* | icc*,ia64*)	# Intel C compiler on ia64
+	  tmp_addflag=' -i_dynamic' ;;
+	efc*,ia64* | ifort*,ia64*)	# Intel Fortran compiler on ia64
+	  tmp_addflag=' -i_dynamic -nofor_main' ;;
+	ifc* | ifort*)			# Intel Fortran compiler
+	  tmp_addflag=' -nofor_main' ;;
+	lf95*)				# Lahey Fortran 8.1
+	  whole_archive_flag_spec=
+	  tmp_sharedflag='--shared' ;;
+	xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+	  tmp_sharedflag='-qmkshrobj'
+	  tmp_addflag= ;;
+	nvcc*)	# Cuda Compiler Driver 2.2
+	  whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  compiler_needs_object=yes
+	  ;;
+	esac
+	case `$CC -V 2>&1 | sed 5q` in
+	*Sun\ C*)			# Sun C 5.9
+	  whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+	  compiler_needs_object=yes
+	  tmp_sharedflag='-G' ;;
+	*Sun\ F*)			# Sun Fortran 8.3
+	  tmp_sharedflag='-G' ;;
+	esac
+	archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+        if test "x$supports_anon_versioning" = xyes; then
+          archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+	    cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+	    echo "local: *; };" >> $output_objdir/$libname.ver~
+	    $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+        fi
+
+	case $cc_basename in
+	xlf* | bgf* | bgxlf* | mpixlf*)
+	  # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+	  whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+	  hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+	  archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+	  if test "x$supports_anon_versioning" = xyes; then
+	    archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+	      cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+	      echo "local: *; };" >> $output_objdir/$libname.ver~
+	      $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+	  fi
+	  ;;
+	esac
+      else
+        ld_shlibs=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+	;;
+	*)
+	  # For security reasons, it is highly recommended that you always
+	  # use absolute paths for naming shared libraries, and exclude the
+	  # DT_RUNPATH tag from executables and libraries.  But doing so
+	  # requires that you compile everything twice, which is a pain.
+	  if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+	    archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	    archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+	  else
+	    ld_shlibs=no
+	  fi
+	;;
+      esac
+      ;;
+
+    sunos4*)
+      archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+    esac
+
+    if test "$ld_shlibs" = no; then
+      runpath_var=
+      hardcode_libdir_flag_spec=
+      export_dynamic_flag_spec=
+      whole_archive_flag_spec=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      allow_undefined_flag=unsupported
+      always_export_symbols=yes
+      archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L=yes
+      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	hardcode_direct=unsupported
+      fi
+      ;;
+
+    aix[4-9]*)
+      if test "$host_cpu" = ia64; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=""
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to AIX nm, but means don't demangle with GNU nm
+	# Also, AIX nm treats weak defined symbols like other global
+	# defined symbols, whereas GNU nm marks them as "W".
+	if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+	  export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+	else
+	  export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# need to do runtime linking.
+	case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+	  for ld_flag in $LDFLAGS; do
+	  if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+	    aix_use_runtimelinking=yes
+	    break
+	  fi
+	  done
+	  ;;
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      archive_cmds=''
+      hardcode_direct=yes
+      hardcode_direct_absolute=yes
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      file_list_spec='${wl}-f,'
+
+      if test "$GCC" = yes; then
+	case $host_os in aix4.[012]|aix4.[012].*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`${CC} -print-prog-name=collect2`
+	  if test -f "$collect2name" &&
+	   strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+	  then
+	  # We have reworked collect2
+	  :
+	  else
+	  # We have old collect2
+	  hardcode_direct=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  hardcode_minus_L=yes
+	  hardcode_libdir_flag_spec='-L$libdir'
+	  hardcode_libdir_separator=
+	  fi
+	  ;;
+	esac
+	shared_flag='-shared'
+	if test "$aix_use_runtimelinking" = yes; then
+	  shared_flag="$shared_flag "'${wl}-G'
+	fi
+	link_all_deplibs=no
+      else
+	# not using gcc
+	if test "$host_cpu" = ia64; then
+	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+	  if test "$aix_use_runtimelinking" = yes; then
+	    shared_flag='${wl}-G'
+	  else
+	    shared_flag='${wl}-bM:SRE'
+	  fi
+	fi
+      fi
+
+      export_dynamic_flag_spec='${wl}-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      always_export_symbols=yes
+      if test "$aix_use_runtimelinking" = yes; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	allow_undefined_flag='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  if ${lt_cv_aix_libpath_+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }'
+  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_="/usr/lib:/lib"
+  fi
+
+fi
+
+  aix_libpath=$lt_cv_aix_libpath_
+fi
+
+        hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+        archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+      else
+	if test "$host_cpu" = ia64; then
+	  hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+	  allow_undefined_flag="-z nodefs"
+	  archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an
+	 # empty executable.
+	 if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  if ${lt_cv_aix_libpath_+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }'
+  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_="/usr/lib:/lib"
+  fi
+
+fi
+
+  aix_libpath=$lt_cv_aix_libpath_
+fi
+
+	 hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  no_undefined_flag=' ${wl}-bernotok'
+	  allow_undefined_flag=' ${wl}-berok'
+	  if test "$with_gnu_ld" = yes; then
+	    # We only use this code for GNU lds that support --whole-archive.
+	    whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+	  else
+	    # Exported symbols can be pulled into shared objects from archives
+	    whole_archive_flag_spec='$convenience'
+	  fi
+	  archive_cmds_need_lc=yes
+	  # This is similar to how AIX traditionally builds its shared libraries.
+	  archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[45]*)
+      export_dynamic_flag_spec=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      case $cc_basename in
+      cl*)
+	# Native MSVC
+	hardcode_libdir_flag_spec=' '
+	allow_undefined_flag=unsupported
+	always_export_symbols=yes
+	file_list_spec='@'
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=".dll"
+	# FIXME: Setting linknames here is a bad hack.
+	archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+	archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+	    sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+	  else
+	    sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+	  fi~
+	  $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+	  linknames='
+	# The linker will not automatically build a static lib if we build a DLL.
+	# _LT_TAGVAR(old_archive_from_new_cmds, )='true'
+	enable_shared_with_static_runtimes=yes
+	exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+	export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+	# Don't use ranlib
+	old_postinstall_cmds='chmod 644 $oldlib'
+	postlink_cmds='lt_outputfile="@OUTPUT@"~
+	  lt_tool_outputfile="@TOOL_OUTPUT@"~
+	  case $lt_outputfile in
+	    *.exe|*.EXE) ;;
+	    *)
+	      lt_outputfile="$lt_outputfile.exe"
+	      lt_tool_outputfile="$lt_tool_outputfile.exe"
+	      ;;
+	  esac~
+	  if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+	    $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+	    $RM "$lt_outputfile.manifest";
+	  fi'
+	;;
+      *)
+	# Assume MSVC wrapper
+	hardcode_libdir_flag_spec=' '
+	allow_undefined_flag=unsupported
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=".dll"
+	# FIXME: Setting linknames here is a bad hack.
+	archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+	# The linker will automatically build a .lib file if we build a DLL.
+	old_archive_from_new_cmds='true'
+	# FIXME: Should let the user specify the lib program.
+	old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+	enable_shared_with_static_runtimes=yes
+	;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+
+
+  archive_cmds_need_lc=no
+  hardcode_direct=no
+  hardcode_automatic=yes
+  hardcode_shlibpath_var=unsupported
+  if test "$lt_cv_ld_force_load" = "yes"; then
+    whole_archive_flag_spec='`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
+  else
+    whole_archive_flag_spec=''
+  fi
+  link_all_deplibs=yes
+  allow_undefined_flag="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=func_echo_all
+    archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+  else
+  ld_shlibs=no
+  fi
+
+      ;;
+
+    dgux*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2.*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+	archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+	archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_direct=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L=yes
+      export_dynamic_flag_spec='${wl}-E'
+      ;;
+
+    hpux10*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+	archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test "$with_gnu_ld" = no; then
+	hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+	hardcode_libdir_separator=:
+	hardcode_direct=yes
+	hardcode_direct_absolute=yes
+	export_dynamic_flag_spec='${wl}-E'
+	# hardcode_minus_L: Not really in the search PATH,
+	# but as the default location of the library.
+	hardcode_minus_L=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+	case $host_cpu in
+	hppa*64*)
+	  archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case $host_cpu in
+	hppa*64*)
+	  archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+
+	  # Older versions of the 11.00 compiler do not understand -b yet
+	  # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
+$as_echo_n "checking if $CC understands -b... " >&6; }
+if ${lt_cv_prog_compiler__b+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler__b=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS -b"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler__b=yes
+       fi
+     else
+       lt_cv_prog_compiler__b=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
+$as_echo "$lt_cv_prog_compiler__b" >&6; }
+
+if test x"$lt_cv_prog_compiler__b" = xyes; then
+    archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+else
+    archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+fi
+
+	  ;;
+	esac
+      fi
+      if test "$with_gnu_ld" = no; then
+	hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+	hardcode_libdir_separator=:
+
+	case $host_cpu in
+	hppa*64*|ia64*)
+	  hardcode_direct=no
+	  hardcode_shlibpath_var=no
+	  ;;
+	*)
+	  hardcode_direct=yes
+	  hardcode_direct_absolute=yes
+	  export_dynamic_flag_spec='${wl}-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	# Try to use the -exported_symbol ld option, if it does not
+	# work, assume that -exports_file does not work either and
+	# implicitly export all symbols.
+	# This should be the same for all languages, so no per-tag cache variable.
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
+$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
+if ${lt_cv_irix_exported_symbol+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  save_LDFLAGS="$LDFLAGS"
+	   LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+	   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_irix_exported_symbol=yes
+else
+  lt_cv_irix_exported_symbol=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+           LDFLAGS="$save_LDFLAGS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
+$as_echo "$lt_cv_irix_exported_symbol" >&6; }
+	if test "$lt_cv_irix_exported_symbol" = yes; then
+          archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+	fi
+      else
+	archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      inherit_rpath=yes
+      link_all_deplibs=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    newsos6)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_shlibpath_var=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+	hardcode_direct=yes
+	hardcode_shlibpath_var=no
+	hardcode_direct_absolute=yes
+	if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+	  archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	  archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+	  hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+	  export_dynamic_flag_spec='${wl}-E'
+	else
+	  case $host_os in
+	   openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+	     archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+	     hardcode_libdir_flag_spec='-R$libdir'
+	     ;;
+	   *)
+	     archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	     hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+	     ;;
+	  esac
+	fi
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      allow_undefined_flag=unsupported
+      archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+	allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+	allow_undefined_flag=' -expect_unresolved \*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+	allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+	hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      else
+	allow_undefined_flag=' -expect_unresolved \*'
+	archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+	archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+	$CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	hardcode_libdir_flag_spec='-rpath $libdir'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_separator=:
+      ;;
+
+    solaris*)
+      no_undefined_flag=' -z defs'
+      if test "$GCC" = yes; then
+	wlarc='${wl}'
+	archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+	case `$CC -V 2>&1` in
+	*"Compilers 5.0"*)
+	  wlarc=''
+	  archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+	  ;;
+	*)
+	  wlarc='${wl}'
+	  archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+	  archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+	  $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+	  ;;
+	esac
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_shlibpath_var=no
+      case $host_os in
+      solaris2.[0-5] | solaris2.[0-5].*) ;;
+      *)
+	# The compiler driver will combine and reorder linker options,
+	# but understands `-z linker_flag'.  GCC discards it without `$wl',
+	# but is careful enough not to reorder.
+	# Supported since Solaris 2.6 (maybe 2.5.1?)
+	if test "$GCC" = yes; then
+	  whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+	else
+	  whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+	fi
+	;;
+      esac
+      link_all_deplibs=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  reload_cmds='$CC -r -o $output$reload_objs'
+	  hardcode_direct=no
+        ;;
+	motorola)
+	  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4.3*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      export_dynamic_flag_spec='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	hardcode_shlibpath_var=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	ld_shlibs=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+      no_undefined_flag='${wl}-z,text'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We can NOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      no_undefined_flag='${wl}-z,text'
+      allow_undefined_flag='${wl}-z,nodefs'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='${wl}-R,$libdir'
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      export_dynamic_flag_spec='${wl}-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+	archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      ld_shlibs=no
+      ;;
+    esac
+
+    if test x$host_vendor = xsni; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+	export_dynamic_flag_spec='${wl}-Blargedynsym'
+	;;
+      esac
+    fi
+  fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+if ${lt_cv_archive_cmds_need_lc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  $RM conftest*
+	echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+	if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } 2>conftest.err; then
+	  soname=conftest
+	  lib=conftest
+	  libobjs=conftest.$ac_objext
+	  deplibs=
+	  wl=$lt_prog_compiler_wl
+	  pic_flag=$lt_prog_compiler_pic
+	  compiler_flags=-v
+	  linker_flags=-v
+	  verstring=
+	  output_objdir=.
+	  libname=conftest
+	  lt_save_allow_undefined_flag=$allow_undefined_flag
+	  allow_undefined_flag=
+	  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+  (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+	  then
+	    lt_cv_archive_cmds_need_lc=no
+	  else
+	    lt_cv_archive_cmds_need_lc=yes
+	  fi
+	  allow_undefined_flag=$lt_save_allow_undefined_flag
+	else
+	  cat conftest.err 1>&5
+	fi
+	$RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
+$as_echo "$lt_cv_archive_cmds_need_lc" >&6; }
+      archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+  case $host_os in
+    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+    *) lt_awk_arg="/^libraries:/" ;;
+  esac
+  case $host_os in
+    mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;;
+    *) lt_sed_strip_eq="s,=/,/,g" ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+  case $lt_search_path_spec in
+  *\;*)
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+    ;;
+  *)
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+    ;;
+  esac
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary.
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+    else
+      test -d "$lt_sys_path" && \
+	lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+  lt_foo="";
+  lt_count=0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo="/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[lt_foo]++; }
+  if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+  # AWK program above erroneously prepends '/' to C:/dos/paths
+  # for these hosts.
+  case $host_os in
+    mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+      $SED 's,/\([A-Za-z]:\),\1,g'` ;;
+  esac
+  sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[4-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[45]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+
+      sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl*)
+    # Native MSVC
+    libname_spec='$name'
+    soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+    library_names_spec='${libname}.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec="$LIB"
+      if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC wrapper
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[23].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[01]* | freebsdelf3.[01]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+  freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[3-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test "$lt_cv_prog_gnu_ld" = yes; then
+		version_type=linux # correct to gnu/linux during the next big refactor
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+	 LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  if  ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+  lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+
+fi
+
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*)	need_version=yes ;;
+    *)				need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+	shlibpath_overrides_runpath=no
+	;;
+      *)
+	shlibpath_overrides_runpath=yes
+	;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+	;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+   test -n "$runpath_var" ||
+   test "X$hardcode_automatic" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$hardcode_direct" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+     test "$hardcode_minus_L" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+   test "$inherit_rpath" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+
+
+
+
+
+  if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $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 dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dl_dlopen=yes
+else
+  ac_cv_lib_dl_dlopen=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_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+
+fi
+
+    ;;
+
+  *)
+    ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+  lt_cv_dlopen="shl_load"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $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 shl_load ();
+int
+main ()
+{
+return shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dld_shl_load=yes
+else
+  ac_cv_lib_dld_shl_load=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_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+  ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $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 dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dl_dlopen=yes
+else
+  ac_cv_lib_dl_dlopen=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_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $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 dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_svld_dlopen=yes
+else
+  ac_cv_lib_svld_dlopen=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_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $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 dld_link ();
+int
+main ()
+{
+return dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dld_dld_link=yes
+else
+  ac_cv_lib_dld_dld_link=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_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+	  if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+	}
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  	  if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self_static=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+	  if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+	}
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP" ; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+    else
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    fi
+    ;;
+  *)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+  # Report which library types will actually be built
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+  test "$can_build_shared" = "no" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test "$enable_shared" = yes && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[4-9]*)
+    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+      test "$enable_shared" = yes && enable_static=no
+    fi
+    ;;
+  esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+  # Make sure either enable_shared or enable_static is yes.
+  test "$enable_shared" = yes || enable_static=yes
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+        ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+# check for headers
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+		   (('a' <= (c) && (c) <= 'i') \
+		     || ('j' <= (c) && (c) <= 'r') \
+		     || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+	|| toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+for ac_header in linux/types.h linux/bsg.h linux/kdev_t.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "#ifdef HAVE_LINUX_TYPES_H
+     # include <linux/types.h>
+     #endif
+
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+# check for functions
+for ac_func in getopt_long
+do :
+  ac_fn_c_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long"
+if test "x$ac_cv_func_getopt_long" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_GETOPT_LONG 1
+_ACEOF
+ GETOPT_O_FILES=''
+else
+  GETOPT_O_FILES='getopt_long.o'
+fi
+done
+
+for ac_func in posix_fadvise
+do :
+  ac_fn_c_check_func "$LINENO" "posix_fadvise" "ac_cv_func_posix_fadvise"
+if test "x$ac_cv_func_posix_fadvise" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_POSIX_FADVISE 1
+_ACEOF
+
+fi
+done
+
+for ac_func in posix_memalign
+do :
+  ac_fn_c_check_func "$LINENO" "posix_memalign" "ac_cv_func_posix_memalign"
+if test "x$ac_cv_func_posix_memalign" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_POSIX_MEMALIGN 1
+_ACEOF
+
+fi
+done
+
+for ac_func in sysconf
+do :
+  ac_fn_c_check_func "$LINENO" "sysconf" "ac_cv_func_sysconf"
+if test "x$ac_cv_func_sysconf" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SYSCONF 1
+_ACEOF
+
+fi
+done
+
+for ac_func in lseek64
+do :
+  ac_fn_c_check_func "$LINENO" "lseek64" "ac_cv_func_lseek64"
+if test "x$ac_cv_func_lseek64" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LSEEK64 1
+_ACEOF
+
+fi
+done
+
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_BUILD_HOST "${host}"
+_ACEOF
+
+
+case "${host}" in
+        *-*-linux-gnu*)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_LINUX 1
+_ACEOF
+
+                os_cflags=''
+
+                os_libs=''
+ ;;
+        *-*-linux*)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_LINUX 1
+_ACEOF
+
+                os_cflags=''
+
+                os_libs=''
+ ;;
+        *-*-freebsd*|*-*-kfreebsd*-gnu*)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_FREEBSD 1
+_ACEOF
+
+                os_cflags=''
+
+                os_libs='-lcam'
+;;
+        *-*-solaris*)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_SOLARIS 1
+_ACEOF
+
+                os_cflags=''
+
+                os_libs=''
+;;
+        *-*-osf*)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_OSF1 1
+_ACEOF
+
+                os_cflags=''
+
+                os_libs=''
+ ;;
+        *-*-cygwin*)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_WIN32 1
+_ACEOF
+
+                os_cflags='-Wno-char-subscripts'
+
+                os_libs=''
+ ;;
+        *-*-mingw*)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_WIN32 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_MINGW 1
+_ACEOF
+
+                os_cflags=''
+
+                os_libs=''
+ ;;
+        *)
+
+cat >>confdefs.h <<_ACEOF
+#define SG_LIB_LINUX 1
+_ACEOF
+
+                os_cflags=''
+
+                os_libs=''
+ ;;
+esac
+
+# Define platform-specific symbol.
+ if echo $host_os | grep 'freebsd' > /dev/null; then
+  OS_FREEBSD_TRUE=
+  OS_FREEBSD_FALSE='#'
+else
+  OS_FREEBSD_TRUE='#'
+  OS_FREEBSD_FALSE=
+fi
+
+ if echo $host_os | grep '^linux' > /dev/null; then
+  OS_LINUX_TRUE=
+  OS_LINUX_FALSE='#'
+else
+  OS_LINUX_TRUE='#'
+  OS_LINUX_FALSE=
+fi
+
+ if echo $host_os | grep '^osf' > /dev/null; then
+  OS_OSF_TRUE=
+  OS_OSF_FALSE='#'
+else
+  OS_OSF_TRUE='#'
+  OS_OSF_FALSE=
+fi
+
+ if echo $host_os | grep '^solaris' > /dev/null; then
+  OS_SOLARIS_TRUE=
+  OS_SOLARIS_FALSE='#'
+else
+  OS_SOLARIS_TRUE='#'
+  OS_SOLARIS_FALSE=
+fi
+
+ if echo $host_os | grep '^mingw' > /dev/null; then
+  OS_WIN32_MINGW_TRUE=
+  OS_WIN32_MINGW_FALSE='#'
+else
+  OS_WIN32_MINGW_TRUE='#'
+  OS_WIN32_MINGW_FALSE=
+fi
+
+ if echo $host_os | grep '^cygwin' > /dev/null; then
+  OS_WIN32_CYGWIN_TRUE=
+  OS_WIN32_CYGWIN_FALSE='#'
+else
+  OS_WIN32_CYGWIN_TRUE='#'
+  OS_WIN32_CYGWIN_FALSE=
+fi
+
+
+# Check whether --enable-linuxbsg was given.
+if test "${enable_linuxbsg+set}" = set; then :
+  enableval=$enable_linuxbsg;
+cat >>confdefs.h <<_ACEOF
+#define IGNORE_LINUX_BSG 1
+_ACEOF
+
+fi
+
+
+# Check whether --enable-win32-spt-direct was given.
+if test "${enable_win32_spt_direct+set}" = set; then :
+  enableval=$enable_win32_spt_direct;
+cat >>confdefs.h <<_ACEOF
+#define WIN32_SPT_DIRECT 1
+_ACEOF
+
+
+fi
+
+
+# Check whether --enable-scsistrings was given.
+if test "${enable_scsistrings+set}" = set; then :
+  enableval=$enable_scsistrings;
+else
+
+cat >>confdefs.h <<_ACEOF
+#define SG_SCSI_STRINGS 1
+_ACEOF
+
+fi
+
+
+ac_config_files="$ac_config_files Makefile include/Makefile lib/Makefile src/Makefile doc/Makefile scripts/Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
+$as_echo_n "checking that generated files are newer than configure... " >&6; }
+   if test -n "$am_sleep_pid"; then
+     # Hide warnings about reused PIDs.
+     wait $am_sleep_pid 2>/dev/null
+   fi
+   { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
+$as_echo "done" >&6; }
+ if test -n "$EXEEXT"; then
+  am__EXEEXT_TRUE=
+  am__EXEEXT_FALSE='#'
+else
+  am__EXEEXT_TRUE='#'
+  am__EXEEXT_FALSE=
+fi
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+  as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+  as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OS_FREEBSD_TRUE}" && test -z "${OS_FREEBSD_FALSE}"; then
+  as_fn_error $? "conditional \"OS_FREEBSD\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OS_LINUX_TRUE}" && test -z "${OS_LINUX_FALSE}"; then
+  as_fn_error $? "conditional \"OS_LINUX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OS_OSF_TRUE}" && test -z "${OS_OSF_FALSE}"; then
+  as_fn_error $? "conditional \"OS_OSF\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OS_SOLARIS_TRUE}" && test -z "${OS_SOLARIS_FALSE}"; then
+  as_fn_error $? "conditional \"OS_SOLARIS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OS_WIN32_MINGW_TRUE}" && test -z "${OS_WIN32_MINGW_FALSE}"; then
+  as_fn_error $? "conditional \"OS_WIN32_MINGW\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OS_WIN32_CYGWIN_TRUE}" && test -z "${OS_WIN32_CYGWIN_FALSE}"; then
+  as_fn_error $? "conditional \"OS_WIN32_CYGWIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by sg3_utils $as_me 1.42, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <dgilbert@interlog.com>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+sg3_utils config.status 1.42
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
+enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
+SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
+ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
+PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
+host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
+host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
+host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
+build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
+build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
+build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
+SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
+Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
+GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
+EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
+FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
+LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
+NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
+LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
+exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
+file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
+want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
+DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
+sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
+AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
+archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
+STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
+lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
+CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
+compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
+GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
+lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
+need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
+MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
+LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
+libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
+postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
+need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
+version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
+install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
+striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in SHELL \
+ECHO \
+PATH_SEPARATOR \
+SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+file_magic_glob \
+want_nocaseglob \
+DLLTOOL \
+sharedlib_from_linklib_cmd \
+AR \
+AR_FLAGS \
+archiver_list_spec \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+nm_file_list_spec \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_pic \
+lt_prog_compiler_wl \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+MANIFEST_TOOL \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_separator \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+install_override_mode \
+finish_eval \
+old_striplib \
+striplib; do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postlink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    TIMESTAMP='$TIMESTAMP'
+    RM='$RM'
+    ofile='$ofile'
+
+
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+    "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
+    "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
+    "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+    "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
+    "scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = ""
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=[	 ]*/{
+h
+s///
+s/^/:/
+s/[	 ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[	 ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+  ac_MKDIR_P=$MKDIR_P
+  case $MKDIR_P in
+  [\\/$]* | ?:[\\/]* ) ;;
+  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$_am_arg" : 'X\(//\)[^/]' \| \
+	 X"$_am_arg" : 'X\(//\)$' \| \
+	 X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+  :C)  { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+  esac
+
+
+  case $ac_file$ac_mode in
+    "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+  # Older Autoconf quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  case $CONFIG_FILES in
+  *\'*) eval set x "$CONFIG_FILES" ;;
+  *)   set x $CONFIG_FILES ;;
+  esac
+  shift
+  for mf
+  do
+    # Strip MF so we end up with the name of the file.
+    mf=`echo "$mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile or not.
+    # We used to match only the files named 'Makefile.in', but
+    # some people rename them; so instead we look at the file content.
+    # Grep'ing the first line is not enough: some people post-process
+    # each Makefile.in and add a new line on top of each file to say so.
+    # Grep'ing the whole file is not good either: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+      dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$mf" : 'X\(//\)[^/]' \| \
+	 X"$mf" : 'X\(//\)$' \| \
+	 X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+    else
+      continue
+    fi
+    # Extract the definition of DEPDIR, am__include, and am__quote
+    # from the Makefile without running 'make'.
+    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+    test -z "$DEPDIR" && continue
+    am__include=`sed -n 's/^am__include = //p' < "$mf"`
+    test -z "$am__include" && continue
+    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+    # Find all dependency output files, they are included files with
+    # $(DEPDIR) in their names.  We invoke sed twice because it is the
+    # simplest approach to changing $(DEPDIR) to its actual value in the
+    # expansion.
+    for file in `sed -n "
+      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+	 sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
+      # Make sure the directory exists.
+      test -f "$dirpart/$file" && continue
+      fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$file" : 'X\(//\)[^/]' \| \
+	 X"$file" : 'X\(//\)$' \| \
+	 X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      as_dir=$dirpart/$fdir; as_fn_mkdir_p
+      # echo "creating $dirpart/$file"
+      echo '# dummy' > "$dirpart/$file"
+    done
+  done
+}
+ ;;
+    "libtool":C)
+
+    # See if we are running on zsh, and set the options which allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}" ; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile="${ofile}T"
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+#   This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that protects backslashes.
+ECHO=$lt_ECHO
+
+# The PATH separator for the build system.
+PATH_SEPARATOR=$lt_PATH_SEPARATOR
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# convert \$build file names to \$host format.
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+
+# convert \$build files to toolchain format.
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method = "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# How to find potential files when deplibs_check_method = "file_magic".
+file_magic_glob=$lt_file_magic_glob
+
+# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
+want_nocaseglob=$lt_want_nocaseglob
+
+# DLL creation program.
+DLLTOOL=$lt_DLLTOOL
+
+# Command to associate shared and link libraries.
+sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
+
+# The archiver.
+AR=$lt_AR
+
+# Flags to create an archive.
+AR_FLAGS=$lt_AR_FLAGS
+
+# How to feed a file listing to the archiver.
+archiver_list_spec=$lt_archiver_list_spec
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Whether to use a lock for old archive extraction.
+lock_old_archive_extraction=$lock_old_archive_extraction
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# Specify filename containing input files for \$NM.
+nm_file_list_spec=$lt_nm_file_list_spec
+
+# The root where to search for dependent libraries,and in which our libraries should be installed.
+lt_sysroot=$lt_sysroot
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Manifest tool.
+MANIFEST_TOOL=$lt_MANIFEST_TOOL
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Permission mode override for installation of shared libraries.
+install_override_mode=$lt_install_override_mode
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" \
+     || (rm -f "$cfgfile"; exit 1)
+
+  if test x"$xsi_shell" = xyes; then
+  sed -e '/^func_dirname ()$/,/^} # func_dirname /c\
+func_dirname ()\
+{\
+\    case ${1} in\
+\      */*) func_dirname_result="${1%/*}${2}" ;;\
+\      *  ) func_dirname_result="${3}" ;;\
+\    esac\
+} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_basename ()$/,/^} # func_basename /c\
+func_basename ()\
+{\
+\    func_basename_result="${1##*/}"\
+} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\
+func_dirname_and_basename ()\
+{\
+\    case ${1} in\
+\      */*) func_dirname_result="${1%/*}${2}" ;;\
+\      *  ) func_dirname_result="${3}" ;;\
+\    esac\
+\    func_basename_result="${1##*/}"\
+} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_stripname ()$/,/^} # func_stripname /c\
+func_stripname ()\
+{\
+\    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\
+\    # positional parameters, so assign one to ordinary parameter first.\
+\    func_stripname_result=${3}\
+\    func_stripname_result=${func_stripname_result#"${1}"}\
+\    func_stripname_result=${func_stripname_result%"${2}"}\
+} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\
+func_split_long_opt ()\
+{\
+\    func_split_long_opt_name=${1%%=*}\
+\    func_split_long_opt_arg=${1#*=}\
+} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\
+func_split_short_opt ()\
+{\
+\    func_split_short_opt_arg=${1#??}\
+\    func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\
+} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\
+func_lo2o ()\
+{\
+\    case ${1} in\
+\      *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\
+\      *)    func_lo2o_result=${1} ;;\
+\    esac\
+} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_xform ()$/,/^} # func_xform /c\
+func_xform ()\
+{\
+    func_xform_result=${1%.*}.lo\
+} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_arith ()$/,/^} # func_arith /c\
+func_arith ()\
+{\
+    func_arith_result=$(( $* ))\
+} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_len ()$/,/^} # func_len /c\
+func_len ()\
+{\
+    func_len_result=${#1}\
+} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+fi
+
+if test x"$lt_shell_append" = xyes; then
+  sed -e '/^func_append ()$/,/^} # func_append /c\
+func_append ()\
+{\
+    eval "${1}+=\\${2}"\
+} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\
+func_append_quoted ()\
+{\
+\    func_quote_for_eval "${2}"\
+\    eval "${1}+=\\\\ \\$func_quote_for_eval_result"\
+} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  # Save a `func_append' function call where possible by direct use of '+='
+  sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+else
+  # Save a `func_append' function call even when '+=' is not available
+  sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+fi
+
+if test x"$_lt_function_replace_fail" = x":"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5
+$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;}
+fi
+
+
+   mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+
+ ;;
+
+  esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/sg3_utils/configure.ac b/sg3_utils/configure.ac
new file mode 100644
index 0000000..4c63024
--- /dev/null
+++ b/sg3_utils/configure.ac
@@ -0,0 +1,94 @@
+AC_INIT(sg3_utils, 1.42, dgilbert@interlog.com)
+
+AM_INIT_AUTOMAKE
+AM_MAINTAINER_MODE
+AM_CONFIG_HEADER(config.h)
+
+AC_PROG_CC
+# AC_PROG_CXX
+AC_PROG_INSTALL
+		
+# Adding libtools to the build seems to bring in C++ environment
+AC_PROG_LIBTOOL
+
+# check for headers
+AC_HEADER_STDC
+AC_CHECK_HEADERS([linux/types.h linux/bsg.h linux/kdev_t.h], [], [],
+     [[#ifdef HAVE_LINUX_TYPES_H
+     # include <linux/types.h>
+     #endif
+     ]])
+
+# check for functions
+AC_CHECK_FUNCS(getopt_long,
+	       GETOPT_O_FILES='',
+	       GETOPT_O_FILES='getopt_long.o')
+AC_CHECK_FUNCS(posix_fadvise)
+AC_CHECK_FUNCS(posix_memalign)
+AC_CHECK_FUNCS(sysconf)
+AC_CHECK_FUNCS(lseek64)
+AC_SUBST(GETOPT_O_FILES)
+
+AC_CANONICAL_HOST
+
+AC_DEFINE_UNQUOTED(SG_LIB_BUILD_HOST, "${host}", [sg3_utils Build Host])
+
+case "${host}" in
+        *-*-linux-gnu*)
+		AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux])
+                AC_SUBST([os_cflags], [''])
+                AC_SUBST([os_libs], ['']) ;;
+        *-*-linux*)
+		AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [sg3_utils on linux])
+                AC_SUBST([os_cflags], [''])
+                AC_SUBST([os_libs], ['']) ;;
+        *-*-freebsd*|*-*-kfreebsd*-gnu*)
+		AC_DEFINE_UNQUOTED(SG_LIB_FREEBSD, 1, [sg3_utils on FreeBSD])
+                AC_SUBST([os_cflags], [''])
+                AC_SUBST([os_libs], ['-lcam']);;
+        *-*-solaris*)
+		AC_DEFINE_UNQUOTED(SG_LIB_SOLARIS, 1, [sg3_utils on Solaris])
+                AC_SUBST([os_cflags], [''])
+                AC_SUBST([os_libs], ['']);;
+        *-*-osf*)
+		AC_DEFINE_UNQUOTED(SG_LIB_OSF1, 1, [sg3_utils on Tru64 UNIX])
+                AC_SUBST([os_cflags], [''])
+                AC_SUBST([os_libs], ['']) ;;
+        *-*-cygwin*)
+		AC_DEFINE_UNQUOTED(SG_LIB_WIN32, 1, [sg3_utils on Win32])
+                AC_SUBST([os_cflags], ['-Wno-char-subscripts'])
+                AC_SUBST([os_libs], ['']) ;;
+        *-*-mingw*)
+		AC_DEFINE_UNQUOTED(SG_LIB_WIN32, 1, [sg3_utils on Win32])
+		AC_DEFINE_UNQUOTED(SG_LIB_MINGW, 1, [also MinGW environment])
+                AC_SUBST([os_cflags], [''])
+                AC_SUBST([os_libs], ['']) ;;
+        *)
+		AC_DEFINE_UNQUOTED(SG_LIB_LINUX, 1, [assume sg3_utils on linux])
+                AC_SUBST([os_cflags], [''])
+                AC_SUBST([os_libs], ['']) ;;
+esac
+
+# Define platform-specific symbol.
+AM_CONDITIONAL(OS_FREEBSD, [echo $host_os | grep 'freebsd' > /dev/null])
+AM_CONDITIONAL(OS_LINUX, [echo $host_os | grep '^linux' > /dev/null])
+AM_CONDITIONAL(OS_OSF, [echo $host_os | grep '^osf' > /dev/null])
+AM_CONDITIONAL(OS_SOLARIS, [echo $host_os | grep '^solaris' > /dev/null])
+AM_CONDITIONAL(OS_WIN32_MINGW, [echo $host_os | grep '^mingw' > /dev/null])
+AM_CONDITIONAL(OS_WIN32_CYGWIN, [echo $host_os | grep '^cygwin' > /dev/null])
+
+AC_ARG_ENABLE([linuxbsg],
+  AC_HELP_STRING([--disable-linuxbsg], [ignore linux bsg (sgv4) if present]),
+  [AC_DEFINE_UNQUOTED(IGNORE_LINUX_BSG, 1, [ignore linux bsg], )], [])
+
+AC_ARG_ENABLE([win32-spt-direct],
+  AC_HELP_STRING([--enable-win32-spt-direct], [enable Win32 SPT Direct]),
+  AC_DEFINE_UNQUOTED(WIN32_SPT_DIRECT, 1, [enable Win32 SPT Direct], )
+)
+
+AC_ARG_ENABLE([scsistrings],
+  [AS_HELP_STRING([--disable-scsistrings],
+		  [Disable full SCSI sense strings])],
+  [], [AC_DEFINE_UNQUOTED(SG_SCSI_STRINGS, 1, [full SCSI sense strings], )])
+
+AC_OUTPUT(Makefile include/Makefile lib/Makefile src/Makefile doc/Makefile scripts/Makefile)
diff --git a/sg3_utils/debian/README.debian4 b/sg3_utils/debian/README.debian4
new file mode 100644
index 0000000..900b04e
--- /dev/null
+++ b/sg3_utils/debian/README.debian4
@@ -0,0 +1,14 @@
+For whatever reason Debian build scripts (e.g. debhelper and dbclean)
+seem to have changed in such a way to be Debian 4.0 ("etch") unfriendly.
+
+So when the ./build_debian.sh script is called on a Debian 4.0 system,
+it fails saying the debhelper is too old. That can be fixed by editing
+the 'control' file, changing this line:
+    Build-Depends: debhelper (>> 7), libtool, libcam-dev [kfreebsd-i386 kfreebsd-amd64]
+to:
+    Build-Depends: debhelper, libtool, libcam-dev [kfreebsd-i386 kfreebsd-amd64]
+
+The script then dies in dbclean and the hack to get around that is to
+edit the 'compat' file. It contains "7" which needs to be changed to
+"4". Evidently "4" is deprecated and "5" is preferable and should work.
+
diff --git a/sg3_utils/debian/changelog b/sg3_utils/debian/changelog
new file mode 100644
index 0000000..2fab436
--- /dev/null
+++ b/sg3_utils/debian/changelog
@@ -0,0 +1,242 @@
+sg3-utils (1.42-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Wed, 17 Feb 2016 15:00:00 -0500
+
+sg3-utils (1.41-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Tue, 28 Apr 2015 11:00:00 -0400
+
+sg3-utils (1.40-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Mon, 10 Nov 2014 23:00:00 -0500
+
+sg3-utils (1.39-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Thu, 12 Jun 2014 09:00:00 -0400
+
+sg3-utils (1.38-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Tue, 01 Apr 2014 15:00:00 -0400
+
+sg3-utils (1.37-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Mon, 14 Oct 2013 15:00:00 -0400
+
+sg3-utils (1.36-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Fri, 31 May 2013 10:00:00 -0400
+
+sg3-utils (1.35-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Thu, 17 Jan 2013 19:00:00 -0500
+
+sg3-utils (1.34-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Sat, 13 Oct 2012 19:00:00 -0400
+
+sg3-utils (1.33-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Wed, 18 Jan 2012 14:00:00 -0500
+
+sg3-utils (1.32-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Wed, 22 Jun 2011 16:00:00 -0400
+
+sg3-utils (1.31-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Wed, 16 Feb 2011 16:00:00 -0500
+
+sg3-utils (1.30-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Fri, 05 Nov 2010 10:30:00 -0400
+
+sg3-utils (1.29-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Wed, 31 Mar 2010 23:00:00 -0400
+
+sg3-utils (1.28-0.1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Douglas Gilbert <dgilbert@interlog.com>  Fri, 02 Oct 2009 00:20:00 -0400
+
+sg3-utils (1.27-0.1) unstable; urgency=low
+
+  [ Martin Pitt ]
+  * Non-maintainer upload; this package blocks DeviceKit, and maintainer is
+    apparently not active any more. Also clean up and modernize the package
+    somewhat while we are at it.
+  * New upstream release (required for current devicekit-disks).
+    (Closes: #532546). Upstream original tarball repacked to not contain
+    debian/ directory.
+  * Rename libsgutils1{,-dev} to libsgutils2-2{,-dev}, upstream bumped SONAME.
+    Also call the library libgsutils2-2 to match SONAME. Add
+    Conflicts/Replaces for "libsgutils2" to provide a clean upgrade from the
+    packages as provided by Upstream and Ubuntu.
+  * debian/rules: Update build rules for upstream Makefile → autotools switch.
+  * debian/rules: Fix cleaning a clean source package.
+  * Demote Recommends to Suggests; the library doesn't actually call the
+    binaries in sg3-utils. (Closes: #532547)
+  * Drop debian/*.dirs, unnecessary with dh_install.
+  * Drop sg3-utils.preinst, not necessary to deal with kernel 2.4 any more.
+  * Drop libsgutils2-0.install, libsgutils2-0-dev.install, these packages
+    don't exist.
+  * Drop libsgutils2.post{inst,rm}: Basically empty, debhelper will create its
+    own.
+  * libsgutils2-dev.install: Drop *.lo.
+  * debian/compat: 4 -> 7. Bump debhelper build-depends accordingly.
+  * debian/control: Bump Standards-Version to 3.8.2.
+  * debian/control: Modernize package description for Linux 2.6.
+    (Closes: #506578)
+  * debian/rules: Drop -k argument from dh_clean (thanks lintian).
+
+  [ Frank Lichtenheld ]
+  * debian/control, debian/rules: Add dependency of libsgutils-dev on
+    libcam-dev on kfreebsd-*. (Closes: #519460)
+
+ -- Martin Pitt <mpitt@debian.org>  Mon, 22 Jun 2009 12:04:20 +0200
+
+sg3-utils (1.24-2) unstable; urgency=low
+
+  * Cleaned up package description (Closes: #445920).
+  * Don't make libtool think rpath is necessary (Closes: #451153)
+  * Capitalized Linux in extended description (Closes: #457526).
+  * Completed sentence in libsgutils1 long description (Closes: #421391).
+  * Added patch from Aurelian Jarno to build on kfreebsd-* (Closes: #455430).
+  * Symlinks in examples directory cleaned up (Closes: #372610).
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Sun, 30 Dec 2007 11:52:58 -0700
+
+sg3-utils (1.24-1) unstable; urgency=low
+
+  * New upstream release
+  * Conflicts with upstream libsgutils package libsgutils-1-0 (closes: #391077)
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Tue,  5 Jun 2007 17:04:21 -0600
+
+sg3-utils (1.21-2.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Fix FTBFS due to old syscall usage (Closes: #395512).
+
+ -- Luk Claes <luk@debian.org>  Sun,  5 Nov 2006 17:23:29 +0100
+
+sg3-utils (1.21-2) unstable; urgency=low
+
+  * Added Depends on libsgutils1 to libsgutils1-dev (closes: #387798)
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Fri, 22 Sep 2006 00:20:28 -0600
+
+sg3-utils (1.21-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Wed, 13 Sep 2006 21:54:30 -0600
+
+sg3-utils (1.20-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Wed, 26 Apr 2006 22:31:15 -0600
+
+sg3-utils (1.17-3) unstable; urgency=low
+
+  * Cleaned up sg_read(8) manpage (Closes: #294521)
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Mon, 13 Feb 2006 17:59:46 -0700
+
+sg3-utils (1.17-2) unstable; urgency=low
+
+  * Add libtool to build-depends
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Tue,  4 Oct 2005 19:40:00 -0600
+
+sg3-utils (1.17-1) unstable; urgency=low
+
+  * New upstream version
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Sat,  1 Oct 2005 13:26:16 -0600
+
+sg3-utils (1.08-2) unstable; urgency=low
+
+  * Fix packaging bug that accidentally left off binaries.  Sigh.
+    (closes: #271906)
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Wed, 15 Sep 2004 22:40:06 -0600
+
+sg3-utils (1.08-1) unstable; urgency=low
+
+  * New upstream version
+  * Unified package description with list of tools actually installed
+    (closes: #271093)
+
+ -- Eric Schwartz (Skif) <emschwar@debian.org>  Sun, 12 Sep 2004 21:22:42 -0600
+
+sg3-utils (1.05-1) unstable; urgency=low
+
+  * New upstream release
+  * updated description to match tools in package (closes: #221143)
+
+ -- Eric Schwartz <emschwar@debian.org>  Tue, 18 Nov 2003 22:22:29 -0700
+
+sg3-utils (1.03-1) unstable; urgency=low
+
+  * New upstream release (closes: #181999)
+
+ -- Eric Schwartz <emschwar@debian.org>  Tue, 29 Apr 2003 20:18:30 -0600
+
+sg3-utils (0.95-4) unstable; urgency=low
+
+  * Only warns if installed on a kernel version < 2.4 (closes: #136434)
+      
+ -- Eric Schwartz <emschwar@debian.org>  Tue, 28 May 2002 22:55:29 -0600
+
+sg3-utils (0.95-3) unstable; urgency=low
+
+  * Extended description to include descriptions of all tools included in
+    the package. (closes: #121968)
+
+ -- Eric Schwartz <emschwar@debian.org>  Sun, 13 Jan 2002 17:09:27 -0700
+
+sg3-utils (0.95-2) unstable; urgency=low
+
+  * Packaging manpages (closes: #122692)
+  * Conflicts with cdwrite (closes: #123779)
+
+ -- Eric Schwartz <emschwar@debian.org>  Wed,  2 Jan 2002 01:05:08 -0700
+
+sg3-utils (0.95-1) unstable; urgency=low
+
+  * Initial Release.
+  * Adjusted Makefile to include $DESTDIR
+
+ -- Eric Schwartz <emschwar@debian.org>  Wed, 14 Nov 2001 17:05:56 -0700
+
diff --git a/sg3_utils/debian/compat b/sg3_utils/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/sg3_utils/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/sg3_utils/debian/control b/sg3_utils/debian/control
new file mode 100644
index 0000000..31f582c
--- /dev/null
+++ b/sg3_utils/debian/control
@@ -0,0 +1,42 @@
+Source: sg3-utils
+Section: admin
+Priority: optional
+Maintainer: Eric Schwartz (Skif) <emschwar@debian.org>
+Build-Depends: debhelper (>> 7), libtool, libcam-dev [kfreebsd-i386 kfreebsd-amd64]
+Standards-Version: 3.8.2
+
+Package: sg3-utils
+Architecture: any
+Depends: ${shlibs:Depends}
+Conflicts: sg-utils, cdwrite
+Replaces: sg-utils
+Description: utilities for devices using the SCSI command set.
+ Most OSes have SCSI pass-through interfaces that enable user space programs
+ to send SCSI commands to a device and fetch the response. With SCSI to ATA
+ Translation (SAT) many ATA disks now can process SCSI commands. Typically
+ each utility in this package implements one SCSI command. See the draft
+ standards at www.t10.org for SCSI command definitions plus SAT. ATA
+ commands are defined in the draft standards at www.t13.org . For a mapping
+ between supported SCSI and ATA commands and utility names in this package
+ see the COVERAGE file.
+
+Package: libsgutils2-2
+Section: libs
+Depends: ${shlibs:Depends}
+Architecture: any
+Conflicts: libsgutils2
+Replaces: libsgutils2
+Suggests: sg3-utils
+Description: utilities for devices using the SCSI command set (shared libraries)
+ Shared library used by the utilities in the sg3-utils package.
+
+Package: libsgutils2-dev
+Section: libdevel
+Architecture: any
+Depends: libsgutils2-2 (= ${binary:Version}), ${shlibs:Depends}, ${kfreebsd:Depends}
+Conflicts: libsgutils1-dev
+Suggests: sg3-utils
+Description: utilities for devices using the SCSI command set (developer files)
+ Developer files (i.e. headers and a static library) which are associated with
+ the utilities in the sg3-utils package.
+
diff --git a/sg3_utils/debian/copyright b/sg3_utils/debian/copyright
new file mode 100644
index 0000000..10ebeaf
--- /dev/null
+++ b/sg3_utils/debian/copyright
@@ -0,0 +1,33 @@
+This package was debianized by Eric Schwartz <emschwar@debian.org> on
+Wed, 14 Nov 2001 17:05:56 -0700.
+
+It was downloaded from <URL:http://sg.danny.cz/sg/>
+
+Upstream Authors: Douglas Gilbert <dgilbert at interlog dot com>, 
+		  Bruce Allen  <ballen at gravity dot phys dot uwm dot edu>,
+		  Peter Allworth <linsol at zeta dot org dot au>,
+		  James Bottomley  <jejb at parisc-linux dot org>,
+		  Lars Marowsky-Bree <lmb at suse dot de>,
+		  Kurt Garloff <garloff at suse dot de>,
+		  Grant Grundler  <grundler at parisc-linux dot org>,
+		  Christophe Varoqui <christophe dot varoqui at free dot fr>,
+		  Michael Weller <eowmob at exp-math dot uni-essen dot de>,
+		  Eric Youngdale <eric at andante dot org>,
+
+Copyright:
+
+This software is copyright(c) 1994-2009 by the authors
+
+You are free to distribute this software under the terms of
+the GNU General Public License.
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL file.
+
+Many parts of this package are covered by the BSD license.
+These include central error processing code and common command
+code found in the lib subdirectory. Most newer utilities also
+use the BSD license. The author's intention is that this code can
+be used freely by others. On Debian systems and those derived from
+Debian, the complete text of the BSD License can be found in
+/usr/share/common-licenses/BSD.
+
diff --git a/sg3_utils/debian/docs b/sg3_utils/debian/docs
new file mode 100644
index 0000000..67fe85a
--- /dev/null
+++ b/sg3_utils/debian/docs
@@ -0,0 +1,7 @@
+README
+README.sg_start
+AUTHORS
+COVERAGE
+CREDITS
+INSTALL
+ChangeLog
diff --git a/sg3_utils/debian/libsgutils2-2.install b/sg3_utils/debian/libsgutils2-2.install
new file mode 100644
index 0000000..093956b
--- /dev/null
+++ b/sg3_utils/debian/libsgutils2-2.install
@@ -0,0 +1 @@
+usr/lib/*.so.*
diff --git a/sg3_utils/debian/libsgutils2-dev.install b/sg3_utils/debian/libsgutils2-dev.install
new file mode 100644
index 0000000..5d09f30
--- /dev/null
+++ b/sg3_utils/debian/libsgutils2-dev.install
@@ -0,0 +1,4 @@
+usr/include/scsi
+usr/lib/*.so
+usr/lib/*.la
+usr/lib/*.a
diff --git a/sg3_utils/debian/rules b/sg3_utils/debian/rules
new file mode 100755
index 0000000..d3a2b0c
--- /dev/null
+++ b/sg3_utils/debian/rules
@@ -0,0 +1,83 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper. 
+# GNU copyright 1997 by Joey Hess.
+#
+# This version is for a hypothetical package that builds an
+# architecture-dependant package, as well as an architecture-independent
+# package.
+
+# Uncomment this to turn on verbose mode. 
+# export DH_VERBOSE=1
+
+DEB_HOST_ARCH_OS := $(shell dpkg-architecture -qDEB_HOST_ARCH_OS 2>/dev/null)
+
+configure: configure-stamp
+configure-stamp:
+	dh_testdir
+	# Add here commands to configure the package.
+	CFLAGS="$(CFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --bindir=/usr/bin --prefix=/usr --mandir=\$${prefix}/share/man
+	touch configure-stamp
+
+build: configure-stamp build-stamp
+build-stamp:
+	dh_testdir
+
+	# Add here commands to compile the package.
+	PREFIX=/usr MANDIR=/usr/share/man $(MAKE) -e
+
+	touch build-stamp
+
+clean:
+	dh_testdir
+	dh_testroot
+
+	# Add here commands to clean up after the build process.
+	-$(MAKE) distclean
+
+	rm -f build-stamp configure-stamp debian/substvars
+
+	dh_clean
+
+install: DH_OPTIONS=
+install: build
+	dh_testdir
+	dh_testroot
+	dh_clean
+	dh_installdirs
+
+	# Add here commands to install the package into debian/tmp
+	$(MAKE) -e install DESTDIR=$(CURDIR)/debian/tmp PREFIX=/usr
+
+	dh_install --autodest --sourcedir=debian/tmp
+
+	dh_installman
+
+# Build architecture-independent files here.
+# Pass -i to all debhelper commands in this target to reduce clutter.
+binary-indep: build install
+# nothing to do here
+
+# Build architecture-dependent files here.
+binary-arch: build install
+	dh_testdir -a
+	dh_testroot -a
+	dh_installdocs -a
+	dh_installexamples -a 
+	dh_installmenu -a
+	dh_installchangelogs ChangeLog -a
+	dh_strip -a
+	dh_link -a
+	dh_compress -a -X archive -X .c -X .h
+	dh_fixperms -a
+	dh_makeshlibs -V -v
+	dh_installdeb -a
+ifeq ($(DEB_HOST_ARCH_OS),kfreebsd)
+	echo kfreebsd:Depends=libcam-dev >>debian/libsgutils2-dev.substvars
+endif
+	dh_shlibdeps -ldebian/tmp/usr/lib -L libsgutils2
+	dh_gencontrol -a
+	dh_md5sums -a
+	dh_builddeb -a
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/sg3_utils/debian/sg3-utils.examples b/sg3_utils/debian/sg3-utils.examples
new file mode 100644
index 0000000..a9da205
--- /dev/null
+++ b/sg3_utils/debian/sg3-utils.examples
@@ -0,0 +1,2 @@
+examples/*
+archive
diff --git a/sg3_utils/debian/sg3-utils.install b/sg3_utils/debian/sg3-utils.install
new file mode 100644
index 0000000..6161376
--- /dev/null
+++ b/sg3_utils/debian/sg3-utils.install
@@ -0,0 +1,2 @@
+usr/bin/*
+usr/share/man/man8/*
diff --git a/sg3_utils/depcomp b/sg3_utils/depcomp
new file mode 100755
index 0000000..fc98710
--- /dev/null
+++ b/sg3_utils/depcomp
@@ -0,0 +1,791 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2013-05-30.07; # UTC
+
+# Copyright (C) 1999-2014 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+  '')
+    echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+    exit 1;
+    ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+  depmode     Dependency tracking mode.
+  source      Source file read by 'PROGRAMS ARGS'.
+  object      Object file output by 'PROGRAMS ARGS'.
+  DEPDIR      directory where to store dependencies.
+  depfile     Dependency file to output.
+  tmpdepfile  Temporary file to use when outputting dependencies.
+  libtool     Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "depcomp $scriptversion"
+    exit $?
+    ;;
+esac
+
+# Get the directory component of the given path, and save it in the
+# global variables '$dir'.  Note that this directory component will
+# be either empty or ending with a '/' character.  This is deliberate.
+set_dir_from ()
+{
+  case $1 in
+    */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
+      *) dir=;;
+  esac
+}
+
+# Get the suffix-stripped basename of the given path, and save it the
+# global variable '$base'.
+set_base_from ()
+{
+  base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
+}
+
+# If no dependency file was actually created by the compiler invocation,
+# we still have to create a dummy depfile, to avoid errors with the
+# Makefile "include basename.Plo" scheme.
+make_dummy_depfile ()
+{
+  echo "#dummy" > "$depfile"
+}
+
+# Factor out some common post-processing of the generated depfile.
+# Requires the auxiliary global variable '$tmpdepfile' to be set.
+aix_post_process_depfile ()
+{
+  # If the compiler actually managed to produce a dependency file,
+  # post-process it.
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form 'foo.o: dependency.h'.
+    # Do two passes, one to just change these to
+    #   $object: dependency.h
+    # and one to simply output
+    #   dependency.h:
+    # which is needed to avoid the deleted-header problem.
+    { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
+      sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
+    } > "$depfile"
+    rm -f "$tmpdepfile"
+  else
+    make_dummy_depfile
+  fi
+}
+
+# A tabulation character.
+tab='	'
+# A newline character.
+nl='
+'
+# Character ranges might be problematic outside the C locale.
+# These definitions help.
+upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
+lower=abcdefghijklmnopqrstuvwxyz
+digits=0123456789
+alpha=${upper}${lower}
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Avoid interferences from the environment.
+gccflag= dashmflag=
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+  # This is just like dashmstdout with a different argument.
+  dashmflag=-xM
+  depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+  # This is just like msvisualcpp but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvisualcpp
+fi
+
+if test "$depmode" = msvc7msys; then
+  # This is just like msvc7 but w/o cygpath translation.
+  # Just convert the backslash-escaped backslashes to single forward
+  # slashes to satisfy depend.m4
+  cygpath_u='sed s,\\\\,/,g'
+  depmode=msvc7
+fi
+
+if test "$depmode" = xlc; then
+  # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
+  gccflag=-qmakedep=gcc,-MF
+  depmode=gcc
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am.  Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+  for arg
+  do
+    case $arg in
+    -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+    *)  set fnord "$@" "$arg" ;;
+    esac
+    shift # fnord
+    shift # $arg
+  done
+  "$@"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
+## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
+## (see the conditional assignment to $gccflag above).
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).  Also, it might not be
+##   supported by the other compilers which use the 'gcc' depmode.
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The second -e expression handles DOS-style file names with drive
+  # letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the "deleted header file" problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+## Some versions of gcc put a space before the ':'.  On the theory
+## that the space means something, we add a space to the output as
+## well.  hp depmode also adds that space, but also prefixes the VPATH
+## to the object.  Take care to not repeat it in the output.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like '#:fec' to the end of the
+    # dependency line.
+    tr ' ' "$nl" < "$tmpdepfile" \
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
+      | tr "$nl" ' ' >> "$depfile"
+    echo >> "$depfile"
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' "$nl" < "$tmpdepfile" \
+      | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+      >> "$depfile"
+  else
+    make_dummy_depfile
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+xlc)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  In older versions, this file always lives in the
+  # current directory.  Also, the AIX compiler puts '$object:' at the
+  # start of each line; $object doesn't have directory information.
+  # Version 6 uses the directory in both cases.
+  set_dir_from "$object"
+  set_base_from "$object"
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$base.u
+    tmpdepfile3=$dir.libs/$base.u
+    "$@" -Wc,-M
+  else
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$dir$base.u
+    tmpdepfile3=$dir$base.u
+    "$@" -M
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  aix_post_process_depfile
+  ;;
+
+tcc)
+  # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
+  # FIXME: That version still under development at the moment of writing.
+  #        Make that this statement remains true also for stable, released
+  #        versions.
+  # It will wrap lines (doesn't matter whether long or short) with a
+  # trailing '\', as in:
+  #
+  #   foo.o : \
+  #    foo.c \
+  #    foo.h \
+  #
+  # It will put a trailing '\' even on the last line, and will use leading
+  # spaces rather than leading tabs (at least since its commit 0394caf7
+  # "Emit spaces for -MD").
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
+  # We have to change lines of the first kind to '$object: \'.
+  sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
+  # And for each line of the second kind, we have to emit a 'dep.h:'
+  # dummy dependency, to avoid the deleted-header problem.
+  sed -n -e 's|^  *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+## The order of this option in the case statement is important, since the
+## shell code in configure will try each of these formats in the order
+## listed in this file.  A plain '-MD' option would be understood by many
+## compilers, so we must ensure this comes after the gcc and icc options.
+pgcc)
+  # Portland's C compiler understands '-MD'.
+  # Will always output deps to 'file.d' where file is the root name of the
+  # source file under compilation, even if file resides in a subdirectory.
+  # The object file name does not affect the name of the '.d' file.
+  # pgcc 10.2 will output
+  #    foo.o: sub/foo.c sub/foo.h
+  # and will wrap long lines using '\' :
+  #    foo.o: sub/foo.c ... \
+  #     sub/foo.h ... \
+  #     ...
+  set_dir_from "$object"
+  # Use the source, not the object, to determine the base name, since
+  # that's sadly what pgcc will do too.
+  set_base_from "$source"
+  tmpdepfile=$base.d
+
+  # For projects that build the same source file twice into different object
+  # files, the pgcc approach of using the *source* file root name can cause
+  # problems in parallel builds.  Use a locking strategy to avoid stomping on
+  # the same $tmpdepfile.
+  lockdir=$base.d-lock
+  trap "
+    echo '$0: caught signal, cleaning up...' >&2
+    rmdir '$lockdir'
+    exit 1
+  " 1 2 13 15
+  numtries=100
+  i=$numtries
+  while test $i -gt 0; do
+    # mkdir is a portable test-and-set.
+    if mkdir "$lockdir" 2>/dev/null; then
+      # This process acquired the lock.
+      "$@" -MD
+      stat=$?
+      # Release the lock.
+      rmdir "$lockdir"
+      break
+    else
+      # If the lock is being held by a different process, wait
+      # until the winning process is done or we timeout.
+      while test -d "$lockdir" && test $i -gt 0; do
+        sleep 1
+        i=`expr $i - 1`
+      done
+    fi
+    i=`expr $i - 1`
+  done
+  trap - 1 2 13 15
+  if test $i -le 0; then
+    echo "$0: failed to acquire lock after $numtries attempts" >&2
+    echo "$0: check lockdir '$lockdir'" >&2
+    exit 1
+  fi
+
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each line is of the form `foo.o: dependent.h',
+  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+  # Do two passes, one to just change these to
+  # `$object: dependent.h' and one to simply `dependent.h:'.
+  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp2)
+  # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+  # compilers, which have integrated preprocessors.  The correct option
+  # to use with these is +Maked; it writes dependencies to a file named
+  # 'foo.d', which lands next to the object file, wherever that
+  # happens to be.
+  # Much of this is similar to the tru64 case; see comments there.
+  set_dir_from  "$object"
+  set_base_from "$object"
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir.libs/$base.d
+    "$@" -Wc,+Maked
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    "$@" +Maked
+  fi
+  stat=$?
+  if test $stat -ne 0; then
+     rm -f "$tmpdepfile1" "$tmpdepfile2"
+     exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
+    # Add 'dependent.h:' lines.
+    sed -ne '2,${
+               s/^ *//
+               s/ \\*$//
+               s/$/:/
+               p
+             }' "$tmpdepfile" >> "$depfile"
+  else
+    make_dummy_depfile
+  fi
+  rm -f "$tmpdepfile" "$tmpdepfile2"
+  ;;
+
+tru64)
+  # The Tru64 compiler uses -MD to generate dependencies as a side
+  # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
+  # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+  # dependencies in 'foo.d' instead, so we check for that too.
+  # Subdirectories are respected.
+  set_dir_from  "$object"
+  set_base_from "$object"
+
+  if test "$libtool" = yes; then
+    # Libtool generates 2 separate objects for the 2 libraries.  These
+    # two compilations output dependencies in $dir.libs/$base.o.d and
+    # in $dir$base.o.d.  We have to check for both files, because
+    # one of the two compilations can be disabled.  We should prefer
+    # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+    # automatically cleaned when .libs/ is deleted, while ignoring
+    # the former would cause a distcleancheck panic.
+    tmpdepfile1=$dir$base.o.d          # libtool 1.5
+    tmpdepfile2=$dir.libs/$base.o.d    # Likewise.
+    tmpdepfile3=$dir.libs/$base.d      # Compaq CCC V6.2-504
+    "$@" -Wc,-MD
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    tmpdepfile3=$dir$base.d
+    "$@" -MD
+  fi
+
+  stat=$?
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  # Same post-processing that is required for AIX mode.
+  aix_post_process_depfile
+  ;;
+
+msvc7)
+  if test "$libtool" = yes; then
+    showIncludes=-Wc,-showIncludes
+  else
+    showIncludes=-showIncludes
+  fi
+  "$@" $showIncludes > "$tmpdepfile"
+  stat=$?
+  grep -v '^Note: including file: ' "$tmpdepfile"
+  if test $stat -ne 0; then
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The first sed program below extracts the file names and escapes
+  # backslashes for cygpath.  The second sed program outputs the file
+  # name when reading, but also accumulates all include files in the
+  # hold buffer in order to output them again at the end.  This only
+  # works with sed implementations that can handle large buffers.
+  sed < "$tmpdepfile" -n '
+/^Note: including file:  *\(.*\)/ {
+  s//\1/
+  s/\\/\\\\/g
+  p
+}' | $cygpath_u | sort -u | sed -n '
+s/ /\\ /g
+s/\(.*\)/'"$tab"'\1 \\/p
+s/.\(.*\) \\/\1:/
+H
+$ {
+  s/.*/'"$tab"'/
+  G
+  p
+}' >> "$depfile"
+  echo >> "$depfile" # make sure the fragment doesn't end with a backslash
+  rm -f "$tmpdepfile"
+  ;;
+
+msvc7msys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove '-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  test -z "$dashmflag" && dashmflag=-M
+  # Require at least two characters before searching for ':'
+  # in the target name.  This is to cope with DOS-style filenames:
+  # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
+  "$@" $dashmflag |
+    sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this sed invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  tr ' ' "$nl" < "$tmpdepfile" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  "$@" || exit $?
+  # Remove any Libtool call
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+  # X makedepend
+  shift
+  cleared=no eat=no
+  for arg
+  do
+    case $cleared in
+    no)
+      set ""; shift
+      cleared=yes ;;
+    esac
+    if test $eat = yes; then
+      eat=no
+      continue
+    fi
+    case "$arg" in
+    -D*|-I*)
+      set fnord "$@" "$arg"; shift ;;
+    # Strip any option that makedepend may not understand.  Remove
+    # the object too, otherwise makedepend will parse it as a source file.
+    -arch)
+      eat=yes ;;
+    -*|$object)
+      ;;
+    *)
+      set fnord "$@" "$arg"; shift ;;
+    esac
+  done
+  obj_suffix=`echo "$object" | sed 's/^.*\././'`
+  touch "$tmpdepfile"
+  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  rm -f "$depfile"
+  # makedepend may prepend the VPATH from the source file name to the object.
+  # No need to regex-escape $object, excess matching of '.' is harmless.
+  sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process the last invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed '1,2d' "$tmpdepfile" \
+    | tr ' ' "$nl" \
+    | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+    | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove '-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  "$@" -E \
+    | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+             -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+    | sed '$ s: \\$::' > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  IFS=" "
+  for arg
+  do
+    case "$arg" in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+        set fnord "$@"
+        shift
+        shift
+        ;;
+    *)
+        set fnord "$@" "$arg"
+        shift
+        shift
+        ;;
+    esac
+  done
+  "$@" -E 2>/dev/null |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
+  echo "$tab" >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvcmsys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/sg3_utils/doc/Makefile.am b/sg3_utils/doc/Makefile.am
new file mode 100644
index 0000000..5111b35
--- /dev/null
+++ b/sg3_utils/doc/Makefile.am
@@ -0,0 +1,41 @@
+
+man_MANS = \
+	scsi_mandat.8 scsi_readcap.8 scsi_ready.8 scsi_satl.8 scsi_start.8 \
+	scsi_stop.8 scsi_temperature.8 sg3_utils.8 sg_compare_and_write.8 \
+	sg_decode_sense.8 sg_format.8 sg_get_config.8 sg_get_lba_status.8 \
+	sg_ident.8 sg_inq.8 sg_logs.8 sg_luns.8 sg_modes.8 sg_opcodes.8 \
+	sg_persist.8 sg_prevent.8 sg_raw.8 sg_rdac.8 sg_read_attr.8 \
+	sg_read_block_limits.8 sg_read_buffer.8 sg_read_long.8 sg_readcap.8 \
+	sg_reassign.8 sg_referrals.8 sg_rep_zones.8 sg_requests.8 \
+	sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 sg_sanitize.8 \
+	sg_sat_identify.8 sg_sat_phy_event.8 sg_sat_read_gplog.8 \
+	sg_sat_set_features.8 sg_senddiag.8 sg_ses.8 sg_ses_microcode.8 \
+	sg_start.8 sg_stpg.8 sg_sync.8 sg_timestamp.8 sg_turs.8 sg_unmap.8 \
+	sg_verify.8 sg_vpd.8 sg_wr_mode.8 sg_write_buffer.8 sg_write_long.8 \
+	sg_write_same.8 sg_write_verify.8 sg_zone.8
+CLEANFILES =
+
+if OS_LINUX
+man_MANS += \
+	rescan-scsi-bus.sh.8 scsi_logging_level.8 sg_copy_results.8 sg_dd.8 \
+	sg_emc_trespass.8 sg_map.8 sg_map26.8 sg_rbuf.8 sg_read.8 sg_reset.8 \
+	sg_scan.8 sg_test_rwbuf.8 sg_xcopy.8 sginfo.8 sgm_dd.8 sgp_dd.8
+CLEANFILES += sg_scan.8
+sg_scan.8: sg_scan.8.linux
+	cp -p $< $@
+endif
+
+
+if OS_WIN32_MINGW
+man_MANS += sg_scan.8
+CLEANFILES += sg_scan.8
+sg_scan.8: sg_scan.8.win32
+	cp -p $< $@
+endif
+
+if OS_WIN32_CYGWIN
+man_MANS += sg_scan.8
+CLEANFILES += sg_scan.8
+sg_scan.8: sg_scan.8.win32
+	cp -p $< $@
+endif
diff --git a/sg3_utils/doc/Makefile.in b/sg3_utils/doc/Makefile.in
new file mode 100644
index 0000000..ac1bbf5
--- /dev/null
+++ b/sg3_utils/doc/Makefile.in
@@ -0,0 +1,548 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@OS_LINUX_TRUE@am__append_1 = \
+@OS_LINUX_TRUE@	rescan-scsi-bus.sh.8 scsi_logging_level.8 sg_copy_results.8 sg_dd.8 \
+@OS_LINUX_TRUE@	sg_emc_trespass.8 sg_map.8 sg_map26.8 sg_rbuf.8 sg_read.8 sg_reset.8 \
+@OS_LINUX_TRUE@	sg_scan.8 sg_test_rwbuf.8 sg_xcopy.8 sginfo.8 sgm_dd.8 sgp_dd.8
+
+@OS_LINUX_TRUE@am__append_2 = sg_scan.8
+@OS_WIN32_MINGW_TRUE@am__append_3 = sg_scan.8
+@OS_WIN32_MINGW_TRUE@am__append_4 = sg_scan.8
+@OS_WIN32_CYGWIN_TRUE@am__append_5 = sg_scan.8
+@OS_WIN32_CYGWIN_TRUE@am__append_6 = sg_scan.8
+subdir = doc
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+man8dir = $(mandir)/man8
+am__installdirs = "$(DESTDIR)$(man8dir)"
+NROFF = nroff
+MANS = $(man_MANS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETOPT_O_FILES = @GETOPT_O_FILES@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+man_MANS = scsi_mandat.8 scsi_readcap.8 scsi_ready.8 scsi_satl.8 \
+	scsi_start.8 scsi_stop.8 scsi_temperature.8 sg3_utils.8 \
+	sg_compare_and_write.8 sg_decode_sense.8 sg_format.8 \
+	sg_get_config.8 sg_get_lba_status.8 sg_ident.8 sg_inq.8 \
+	sg_logs.8 sg_luns.8 sg_modes.8 sg_opcodes.8 sg_persist.8 \
+	sg_prevent.8 sg_raw.8 sg_rdac.8 sg_read_attr.8 \
+	sg_read_block_limits.8 sg_read_buffer.8 sg_read_long.8 \
+	sg_readcap.8 sg_reassign.8 sg_referrals.8 sg_rep_zones.8 \
+	sg_requests.8 sg_reset_wp.8 sg_rmsn.8 sg_rtpg.8 sg_safte.8 \
+	sg_sanitize.8 sg_sat_identify.8 sg_sat_phy_event.8 \
+	sg_sat_read_gplog.8 sg_sat_set_features.8 sg_senddiag.8 \
+	sg_ses.8 sg_ses_microcode.8 sg_start.8 sg_stpg.8 sg_sync.8 \
+	sg_timestamp.8 sg_turs.8 sg_unmap.8 sg_verify.8 sg_vpd.8 \
+	sg_wr_mode.8 sg_write_buffer.8 sg_write_long.8 sg_write_same.8 \
+	sg_write_verify.8 sg_zone.8 $(am__append_1) $(am__append_3) \
+	$(am__append_5)
+CLEANFILES = $(am__append_2) $(am__append_4) $(am__append_6)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu doc/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+install-man8: $(man_MANS)
+	@$(NORMAL_INSTALL)
+	@list1=''; \
+	list2='$(man_MANS)'; \
+	test -n "$(man8dir)" \
+	  && test -n "`echo $$list1$$list2`" \
+	  || exit 0; \
+	echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+	$(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+	{ for i in $$list1; do echo "$$i"; done;  \
+	if test -n "$$list2"; then \
+	  for i in $$list2; do echo "$$i"; done \
+	    | sed -n '/\.8[a-z]*$$/p'; \
+	fi; \
+	} | while read p; do \
+	  if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; echo "$$p"; \
+	done | \
+	sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+	sed 'N;N;s,\n, ,g' | { \
+	list=; while read file base inst; do \
+	  if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+	    echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+	    $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+	  fi; \
+	done; \
+	for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+	while read files; do \
+	  test -z "$$files" || { \
+	    echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+	    $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+	done; }
+
+uninstall-man8:
+	@$(NORMAL_UNINSTALL)
+	@list=''; test -n "$(man8dir)" || exit 0; \
+	files=`{ for i in $$list; do echo "$$i"; done; \
+	l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+	  sed -n '/\.8[a-z]*$$/p'; \
+	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+	dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(MANS)
+installdirs:
+	for dir in "$(DESTDIR)$(man8dir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-man
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+	cscopelist-am ctags-am distclean distclean-generic \
+	distclean-libtool distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-man8 install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+	ps ps-am tags-am uninstall uninstall-am uninstall-man \
+	uninstall-man8
+
+.PRECIOUS: Makefile
+
+@OS_LINUX_TRUE@sg_scan.8: sg_scan.8.linux
+@OS_LINUX_TRUE@	cp -p $< $@
+@OS_WIN32_MINGW_TRUE@sg_scan.8: sg_scan.8.win32
+@OS_WIN32_MINGW_TRUE@	cp -p $< $@
+@OS_WIN32_CYGWIN_TRUE@sg_scan.8: sg_scan.8.win32
+@OS_WIN32_CYGWIN_TRUE@	cp -p $< $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/sg3_utils/doc/README b/sg3_utils/doc/README
new file mode 100644
index 0000000..8920562
--- /dev/null
+++ b/sg3_utils/doc/README
@@ -0,0 +1,34 @@
+Various html files used to be included in this directory but they were
+beginning to bloat the size of the source tarball so they have been
+removed in version 1.24 .
+
+Here are the urls of the files that were previously here plus a brief
+summary:
+
+http://sg.danny.cz/sg/sg3_utils.html
+    - overview and examples of this package
+
+http://sg.danny.cz/sg/sg_dd.html
+    - discussion and examples of the sg_dd utility which is a dd variant
+      (also discusses the sgm_dd, sgp_dd and sg_read utilities)
+
+http://sg.danny.cz/sg/sg_ses.html
+    - discussion and examples of the sg_ses utility. SCSI Enclosure
+      Services (SES) devices may contain a lot of information,
+      structured in a non-trivial way.
+
+http://sg.danny.cz/sg/sg_io.html
+    - discussion of Linux SG_IO ioctl (SCSI pass-through)
+
+http://sg.danny.cz/sg/tools.html
+    - overview of around 25 storage and SCSI tools
+
+
+There are two versions of sg_scan: one for Linux and the other for Windows.
+They have different man pages: sg_scan.8.linux and sg_scan.8.win32 with
+the Makefile logic (rule in Makefile.am) copying the appropriate one to
+sg_scan.8 . sg_scan is not supported for other ports.
+
+
+Douglas Gilbert
+10th February 2010
diff --git a/sg3_utils/doc/rescan-scsi-bus.sh.8 b/sg3_utils/doc/rescan-scsi-bus.sh.8
new file mode 100644
index 0000000..a5eb7c4
--- /dev/null
+++ b/sg3_utils/doc/rescan-scsi-bus.sh.8
@@ -0,0 +1,112 @@
+.TH RESCAN-SCSI-BUS.SH "1" "January 2016" "rescan-scsi-bus.sh" "User Commands"
+.SH NAME
+rescan-scsi-bus.sh \- script to add and remove SCSI devices without rebooting
+.SH SYNOPSIS
+.B rescan-scsi-bus.sh
+[\fI\-\-alltargets\fR] [\fI\-\-attachpq3\fR] [\fI\-c\fR] [\fI\-\-color\fR]
+[\fI\-\--channels=CLIST\fR] [\fI\-d\fR] [\fI\-\-flush\fR]
+[\fI\-\-forceremove\fR] [\fI\-\-forcerescan\fR] [\fI\-\-help\fR]
+[\fI\-\-hosts=HLIST\fR] [\fI\-\-ids=TLIST\fR] [\fI\-\-issue\-lip\fR]
+[\fI\-l\fR] [\fI\-L NUM\fR] [\fI\-\-luns=LLIST\fR] [\fI\-\-multipath\fR]
+[\fI\-\-nooptscan\fR] [\fI\-\-remove\fR] [\fI\-\-removelun2\fR]
+[\fI\-\-resize\fR] [\fI\-\-sparselun\fR] [\fI\-\-sync\fR] [\fI\-\-update\fR]
+[\fI\-\-version\fR] [\fI\-\-wide\fR] [\fIHOST1 \fR[\fIHOST2 \fR...]]
+.SH OPTIONS
+.TP
+\fB\-a\fR, \fB\-\-alltargets\fR
+scan all targets, not just currently existing [default: disabled]
+.TP
+\fB\-\-attachpq3\fR
+tell kernel to attach sg to LUN 0 that reports PQ=3
+.TP
+\fB\-c\fR
+enables scanning of channels 0 1   [default: 0 / all detected ones]
+.TP
+\fB\-\-color\fR
+use coloured prefixes OLD/NEW/DEL
+.TP
+\fB\-\-channels\fR=\fICLIST\fR
+scan only channel(s) in \fICLIST\fR
+.TP
+\fB\-d\fR
+enable debug                       [default: 0]
+.TP
+\fB\-f\fR, \fB\-\-flush\fR
+flush failed multipath devices     [default: disabled]
+.TP
+\fB\-\-forceremove\fR
+remove and readd every device (DANGEROUS)
+.TP
+\fB\-\-forcerescan\fR
+rescan existing devices
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print usage message then exit
+.TP
+\fB\-\-hosts\fR=\fIHLIST\fR
+scan only host(s) in \fIHLIST\fR
+.TP
+\fB\-\-ids\fR=\fITLIST\fR
+scan only target ID(s) in \fITLIST\fR
+.TP
+\fB\-i\fR, \fB\-\-issue\-lip\fR
+issue a FibreChannel LIP reset     [default: disabled]
+.TP
+\fB\-l\fR
+activates scanning for LUNs 0\-\-7   [default: 0]
+.TP
+\fB\-L\fR NUM
+activates scanning for LUNs 0\-\-NUM [default: 0]
+.TP
+\fB\-\-largelun\fR
+tell kernel to support LUNs > 7 even on SCSI2 devs
+.TP
+\fB\-\-luns\fR=\fINLIST\fR
+scan only lun(s) in \fINLIST\fR
+.TP
+\fB\-m\fR, \fB\-\-multipath\fR
+update multipath devices           [default: disabled]
+.TP
+\fB\-\-nooptscan\fR
+don't stop looking for LUNs is 0 is not found
+.TP
+\fB\-r\fR, \fB\-\-remove\fR
+enables removing of devices        [default: disabled]
+.TP
+\fB\-\-reportlun2\fR
+tell kernel to try REPORT_LUN even on SCSI2 devices
+.TP
+\fB\-s\fR, \fB\-\-resize\fR
+look for resized disks and reload associated multipath devices, if applicable
+.TP
+\fB\-\-sparselun\fR
+tell kernel to support sparse LUN numbering
+.TP
+\fB\-\-sync\fR, \fB\-\-nosync\fR
+issue a sync / no sync [default: sync if remove]
+.TP
+\fB\-u\fR, \fB\-\-update\fR
+look for existing disks that have been remapped
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+shows version string then exits. The version string is a numeric datestamp
+of the form YYYYMMDD.
+.TP
+\fB\-w\fR, \fB\-\-wide\fR
+scan for target device IDs 0\-\-15   [default: 0\-\-7]
+.IP
+Host numbers may thus be specified either directly on cmd line (deprecated) or
+or with the \fB\-\-hosts\fR=\fILIST\fR parameter (recommended).
+.PP
+Arguments to options that end in \fILIST\fR (e.g. \fITLIST\fR) can have this
+form:
+.br
+    A[\-B][,C[\-D]]...
+.br
+which is a comma separated list of single values and/or ranges (no spaces
+allowed).
+.SH SEE ALSO
+\fBrescan-scsi-bus.sh\fR Homepage:
+\fBhttp://www.garloff.de/kurt/linux/#rescan-scsi\fR
+.PP
+\fBsg3_utils\fR Homepage: \fBhttp://sg.danny.cz/sg\fR
diff --git a/sg3_utils/doc/scsi_logging_level.8 b/sg3_utils/doc/scsi_logging_level.8
new file mode 100644
index 0000000..10e946b
--- /dev/null
+++ b/sg3_utils/doc/scsi_logging_level.8
@@ -0,0 +1,119 @@
+.TH SCSI_LOGGING_LEVEL "8" "January 2014" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+scsi_logging_level \- access Linux SCSI logging level information
+.SH SYNOPSIS
+.B scsi_logging_level
+[\fI\-\-all=LEV\fR] [\fI\-\-create\fR] [\fI\-\-error=LEV\fR] [\fI\-\-get\fR]
+[\fI\-\-help\fR] [\fI\-\-highlevel=LEV\fR] [\fI\-\-hlcomplete=LEV\fR]
+[\fI\-\-hlqueue=LEV\fR] [\fI\-\-ioctl=LEV\fR] [\fI\-\-llcomplete=LEV\fR]
+[\fI\-\-llqueue=LEV\fR] [\fI\-\-lowlevel=LEV\fR] [\fI\-\-midlevel=LEV\fR]
+[\fI\-\-mlcomplete=LEV\fR] [\fI\-\-mlqueue=LEV\fR] [\fI\-\-scan=LEV\fR]
+[\fI\-\-set\fR] [\fI\-\-timeout=LEV\fR] [\fI\-\-version\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This bash shell script accesses the Linux SCSI subsystem logging
+level. The current values can be shown (e.g. with \fI\-\-get\fR)
+or changed (e.g. with \fI\-\-set\fR). Superuser permissions will
+typically be required to set the logging level.
+.PP
+One of these options: \fI\-\-create\fR, \fI\-\-get\fR or \fI\-\-set\fR
+is required. Only one of them can be given.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-a\fR, \fB\-\-all\fR=\fILEV\fR
+\fILEV\fR is used for all SCSI_LOG fields.
+.TP
+\fB\-c\fR, \fB\-\-create\fR
+Options are parsed and placed in internal fields that are displayed but
+no logging levels are changed within the Linux kernel.
+.TP
+\fB\-E\fR, \fB\-\-error\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_ERROR field.
+.TP
+\fB\-g\fR, \fB\-\-get\fR
+Fetches the current SCSI logging levels from the Linux kernel and
+displays them.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-highlevel\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_HLQUEUE and SCSI_LOG_HLCOMPLETE fields.
+.TP
+\fB\-\-hlcomplete\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_HLCOMPLETE field.
+.TP
+\fB\-\-hlqueue\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_HLQUEUE field.
+.TP
+\fB\-I\fR, \fB\-\-ioctl\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_IOCTL field.
+.TP
+\fB\-\-llcomplete\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_LLCOMPLETE field.
+.TP
+\fB\-\-llqueue\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_LLQUEUE field.
+.TP
+\fB\-L\fR, \fB\-\-lowlevel\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_LLQUEUE and SCSI_LOG_LLCOMPLETE fields.
+.TP
+\fB\-M\fR, \fB\-\-midlevel\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_MLQUEUE and SCSI_LOG_MLCOMPLETE fields.
+.TP
+\fB\-\-mlcomplete\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_MLCOMPLETE field.
+.TP
+\fB\-\-mlqueue\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_MLQUEUE field.
+.TP
+\fB\-S\fR, \fB\-\-scan\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_SCAN field.
+.TP
+\fB\-s\fR, \fB\-\-set\fR
+Uses the fields specified in this command's options and attempts to
+apply them to the Linux SCSI subsystem logging levels. Typically superuser
+permissions will be required to do this.
+.TP
+\fB\-T\fR, \fB\-\-timeout\fR=\fILEV\fR
+\fILEV\fR is placed in the SCSI_LOG_TIMEOUT field.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Outputs the version information and then exits.
+.SH NOTES
+The \fI\-\-get\fR and \fI\-\-set\fR options access the
+/proc/sys/dev/scsi/logging_level pseudo file.
+.SH EXIT STATUS
+The exit status of this script is 0 when it is successful. Any other
+exit status indicates that an error has occurred.
+.SH EXAMPLES
+The following will set SCSI_LOG_ERROR to level 5 in the Linux kernel. It
+requires root permissions:
+.PP
+  scsi_logging_level \-s \-E 5
+.PP
+So as to not interfere with other SCSI subsystem upper level drivers (ULDs)
+which most likely will be active at the same time, the Linux sg driver uses
+SCSI_LOG_TIMEOUT for logging purposes. To see full debugging and trace from
+the sg driver use:
+.PP
+  scsi_logging_level \-s \-T 7
+.PP
+The output from the sg driver caused by this will go to the system
+logs (e.g. /var/log/syslog). To reduce the amount of output use a number
+lower than 7. Using 0 will turn off the tracing and debug.
+.SH AUTHORS
+Written by IBM. Small alterations by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co IBM Corp. 2006
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.PP
+The software was obtained from an IBM package called s390\-tools\-1.6.2
+found on that company's "developerworks" site. The most recent version of
+that package at this time is 1.8.3 .
diff --git a/sg3_utils/doc/scsi_mandat.8 b/sg3_utils/doc/scsi_mandat.8
new file mode 100644
index 0000000..7606861
--- /dev/null
+++ b/sg3_utils/doc/scsi_mandat.8
@@ -0,0 +1,44 @@
+.TH SCSI_MANDAT "8" "May 2013" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+scsi_mandat \- check SCSI device support for mandatory commands
+.SH SYNOPSIS
+.B scsi_mandat
+[\fI\-\-help\fR] [\fI\-\-log\fR] [\fI\-\-quiet\fR] [\fI\-\-verbose\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This bash shell script calls several SCSI commands on the given
+\fIDEVICE\fR. These SCSI commands are considered mandatory (although
+that varies a little depending on which standard/draft the \fIDEVICE\fR
+complies with). The results of each test and a pass/fail count are
+output.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-L\fR, \fB\-\-log\fR
+the output to stderr (from each SCSI command executed) is appended to
+a file called 'scsi_mandat.err' in the current working directory.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+the amount of output is reduced and typically only the pass/fail
+count is output.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level or verbosity.
+.SH EXIT STATUS
+The exit status of this script is the number of "bad" errors found.
+So an exit status of 0 means all mandatory SCSI commands worked as
+expected.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2011\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq,sg_luns,sg_turs,sg_requests,sg_vpd,sg_senddiag (sg3_utils)
diff --git a/sg3_utils/doc/scsi_readcap.8 b/sg3_utils/doc/scsi_readcap.8
new file mode 100644
index 0000000..40532b0
--- /dev/null
+++ b/sg3_utils/doc/scsi_readcap.8
@@ -0,0 +1,51 @@
+.TH SCSI_READCAP "8" "May 2013" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+scsi_readcap \- do SCSI READ CAPACITY command on disks
+.SH SYNOPSIS
+.B scsi_readcap
+[\fI\-\-brief\fR] [\fI\-\-help\fR] [\fI\-\-long\fR] [\fI\-\-verbose\fR]
+\fIDEVICE\fR [\fIDEVICE\fR]*
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This bash shell script calls the sg_readcap utility on each given
+\fIDEVICE\fR. This will send a SCSI READ CAPACITY command to each
+\fIDEVICE\fR.
+.PP
+The default action of this script is to send the 10 byte cdb READ
+CAPACITY(10) command to each \fIDEVICE\fR. If a response indicates
+the number of blocks is greater than or equal to '2**32 \- 1' then
+the READ CAPACITY(16) is sent and its response is output.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-brief\fR
+shortens the output to two hexadecimal numbers, both prefixed by '0x'.
+The first number is the number of blocks available and the second is
+the size of each blocks in bytes (e.g. '0x12a19eb0 0x200'). If an error
+is detected '0x0 0x0' is output and the script continues if there are
+more \fIDEVICE\fRs.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-l\fR, \fB\-\-long\fR
+the default is to send the READ CAPACITY(10) command (i.e. the 10 byte
+cdb variant). When this option is given the READ CAPACITY(16) command
+is sent. The latter command yields more information in its response.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level or verbosity.
+.SH EXIT STATUS
+The exit status of this script is 0 when it is successful. Otherwise the
+exit status is that of the last sg_readcap utility called. See
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2009\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_readcap (sg3_utils)
diff --git a/sg3_utils/doc/scsi_ready.8 b/sg3_utils/doc/scsi_ready.8
new file mode 100644
index 0000000..50a6ed7
--- /dev/null
+++ b/sg3_utils/doc/scsi_ready.8
@@ -0,0 +1,40 @@
+.TH SCSI_READY "8" "May 2013" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+scsi_ready \- do SCSI TEST UNIT READY on devices
+.SH SYNOPSIS
+.B scsi_ready
+[\fI\-\-brief\fR] [\fI\-\-help\fR] [\fI\-\-verbose\fR]
+\fIDEVICE\fR [\fIDEVICE\fR]*
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This bash shell script calls the sg_turs utility on each given
+\fIDEVICE\fR. This will send a SCSI TEST UNIT READY command to each
+\fIDEVICE\fR. Disks, tape drives and DVD/BD players amongst others
+may respond to this SCSI command.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-brief\fR
+for each \fIDEVICE\fR given output a line containing either '    ready'
+or '    device not ready'. If \fIDEVICE\fR is not found or there is
+another serious error then an error message will appear instead.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level or verbosity.
+.SH EXIT STATUS
+The exit status of this script is 0 when it is successful. Otherwise the
+exit status is that of the last sg_turs utility called. See
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2009\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_turs (sg3_utils)
diff --git a/sg3_utils/doc/scsi_satl.8 b/sg3_utils/doc/scsi_satl.8
new file mode 100644
index 0000000..da6c43a
--- /dev/null
+++ b/sg3_utils/doc/scsi_satl.8
@@ -0,0 +1,44 @@
+.TH SCSI_SATL "8" "May 2013" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+scsi_satl \- check SCSI to ATA Translation (SAT) device support
+.SH SYNOPSIS
+.B scsi_satl
+[\fI\-\-help\fR] [\fI\-\-log\fR] [\fI\-\-quiet\fR] [\fI\-\-verbose\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This bash shell script calls several SCSI commands on the given
+\fIDEVICE\fR that is assumed to be an ATA device behind a SCSI
+to ATA Translation (SAT) layer (SATL). The results of each test
+and a pass/fail count are output.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-L\fR, \fB\-\-log\fR
+the output to stderr (from each SCSI command executed) is appended to
+a file called 'scsi_satl.err' in the current working directory.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+the amount of output is reduced and typically only the pass/fail
+count is output.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level or verbosity.
+.SH EXIT STATUS
+The exit status of this script is the number of "bad" errors found.
+So an exit status of 0 means all mandatory SCSI commands worked as
+expected.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2011\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq, sg_luns, sg_turs, sg_requests, sg_vpd, sg_senddiag, sg_modes,
+.B sg_sat_identify (sg3_utils)
diff --git a/sg3_utils/doc/scsi_start.8 b/sg3_utils/doc/scsi_start.8
new file mode 100644
index 0000000..f98985b
--- /dev/null
+++ b/sg3_utils/doc/scsi_start.8
@@ -0,0 +1,40 @@
+.TH SCSI_START "8" "May 2013" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+scsi_start \- start one or more SCSI disks
+.SH SYNOPSIS
+.B scsi_start
+[\fI\-\-help\fR] [\fI\-\-verbose\fR] [\fI\-\-wait\fR]
+\fIDEVICE\fR [\fIDEVICE\fR]*
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This bash shell script calls the sg_start utility on each given
+\fIDEVICE\fR. The purpose is to spin up (start) each given \fIDEVICE\fR.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level or verbosity.
+.TP
+\fB\-w\fR, \fB\-\-wait\fR
+wait for the spin up (start) on each given \fIDEVICE\fR to complete.
+The default action is to do each start in immediate mode.
+.SH NOTES
+If a large number of disks are spun up at the same time (i.e. without
+the \fI\-\-wait\fR option) then the power supply may be overloaded.
+.SH EXIT STATUS
+The exit status of this script is 0 when it is successful. Otherwise the
+exit status is that of the last sg_start utility called. See
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2009\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_start (sg3_utils)
diff --git a/sg3_utils/doc/scsi_stop.8 b/sg3_utils/doc/scsi_stop.8
new file mode 100644
index 0000000..488c7cf
--- /dev/null
+++ b/sg3_utils/doc/scsi_stop.8
@@ -0,0 +1,41 @@
+.TH SCSI_STOP "8" "May 2013" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+scsi_stop \- stop (spin down) one or more SCSI disks
+.SH SYNOPSIS
+.B scsi_stop
+[\fI\-\-help\fR] [\fI\-\-verbose\fR] [\fI\-\-wait\fR]
+\fIDEVICE\fR [\fIDEVICE\fR]*
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This bash shell script calls the sg_start utility on each given
+\fIDEVICE\fR. The purpose is to spin down (stop) each given \fIDEVICE\fR.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level or verbosity.
+.TP
+\fB\-w\fR, \fB\-\-wait\fR
+wait for the spin down (stop) on each given \fIDEVICE\fR to complete.
+The default action is to do each stop in immediate mode.
+.SH NOTES
+The sg_start utility calls the SCSI START STOP UNIT command and can
+either start (spin up) or stop (spin down) a SCSI disk depending
+on the given command line options.
+.SH EXIT STATUS
+The exit status of this script is 0 when it is successful. Otherwise the
+exit status is that of the last sg_start utility called. See
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2009\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_start (sg3_utils)
diff --git a/sg3_utils/doc/scsi_temperature.8 b/sg3_utils/doc/scsi_temperature.8
new file mode 100644
index 0000000..e9ff181
--- /dev/null
+++ b/sg3_utils/doc/scsi_temperature.8
@@ -0,0 +1,35 @@
+.TH SCSI_TEMPERATURE "8" "May 2011" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+scsi_temperature \- fetch the temperature of a SCSI device
+.SH SYNOPSIS
+.B scsi_temperature
+[\fI\-\-help\fR] [\fI\-\-verbose\fR]
+\fIDEVICE\fR [\fIDEVICE\fR]*
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This bash shell script calls the sg_logs utility on each given
+\fIDEVICE\fR in order to find the device's temperature. The Temperature
+log page is checked first and if it is not available then the Informational
+Exceptions log page is checked.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level or verbosity.
+.SH EXIT STATUS
+The exit status of this script is 0 when it is successful. Otherwise the
+exit status is that of the last sg_logs utility called. See
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2011\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_logs (sg3_utils)
diff --git a/sg3_utils/doc/sg3_utils.8 b/sg3_utils/doc/sg3_utils.8
new file mode 100644
index 0000000..233a9e9
--- /dev/null
+++ b/sg3_utils/doc/sg3_utils.8
@@ -0,0 +1,533 @@
+.TH SG3_UTILS "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg3_utils \- a package of utilities for sending SCSI commands
+.SH SYNOPSIS
+.B sg_*
+[\fI\-\-enumerate\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-in=FN\fR]
+[\fI\-\-maxlen=LEN\fR] [\fI\-\-raw\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+[\fIOTHER_OPTIONS\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+sg3_utils is a package of utilities that send SCSI commands to the given
+\fIDEVICE\fR via a SCSI pass through interface provided by the host
+operating system.
+.PP
+The names of all utilities start with "sg" and most start with "sg_" often
+followed by the name, or a shortening of the name, of the SCSI command that
+they send. For example the "sg_verify" utility sends the SCSI VERIFY
+command. A mapping between SCSI commands and the sg3_utils utilities that
+issue them is shown in the COVERAGE file. The sg_raw utility can be used to
+send an arbitrary SCSI command (supplied on the command line) to the
+given \fIDEVICE\fR.
+.PP
+sg_decode_sense can be used to decode SCSI sense data given on the command
+line or in a file. sg_raw \-vvv will output the T10 name of a given SCSI
+CDB which is most often 16 bytes or less in length.
+.PP
+SCSI draft standards can be found at http://www.t10.org . The standards
+themselves can be purchased from ANSI and other standards organizations.
+A good overview of various SCSI standards can be seen in
+http://www.t10.org/scsi\-3.htm with the SCSI command sets in the upper part
+of the diagram. SCSI commands in common with all device types can be found
+in SPC of which SPC\-4 is the latest major version. Block device specific
+commands (e.g. as used by disks) are in SBC, those for tape drives in SSC
+and those for CD/DVD/BD drives in MMC.
+.PP
+It is becoming more common to control ATA disks with the SCSI command set.
+This involves the translation of SCSI commands to their corresponding
+ATA equivalents (and that is an imperfect mapping in some cases). The
+relevant standard is called SCSI to ATA Translation (SAT and SAT\-2
+are now standards at INCITS(ANSI) and ISO while SAT\-3 is at the draft
+stage). The logic to perform the command translation is often called
+a SAT Layer or SATL and may be within an operating system, in host bus
+adapter firmware or in an external device (e.g. associated with a SAS
+expander). See http://www.t10.org for more information.
+.PP
+There is some support for SCSI tape devices but not for their basic
+commands. The reader is referred to the "mt" utility.
+.PP
+There are two generations of command line option usage. The newer
+utilities (written since July 2004) use the getopt_long() function to parse
+command line options. With that function, each option has two representations:
+a short form (e.g. '\-v') and a longer form (e.g. '\-\-verbose'). If an
+argument is required then it follows a space (optionally) in the short form
+and a "=" in the longer form (e.g. in the sg_verify utility '\-l 2a6h'
+and '\-\-lba=2a6h' are equivalent). Note that with getopt_long(), short form
+options can be elided, for example: '\-all' is equivalent to '\-a \-l \-l'.
+The \fIDEVICE\fR argument may appear after, between or prior to any options.
+.PP
+The older utilities, such as sg_inq, had individual command line processing
+code typically based on a single "\-" followed by one or more characters. If
+an argument is needed then it follows a "=" (e.g. '\-p=1f' in sg_modes with
+its older interface). Various options can be elided as long as it is not
+ambiguous (e.g. '\-vv' to increase the verbosity).
+.PP
+Over time the command line interface of these older utilities became messy
+and overloaded with options. So in sg3_utils version 1.23 the command line
+interface of these older utilities was altered to have both a cleaner
+getopt_long() interface and their older interface for backward compatibility.
+By default these older utilities use their getopt_long() based interface.
+That can be overridden by defining the SG3_UTILS_OLD_OPTS environment
+variable or using '\-O' or '\-\-old' as the first command line option. The
+man pages of the older utilities documents the details.
+.PP
+Several sg3_utils utilities are based on the Unix dd command (e.g. sg_dd)
+and permit copying data at the level of SCSI READ and WRITE commands. sg_dd
+is tightly bound to Linux and hence is not ported to other OSes. A more
+generic utility (than sg_dd) called ddpt in a package of the same name has
+been ported to other OSes.
+.SH LINUX DEVICE NAMING
+Most disk block devices have names like /dev/sda, /dev/sdb, /dev/sdc, etc.
+SCSI disks in Linux have always had names like that but in recent Linux
+kernels it has become more common for many other disks (including SATA
+disks and USB storage devices) to be named like that. Partitions within a
+disk are specified by a number appended to the device name, starting at
+1 (e.g. /dev/sda1 ).
+.PP
+Tape drives are named /dev/st<num> or /dev/nst<num> where <num> starts
+at zero. Additionally one letter from this list: "lma" may be appended to
+the name. CD, DVD and BD readers (and writers) are named /dev/sr<num>
+where <num> start at zero. There are less used SCSI device type names,
+the dmesg and the lsscsi commands may help to find if any are attached to
+a running system.
+.PP
+There is also a SCSI device driver which offers alternate generic access
+to SCSI devices. It uses names of the form /dev/sg<num> where <num> starts
+at zero. The "lsscsi \-g" command may be useful in finding these and which
+generic name corresponds to a device type name (e.g. /dev/sg2 may
+correspond to /dev/sda). In the lk 2.6 series a block SCSI generic
+driver was introduced and its names are of the form
+/dev/bsg/<h:c:t:l> where h, c, t and l are numbers. Again see the lsscsi
+command to find the correspondence between that SCSI tuple (i.e. <h:c:t:l>)
+and alternate device names.
+.PP
+Prior to the Linux kernel 2.6 series these utilities could only use
+generic device names (e.g. /dev/sg1 ). In almost all cases in the Linux
+kernel 2.6 series, any device name can be used by these utilities.
+.PP
+Very little has changed in Linux device naming in the Linux kernel 3
+and 4 series.
+.SH WINDOWS DEVICE NAMING
+Storage and related devices can have several device names in Windows.
+Probably the most common in the volume name (e.g. "D:"). There are also
+a "class" device names such as "PhysicalDrive<n>", "CDROM<n>"
+and "TAPE<n>". <n> is an integer starting at 0 allocated in ascending
+order as devices are discovered (and sometimes rediscovered).
+.PP
+Some storage devices have a SCSI lower level device name which starts
+with a SCSI (pseudo) adapter name of the form "SCSI<n>:". To this is added
+sub\-addressing in the form of a "bus" number, a "target" identifier and
+a LUN (Logical Unit Number). The "bus" number is also known as a "PathId".
+These are assembled to form a device name of the
+form: "SCSI<n>:<bus>,<target>,<lun>". The trailing ",<lun>" may be omitted
+in which case a LUN of zero is assumed. This lower level device name cannot
+often be used directly since Windows blocks attempts to use it if a class
+driver has "claimed" the device. There are SCSI device types (e.g.
+Automation/Drive interface type) for which there is no class driver. At
+least two transports ("bus types" in Windows jargon): USB and IEEE 1394 do
+not have a "scsi" device names of this form.
+.PP
+In keeping with DOS file system conventions, the various device names
+can be given in upper, lower or mixed case. Since "PhysicalDrive<n>" is
+tedious to write, a shortened form of "PD<n>" is permitted by all
+utilities in this package.
+.PP
+A single device (e.g. a disk) can have many device names. For
+example: "PD0" can also be "C:", "D:" and "SCSI0:0,1,0". The two volume names
+reflect that the disk has two partitions on it. Disk partitions that are
+not recognized by Windows are not usually given a volume name. However
+Vista does show a volume name for a disk which has no partitions recognized
+by it and when selected invites the user to format it (which may be rather
+unfriendly to other OSes).
+.PP
+These utilities assume a given device name is in the Win32 device namespace.
+To make that explicit "\\\\.\\" can be prepended to the device names mentioned
+in this section. Beware that backslash is an escape character in Unix like
+shells and the C programming language. In a shell like Msys (from MinGW)
+each backslash may need to be typed twice.
+.PP
+The sg_scan utility within this package lists out Windows device names in
+a form that is suitable for other utilities in this package to use.
+.SH FREEBSD DEVICE NAMING
+SCSI disks have block names of the form /dev/da<num> where <num> is an
+integer starting at zero. The "da" is replaced by "sa" for SCSI tape
+drives and "cd" for SCSI CD/DVD/BD drives. Each SCSI device has a
+corresponding pass\-through device name of the form /dev/pass<num>
+where <num> is an integer starting at zero. The "camcontrol devlist"
+command may be useful for finding out which SCSI device names are
+available and the correspondence between class and pass\-through names.
+.SH SOLARIS DEVICE NAMING
+SCSI device names below the /dev directory have a form like: c5t4d3s2
+where the number following "c" is the controller (HBA) number, the number
+following "t" is the target number (from the SCSI parallel interface days)
+and the number following "d" is the LUN. Following the "s" is the slice
+number which is related to a partition and by convention "s2" is the whole
+disk.
+.PP
+OpenSolaris also has a c5t4d3p2 form where the number following the "p" is
+the partition number apart from "p0" which is the whole disk. So a whole
+disk may be referred to as either c5t4d3, c5t4d3s2 or c5t4d3p0 .
+.PP
+And these device names are duplicated in the /dev/dsk and /dev/rdsk
+directories. The former is the block device name and the latter is
+for "raw" (or char device) access which is what sg3_utils needs. So in
+OpenSolaris something of the form 'sg_inq /dev/rdsk/c5t4d3p0' should work.
+If it doesn't work then add a '\-vvv' option for more debug information.
+Trying this form 'sg_inq /dev/dsk/c5t4d3p0' (note "rdsk" changed to "dsk")
+will result in an "inappropriate ioctl for device" error.
+.PP
+The device names within the /dev directory are typically symbolic links to
+much longer topological names in the /device directory. In Solaris cd/dvd/bd
+drives seem to be treated as disks and so are found in the /dev/rdsk
+directory. Tape drives appear in the /dev/rmt directory.
+.PP
+There is also a sgen (SCSI generic) driver which by default does not attach
+to any device. See the /kernel/drv/sgen.conf file to control what is
+attached. Any attached device will have a device name of the
+form /dev/scsi/c5t4d3 .
+.PP
+Listing available SCSI devices in Solaris seems to be a challenge. "Use
+the 'format' command" advice works but seems a very dangerous way to list
+devices. [It does prompt again before doing any damage.] 'devfsadm \-Cv'
+cleans out the clutter in the /dev/rdsk directory, only leaving what
+is "live". The "cfgadm \-v" command looks promising.
+.SH EXIT STATUS
+To aid scripts that call these utilities, the exit status is set to indicate
+success (0) or failure (1 or more). Note that some of the lower values
+correspond to the SCSI sense key values. The exit status values are:
+.TP
+.B 0
+success
+.TP
+.B 1
+syntax error. Either illegal command line options, options with bad
+arguments or a combination of options that is not permitted.
+.TP
+.B 2
+the \fIDEVICE\fR reports that it is not ready for the operation requested.
+The \fIDEVICE\fR may be in the process of becoming ready (e.g.  spinning up
+but not at speed) so the utility may work after a wait. In Linux the
+\fIDEVICE\fR may be temporarily blocked while error recovery is taking place.
+.TP
+.B 3
+the \fIDEVICE\fR reports a medium or hardware error (or a blank check). For
+example an attempt to read a corrupted block on a disk will yield this value.
+.TP
+.B 5
+the \fIDEVICE\fR reports an "illegal request" with an additional sense code
+other than "invalid command operation code". This is often a supported
+command with a field set requesting an unsupported capability. For commands
+that require a "service action" field this value can indicate that the
+command with that service action value is not supported.
+.TP
+.B 6
+the \fIDEVICE\fR reports a "unit attention" condition. This usually indicates
+that something unrelated to the requested command has occurred (e.g. a device
+reset) potentially before the current SCSI command was sent. The requested
+command has not been executed by the device. Note that unit attention
+conditions are usually only reported once by a device.
+.TP
+.B 7
+the \fIDEVICE\fR reports a "data protect" sense key. This implies some
+mechanism has blocked writes (or possibly all access to the media).
+.TP
+.B 9
+the \fIDEVICE\fR reports an illegal request with an additional sense code
+of "invalid command operation code" which means that it doesn't support the
+requested command.
+.TP
+.B 10
+the \fIDEVICE\fR reports a "copy aborted". This implies another command or
+device problem has stopped a copy operation. The EXTENDED COPY family of
+commands (including WRITE USING TOKEN) may return this sense key.
+.TP
+.B 11
+the \fIDEVICE\fR reports an aborted command. In some cases aborted
+commands can be retried immediately (e.g. if the transport aborted
+the command due to congestion).
+.TP
+.B 14
+the \fIDEVICE\fR reports a miscompare sense key. VERIFY and COMPARE AND
+WRITE commands may report this.
+.TP
+.B 15
+the utility is unable to open, close or use the given \fIDEVICE\fR or some
+other file. The given file name could be incorrect or there may be
+permission problems. Adding the '\-v' option may give more information.
+.TP
+.B 20
+the \fIDEVICE\fR reports it has a check condition but "no sense"
+and non\-zero information in its additional sense codes. Some polling
+commands (e.g. REQUEST SENSE) can receive this response. There may
+be useful information in the sense data such as a progress indication.
+.TP
+.B 21
+the \fIDEVICE\fR reports a "recovered error". The requested command
+was successful. Most likely a utility will report a recovered error
+to stderr and continue, probably leaving the utility with an exit
+status of 0 .
+.TP
+.B 24
+the \fIDEVICE\fR reports a SCSI status of "reservation conflict". This
+means access to the \fIDEVICE\fR with the current command has been blocked
+because another machine (HBA or SCSI "initiator") holds a reservation on
+this \fIDEVICE\fR. On modern SCSI systems this is related to the use of
+the PERSISTENT RESERVATION family of commands.
+.TP
+.B 25
+the \fIDEVICE\fR reports a SCSI status of "condition met". Currently only
+the PRE\-FETCH command (see SBC\-4) yields this status.
+.TP
+.B 26
+the \fIDEVICE\fR reports a SCSI status of "busy". SAM\-5 defines this
+status as the logical unit is temporarily unable to process a command.
+It is recommended to re-issue the command.
+.TP
+.B 27
+the \fIDEVICE\fR reports a SCSI status of "task set full".
+.TP
+.B 28
+the \fIDEVICE\fR reports a SCSI status of "ACA active". ACA is "auto
+contingent allegiance" and is seldom used.
+.TP
+.B 29
+the \fIDEVICE\fR reports a SCSI status of "task aborted". SAM\-5 says:
+"This status shall be returned if a command is aborted by a command or task
+management function on another I_T nexus and the Control mode page TAS bit
+is set to one".
+.TP
+.B 33
+the command sent to \fIDEVICE\fR has timed out.
+.TP
+.B 40
+the command sent to \fIDEVICE\fR has received an "aborted command" sense
+key with an additional sense code of 0x10. This group is related to
+problems with protection information (PI or DIF). For example this error
+may occur when reading a block on a drive that has never been written (or
+is unmapped) if that drive was formatted with type 1, 2 or 3 protection.
+.TP
+.B 97
+a SCSI command response failed sanity checks.
+.TP
+.B 98
+the \fIDEVICE\fR reports it has a check condition but the error
+doesn't fit into any of the above categories.
+.TP
+.B 99
+any errors that can't be categorized into values 1 to 98 may yield
+this value. This includes transport and operating system errors
+after the command has been sent to the device.
+.TP
+.B 126
+the utility was found but could not be executed. That might occur if the
+executable does not have execute permissions.
+.TP
+.B 127
+This is the exit status for utility not found. That might occur when a
+script calls a utility in this package but the PATH environment variable
+has not been properly set up, so the script cannot find the executable.
+.TP
+.B 128 + <signum>
+If a signal kills a utility then the exit status is 128 plus the signal
+number. For example if a segmentation fault occurs then a utility is
+typically killed by SIGSEGV which according to 'man 7 signal' has an
+associated signal number of 11; so the exit status will be 139 .
+.TP
+.B 255
+the utility tried to yield an exit status of 255 or larger. That should
+not happen; given here for completeness.
+.PP
+Most of the error conditions reported above will be repeatable (an
+example of one that is not is "unit attention") so the utility can
+be run again with the '\-v' option (or several) to obtain more
+information.
+.SH COMMON OPTIONS
+Arguments to long options are mandatory for short options as well. In the
+short form an argument to an option uses zero or more spaces as a
+separator (i.e. the short form does not use "=" as a separator).
+.PP
+If an option takes a numeric argument then that argument is assumed to
+be decimal unless otherwise indicated (e.g. with a leading "0x", a
+trailing "h" or as noted in the usage message).
+.PP
+Some options are used uniformly in most of the utilities in this
+package. Those options are listed below. Note that there are some
+exceptions.
+.TP
+\fB\-e\fR, \fB\-\-enumerate\fR
+some utilities (e.g. sg_ses and sg_vpd) store a lot of information in
+internal tables. This option will output that information in some readable
+form (e.g. sorted by an acronym or by page number) then exit. Note that
+with this option \fIDEVICE\fR is ignored (as are most other options) and no
+SCSI IO takes place, so the invoker does not need any elevated permissions.
+.TP
+\fB\-h\fR, \fB\-?\fR, \fB\-\-help\fR
+output the usage message then exit. In a few older utilities the '\-h'
+option requests hexadecimal output. In these cases the '\-?' option will
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+for SCSI commands that yield a non\-trivial response, print out that
+response in ASCII hexadecimal. To produce hexadecimal that can be parsed
+by other utilities (e.g. without a relative address to the left and without
+trailing ASCII) use this option three or four times.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIFN\fR
+many SCSI commands fetch a significant amount of data (returned in the
+data\-in buffer) which several of these utilities decode (e.g. sg_vpd and
+sg_logs). To separate the two steps of fetching the data from a SCSI device
+and then decoding it, this option has been added. The first step (fetching
+the data) can be done using the \fI\-\-hex\fR or \fI\-\-raw\fR option and
+redirecting the command line output to a file (often done with ">" in Unix
+based operating systems). The difference between \fI\-\-hex\fR and
+\fI\-\-raw\fR is that the former produces output in ASCII hexadecimal
+while \fI\-\-raw\fR produces its output in "raw" binary.
+.br
+The second step (i.e. decoding the SCSI response data now held in a file)
+can be done using this \fI\-\-in=FN\fR option where the file name is
+\fIFN\fR. If "\-" is used for \fIFN\fR then stdin is assumed, again this
+allows for command line redirection (or piping). That file (or stdin)
+is assumed to contain ASCII hexadecimal unless the \fI\-\-raw\fR option is
+also given in which case it is assumed to be binary. Notice that the meaning
+of the \fI\-\-raw\fR option is "flipped" when used with \fI\-\-in=FN\fR to
+act on the input, typically it acts on the output data.
+.br
+Since the structure of the data returned by SCSI commands varies
+considerably then the usage information or manpage of the utility being
+used should be checked. In some cases \fI\-\-hex\fR may need to be used
+multiple times (and is more conveniently given as '\-HH' or '\-HHH). In
+other cases the name of this option is \fI\-\-inhex=FN\fR.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+several important SCSI commands (e.g. INQUIRY and MODE SENSE) have response
+lengths that vary depending on many factors, only some of which these
+utilities take into account. The maximum response length is typically
+specified in the 'allocation length' field of the cdb. In the absence of
+this option, several utilities use a default allocation length (sometimes
+recommended in the SCSI draft standards) or a "double fetch" strategy.
+See sg_logs(8) for its description of a "double fetch" strategy. These
+techniques are imperfect and in the presence of faulty SCSI targets can
+cause problems (e.g. some USB mass storage devices freeze if they receive
+an INQUIRY allocation length other than 36). Also use of this option
+disables any "double fetch" strategy that may have otherwise been used.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+for SCSI commands that yield a non\-trivial response, output that response
+in binary to stdout. If any error messages or warning are produced they are
+usually sent to stderr so as to not interfere with the output from this
+option.
+.br
+Some utilities that consume data to send to the \fIDEVICE\fR along with the
+SCSI command, use this option. Alernatively the \fI\-\-in=FN\fR option causes
+\fIDEVICE\fR to be ignored and the response data (to be decoded) fetched
+from a file named \fIFN\fR. In these cases this option may indicate that
+binary data can be read from stdin or from a nominated file (e.g. \fIFN\fR).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output). Can be used multiple
+times to further increase verbosity. The additional output is usually sent
+to stderr.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit. Each utility has its own version
+number and date of last code change.
+.SH NUMERIC ARGUMENTS
+Many utilities have command line options that take numeric arguments. These
+numeric arguments can be large values (e.g. a logical block address (LBA) on
+a disk) and can be inconvenient to enter in the default decimal
+representation. So various other representations are permitted.
+.PP
+Multiplicative suffixes are accepted. They are one, two or three letter
+strings appended directly after the number to which they apply:
+.PP
+   c C         *1
+.br
+   w W         *2
+.br
+   b B         *512
+.br
+   k K KiB     *1024
+.br
+   KB          *1000
+.br
+   m M MiB     *1048576
+.br
+   MB          *1000000
+.br
+   g G GiB     *(2^30)
+.br
+   GB          *(10^9)
+.br
+   t T TiB     *(2^40)
+.br
+   TB          *(10^12)
+.br
+   p P PiB     *(2^50)
+.br
+   PB          *(10^15)
+.PP
+An example is "2k" for 2048. The large tera and peta suffixes are only
+available for numeric arguments that might require 64 bits to represent
+internally.
+.PP
+A suffix of the form "x<n>" multiplies the leading number by <n>. An
+example is "2x33" for "66". The leading number cannot be "0" (zero) as
+that would be interpreted as a hexadecimal number (see below).
+.PP
+These multiplicative suffixes are compatible with GNU's dd command (since
+2002) which claims compliance with SI and with IEC 60027\-2.
+.PP
+Alternatively numerical arguments can be given in hexadecimal. There are
+two syntaxes. The number can be preceded by either "0x" or "0X" as found
+in the C programming language. The second hexadecimal representation is a
+trailing "h" or "H" as found in (storage) standards. When hex numbers are
+given, multipliers cannot be used. For example the decimal value "256" can
+be given as "0x100" or "100h".
+.SH MICROCODE AND FIRMWARE
+There are two standardized methods for downloading microcode (i.e. device
+firmware) to a SCSI device. The more general way is with the SCSI WRITE
+BUFFER command, see the sg_write_buffer utility. SCSI enclosures have
+their own method based on the Download microcode control/status diagnostic
+page, see the sg_ses_microcode utility.
+.SH SCRIPTS, EXAMPLES and UTILS
+There are several bash shell scripts in the 'scripts' subdirectory that
+invoke compiled utilities (e.g. sg_readcap). Several of the scripts start
+with 'scsi_' rather than 'sg_'. One purpose of these scripts is to call the
+same utility (e.g. sg_readcap) on multiple devices. Most of the basic
+compiled utilities only allow one device as an argument. Some distributions
+install these scripts in a more visible directory (e.g. /usr/bin). Some of
+these scripts have man page entries. See the README file in the 'scripts'
+subdirectory.
+.PP
+There is some example C code plus examples of complex invocations in
+the 'examples' subdirectory. There is also a README file. The example C
+may be a simpler example of how to use a SCSI pass\-through in Linux
+than the main utilities (found in the 'src' subdirectory). This is due
+to the fewer abstraction layers (e.g. they don't worry the MinGW in
+Windows may open a file in text rather than binary mode).
+.PP
+Some utilities that the author has found useful have been placed in
+the 'utils' subdirectory.
+.SH WEB SITE
+There is a web page discussing this package at
+http://sg.danny.cz/sg/sg3_utils.html . The device naming used by this
+package on various operating systems is discussed at:
+http://sg.danny.cz/sg/device_name.html .
+.SH AUTHORS
+Written by Douglas Gilbert. Some utilities have been contributed, see the
+CREDITS file and individual source files (in the 'src' directory).
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 1999\-2016 Douglas Gilbert
+.br
+Some utilities are distributed under a GPL version 2 license while
+others, usually more recent ones, are under a FreeBSD license. The files
+that are common to almost all utilities and thus contain the most reusable
+code, namely sg_lib.[hc], sg_cmds_basic.[hc] and sg_cmds_extra.[hc] are
+under a FreeBSD license. There is NO warranty; not even for MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sdparm(sdparm), ddpt(ddpt), lsscsi(lsscsi), dmesg(1), mt(1)
diff --git a/sg3_utils/doc/sg_compare_and_write.8 b/sg3_utils/doc/sg_compare_and_write.8
new file mode 100644
index 0000000..4708318
--- /dev/null
+++ b/sg3_utils/doc/sg_compare_and_write.8
@@ -0,0 +1,170 @@
+.TH "COMPARE AND WRITE" "8" "March 2014" "sg3_utils\-1.38" SG3_UTILS
+.SH NAME
+sg_compare_and_write \- send the SCSI COMPARE AND WRITE command
+.SH SYNOPSIS
+.B sg_compare_and_write
+[\fI\-\-dpo\fR] [\fI\-\-fua\fR] [\fI\-\-fua_nv\fR] [\fI\-\-help\fR]
+\fI\-\-in=IF\fR [\fI\-\-inw=WF\fR] \fI\-\-lba=LBA\fR [\fI\-\-num=NUM\fR]
+[\fI\-\-quiet\fR] [\fI\-\-timeout=TO\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] [\fI\-\-wrprotect=WP\fR] [\fI\-\-xferlen=LEN\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+Send the SCSI COMPARE AND WRITE command to \fIDEVICE\fR. This utility
+reads a compare buffer and a write buffer from either one or two files. If
+the \fI\-\-inw=WF\fR option is not given then the concatenated compare
+and write buffers are read from the file indicated by the \fI\-\-in=IF\fR
+option. If the \fI\-\-inw=WF\fR option is given then the compare buffer
+is read from the file indicated by the \fI\-\-in=IF\fR while the write
+buffer is read from the file indicated by the \fI\-\-inw=WF\fR.
+.PP
+Those buffers are expected to each contain \fINUM\fR blocks of data. The
+compare starts at at logical block address \fILBA\fR on the \fIDEVICE\fR
+and if the comparison fails (i.e. the provided compare buffer does not
+equal at \fILBA\fR on the \fIDEVICE\fR) then the COMPARE AND WRITE command
+finishes with a sense key of MISCOMPARE. In this case this utility will
+completes and set an exit status of 14 (which happens to be the sense key
+value of MISCOMPARE).
+.PP
+If the comparison succeeds then the provided write buffer is written to
+starting at \fILBA\fR for \fINUM\fR blocks on the \fIDEVICE\fR.
+.PP
+The actual number of bytes transferred in the data\-out buffer of the
+COMPARE AND WRITE command may need to be given by the user with the
+\fI\-\-xferlen=LEN\fR option. \fILEN\fR defaults to (2 * \fINUM\fR * 512)
+which is 1024 for the default \fINUM\fR of 1. If the block size is
+other than 512 then the user will need to use \fI\-\-xferlen=LEN\fR option.
+If protection information is given (indicated by a value of \fIWP\fR
+other than 0 (the default)) then for a \fINUM\fR of 1 \fILEN\fR should
+be 1040 . Note that the SCSI READ CAPACITY command is not checked by
+this utility (e.g. to find the block size).
+.PP
+The definition of the SCSI COMPARE AND WRITE command requires that the
+\fIDEVICE\fR implement the compare and optional write as an uninterrupted
+series of actions. Depending on some other \fIDEVICE\fR settings a
+verify operation may occur prior to the compare.
+.PP
+When a mismatch occurs between the compare buffer and the blocks starting
+at \fILBA\fR read from the \fIDEVICE\fR the sense buffer containing the
+MISCOMPARE sense key causes several messages to be sent to stderr (including
+the offset of the first byte mismatch). To suppress these messages use the
+\fI\-\-quiet\fR option. With or without the \fI\-\-quiet\fR option the exit
+status will be set to 14.
+.PP
+This command is defined in SBC\-3 whose most recent revision is 36. SBC\-3
+and other SCSI documents can be found at http://www.t10.org .
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long option name.
+.TP
+\fB\-d\fR, \fB\-\-dpo\fR
+Set the DPO bit in the COMPARE AND WRITE CDB
+.TP
+\fB\-f\fR, \fB\-\-fua\fR
+Set the FUA bit in the COMPARE AND WRITE CDB
+.TP
+\fB\-F\fR, \fB\-\-fua_nv\fR
+Set the FUA_NV bit in the COMPARE AND WRITE CDB. This bit was removed in
+SBC\-3 revision 35d and its position marked as "reserved".
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
+read data (binary) from file named \fIIF\fR. This will either be the combined
+compare and write buffers (when the \fI\-\-inw=WF\fR option is not given) or
+just the compare buffer (when the \fI\-\-inw=WF\fR option is given). If
+\fIIF\fR is '\-' then stdin (e.g. a pipe) is read.
+.TP
+\fB\-D\fR, \fB\-\-inw\fR=\fIWF\fR
+read data (binary) from file named \fIWF\fR. This will the write buffer
+that will become the second half of the data\-out buffer sent to the
+\fIDEVICE\fR associated with the COMPARE AND WRITE command. Note that
+when this option is given then the \fI\-\-in=IF\fR is expected to hold
+the associated compare buffer.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the logical block address to start the COMPARE AND WRITE
+command. Assumed to be in decimal unless prefixed with '0x' or has a
+trailing 'h'.
+.TP
+\fB\-n\fR, \fB\-\-num\fR=\fINUM\fR
+where \fINUM\fR is the number of blocks, starting at \fILBA\fR, to read
+and compare with the verify instance. And given a match, the \fINUM\fR of
+blocks to write starting \fILBA\fR. The default value for \fINUM\fR is 1.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+suppress the sense buffer messages associated with a MISCOMPARE sense key
+that would otherwise be sent to stderr. Still set the exit status to 14
+which is the sense key value indicating a MISCOMPARE.
+.TP
+\fB\-t\fR, \fB\-\-timeout\fR=\fITO\fR
+where \fITO\fR is the command timeout value in seconds. The default value is
+60 seconds. If \fINUM\fR is large (or zero) a WRITE SAME command may require
+considerably more time than 60 seconds to complete.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the degree of verbosity (debug messages).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+output version string then exit.
+.TP
+\fB\-w\fR, \fB\-\-wrprotect\fR=\fIWP\fR
+set the WRPROTECT field in the cdb to \fIWP\fR. The default value is 0 which
+implies no protection information is sent (along with the user data) by this
+utility.
+.TP
+\fB\-x\fR, \fB\-\-xferlen\fR=\fILEN\fR
+where \fILEN\fR is the data out buffer length in byte. It defaults to (2 *
+\fINUM\fR * 512) bytes. If the \fIDEVICE\fR block size is other than 512
+bytes or \fIWP\fR is non-zero (implying additional protection information)
+then this default will be incorrect; the use must supply the correct value
+for \fILEN\fR
+.SH NOTES
+Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.SH EXIT STATUS
+The exit status of sg_compare_and_write is 0 when it is successful. If the
+compare step fails then the exit status is 14. For other exit status values
+see the EXIT STATUS section in the sg3_utils(8) man page.
+.PP
+Earlier versions of this utility set an exit status of 98 when there was a
+MISCOMPARE.
+.SH AUTHORS
+Written by Shahar Salzman. Maintained by Douglas Gilbert. Additions by
+Eric Seppanen.
+.SH "REPORTING BUGS"
+Report bugs to shahar.salzman@kaminario.com or dgilbert@interlog.com
+.SH COPYRIGHT
+Copyright \(co 2012\-2014 Kaminario Technologies LTD
+
+.br
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+.br
+* Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+.br
+* Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+.br
+* Neither the name of the <organization> nor the names of its contributors may
+be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+.br
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL Kaminario Technologies LTD BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+.SH "SEE ALSO"
+.B sg_xcopy, sg_receive_copy_results(sg3_utils)
diff --git a/sg3_utils/doc/sg_copy_results.8 b/sg3_utils/doc/sg_copy_results.8
new file mode 100644
index 0000000..9e67e52
--- /dev/null
+++ b/sg3_utils/doc/sg_copy_results.8
@@ -0,0 +1,126 @@
+.TH SG_COPY_RESULTS "8" "September 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_copy_results \- send SCSI RECEIVE COPY RESULTS command (XCOPY related)
+.SH SYNOPSIS
+.B sg_copy_results
+[\fI\-\-failed\fR|\fI\-\-params\fR|\fI\-\-receive\fR|\fI\-\-status\fR]
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-list_id=ID\fR] [\fI\-\-readonly\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-xfer_len=BTL\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility is designed to query the status of the SCSI Extended
+Copy (XCOPY) facility (see SPC\-3 revision 23 sections 6.3 and 6.17), present
+in some modern storage arrays. This utility sends a SCSI RECEIVE COPY
+RESULTS command to the given \fIDEVICE\fR and displays the response.
+.PP
+During the draft stages of SPC\-4 the T10 committee has expanded the XCOPY
+command so that it now has two variants: "LID1" (for a List Identifier
+length of 1 byte) and "LID4" (for a List Identifier length of 4 bytes).
+This utility supports the older, LID1 variant which is also found in SPC\-3
+and earlier. While the LID1 variant in SPC\-4 is command level (binary)
+compatible with XCOPY as defined in SPC\-3, some of the command naming has
+changed. This utility uses the older, SPC\-3 XCOPY names.
+.PP
+The command has four distinct modes of operation, distinguished by
+the service action field:
+.TP
+\fBCOPY STATUS  [SPC\-4: RECEIVE COPY STATUS(LID1)]\fR
+Displays the current status of the EXTENDED COPY command identified by
+the list id field.
+.TP
+\fBRECEIVE DATA  [SPC\-4: RECEIVE COPY DATA(LID1)]\fR
+Return the held data read by the EXTENDED COPY command identified by
+the list id field. This option is only meaningful if the respective
+segment descriptor are supported.
+.TP
+\fBOPERATING PARAMETERS  [SPC\-4: RECEIVE COPY OPERATING PARAMETERS]\fR
+Return copy manager operating parameters. This option is also useful
+to determine if the SCSI Extended Copy facility is supported.
+.TP
+\fBFAILED SEGMENT DETAILS  [SPC\-4: RECEIVE COPY FAILURE DETAILS(LID1)]\fR
+Return copy target device sense data and other information about any
+failed segments.
+
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-f\fR, \fB\-\-failed\fR
+sets the service action field to FAILED SEGMENT DETAILS [4].
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+prints out the response buffer in hex.
+.TP
+\fB\-l\fR, \fB\-\-list_id\fR=\fIID\fR
+sets the list identifier field to \fIID\fR (default: 0).
+.TP
+\fB\-p\fR, \fB\-\-params\fR
+sets the service action field to OPERATING PARAMETERS [3].
+This is the default.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-r\fR, \fB\-\-receive\fR
+sets the service action field to RECEIVE DATA [1].
+.TP
+\fB\-s\fR, \fB\-\-status\fR
+sets the service action field to COPY STATUS [0].
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-x\fR, \fB\-\-xfer_len\fR=\fIBTL\fR
+sets the allocation length field to \fIBTL\fR. It is the byte transfer
+length and is the maximum (byte) size of the response. \fIBTL\fR must be
+less than 10000 and defaults to 520.
+.SH NOTES
+Decoding of \fIRECEIVE DATA\fR service action is not implemented.
+.PP
+In a similar way the functionality of sg_xcopy has been ported to the
+more general ddpt utility (and package), the functionality of this utility
+has been ported to the ddptctl utility.
+.SH EXAMPLES
+Query the operating parameters for a device:
+.PP
+# sg_copy_results \-p /dev/sdo
+.br
+Receive copy results (report operating parameters):
+    Supports no list identifier: no
+    Maximum target descriptor count: 2
+    Maximum segment descriptor count: 1
+    Maximum descriptor list length: 92 bytes
+    Maximum segment length: 33553920 bytes
+    Inline data not supported
+    Held data limit: 0 bytes
+    Maximum stream device transfer size: 0 bytes
+    Total concurrent copies: 0
+    Maximum concurrent copies: 255
+    Data segment granularity: 512 bytes
+    Inline data granularity: 1 bytes
+    Held data granularity: 1 bytes
+    Implemented descriptor list:
+        Segment descriptor 0x02: Copy from block device to block device
+        Target descriptor 0xe4: Identification descriptor
+
+.SH EXIT STATUS
+The exit status of sg_copy_results is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2012\-2014 Hannes Reinecke and Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_xcopy(sg3_utils), ddpt,ddptctl(ddpt)
diff --git a/sg3_utils/doc/sg_dd.8 b/sg3_utils/doc/sg_dd.8
new file mode 100644
index 0000000..8e61675
--- /dev/null
+++ b/sg3_utils/doc/sg_dd.8
@@ -0,0 +1,514 @@
+.TH SG_DD "8" "June 2013" "sg3_utils\-1.37" SG3_UTILS
+.SH NAME
+sg_dd \- copy data to and from files and devices, especially SCSI
+devices
+.SH SYNOPSIS
+.B sg_dd
+[\fIbs=BS\fR] [\fIconv=CONV\fR] [\fIcount=COUNT\fR] [\fIibs=BS\fR]
+[\fIif=IFILE\fR] [\fIiflag=FLAGS\fR] [\fIobs=BS\fR] [\fIof=OFILE\fR]
+[\fIoflag=FLAGS\fR] [\fIseek=SEEK\fR] [\fIskip=SKIP\fR] [\fI\-\-help\fR]
+[\fI\-\-version\fR]
+.PP
+[\fIblk_sgio=\fR{0|1}] [\fIbpt=BPT\fR] [\fIcdbsz=\fR{6|10|12|16}]
+[\fIcoe=\fR{0|1|2|3}] [\fIcoe_limit=CL\fR] [\fIdio=\fR{0|1}]
+[\fIodir=\fR{0|1}] [\fIof2=OFILE2\fR] [\fIretries=RETR\fR] [\fIsync=\fR{0|1}]
+[\fItime=\fR{0|1}] [\fIverbose=VERB\fR] [\fI\-V\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Copy data to and from any files. Specialized for "files" that are Linux SCSI
+generic (sg) devices, raw devices or other devices that support the SG_IO
+ioctl (which are only found in the lk 2.6 series). Similar syntax and
+semantics to
+.B dd(1)
+command.
+.PP
+The first group in the synopsis above are "standard" Unix
+.B dd(1)
+operands. The second group are extra options added by this utility.
+Both groups are defined below.
+.PP
+This utility is only supported on Linux whereas most other utilities in the
+sg3_utils package have been ported to other operating systems. A utility
+called "ddpt" has similar syntax and functionality to sg_dd. ddpt drops some
+Linux specific features while adding some other generic features. This allows
+ddpt to be ported to other operating systems.
+.SH OPTIONS
+.TP
+\fBblk_sgio\fR={0|1}
+when set to 0, block devices (e.g. /dev/sda) are treated like normal
+files (i.e.
+.B read(2)
+and
+.B write(2)
+are used for IO). When set to 1, block devices are assumed to accept the
+SG_IO ioctl and SCSI commands are issued for IO. This is only supported
+for 2.6 series kernels. Note that ATAPI devices (e.g. cd/dvd players) use
+the SCSI command set but ATA disks do not (unless there is a protocol
+conversion as often occurs in the USB mass storage class). If the input
+or output device is a block device partition (e.g. /dev/sda3) then setting
+this option causes the partition information to be ignored (since access
+is directly to the underlying device). Default is 0. See the 'sgio' flag.
+.TP
+\fBbpt\fR=\fIBPT\fR
+each IO transaction will be made using \fIBPT\fR blocks (or less if near
+the end of the copy). Default is 128 for block sizes less that 2048
+bytes, otherwise the default is 32. So for bs=512 the reads and writes
+will each convey 64 KiB of data by default (less if near the end of the
+transfer or memory restrictions). When cd/dvd drives are accessed, the
+block size is typically 2048 bytes and bpt defaults to 32 which again
+implies 64 KiB transfers. The block layer when the blk_sgio=1 option
+is used has relatively low upper limits for transfer sizes (compared
+to sg device nodes, see /sys/block/<dev_name>/queue/max_sectors_kb ).
+.TP
+\fBbs\fR=\fIBS\fR
+where \fIBS\fR
+.B must
+be the block size of the physical device (if either the input or output
+files are accessed via SCSI commands). Note that this differs from
+.B dd(1)
+which permits \fIBS\fR to be an integral multiple. Default is 512 which
+is usually correct for disks but incorrect for cdroms (which normally
+have 2048 byte blocks). For this utility the maximum size of each individual
+IO operation is \fIBS\fR * \fIBPT\fR bytes.
+.TP
+\fBcdbsz\fR={6|10|12|16}
+size of SCSI READ and/or WRITE commands issued on sg device
+names (or block devices when 'iflag=sgio' and/or 'oflag=sgio' is given).
+Default is 10 byte SCSI command blocks (unless calculations indicate
+that a 4 byte block number may be exceeded or \fIBPT\fR is greater than
+16 bits (65535), in which case it defaults to 16 byte SCSI commands).
+.TP
+\fBcoe\fR={0|1|2|3}
+set to 1 or more for continue on error. Only applies to errors on sg
+devices or block devices with the 'sgio' flag set. Thus errors on other
+files will stop sg_dd. Default is 0 which implies stop on any error. See
+the 'coe' flag for more information.
+.TP
+\fBcoe_limit\fR=\fICL\fR
+where \fICL\fR is the maximum number of consecutive bad blocks stepped
+over (due to "coe>0") on reads before the copy terminates. This only
+applies when \fIIFILE\fR is accessed via the SG_IO ioctl. The default
+is 0 which is interpreted as no limit. This option is meant to stop
+the copy soon after unrecorded media is detected while still
+offering "continue on error" capability.
+.TP
+\fBconv\fR=\fBsparse\fR
+see the CONVERSIONS section below.
+.TP
+\fBcount\fR=\fICOUNT\fR
+copy \fICOUNT\fR blocks from \fIIFILE\fR to \fIOFILE\fR. Default is the
+minimum (of \fIIFILE\fR and \fIOFILE\fR) number of blocks that sg devices
+report from SCSI READ CAPACITY commands or that block devices (or their
+partitions) report. Normal files are not probed for their size. If
+\fIskip=SKIP\fR or \fIskip=SEEK\fR are given and the count is derived (i.e.
+not explicitly given) then the derived count is scaled back so that the
+copy will not overrun the device. If the file name is a block device
+partition and \fICOUNT\fR is not given then the size of the partition
+rather than the size of the whole device is used. If \fICOUNT\fR is not
+given (or \fIcount=\-1\fR) and cannot be derived then an error message is
+issued and no copy takes place.
+.TP
+\fBdio\fR={0|1}
+default is 0 which selects indirect (buffered) IO on sg devices. Value of 1
+attempts direct IO which, if not available, falls back to indirect IO and
+notes this at completion. If direct IO is selected and /proc/scsi/sg/allow_dio
+has the value of 0 then a warning is issued (and indirect IO is performed).
+For finer grain control use 'iflag=dio' or 'oflag=dio'.
+.TP
+\fBibs\fR=\fIBS\fR
+if given must be the same as \fIBS\fR given to 'bs=' option.
+.TP
+\fBif\fR=\fIIFILE\fR
+read from \fIIFILE\fR instead of stdin. If \fIIFILE\fR is '\-' then stdin
+is read. Starts reading at the beginning of \fIIFILE\fR unless \fISKIP\fR
+is given.
+.TP
+\fBiflag\fR=\fIFLAGS\fR
+where \fIFLAGS\fR is a comma separated list of one or more flags outlined
+below.  These flags are associated with \fIIFILE\fR and are ignored when
+\fIIFILE\fR is stdin.
+.TP
+\fBobs\fR=\fIBS\fR
+if given must be the same as \fIBS\fR given to 'bs=' option.
+.TP
+\fBodir\fR={0|1}
+when set to one opens block devices (e.g. /dev/sda) with the O_DIRECT
+flag. User memory buffers are aligned to the page size when set. The
+default is 0 (i.e. the O_DIRECT flag is not used). Has no effect on sg,
+normal or raw files. If blk_sgio is also set then both are honoured:
+block devices are opened with the O_DIRECT flag and SCSI commands are
+issued via the SG_IO ioctl.
+.TP
+\fBof\fR=\fIOFILE\fR
+write to \fIOFILE\fR instead of stdout. If \fIOFILE\fR is '\-' then writes
+to stdout.  If \fIOFILE\fR is /dev/null then no actual writes are performed.
+If \fIOFILE\fR is '.' (period) then it is treated the same way as
+/dev/null (this is a shorthand notation). If \fIOFILE\fR exists then it
+is _not_ truncated; it is overwritten from the start of \fIOFILE\fR
+unless 'oflag=append' or \fISEEK\fR is given.
+.TP
+\fBof2\fR=\fIOFILE2\fR
+write output to \fIOFILE2\fR. The default action is not to do this additional
+write (i.e. when this option is not given). \fIOFILE2\fR is assumed to be
+a normal file or a fifo (i.e. a named pipe). \fIOFILE2\fR is opened for writing,
+created if necessary, and closed at the end of the transfer. If \fIOFILE2\fR
+is a fifo (named pipe) then some other command should be consuming that
+data (e.g. 'md5sum OFILE2'), otherwise this utility will block.
+.TP
+\fBoflag\fR=\fIFLAGS\fR
+where \fIFLAGS\fR is a comma separated list of one or more flags outlined
+below.  These flags are associated with \fIOFILE\fR and are ignored when
+\fIOFILE\fR is /dev/null, '.' (period), or stdout.
+.TP
+\fBretries\fR=\fIRETR\fR
+sometimes retries at the host are useful, for example when there is a
+transport error. When \fIRETR\fR is greater than zero then SCSI READs and
+WRITEs are retried on error, \fIRETR\fR times. Default value is zero.
+.TP
+\fBseek\fR=\fISEEK\fR
+start writing \fISEEK\fR bs\-sized blocks from the start of \fIOFILE\fR.
+Default is block 0 (i.e. start of file).
+.TP
+\fBskip\fR=\fISKIP\fR
+start reading \fISKIP\fR bs\-sized blocks from the start of \fIIFILE\fR.
+Default is block 0 (i.e. start of file).
+.TP
+\fBsync\fR={0|1}
+when 1, does SYNCHRONIZE CACHE command on \fIOFILE\fR at the end of the
+transfer. Only active when \fIOFILE\fR is a sg device file name or a block
+device and 'blk_sgio=1' is given.
+.TP
+\fBtime\fR={0|1}
+when 1, times transfer and does throughput calculation, outputting the
+results (to stderr) at completion. When 0 (default) doesn't perform timing.
+.TP
+\fBverbose\fR=\fIVERB\fR
+as \fIVERB\fR increases so does the amount of debug output sent to stderr.
+Default value is zero which yields the minimum amount of debug output.
+A value of 1 reports extra information that is not repetitive. A value
+2 reports cdbs and responses for SCSI commands that are not repetitive
+(i.e. other that READ and WRITE). Error processing is not considered
+repetitive. Values of 3 and 4 yield output for all SCSI commands (and
+Unix read() and write() calls) so there can be a lot of output.
+This only occurs for scsi generic (sg) devices and block devices when
+the 'blk_sgio=1' option is set.
+.TP
+\fB\-\-help\fR
+outputs usage message and exits.
+.TP
+\fB\-\-version\fR
+outputs version number information and exits.
+.TP
+\fB\-V\fR
+outputs version number information and exits.
+.SH CONVERSIONS
+One or more conversions can be given to the "conv=" option. If more than
+one is given, they should be comma separated. sg_dd does not perform the
+traditional dd conversions (e.g. ASCII to EBCDIC). Recently added
+conversions overlap somewhat with the flags so some conversions are
+now supported by sg_dd.
+.TP
+noerror
+this conversion is very close to "iflag=coe" and is treated as such. See
+the "coe" flag. Note that an error on \fIOFILE\fR will stop the copy.
+.TP
+notrunc
+this conversion is accepted for compatibilty with dd and ignored since
+the default action of this utility is not to truncate \fIOFILE\fR.
+.TP
+null
+has no affect, just a placeholder.
+.TP
+sparse
+FreeBSD supports "conv=sparse" so the same syntax is supported in sg_dd.
+See "sparse" in the FLAGS sections for more information.
+.TP
+sync
+is ignored by sg_dd. With dd it means supply zero fill (rather than skip)
+and is typically used like this "conv=noerror,sync" to have the same
+functionality as sg_dd's "iflag=coe".
+.SH FLAGS
+Here is a list of flags and their meanings:
+.TP
+append
+causes the O_APPEND flag to be added to the open of \fIOFILE\fR. For regular
+files this will lead to data appended to the end of any existing data.
+Cannot be used together with the \fIseek=SEEK\fR option as they conflict.
+The default action of this utility is to overwrite any existing data
+from the beginning of the file or, if \fISEEK\fR is given, starting at
+block \fISEEK\fR. Note that attempting to 'append' to a device file (e.g.
+a disk) will usually be ignored or may cause an error to be reported.
+.TP
+coe
+continue on error. Only active for sg devices and block devices that
+have the 'sgio' flag set. 'iflag=coe oflag=coe' and 'coe=1' are
+equivalent. Use this flag twice (e.g. 'iflag=coe,coe') to have the
+same action as the 'coe=2'. A medium, hardware or blank check error
+while reading will re\-read blocks prior to the bad block, then try to
+recover the bad block, supplying zeros if that fails, and finally reread
+the blocks after the bad block. A medium, hardware or blank check error
+while writing is noted and ignored. The recovery of the bad block when
+reading uses the SCSI READ LONG command if 'coe' given twice or
+more (also with the command line option 'coe=2'). Further, the READ LONG
+will set its CORRCT bit if 'coe' given thrice. SCSI disks may automatically
+try and remap faulty sectors (see the AWRE and ARRE in the read write
+error recovery mode page (the sdparm utility can access and possibly change
+these attributes)). Errors occurring on other files types will stop sg_dd.
+Error messages are sent to stderr. This flag is similar
+ o 'conv=noerror,sync' in the
+.B dd(1)
+utility. See note about READ LONG below.
+.TP
+dio
+request the sg device node associated with this flag does direct IO.
+If direct IO is not available, falls back to indirect IO and notes
+this at completion. If direct IO is selected and /proc/scsi/sg/allow_dio
+has the value of 0 then a warning is issued (and indirect IO is performed).
+.TP
+direct
+causes the O_DIRECT flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR. This flag requires some memory alignment on IO. Hence user
+memory buffers are aligned to the page size. Has no effect on sg, normal
+or raw files. If 'iflag=sgio' and/or 'oflag=sgio' is also set then both
+are honoured: block devices are opened with the O_DIRECT flag and SCSI
+commands are issued via the SG_IO ioctl.
+.TP
+dpo
+set the DPO bit (disable page out) in SCSI READ and WRITE commands. Not
+supported for 6 byte cdb variants of READ and WRITE. Indicates that
+data is unlikely to be required to stay in device (e.g. disk) cache.
+May speed media copy and/or cause a media copy to have less impact
+on other device users.
+.TP
+dsync
+causes the O_SYNC flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR. The 'd' is prepended to lower confusion with the 'sync=0|1'
+option which has another action (i.e. a synchronisation to media at the
+end of the transfer).
+.TP
+excl
+causes the O_EXCL flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR.
+.TP
+flock
+after opening the associated file (i.e. \fIIFILE\fR and/or \fIOFILE\fR)
+an attempt is made to get an advisory exclusive lock with the flock()
+system call. The flock arguments are "FLOCK_EX | FLOCK_NB" which will
+cause the lock to be taken if available else a "temporarily unavailable"
+error is generated. An exit status of 90 is produced in the latter case
+and no copy is done.
+.TP
+fua
+causes the FUA (force unit access) bit to be set in SCSI READ and/or WRITE
+commands. This only has an effect with sg devices or block devices
+that have the 'sgio' flag set. The 6 byte variants of the SCSI READ and
+WRITE commands do not support the FUA bit.
+.TP
+nocache
+use posix_fadvise() to advise corresponding file there is no need to fill
+the file buffer with recently read or written blocks.
+.TP
+null
+has no affect, just a placeholder.
+.TP
+sgio
+causes block devices to be accessed via the SG_IO ioctl rather than
+standard UNIX read() and write() commands. When the SG_IO ioctl is
+used the SCSI READ and WRITE commands are used directly to move
+data. sg devices always use the SG_IO ioctl. This flag offers finer
+grain control compared to the otherwise identical 'blk_sgio=1' option.
+.TP
+sparse
+after each \fIBS\fR * \fIBPT\fR byte segment is read from the input,
+it is checked for being all zeros. If so, nothing is written to the output
+file unless this is the last segment of the transfer. This flag is only
+active with the oflag option. It cannot be used when the output is not
+seekable (e.g. stdout). It is ignored if the output file is /dev/null .
+Note that this utility does not remove the \fIOFILE\fR prior to starting
+to write to it. Hence it may be advantageous to manually remove the
+\fIOFILE\fR if it is large prior to using oflag=sparse. The last segment
+is always written so regular files will show the same length and so
+programs like md5sum and sha1sum will generate the same value regardless
+of whether oflag=sparse is given or not. This option may be used when the
+\fIOFILE\fR is a raw device but is probably only useful if the device is
+known to contain zeros (e.g. a SCSI disk after a FORMAT command).
+.SH RETIRED OPTIONS
+Here are some retired options that are still present:
+.TP
+append=0 | 1
+when set, equivalent to 'oflag=append'. When clear the action is
+to overwrite the existing file (if it exists); this is the default.
+See the 'append' flag.
+.TP
+fua=0 | 1 | 2 | 3
+force unit access bit. When 3, fua is set on both \fIIFILE\fR and
+\fIOFILE\fR; when 2, fua is set on \fIIFILE\fR;, when 1, fua is set on
+\fIOFILE\fR; when 0 (default), fua is cleared on both. See the 'fua' flag.
+.SH NOTES
+Block devices (e.g. /dev/sda and /dev/hda) can be given for \fIIFILE\fR.
+If neither '\-iflag=direct', 'iflag=sgio' nor 'blk_sgio=1' is given then
+normal block IO involving buffering and caching is performed. If
+only '\-iflag=direct' is given then the buffering and caching is
+bypassed (this is applicable to both SCSI devices and ATA disks).
+If 'iflag=sgio' or 'blk_sgio=1' is given then the SG_IO ioctl is used on
+the given file causing SCSI commands to be sent to the device and that also
+bypasses most of the actions performed by the block layer (this is only
+applicable to SCSI devices, not ATA disks). The same applies for block
+devices given for \fIOFILE\fR.
+.PP
+Various numeric arguments (e.g. \fISKIP\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.PP
+The \fICOUNT\fR, \fISKIP\fR and \fISEEK\fR arguments can take 64 bit
+values (i.e. very big numbers). Other values are limited to what can fit in
+a signed 32 bit number.
+.PP
+Data usually gets to the user space in a 2 stage process: first the
+SCSI adapter DMAs into kernel buffers and then the sg driver copies
+this data into user memory (write operations reverse this sequence).
+This is called "indirect IO" and there is a 'dio' option to
+select "direct IO" which will DMA directly into user memory. Due to some
+issues "direct IO" is disabled in the sg driver and needs a
+configuration change to activate it. This is typically done
+with 'echo 1 > /proc/scsi/sg/allow_dio'.
+.PP
+All informative, warning and error output is sent to stderr so that
+dd's output file can be stdout and remain unpolluted. If no options
+are given, then the usage message is output and nothing else happens.
+.PP
+Even if READ LONG succeeds on a "bad" block when 'coe=2' (or 'coe=3')
+is given, the recovered data may not be useful. There are no guarantees
+that the user data will appear "as is" in the first 512 bytes.
+.PP
+A raw device must be bound to a block device prior to using sg_dd.
+See
+.B raw(8)
+for more information about binding raw devices. To be safe, the sg device
+mapping to SCSI block devices should be checked with 'cat /proc/scsi/scsi',
+or sg_map before use.
+.PP
+Disk partition information can often be found with
+.B fdisk(8)
+[the "\-ul" argument is useful in this respect].
+.PP
+For sg devices (and block devices when blk_sgio=1 is given) this utility
+issues SCSI READ and WRITE (SBC) commands which are appropriate for disks and
+reading from CD/DVD/HD\-DVD/BD drives. Those commands
+are not formatted correctly for tape devices so sg_dd should not be used on
+tape devices. If the largest block address of the requested transfer
+exceeds a 32 bit block number (i.e 0xffff) then a warning is issued and
+the sg device is accessed via SCSI READ(16) and WRITE(16) commands.
+.PP
+The attributes of a block device (partition) are ignored when 'blk_sgio=1'
+is used. Hence the whole device is read (rather than just the second
+partition) by this invocation:
+.PP
+   sg_dd if=/dev/sdb2 blk_sgio=1 of=t bs=512
+.SH EXAMPLES
+.PP
+Looks quite similar in usage to dd:
+.PP
+   sg_dd if=/dev/sg0 of=t bs=512 count=1MB
+.PP
+This will copy 1 million 512 byte blocks from the device associated with
+/dev/sg0 (which should have 512 byte blocks) to a file called t.
+Assuming /dev/sda and /dev/sg0 are the same device then the above is
+equivalent to:
+.PP
+   dd if=/dev/sda iflag=direct of=t bs=512 count=1000000
+.PP
+although dd's speed may improve if bs was larger and count was suitably
+reduced. The use of the 'iflag=direct' option bypasses the buffering and
+caching that is usually done on a block device.
+.PP
+Using a raw device to do something similar on a ATA disk:
+.PP
+   raw /dev/raw/raw1 /dev/hda
+.br
+   sg_dd if=/dev/raw/raw1 of=t bs=512 count=1MB
+.PP
+To copy a SCSI disk partition to an ATA disk partition:
+.PP
+   raw /dev/raw/raw2 /dev/hda3
+.br
+   sg_dd if=/dev/sg0 skip=10123456 of=/dev/raw/raw2 bs=512
+.PP
+This assumes a valid partition is found on the SCSI disk at the given
+skip block address (past the 5 GB point of that disk) and that
+the partition goes to the end of the SCSI disk. An explicit count
+is probably a safer option. The partition is copied to /dev/hda3 which
+is an offset into the ATA disk /dev/hda . The exact number of blocks
+read from /dev/sg0 are written to /dev/hda (i.e. no padding).
+.PP
+To time a streaming read of the first 1 GB (2 ** 30 bytes) on a disk
+this utility could be used:
+.PP
+   sg_dd if=/dev/sg0 of=/dev/null bs=512 count=2m time=1
+.PP
+On completion this will output a line like:
+"time to transfer data was 18.779506 secs, 57.18 MB/sec". The "MB/sec"
+in this case is 1,000,000 bytes per second.
+.PP
+The 'of2=' option can be used to copy data and take a md5sum of it
+without needing to re\-read the data:
+.PP
+  mkfifo fif
+.br
+  md5sum fif &
+.br
+  sg_dd if=/dev/sg3 iflag=coe of=sg3.img oflag=sparse of2=fif bs=512
+.PP
+This will image /dev/sg3 (e.g. an unmounted disk) and place the contents
+in the (sparse) file sg3.img . Without re\-reading the data it will also
+perform a md5sum calculation on the image.
+.SH SIGNALS
+The signal handling has been borrowed from dd: SIGINT, SIGQUIT and
+SIGPIPE output the number of remaining blocks to be transferred and
+the records in + out counts; then they have their default action.
+SIGUSR1 causes the same information to be output yet the copy continues.
+All output caused by signals is sent to stderr.
+.SH EXIT STATUS
+The exit status of sg_dd is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page. Since this utility works at a higher level
+than individual commands, and there are 'coe' and 'retries' flags,
+individual SCSI command failures do not necessary cause the process
+to exit.
+.PP
+An additional exit status of 90 is generated if the flock flag is given
+and some other process holds the advisory exclusive lock.
+.SH AUTHORS
+Written by Douglas Gilbert and Peter Allworth.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2000\-2013 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+There is a web page discussing sg_dd at http://sg.danny.cz/sg/sg_dd.html
+.PP
+A POSIX threads version of this utility called
+.B sgp_dd
+is in the sg3_utils package. Another version from that package is called
+.B sgm_dd
+and it uses memory mapped IO to speed transfers from sg devices.
+.PP
+The lmbench package contains
+.B lmdd
+which is also interesting. For moving data to and from tapes see
+.B dt
+which is found at http://www.scsifaq.org/RMiller_Tools/index.html
+.PP
+To change mode parameters that effect a SCSI device's caching and error
+recovery see
+.B sdparm(sdparm)
+.PP
+To verify the data on the media or to verify it against some other
+copy of the data see
+.B sg_verify(sg3_utils)
+.PP
+See also
+.B raw(8), dd(1), ddrescue(GNU), ddpt
diff --git a/sg3_utils/doc/sg_decode_sense.8 b/sg3_utils/doc/sg_decode_sense.8
new file mode 100644
index 0000000..e68a795
--- /dev/null
+++ b/sg3_utils/doc/sg_decode_sense.8
@@ -0,0 +1,131 @@
+.TH SG_DECODE_SENSE "8" "August 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_decode_sense \- decode SCSI sense data
+.SH SYNOPSIS
+.B sg_decode_sense
+[\fI\-\-binary=FN\fR] [\fI\-\-file=FN\fR] [\fI\-\-help\fR]
+[\fI\-\-hex\fR] [\fI\-\-nospace\fR] [\fI\-\-status=SS\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-write=WFN\fR]
+[H1 H2 H3 ...]
+.SH DESCRIPTION
+.\" Add any additional description here
+This utility takes SCSI sense data in binary or as a sequence of
+ASCII hexadecimal bytes and decodes it. The primary reference for the
+decoding is SPC\-3 ANSI INCITS 408-2005 and the most recent draft
+SPC\-4 revision 37 which can be found at http://www.t10.org and other
+locations on the internet.
+.PP
+SCSI sense data is often found in kernel log files as a result of
+something going wrong but may just be informative. It is often shown as
+a sequence of hexadecimal bytes, starting with 70, 71, 72, 73, f0 or f1.
+Sense data could be up to 252 bytes long but typically is much shorter
+than that, 18 bytes long is often seen and is usually associated with
+the older "fixed" format sense data.
+.PP
+The sense data can be provided on the command line or in a file. If
+given on the command line the sense data should be a sequence of
+hexadecimal bytes separated by space. Alternatively a file can be
+given with the contents in binary or ASCII hexadecimal bytes. The
+latter form can contain several lines each with none, one or more
+ASCII hexadecimal bytes separated by space (comma or tab). The
+hash symbol may appear and it and the rest of the line is ignored
+making it useful for comments.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-binary\fR=\fIFN\fR
+the sense data is read in binary from a file called \fIFN\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+this option is used in conjunction with \fI\-\-write=WFN\fR in order to
+change the output written to \fIWFN\fR to lines of ASCII hex bytes suitable
+for a C language compiler. Each line contains up to 16 bytes (e.g. a line
+starting with "0x3b,0x07,0x00,0xff").
+.TP
+\fB\-f\fR, \fB\-\-file\fR=\fIFN\fR
+the sense data is read in ASCII hexadecimal from a file called \fIFN\fR.
+The sense data should appear as a sequence of bytes separated by space,
+comma, tab or newline. Everything from and including a hash symbol to the
+end of that line is ignored. If \fI\-\-nospace\fR is set then no separator
+is required between the ASCII hexadecimal digits in \fIFN\fR with bytes
+decoded from pairs of ASCII hexadecimal digits.
+.TP
+\fB\-n\fR, \fB\-\-nospace\fR
+expect ASCII hexadecimal to be a string of hexadecimal digits with no
+spaces between them. Bytes are decoded by taking two hexadecimal digits
+at a time, so an even number of digits is expected. The string of
+hexadecimal digits may be on the command line (replacing "H1 H2 H3")
+or spread across multiple lines the \fIFN\fR given to \fI\-\-file=\fR.
+On the command line, spaces (or other whitespace characters) between
+sequences of hexadecimal digits are ignored; the maximum command line
+hex string is 1023 characters long.
+.TP
+\fB\-s\fR, \fB\-\-status\fR=\fISS\fR
+where \fISS\fR is a SCSI status byte value, given in hexadecimal. The
+SCSI status byte is related to but distinct from sense data.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the degree of verbosity (debug messages).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+output version string then exit.
+.TP
+\fB\-w\fR, \fB\-\-write\fR=\fIWFN\fR
+writes the sense data out to a file called \fIWFN\fR. If necessary \fIWFN\fR
+is created. If \fIWFN\fR exists then it is truncated prior to writing the
+sense data to it. If the \fI\-\-hex\fR option is also given then ASCII hex
+is written to \fIWFN\fR (see the \fI\-\-hex\fR option description);
+otherwise binary is written to \fIWFN\fR. This option is a convenience and
+may be helpful in converting the ASCII hexadecimal representation of sense
+data (or anything else) into the equivalent binary or a compilable ASCII
+hex form.
+.SH NOTES
+Unlike most utilities in this package, this utility does not access a
+SCSI device (logical unit). This utility accesses a library associated
+with this package. Amongst other things the library decodes SCSI sense
+data.
+.PP
+T10 defined SCSI command names given a CDB can be decoded using the sg_raw
+utility with the '\-vvv' option.
+.SH EXAMPLES
+Sense data is often printed out in kernel logs and sometimes on the
+command line when verbose or debug flags are given. It will be at least
+8 bytes long, often 18 bytes long but may be longer. A sense data string
+might look like this:
+.PP
+f0 00 03 00 00 12 34 0a  00 00 00 00 11 00 00 00
+.br
+00 00
+.PP
+Cut and paste it after the sg_decode_sense command:
+.PP
+  sg_decode_sense f0 00 03 00 00 12 34 0a 00 00 00 00 11 00 00 00 00 00
+.PP
+and for this sense data the output should look like this:
+.PP
+ Fixed format, current;  Sense key: Medium Error
+.br
+ Additional sense: Unrecovered read error
+.br
+  Info fld=0x1234 [4660]
+.PP
+For a medium error the Info field is the logical block address (LBA)
+of the lowest numbered block that the associated SCSI command was not
+able to read (verify or write).
+.SH EXIT STATUS
+The exit status of sg_decode_sense is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2010\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_requests,sg_raw(sg3_utils)
diff --git a/sg3_utils/doc/sg_emc_trespass.8 b/sg3_utils/doc/sg_emc_trespass.8
new file mode 100644
index 0000000..94b592b
--- /dev/null
+++ b/sg3_utils/doc/sg_emc_trespass.8
@@ -0,0 +1,52 @@
+.TH SG_EMC_TRESPASS "8" "December 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_emc_trespass \- change ownership of SCSI LUN from another
+Service\-Processor to this one
+.SH SYNOPSIS
+.B sg_emc_trespass
+[\fI\-d\fR] [\fI\-hr\fR] [\fI\-s\fR]
+[\fI\-V\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+sg_emc_trespass sends an EMC\-specific Trespass Command to the \fIDEVICE\fR
+with the selected options. This Mode Select changes the ownership of the LUN
+of the device from another Service\-Processor to the one the command was
+received on.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-d\fR
+outputs some extra debug information associated with executing this command
+.TP
+\fB\-hr\fR
+Sets the 'Honor Reservation' bit in the command. If set, the trespass
+will only succeed to change the ownership from the Peer SP if the Peer
+SP does not have an outstanding SCSI reservation for the LUN. By
+default, the reservation state will be ignored.
+.TP
+\fB\-s\fR
+Send the short version of the trespass command instead of the long
+version. The short version is supported on the EMC FC5300, FC4500 and
+FC4700. The long version (default) is supported on the CLARiiON CX and
+AX family arrays.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be a SCSI
+generic (sg) device. In the 2.6 series block devices (e.g. SCSI disks
+and DVD drives) can also be specified. For example "sg_start 0 /dev/sda"
+will work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_emc_trespass is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Lars Marowsky\-Bree, based on sg_start.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2012 Lars Marowsky\-Bree, Douglas Gilbert.
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg3_utils/doc/sg_format.8 b/sg3_utils/doc/sg_format.8
new file mode 100644
index 0000000..2e8cda2
--- /dev/null
+++ b/sg3_utils/doc/sg_format.8
@@ -0,0 +1,571 @@
+.TH SG_FORMAT "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_format \- format, resize a SCSI disk or format a tape
+.SH SYNOPSIS
+.B sg_format
+[\fI\-\-cmplst=\fR{0|1}] [\fI\-\-count=COUNT\fR] [\fI\-\-dcrt\fR]
+[\fI\-\-early\fR] [\fI\-\-ffmy=FFMT\fR] [\fI\-\-fmtpinfo=FPI\fR]
+[\fI\-\-format\fR] [\fI\-\-help\fR] [\fI\-\-ip_def\fR] [\fI\-\-long\fR]
+[\fI\-\-mode=MP\fR] [\fI\-\-pfu=PFU\fR] [\fI\-\-pie=PIE\fR] [\fI\-\-pinfo\fR]
+[\fI\-\-poll=PT\fR] [\fI\-\-resize\fR] [\fI\-\-rto_req\fR]
+[\fI\-\-security\fR] [\fI\-\-six\fR] [\fI\-\-size=SIZE\fR] [\fI\-\-tape=FM\fR]
+[\fI\-\-verbose\fR] [\fI\-\-verify\fR] [\fI\-\-version\fR] [\fI\-\-wait\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Not all SCSI direct access devices need to be formatted and some have vendor
+specific formatting procedures. SCSI disks with rotating media are probably
+the largest group that do support a 'standard' format operation. They are
+typically factory formatted to a block size of 512 bytes with the largest
+number of blocks that the manufacturer recommends. The manufacturer's
+recommendation typically leaves aside a certain number of tracks, spread
+across the media, for reassignment of blocks to logical block addresses
+during the life of the disk.
+.PP
+This utility can format modern SCSI disks and potentially change their block
+size (if permitted) and the block count (i.e. number of accessible blocks on
+the media also known as "resizing"). Resizing a disk to less than the
+manufacturer's recommended block count is sometimes called "short
+stroking" (see NOTES section). Resizing the block count while not changing
+the block size may not require a format operation. The SBC\-2 standard (see
+www.t10.org) has obsoleted the "format device" mode page. Many of the low
+level details found in that mode page are now left up to the discretion of
+the manufacturer.
+.PP
+When this utility is used without options (i.e. it is only given a
+\fIDEVICE\fR argument) it prints out the existing block size and block count
+derived from two sources. These two sources are a block descriptor in the
+response to a MODE SENSE command and the response to a READ CAPACITY command.
+The reason for this double check is to detect a "format corrupt" state (see
+the NOTES section). This usage will not modify the disk.
+.PP
+When this utility is used with the "\-\-format" (or "\-F") option it will
+attempt to format the given DEVICE. There is a 15 second pause during which
+time the user is invited thrice (5 seconds apart) to abort sg_format. This
+occurs just prior the SCSI FORMAT UNIT command being issued. See the NOTES
+section for more information.
+.PP
+Protection information is optional and is made up of one or more protection
+intervals, each made up of 8 bytes associated with each logical block. Four
+protection types are defined with protection type 0 being no protection
+intervals. See the PROTECTION INFORMATION section below for more information.
+.PP
+When the \fI\-\-tape=FM\fR option is given then the SCSI FORMAT MEDIUM
+command is sent to the \fIDEVICE\fR. FORMAT MEDIUM is defined in SSC and
+prepares a volume for use which may include partitioning the medium. See
+the section below on TAPE for more information.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long
+option name.
+.TP
+\fB\-C\fR, \fB\-\-cmplst\fR={0|1}
+sets the CMPLST ("complete list") bit in the FORMAT UNIT cdb to 0 or 1.
+The default is 1 in which case the existing GLIST (grown list) is ignored.
+If the value is 0 then the existing GLIST is taken into account. See the
+LISTS section below. In most cases this bit should be left set; some MO
+disk drives need this bit cleared.
+.TP
+\fB\-c\fR, \fB\-\-count\fR=\fICOUNT\fR
+where \fICOUNT\fR is the number of blocks to be formatted or media to be
+resized to. Can be used with either \fI\-\-format\fR or \fI\-\-resize\fR.
+With \fI\-\-format\fR this option need not be given in which case it is
+assumed to be zero. With \fI\-\-format\fR the interpretation of \fICOUNT\fR
+is:
+.br
+  (\fICOUNT\fR > 0) : only format the first \fICOUNT\fR blocks and READ
+.br
+                CAPACITY will report \fICOUNT\fR blocks after format
+.br
+  (\fICOUNT\fR = 0) and block size unchanged : use existing block count
+.br
+  (\fICOUNT\fR = 0) and block size changed : recommended maximum block
+.br
+                                       count for new block size
+.br
+  (\fICOUNT\fR = \-1) : use recommended maximum block count
+.br
+  (\fICOUNT\fR < \-1) : illegal
+.br
+With \fI\-\-resize\fR this option must be given and \fICOUNT\fR has this
+interpretation:
+.br
+  (\fICOUNT\fR > 0) : after resize READ CAPACITY will report \fICOUNT\fR
+.br
+                blocks
+.br
+  (\fICOUNT\fR = 0) : after resize READ CAPACITY will report 0 blocks
+.br
+  (\fICOUNT\fR = \-1) : after resize READ CAPACITY will report its
+.br
+                 maximum number of blocks
+.br
+  (\fICOUNT\fR < \-1) : illegal
+.br
+In both cases if the given \fICOUNT\fR exceeds the maximum number of
+blocks (for the block size) then the disk reports an error.
+See NOTES section below.
+.TP
+\fB\-D\fR, \fB\-\-dcrt\fR
+this option sets the DCRT bit in the FORMAT UNIT command's parameter list
+header. It will "disable certification". Certification verifies that blocks
+are usable during the format process. Using this option may speed the format.
+The default action of this utility (i.e. when this option is not given) is
+to clear the DCRT bit thereby requesting "media certification". When the DCRT
+bit is set, the FOV bit must also be set hence sg_format does that.
+.TP
+\fB\-e\fR, \fB\-\-early\fR
+during a format operation, The default action of this utility is to poll the
+disk every 60 seconds to determine the progress of the format operation until
+it is finished. When this option is given this utility will exit "early",
+that is as soon as the format operation has commenced. Then the user can
+monitor the progress of the ongoing format operation with other
+utilities (e.g. sg_turs(8) or sg_requests(8)). This option and \fI\-\-wait\fR
+are mutually exclusive.
+.TP
+\fB\-t\fR, \fB\-\-ffmt\fR=\fIFFMT\fR
+\fIFFMT\fR is placed in a field of the same name in the FORMAT UNIT cdb.
+The field was introduced in SBC\-4 revision 10. The default value is 0 which
+implies the former action to typically write to all blocks on the
+\fIDEVICE\fR and that can take a long time.
+.br
+\fIFFMT\fR has values 1 and 2 for fast format with 3 being reserved
+currently. The difference between 1 and 2 concerns read operations on LBAs to
+which no data has been written to, since the fast format. When \fIFFMT\fR
+is 1 the read operation should return "unspecified logical block data" and
+complete without error. When \fIFFMT\fR is 2 the read operation should
+yield check condition status with a sense key set to hardware error, medium
+error or command aborted. See SBC\-4 revsion 10 section 4.35 for more
+details.
+.TP
+\fB\-f\fR, \fB\-\-fmtpinfo\fR=\fIFPI\fR
+sets the FMTPINFO field in the FORMAT UNIT cdb to a value between 0 and 3.
+The default value is 0. The FMTPINFO field from SBC\-3 revision 16 is a 2
+bit field (bits 7 and 6 of byte 1 in the cdb). Prior to that it was a single
+bit field (bit 7 of byte 1 in the cdb) and there was an accompanying bit
+called RTO_REQ (bit 6 of byte 1 in the cdb). The deprecated
+options "\-\-pinfo" and "\-\-rto\-req" represent the older usage. This
+option should be used in their place. See the PROTECTION INFORMATION section
+below for more information.
+.TP
+\fB\-F\fR, \fB\-\-format\fR
+issue a SCSI FORMAT UNIT command.
+.B This will destroy all the data held on the media.
+This option is required to change the block size of a disk. The user is given
+a 15 second count down to ponder the wisdom of doing this, during which time
+control\-C (amongst other Unix commands) can be used to kill this process
+before it does any damage.
+.br
+When used three times (or more) the preliminary MODE SENSE and SELECT
+commands are bypassed, leaving only the initial INQUIRY and FORMAT UNIT
+commands. This is for emergency use (e.g. when the MODE SENSE/SELECT
+commands are not working) and cannot change the logical block size.
+.br
+See NOTES section for implementation details and EXAMPLES section for typical
+use.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage information then exit.
+.TP
+\fB\-I\fR, \fB\-\-ip_def\fR
+sets the default Initialization Pattern. Some disks (SSDs) use this to flag
+that a format should fully provision (i.e. associate a physical block with
+every logical block). The same disks (SSDs) might thin provision if this
+option is not given. If this option is given then the \fI\-\-security\fR
+option cannot be given.
+.TP
+\fB\-l\fR, \fB\-\-long\fR
+the default action of this utility is to assume 32 bit logical block
+addresses. With 512 byte block size this permits more than 2
+terabytes (almost 2 ** 41 bytes) on a single disk. This option selects
+commands and parameters that allow for 64 bit logical block addresses.
+Specifically this option sets the "longlba" flag in the MODE SENSE (10)
+command and uses READ CAPACITY (16) rather than READ CAPACITY (10). If this
+option is not given and READ CAPACITY (10) or MODE SELECT detects a disk
+the needs more than 32 bits to represent its logical blocks then it is
+set internally. This option does not set the LONGLIST bit in the FORMAT UNIT
+command. The LONGLIST bit is set as required depending other
+parameters (e.g. when '\-\-pie=PIE' is greater than zero).
+.TP
+\fB\-M\fR, \fB\-\-mode\fR=\fIMP\fR
+\fIMP\fR is a mode page number (0 to 62 inclusive) that will be used for
+reading and perhaps changing the device logical block size. The default
+is 1 which is the Read\-Write Error Recovery mode page.
+.TP
+\fB\-P\fR, \fB\-\-pfu\fR=\fIPFU\fR
+sets the "Protection Field Usage" field in the parameter block associated
+with a FORMAT UNIT command to \fIPFU\fR. The default value is 0, the only
+other defined value currently is 1. See the PROTECTION INFORMATION section
+below for more information.
+.TP
+\fB\-q\fR, \fB\-\-pie\fR=\fIPIE\fR
+sets the "Protection Interval Exponent" field in the parameter block
+associated with a FORMAT UNIT command to \fIPIE\fR. The default value is 0.
+\fIPIE\fR can only be non-zero with protection types 2 and 3.
+The value of 0 is typical for 512 byte blocks; with 4096 byte blocks a value
+of 3 may be appropriate (i.e. 8 protection intervals interleaved with 4096
+bytes of user data). A device may not support any non-zero values. This
+field first appeared in SBC\-3 revision 18.
+.TP
+\fB\-p\fR, \fB\-\-pinfo\fR
+this option is deprecated, use the \fI\-\-fmtpinfo=FPI\fR option instead.
+If used, then it sets bit 7 of byte 1 in the FORMAT UNIT cdb and that
+is equivalent to setting \fI\-\-fmtpinfo=2\fR. [So if \fI\-\-pinfo\fR is
+used (plus \fI\-\-fmtpinfo=FPI\fR and \fI\-\-pfu=PFU\fR are not given or
+their arguments are 0) then protection type 1 is selected.]
+.TP
+\fB\-x\fR, \fB\-\-poll\fR=\fIPT\fR
+where \fIPT\fR is the type of poll used. If \fIPT\fR is 0 then a TEST UNIT
+READY command is used, otherwise a REQUEST SENSE command is used. The
+default is currently 0 but this will change to 1 in the near future. See
+the NOTES sections below.
+.TP
+\fB\-r\fR, \fB\-\-resize\fR
+rather than format the disk, it can be resized. This means changing the
+number of blocks on the device reported by the READ CAPACITY command.
+This option should be used with the \fI\-\-count=COUNT\fR option.
+The contents of all logical blocks on the media remain unchanged when
+this option is used. This means that any resize operation can be
+reversed. This option cannot be used together with either \fI\-\-format\fR
+or a \fI\-\-size=SIZE\fR whose argument is different to the existing block
+size.
+.TP
+\fB\-R\fR, \fB\-\-rto_req\fR
+The option is deprecated, use the \fI\-\-fmtpinfo=FPI\fR option instead.
+If used, then it sets bit 6 of byte 1 in the FORMAT UNIT cdb.
+.TP
+\fB\-S\fR, \fB\-\-security\fR
+sets the "Security Initialization" (SI) bit in the FORMAT UNIT command's
+initialization pattern descriptor within the parameter list. According
+to SBC\-3 the default initialization pattern "shall be written using a
+security erasure write technique". See the NOTES section on the SCSI
+SANITIZE command. If this option is given then the \fI\-\-ip_def\fR option
+cannot be given.
+.TP
+\fB\-6\fR, \fB\-\-six\fR
+Use 6 byte variants of MODE SENSE and MODE SELECT. The default action
+is to use the 10 byte variants. Some MO drives need this option set
+when doing a format.
+.TP
+\fB\-s\fR, \fB\-\-size\fR=\fISIZE\fR
+where \fISIZE\fR is the block size (i.e. number of bytes in each block) to
+format the device to. The default value is whatever is currently reported
+by the block descriptor in a MODE SENSE command. If the block size given by
+this option is different from the current value then a MODE SELECT command
+is used to change it prior to the FORMAT UNIT command being started (as
+recommended in the draft standard). Many SCSI disks have 512 byte sectors
+by default and allow up to 16 bytes extra in a sector (i.e. 528 byte
+sectors).  If the given size in unacceptable with the disk, most likely
+an "Invalid field in parameter list" message will appear in sense
+data (requires the use of '\-v' to decode sense data).
+.TP
+\fB\-T\fR, \fB\-\-tape\fR=\fIFM\fR
+will send a FORMAT MEDIUM command to the \fIDEVICE\fR with the FORMAT field
+set to \fIFM\fR. This option is used to prepare a tape (i.e. the "medium")
+in a tape drive for use. Values for \fIFM\fR include 0 to do the "default"
+format; 1 to partition a volume and 2 to do a default format then partition.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output). "\-vvv" gives
+a lot more debug output.
+.TP
+\fB\-y\fR, \fB\-\-verify\fR
+set the VERIFY bit in the FORMAT MEDIUM cdb. The default is that the VERIFY
+bit is clear. This option is only appropriate for tapes.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-w\fR, \fB\-\-wait\fR
+the default format action is to set the "IMMED" bit in the FORMAT UNIT
+command's (short) parameter header. If this option (i.e. \fI\-\-wait\fR) is
+given then the "IMMED" bit is not set. If \fI\-\-wait\fR is given the
+FORMAT UNIT command waits until the format operation completes before
+returning its response. This can be many hours on large disks. This
+utility sets a 15 hour timeout on such a FORMAT UNIT command! Some recent
+SSDs go to the other extreme of completing a format operation in 1.5
+seconds hence waiting is not an issue.
+.SH LISTS
+The SBC\-3 draft (revision 36) defines PLIST, CLIST, DLIST and GLIST in
+section 4.13 on "Medium defects". Briefly, the PLIST is the "primary"
+list of manufacturer detected defects, the CLIST ("certification" list)
+contains those detected during the format operation, the DLIST is a list of
+defects that can be given to the format operation. The GLIST is the grown
+list which starts in the format process as CLIST+DLIST and can "grow" later
+due to automatic reallocation (see the ARRE and AWRE bits in the
+Read\-Write Error Recovery mode page (see sdparm(8))) and use of the
+SCSI REASSIGN BLOCKS command (see sg_reassign(8)).
+.PP
+The CMPLST bit (controlled by the \fI\-\-cmplst=\fR0|1 option) determines
+whether the existing GLIST, when the format operation is invoked,
+is taken into account. The sg_format utility sets the FOV bit to zero
+which causes DPRY=0, so the PLIST is taken into account, and DCRT=0, so
+the CLIST is generated and used during the format process.
+.PP
+The sg_format utility does not permit a user to provide a defect
+list (i.e. DLIST).
+.SH PROTECTION INFORMATION
+Protection Information (PI) is additional information held with logical
+blocks so that an application and/or host bus adapter can check the
+correctness of those logical blocks. PI is placed in one or more
+protection intervals beside each logical block. A protection interval
+contains 8 bytes made up of a 2 byte "logical block guard" (CRC), a 2
+byte "logical block application guard", and a 4 byte "logical block
+reference tag". Devices with 512 byte logical block size typically have
+one protection interval appended, making its logical block data 520 bytes
+long. Devices with 4096 byte logical block size often have 8 protection
+intervals spread across its logical block data for a total size of 4160
+bytes. Note that for all other purposes the logical block size is considered
+to be 512 and 4096 bytes respectively.
+.PP
+SBC\-3 drafts have added several "protection types" to the PI introduced in
+the SBC\-2 standard. SBC\-3 defines 4 protection types (types 0 to 3) with
+protection type 0 meaning no PI is maintained. While a device may support
+one or more protection types, it can only be formatted with 1 of the 4. To
+change a device's protection type, it must be re\-formatted. For more
+information see the Protection Information in section 4.22 of draft SBC\-3
+revision 36.
+.PP
+A device that supports PI information (i.e. supports one or more protection
+types 1, 2 and 3) sets the "PROTECT" bit in its standard INQUIRY response. It
+also sets the SPT field in the EXTENDED INQUIRY VPD page response to indicate
+which protection types it supports. Given PROTECT=1 then SPT=0 implies the
+device supports PI type 1 only, SPT=1 implies the device supports PI types 1
+and 2, and various other non\-obvious mappings up to SPT=7 which implies
+protection types 1, 2 and 3 are supported. The
+.B current
+protection type of a disk can be found in the "P_TYPE" and "PROT_EN"
+fields in the response of a READ CAPACITY (16) command (e.g. with
+the 'sg_readcap \-\-long' utility).
+.PP
+Given that a device supports a particular protection type, a user can
+then choose to format that disk with that protection type by setting
+the "FMTPINFO" and "Protection Field Usage" fields in the FORMAT UNIT
+command. Those fields correspond to the \fI\-\-fmtpinfo=FPI\fR and the
+\fI\-\-pfu=PFU\fR options in this utility. The list below shows the four
+protection types followed by the options of this utility needed to select
+them:
+.br
+  \fB0\fR : \-\-fmtpinfo=0 \-\-pfu=0
+.br
+  \fB1\fR : \-\-fmtpinfo=2 \-\-pfu=0
+.br
+  \fB2\fR : \-\-fmtpinfo=3 \-\-pfu=0
+.br
+  \fB3\fR : \-\-fmtpinfo=3 \-\-pfu=1
+.br
+The default value of \fIFPI\fR (in \fI\-\-fmtpinfo=FPI\fR) is 0 and the
+default value of \fIPFU\fR (in \fI\-\-pfu=PFU\fR) is 0. So if neither
+\fI\-\-fmtpinfo=FPI\fR nor \fI\-\-pfu=PFU\fR are given then protection
+type 0 (i.e. no protection information) is chosen.
+.SH NOTES
+The SBC\-2 standard states that the REQUEST SENSE command should be used
+for obtaining progress indication when the format command is underway.
+However, tests on a selection of disks shows that TEST UNIT READY
+commands yield progress indications (but not REQUEST SENSE commands). So
+the current version of this utility defaults to using TEST UNIT READY
+commands to poll the disk to find out the progress of the format. The
+\fI\-\-poll=PT\fR option has been added to control this.
+.PP
+When the \fI\-\-format\fR option is given without the \fI\-\-wait\fR option
+then the SCSI FORMAT UNIT command is issued with the IMMED bit set which
+causes the SCSI command to return after it has started the format operation.
+The \fI\-\-early\fR option will cause sg_format to exit at that point.
+Otherwise the \fIDEVICE\fR is polled every 60 seconds with TEST UNIT READY
+or REQUEST SENSE commands until it reports an "all clear" (i.e. the format
+operation has completed). Normally these polling commands will result in a
+progress indicator (expressed as a percentage) being output to the screen.
+If the user gets bored watching the progress report then sg_format process
+can be terminated (e.g. with control\-C) without affecting the format
+operation which continues. However a target or device reset (or a power
+cycle) will probably cause the device to become "format corrupt".
+.PP
+When the \fI\-\-format\fR and \fI\-\-wait\fR options are both given then
+this utility may take a long time to return. In this case care should be
+taken not to send any other SCSI commands to the disk as it may not respond
+leaving those commands queued behind the active format command. This may
+cause a timeout in the OS driver (in a lot shorter period than 15 hours
+applicable to some format operations). This may result in the OS resetting
+the disk leaving the format operation incomplete. This may leave the
+disk in a "format corrupt" state requiring another format to remedy
+the situation.
+.PP
+When the block size (i.e. the number of bytes in each block) is changed
+on a disk two SCSI commands must be sent: a MODE SELECT to change the block
+size followed by a FORMAT command. If the MODE SELECT command succeeds and
+the FORMAT fails then the disk may be in a state that the draft standard
+calls "format corrupt". A block descriptor in a subsequent MODE SENSE
+will report the requested new block size while a READ CAPACITY command
+will report the existing (i.e. different) block size. Alternatively
+the READ CAPACITY command may fail, reporting the device is not ready,
+potentially requiring a format. The solution to this situation is to
+do a format again (and this time the new block size does not have to
+be given) or change the block size back to the original size.
+.PP
+The SBC\-2 standard states that the block count can be set back to the
+manufacturer's maximum recommended value in a format or resize operation.
+This can be done by placing an address of 0xffffffff (or the 64 bit
+equivalent) in the appropriate block descriptor field to a MODE SELECT
+command. In signed (two's complement) arithmetic that value corresponds to
+'\-1'. So a \fI\-\-count=\fR\-1 causes the block count to be set back to
+the manufacturer's maximum recommended value. To see exactly which SCSI
+commands are being executed and parameters passed add the "\-vvv" option to
+the sg_format command line.
+.PP
+Short stroking is a technique to trade off capacity for performance. Rotating
+disk performance is usually highest on the outer tracks (i.e. lower logical
+block addresses) so by resizing or reformatting a disk to a smaller capacity,
+average performance will usually be increased.
+.PP
+Other utilities may be useful in finding information associated with
+formatting. These include sg_inq(8) to fetch standard INQUIRY
+information (e.g. the PROTECT bit) and to fetch the EXTENDED INQUIRY
+VPD page (e.g. RTO and GRD_CHK bits). The sdparm(8) utility can be
+used to access and potentially change the now obsolete format mode page.
+.PP
+scsiformat is another utility available for formatting SCSI disks
+with Linux. It dates from 1997 (most recent update) and may be useful for
+disks whose firmware is of that vintage.
+.PP
+The \fICOUNT\fR numeric argument may include a multiplicative suffix or be
+given in hexadecimal. See the "NUMERIC ARGUMENTS" section in the
+sg3_utils(8) man page.
+.PP
+The SCSI SANITIZE command was introduced in SBC\-3 revision 27. It is closely
+related to the ATA sanitize disk feature set and can be used to remove all
+existing data from a disk. Sanitize is more likely to be implemented on
+modern disks (including SSDs) than FORMAT UNIT's security initialization
+feature (see the \fI\-\-security\fR option) and in some cases much faster.
+.PP
+SSDs that support thin provisioning will typically unmap all logical blocks
+during a format. The reason is to improve the SSD's endurance. Also thin
+provisioned formats typically complete faster than fully provisioned ones
+on the same disk (see the \fI\-\-ip_def\fR option). In either case format
+operations on SSDs tend to be a lot faster than they are on hard disks with
+spinning media.
+.SH TAPE
+Tape system use a variant of the FORMAT UNIT command used on disks. Tape
+systems use the FORMAT MEDIUM command which is simpler with only three
+fields in the cdb typically used. Apart from sharing the same opcode the
+cdbs of FORMAT UNIT and FORMAT MEDIUM are quite different. FORMAT MEDIUM's
+fields are VERIFY, IMMED and FORMAT (with TRANSFER LENGTH always set to 0).
+The VERIFY bit field is set with the \fI\-\-verify\fR option. The IMMED bit
+is manipulated by the \fI\-\-wait\fR option in the same way it is for disks;
+one difference is that if the \fI\-\-poll=PT\fR option is not given then it
+defaults to \fIPT\fR of 1 which means the poll is done with REQUEST SENSE
+commands.
+.PP
+The argument given to the \fI\-\-tape=FM\fR option is used to set the FORMAT
+field. \fIFM\fR can take values from "\-1" to "15" where "\-1" (the default)
+means don't do a tape format; value "8" to "15" are for vendor specific
+formats. The \fI\-\-early\fR option may also be used to set the IMMED
+bit and then exit this utility (rather than poll periodically until it is
+finished). In this case the tape drive will still be busy doing the format
+for some time but, according to T10, should still respond in full to the
+INQUIRY and REPORT LUNS commands. Other commands (including REQUEST SENSE)
+should yield a "not ready" sense key with an additional sense code
+of "Logical unit not ready, format in progress". Additionally REQUEST SENSE
+should contain a progress indication in its sense data.
+.PP
+When \fIFM\fR is 1 or 2 then the settings in the Medium partition mode page
+control the partitioning. That mode page can be viewed and modified with the
+sdparm utility.
+.PP
+Prior to invoking this utility the tape may need to be positioned to the
+beginning of partition 0. In Linux that can typically be done with the mt
+utility (e.g. 'mt -f /dev/st0 rewind').
+.SH EXAMPLES
+These examples use Linux device names. For suitable device names in
+other supported Operating Systems see the sg3_utils(8) man page.
+.PP
+In the first example below simply find out the existing block count and
+size derived from two sources: a block descriptor in a MODE SELECT command
+response and from the response of a READ CAPACITY commands. No changes
+are made:
+.PP
+   # sg_format /dev/sdm
+.PP
+Now a simple format, leaving the block count and size as they were previously.
+The FORMAT UNIT command is executed in IMMED mode and the device is polled
+every 60 seconds to print out a progress indication:
+.PP
+   # sg_format \-\-format /dev/sdm
+.PP
+Now the same format, but waiting (passively) until the format operation is
+complete:
+.PP
+   # sg_format \-\-format \-\-wait /dev/sdm
+.PP
+Next is a format in which the block size is changed to 520 bytes and the block
+count is set to the manufacturer's maximum value (for that block size). Note,
+not all disks support changing the block size:
+.PP
+   # sg_format \-\-format \-\-size=520 /dev/sdm
+.PP
+Now a resize operation so that only the first 0x10000 (65536) blocks on a disk
+are accessible. The remaining blocks remain unaltered.
+.PP
+   # sg_format \-\-resize \-\-count=0x10000 /dev/sdm
+.PP
+Now resize the disk back to its normal (maximum) block count:
+.PP
+   # sg_format \-\-resize \-\-count=\-1 /dev/sdm
+.PP
+One reason to format a SCSI disk is to add protection information. First
+check which protection types are supported by a disk (by checking the SPT
+field in the Extended inquiry VPD page together with the Protect bit in the
+standard inquiry response):
+.PP
+   # sg_vpd \-p ei \-l /dev/sdb
+.br
+   extended INQUIRY data VPD page:
+.br
+     ACTIVATE_MICROCODE=0
+.br
+     SPT=1 [protection types 1 and 2 supported]
+.br
+     ....
+.PP
+Format with type 1 protection:
+.PP
+   # sg_format \-\-format \-\-fmtpinfo=2 /dev/sdm
+.PP
+After a successful format with type 1 protection, READ CAPACITY(16)
+should show something like this:
+.PP
+   # sg_readcap \-l /dev/sdm
+.br
+   Read Capacity results:
+.br
+      Protection: prot_en=1, p_type=0, p_i_exponent=0 [type 1 protection]
+.br
+      Logical block provisioning: lbpme=0, lbprz=0
+.br
+      ....
+.PP
+To format with type 3 protection:
+.PP
+   # sg_format \-\-format \-\-fmtpinfo=3 \-\-pfu=1 /dev/sdm
+.PP
+For the disk shown above this will probably fail because the Extended inquiry
+VPD page showed only types 1 and 2 protection are supported.
+.SH EXIT STATUS
+The exit status of sg_format is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page. Unless the \fI\-\-wait\fR option is given, the
+exit status may not reflect the success of otherwise of the format.
+Using sg_turs(8) and sg_readcap(8) after the format operation may be wise.
+.SH AUTHORS
+Written by Grant Grundler, James Bottomley and Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2005\-2016 Grant Grundler, James Bottomley and Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_turs(8), sg_requests(8), sg_inq(8), sg_modes(8), sg_vpd(8),
+.B sg_reassign(8), sg_readcap(8), sg3_utils(8),
+.B sg_sanitize(8) [all in sg3_utils],
+.B mt(mt-st), sdparm(8), scsiformat (old), hdparm(8)
diff --git a/sg3_utils/doc/sg_get_config.8 b/sg3_utils/doc/sg_get_config.8
new file mode 100644
index 0000000..ba0c392
--- /dev/null
+++ b/sg3_utils/doc/sg_get_config.8
@@ -0,0 +1,143 @@
+.TH SG_GET_CONFIG "8" "December 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_get_config \- send SCSI GET CONFIGURATION command (MMC\-4 +)
+.SH SYNOPSIS
+.B sg_get_config
+[\fI\-\-brief\fR] [\fI\-\-current\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-inner\-hex\fR] [\fI\-\-list\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR]
+[\fI\-\-rt=RT\fR] [\fI\-\-starting=FC\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI GET CONFIGURATION command to \fIDEVICE\fR and decodes the
+response. The response includes the features and profiles of the device.
+Typically these devices are CD, DVD, HD\-DVD and BD players that may (but
+not necessarily) have media in them. These devices may well be connected via
+ATAPI, USB or IEEE 1394 transports. In such cases they are "SCSI" devices
+only in the sense that they use the "Multi\-Media command" set (MMC).
+MMC is a specialized SCSI command set whose definition can be found
+at http://www.t10.org .
+.PP
+This utility is based on the MMC\-4 and later draft standards. See section
+5 on "Features and Profile for Multi_Media devices" for more information on
+specific feature parameters and profiles. The manufacturer's product manual
+may also be useful.
+.PP
+Since modern DVD and BD writers support many features and profiles, the
+decoded output from this utility can be large. There are various ways to cut
+down the output. If the \fI\-\-brief\fR option is used only the feature names
+are shown and the feature parameters are not decoded. Alternatively if only
+one feature is of interest then this combination of options is
+appropriate: "\-\-rt=2 \-\-starting=\fIFC\fR". Another possibility is to show
+only the features that are relevant to the media in the drive (i.e. "current")
+with the "\-\-rt=1" option.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-brief\fR
+show the feature names but don't decode the parameters of those features.
+When used with \fI\-\-list\fR outputs known feature names but not known
+profile names.
+.TP
+\fB\-c\fR, \fB\-\-current\fR
+output features marked as current. This option is equivalent to '\-\-rt=1'.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output the response in hex (don't decode response).
+.TP
+\fB\-i\fR, \fB\-\-inner\-hex\fR
+decode to the feature name level then output each feature's data in hex.
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+list all known feature and profile names. Ignore the device name (if given).
+Simply lists the feature names and profiles (followed by their hex values)
+that this utility knows about. If \fI\-\-brief\fR is also given then only
+feature names are listed.
+.TP
+\fB\-q\fR, \fB\-\-readonly\fR
+opens the DEVICE read\-only rather than read\-write which is the
+default. The Linux sg driver needs read\-write access for the SCSI
+GET CONFIGURATION command but other access methods may require
+read\-only access.
+.TP
+\fB\-r\fR, \fB\-\-rt\fR=\fIRT\fR
+where \fIRT\fR is the field of that name in the GET CONFIGURATION cdb.
+Allowable values are 0, 1, 2, or 3 . The command's action also depends on
+the value given to the \fI\-\-starting=FC\fR option. The default value is 0.
+When \fIRT\fR is 0 then all features, regardless of currency, are
+returned (whose feature code is greater than or equal to \fIFC\fR given
+to \fI\-\-starting=\fR). When \fIRT\fR is 1 then all current features are
+returned (whose feature code is greater than or equal to \fIFC\fR). When
+\fIRT\fR is 2 then the feature whose feature code is equal to \fIFC\fR,
+if any, is returned.  When \fIRT\fR is 3 the response is reserved (probably
+yields an "illegal field in cdb" error). To simplify the meanings of the
+\fIRT\fR values are:
+.br
+  \fB0\fR : all features, current on not
+.br
+  \fB1\fR : only current features
+.br
+  \fB2\fR : only feature whose code is \fIFC\fR
+.br
+  \fB3\fR : reserved
+.br
+.TP
+\fB\-R\fR, \fB\-\-raw\fR
+output response in binary (to stdout). Note that the short form is \fI\-R\fR
+unlike most other utilities in this package that use \fI\-r\fR for this
+action.
+.TP
+\fB\-s\fR, \fB\-\-starting\fR=\fIFC\fR
+where \fIFC\fR is the feature code value. This option works closely with
+the \fI\-\-rt=RT\fR option. The \fIFC\fR value is in the range 0 to
+65535 (0xffff) inclusive. Its default value is 0. A value prefixed
+with "0x" (or a trailing 'h') is interpreted as hexadecimal.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+There are multiple versions of the MMC (draft) standards: MMC [1997],
+MMC\-2 [2000],  MMC\-3 [2002], MMC\-4 and MMC\-5. The first three are now
+ANSI INCITS standards with the year they became standards shown in
+brackets. The draft immediately prior to standardization can
+be found at http://www.t10.org . In the initial MMC standard there
+was no GET CONFIGURATION command and the relevant information was
+obtained from the "CD capabilities and mechanical status mode
+page" (mode page 0x2a). It was later renamed the "MM capabilities and
+mechanical status mode page" and has been made obsolete in MMC\-4 and
+MMC\-5. The GET CONFIGURATION command was introduced in MMC\-2 and has
+become a replacement for that mode page. New features such as support
+for "BD" (blue ray) media type can only be found by using the
+GET CONFIGURATION command. Hence older CD players may not support
+the GET CONFIGURATION command in which case the "MM capabilities ..."
+mode page can be checked with sdparm(8), sginfo(8) or sg_modes(8).
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be
+a SCSI generic (sg) device. In the 2.6 series block devices
+can also be specified. For example "sg_get_config /dev/hdc"
+will work in the 2.6 series kernels as long as /dev/hdc is
+an ATAPI device. In the 2.6 series external DVD writers attached
+via USB could be queried with "sg_get_config /dev/scd1" for example.
+.SH EXIT STATUS
+The exit status of sg_get_config is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2012 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sginfo(8), sg_modes(8), sg_inq(8), sg_prevent(8),
+.B sg_start(8) [all in sg3_utils],
+.B sdparm(8)
diff --git a/sg3_utils/doc/sg_get_lba_status.8 b/sg3_utils/doc/sg_get_lba_status.8
new file mode 100644
index 0000000..30f179a
--- /dev/null
+++ b/sg3_utils/doc/sg_get_lba_status.8
@@ -0,0 +1,88 @@
+.TH SG_GET_LBA_STATUS "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_get_lba_status \- send SCSI GET LBA STATUS command
+.SH SYNOPSIS
+.B sg_get_lba_status
+[\fI\-\-brief\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-lba=LBA\fR]
+[\fI\-\-maxlen=LEN\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send the SCSI GET LBA STATUS command to the \fIDEVICE\fR and outputs the
+response. This command was introduced in (draft) SBC\-3 revision 20 and
+devices that support logical block provisioning should support this command.
+.PP
+The default action is to decode the response into one LBA status descriptor
+per line output to stdout. The descriptor LBA is output in hex (prefixed
+by '0x') and the number of blocks is output in decimal followed by the
+provisioning status in decimal. The provisioning status can be in the
+range 0 to 15 of which only 0 (mapped), 1 (unmapped) and 2 (anchored) are
+used currently. The amount of output can be reduced by the \fI\-\-brief\fR
+option.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-brief\fR
+when use once then one LBA status descriptor per line is output to stdout.
+Each line has this
+format: "0x<descriptor_LBA>  0x<blocks> <provisioning_status>". So the
+descriptor's starting LBA and number of blocks are output in hex and the
+provisioning status in decimal. When used twice (e.g. '\-bb' or '\-\-brief
+\-\-brief') then the provisioning status of the given \fILBA\fR (or LBA 0
+if the \fI\-\-lba\fR option is not given) is output to stdout. A check is
+made that the given \fILBA\fR lies in the range of the first returned LBA
+status descriptor (as it should according to SBC\-3 revision 20) and
+warnings are sent to stderr if it doesn't.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output response to this command in ASCII hex.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the starting Logical Block Address (LBA) to check the
+provisioning status for. Note that the \fIDEVICE\fR chooses how many
+following blocks that it will return provisioning status for.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in
+the cdb's "allocation length" field. If not given then 24 is used. 24 is
+enough space for the response header and one LBA status descriptor.
+\fILEN\fR should be 8 plus a multiple of 16 (e.g. 24, 40, and 56 are suitable).
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response in binary (to stdout).
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output). Additional output
+caused by this option is sent to stderr.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+In SBC\-3 revision 25 the calculation associated with the Parameter Data
+Length field in the response was modified. Prior to that the byte offset
+was 8 and in revision 25 it was changed to 4.
+.PP
+For a discussion of logical block provisioning see section 4.7 of sbc3r29.pdf
+at http://www.t10.org (or the corresponding section of a later draft).
+.SH EXIT STATUS
+The exit status of sg_get_lba_status is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2009\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_write_same(8), sg_unmap(8)
diff --git a/sg3_utils/doc/sg_ident.8 b/sg3_utils/doc/sg_ident.8
new file mode 100644
index 0000000..e223bea
--- /dev/null
+++ b/sg3_utils/doc/sg_ident.8
@@ -0,0 +1,119 @@
+.TH SG_IDENT "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_ident \- send SCSI REPORT/SET IDENTIFYING INFORMATION command
+.SH SYNOPSIS
+.B sg_ident
+[\fI\-\-ascii\fR] [\fI\-\-clear\fR] [\fI\-\-help\fR] [\fI\-\-itype=IT\fR]
+[\fI\-\-raw\fR] [\fI\-\-set\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send a SCSI REPORT IDENTIFYING INFORMATION or SET IDENTIFYING INFORMATION
+command to \fIDEVICE\fR. Prior to SPC\-4 (revision 7) these
+commands were called REPORT DEVICE IDENTIFIER and SET DEVICE IDENTIFIER
+respectively. SCSI devices that support these two commands allow users
+to write (set) identifying information and report it back at some
+later time. The information is persistent (i.e. stored on some
+non\-volatile medium within the SCSI device that will survive a power
+outage).
+.PP
+Typically the space allocated for the information is limited:
+SPC\-4 (revision 7) states that for information type 0, the minimum
+length is 64 bytes and the maximum is 512 bytes. For other information
+types (1 to 126 inclusive) the maximum length is 256 bytes. Also
+information types 1 to 126 (inclusive) should contain a null
+terminated UTF\-8 string. The author has seen older disks that only
+support 16 bytes.
+.PP
+The default action when no options are given is to invoke the
+Report Identifying Information command with the information type defaulting
+to zero. Error reports are sent to stderr. By default the information is
+shown in ASCII\-HEX (up to 16 bytes per line) with an ASCII representation
+to the right with dots replacing non printable characters.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-A\fR, \fB\-\-ascii\fR
+invokes the Report Identifying Information command and if anything is
+found interprets it as ASCII (or UTF\-8 depending on the locale)
+and prints the information to stdout.
+.TP
+\fB\-C\fR, \fB\-\-clear\fR
+invokes the Set Identifying Information command with an information length
+of zero. This has the effect of clearing the existing information.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-i\fR, \fB\-\-itype\fR=\fIIT\fR
+where \fIIT\fR is the information type. Defaults to zero. The maximum value
+is 127 which is special and cannot be used with \fI\-\-set\fR or
+\fI\-\-clear\fR. The information type of 127 (if supported) causes the REPORT
+IDENTIFYING INFORMATION command to respond with a list of available
+information types and their maximum lengths in bytes. The odd numbered
+information types between 3 and 125 (inclusive) are not to be used (as they
+clash with the SCC\-2 standard).
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+invokes the Report Identifying information command and if anything
+is found sends the information (which may be binary) to stdout. Nothing else
+is sent to stdout however error reports, if any, are sent to stderr.
+.TP
+\fB\-S\fR, \fB\-\-set\fR
+first reads stdin until an EOF is detected then invokes the Set Identifying
+Information command to set what has been fetched from stdin as the
+information. The amount of data read must be between 1 and 512 bytes
+length (inclusive).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.PP
+This utility permits users to write their own identifying information to
+their SCSI devices. There are several other types of descriptors (or
+designators) that the user cannot change. These include the SCSI INQUIRY
+command with its standard vendor and product identification strings and the
+product revision level; plus the large amount of information provided by
+the "Device Identification" VPD page (see sg_vpd). There is also the READ
+MEDIA SERIAL NUMBER command (see sg_rmsn). The MMC\-4 command set for CD
+and DVDs has a "media serial number" feature (0x109) [and a "logical unit
+serial number" feature]. These can be viewed with the sg_get_config utility.
+.SH EXAMPLES
+First, to see if there is an existing information whose format
+is unknown (for information type 0), use no options:
+.PP
+  # sg_ident /dev/sdb
+.br
+   00     31 32 33 34 35 36 37 38  39 30          1234567890
+.PP
+If it is ASCII then it can printed as such:
+.PP
+  # sg_ident \-\-ascii /dev/sdb
+.br
+  1234567890
+.PP
+The information can be copied to a file, cleared and then
+re\-asserted with this sequence:
+.PP
+  # sg_ident \-\-raw /dev/sdb > t
+.br
+  # sg_ident \-\-clear /dev/sdb
+.br
+  # cat t | sg_ident \-\-set /dev/sdb
+.SH EXIT STATUS
+The exit status of sg_ident is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2005\-2012 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_vpd(sg3_utils), sg_rmsn(sg3_utils), sg_get_config(sg3_utils)
diff --git a/sg3_utils/doc/sg_inq.8 b/sg3_utils/doc/sg_inq.8
new file mode 100644
index 0000000..11b4c16
--- /dev/null
+++ b/sg3_utils/doc/sg_inq.8
@@ -0,0 +1,436 @@
+.TH SG_INQ "8" "January 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_inq \- issue SCSI INQUIRY command and/or decode its response
+.SH SYNOPSIS
+.B sg_inq
+[\fI\-\-ata\fR] [\fI\-\-block=0|1\fR] [\fI\-\-cmddt\fR]
+[\fI\-\-descriptors\fR] [\fI\-\-export\fR] [\fI\-\-extended\fR]
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-id\fR] [\fI\-\-inhex=FN\fR]
+[\fI\-\-len=LEN\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR]
+[\fI\-\-raw\fR] [\fI\-\-vendor\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] [\fI\-\-vpd\fR] \fIDEVICE\fR
+.PP
+.B sg_inq
+[\fI\-36\fR] [\fI\-a\fR] [\fI\-A\fR] [\fI\-b\fR] [\fI\-\-B=0|1\fR] [\fI\-c\fR]
+[\fI\-cl\fR] [\fI\-d\fR] [\fI\-e\fR] [\fI\-h\fR] [\fI\-H\fR] [\fI\-i\fR]
+[\fI\-I=FN\fR] [\fI\-l=LEN\fR] [\fI\-m\fR] [\fI\-M\fR] [\fI\-o=OPCODE_PG\fR]
+[\fI\-p=VPD_PG\fR] [\fI\-P\fR] [\fI\-r\fR] [\fI\-s\fR] [\fI\-u\fR] [\fI\-v\fR]
+[\fI\-V\fR] [\fI\-x\fR] [\fI\-36\fR] [\fI\-?\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility, when \fIDEVICE\fR is given, sends a SCSI INQUIRY command to it
+then outputs the response. All SCSI devices are meant to respond to
+a "standard" INQUIRY command with at least a 36 byte response (in SCSI 2 and
+higher). An INQUIRY is termed as "standard" when both the EVPD and CmdDt (now
+obsolete) bits are clear.
+.PP
+Alternatively the \fI\-\-inhex=FN\fR option can be given. In this case
+\fIFN\fR is assumed to be a file name ('\-' for stdin) containing ASCII
+hexadecimal representing an INQUIRY response.
+.PP
+This utility supports two command line syntaxes. The preferred one is shown
+first in the synopsis and is described in the main OPTIONS section. A later
+section titled OLDER COMMAND LINE OPTIONS describes the second group of
+options.
+.PP
+An important "non\-standard" INQUIRY page is the Device Identification
+Vital Product Data (VPD) page [0x83]. Since SPC\-3, support for this page
+is mandatory. The \fI\-\-id\fR option decodes this page. New VPD page
+information is no longer being added to this utility. To get information
+on new VPD pages see the sg_vpd(8) or sdparm(8) utilities.
+.PP
+In Linux, if the \fIDEVICE\fR exists and the SCSI INQUIRY fails (e.g. because
+the SG_IO ioctl is not supported) then an ATA IDENTIFY (PACKET) DEVICE is
+tried. If it succeeds then device identification strings are output. The
+\fI\-\-raw\fR and \fI\-\-hex\fR options can be used to manipulate the output.
+If the \fI\-\-ata\fR option is given then the SCSI INQUIRY is not performed
+and the \fIDEVICE\fR is assumed to be ATA (or ATAPI).
+.PP
+The reference document used for interpreting an INQUIRY is T10/BSR INCITS
+502 Revision 07 which is draft SPC\-5 revision 07, 26 November 2015). It can
+be found at http://www.t10.org . Obsolete and reserved items in the standard
+INQUIRY response output are displayed in brackets. The reference document for
+the ATA IDENTIFY (PACKET) DEVICE command is ATA8\-ACS found at
+http://www.t13.org .
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long
+option name.
+.TP
+\fB\-a\fR, \fB\-\-ata\fR
+Assume given \fIDEVICE\fR is an ATA or ATAPI device which can receive ATA
+commands from the host operating system. Skip the SCSI INQUIRY command and
+use either the ATA IDENTIFY DEVICE command (for non-packet devices) or the
+ATA IDENTIFY PACKET DEVICE command. To show the response in hex, add
+a '\-\-verbose' option. This option is only available in Linux.
+.TP
+\fB\-B\fR, \fB\-\-block\fR=\fI0|1\fR
+this option controls how the file handle to the \fIDEVICE\fR is opened. If
+this argument is 0 then the open is non\-blocking. If the argument is 1 then
+the open is blocking. In Unix a non\-blocking open is indicated by a
+O_NONBLOCK flag while a blocking open is indicated by the absence of that
+flag. The default value depends on the operating system and the type of
+\fIDEVICE\fR node. For Linux pass\-throughs (i.e. the sg and bsg drivers)
+the default is 0.
+.TP
+\fB\-c\fR, \fB\-\-cmddt\fR
+set the Command Support Data (CmdDt) bit (defaults to clear(0)). Used in
+conjunction with the \fI\-\-page=PG\fR option where \fIPG\fR specifies the
+SCSI command opcode to query. When used twice (e.g. '\-cc') this utility
+forms a list by looping over all 256 opcodes (0 to 255 inclusive) only
+outputting a line for found commands. The CmdDt bit is now obsolete.
+It has been replaced by the REPORT SUPPORTED OPERATION CODES command,
+see the sg_opcodes(8) utility.
+.TP
+\fB\-d\fR, \fB\-\-descriptors\fR
+decodes and prints the version descriptors found in a standard INQUIRY
+response. There are up to 8 of them. Version descriptors indicate which
+versions of standards and/or drafts the \fIDEVICE\fR complies with. The
+normal components of a standard INQUIRY are output (typically from
+the first 36 bytes of the response) followed by the version descriptors
+if any.
+.TP
+\fB\-e\fR
+see entry below for \fI\-\-vpd\fR.
+.TP
+\fB\-u\fR, \fB\-\-export\fR
+prints out information obtained from the device. The output can be
+modified by selecting a VPD page with \fIPG\fR (from
+\fI\-\-page=PG\fR). If the device identification VPD page 0x83 is
+given it prints out information in the form:
+"SCSI_IDENT_<assoc>_<type>=<ident>" to stdout. If the device serial
+number VPD page 0x80 is given it prints out information in the form:
+"SCSI_SERIAL=<ident>". Other VPD pages are not supported. If no VPD
+page is given it prints out information in the form:
+"SCSI_VENDOR=<vendor>", "SCSI_MODEL=<model>", and
+"SCSI_REVISION=<rev>", taken from the standard inquiry. This may be
+useful for tools like udev(7) in Linux.
+.TP
+\fB\-E\fR, \fB\-x\fR, \fB\-\-extended\fR
+prints the extended INQUIRY VPD page [0x86].
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit. When used twice, after the
+usage message, there is a list of available abbreviations than can be
+given to the \fI\-\-page=PG\fR option.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+rather than decode a standard INQUIRY response, a VPD page or command
+support data; print out the response in hex to stdout. Error messages and
+warnings are typically output to stderr. When used twice with the ATA
+Information VPD page [0x89] decodes the start of the response then outputs
+the ATA IDENTIFY (PACKET) DEVICE response in hexadecimal bytes (not 16 bit
+words). When used three times with the ATA Information VPD page [0x89] or
+the \fI\-\-ata\fR option, this utility outputs the ATA IDENTIFY (PACKET)
+DEVICE response in hexadecimal words suitable for input
+to 'hdparm \-\-Istdin'.  See note below.
+.br
+To generate output suitable for placing in a file that can be used by a
+later invocation with the \fI\-\-inhex=FN\fR option, use the '\-HHHH'
+option (e.g. 'sg_inq \-p di -HHHH /dev/sg3 > dev_id.hex').
+.TP
+\fB\-i\fR, \fB\-\-id\fR
+prints the device identification VPD page [0x83].
+.TP
+\fB\-I\fR, \fB\-\-inhex\fR=\fIFN\fR
+\fIFN\fR is expected to be a file name (or '\-' for stdin) which contains
+ASCII hexadecimal or binary representing an INQUIRY (including VPD page)
+response. This utility will then decode that response. It is preferable to
+also supply the \fI\-\-page=PG\fR option, if not this utility will attempt
+to guess which VPD page (or standard INQUIRY) the response is associated
+with. The hexadecimal should be arranged as 1 or 2 digits representing a
+byte each of which is whitespace or comma separated. Anything from and
+including a hash mark to the end of line is ignored. If the \fI\-\-raw\fR
+option is also given then \fIFN\fR is treated as binary.
+.TP
+\fB\-l\fR, \fB\-\-len\fR=\fILEN\fR
+the number \fILEN\fR is the "allocation length" field in the INQUIRY cdb.
+This is the (maximum) length of the response to be sent by the device.
+The default value of \fILEN\fR is 0 which is interpreted as: first request
+is for 36 bytes and if necessary execute another INQUIRY if the "additional
+length" field in the response indicates that more than 36 bytes is available.
+If \fILEN\fR is greater than 0 then only one INQUIRY command is performed.
+See paragraph below about "36 byte INQUIRYs".
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+this option has the same action as the \fI\-\-len=LEN\fR option. It has
+been added for compatibility with the sg_vpd, sg_modes and sg_logs
+utilities.
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPG\fR
+the \fIPG\fR argument can be either a number of an abbreviation for a VPD
+page. To enumerate the available abbreviations for VPD pages use '\-hh' or
+a bad abbreviation (e.g, '\-\-page=xxx'). When the \fI\-\-cmddt\fR option is
+given (once) then \fIPG\fR is interpreted as an opcode number (so VPD page
+abbreviations make little sense).
+.br
+If \fIPG\fR is a negative number, then a standard INQUIRY is performed. This
+can be used to override some guessing logic associated with the
+\fI\-\-inhex=FN\fR option.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+in the absence of \fI\-\-inhex=FN\fR then output response in binary.
+The output should be piped to a file or another utility when this option is
+used. The binary is sent to stdout, and errors are sent to stderr.
+.br
+if used with \fI\-\-inhex=FN\fR then the contents of \fIFN\fR is treated as
+binary.
+.TP
+\fB\-s\fR, \fB\-\-vendor\fR
+output a standard INQUIRY response's vendor specific field from offset 36
+to 55 in ASCII. When used twice (i.e. '\-ss') also output the vendor
+specific field from offset 96 in ASCII. This is only done if the data
+passes some simple sanity checks.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string then exit.
+.TP
+\fB\-e\fR, \fB\-\-vpd\fR
+set the Enable Vital Product Data (EVPD) bit (defaults to clear(0)). Used in
+conjunction with the \fI\-\-page=PG\fR option where \fIPG\fR specifies the
+VPD page number to query. If the \fI\-\-page=PG\fR is not given then \fIPG\fR
+defaults to zero which is the "Supported VPD pages" VPD page.
+.SH NOTES
+Some devices with weak SCSI command set implementations lock up when
+they receive commands they don't understand (or even response lengths
+that they don't expect). Such devices need to be treated carefully,
+use the '\-\-len=36' option. Without this option this utility will issue
+an initial standard INQUIRY requesting 36 bytes of response data. If
+the device indicates it could have supplied more data then a second
+INQUIRY is issued to fetch the longer response. That second command may
+lock up faulty devices.
+.PP
+ATA or ATAPI devices that use a SCSI to ATA Translation layer (see
+SAT at www.t10.org) may support the ATA Information VPD page. This
+returns the IDENTIFY (PACKET) DEVICE response amongst other things.
+The ATA Information VPD page can be fetched with '\-\-page=ai'.
+.PP
+In the INQUIRY standard response there is a 'MultiP' flag which is set
+when the device has 2 or more ports. Some vendors use the preceding
+vendor specific ('VS') bit to indicate which port is being accessed by
+the INQUIRY command (0 \-> relative port 1 (port "a"), 1 \-> relative
+port 2 (port "b")). When the 'MultiP' flag is set, the preceding vendor
+specific bit is shown in parentheses. SPC\-3 compliant devices should
+use the device identification VPD page (0x83) to show which port is
+being used for access and the SCSI ports VPD page (0x88) to show all
+available ports on the device.
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be
+a SCSI generic (sg) device. In the 2.6 series block devices (e.g. disks
+and ATAPI DVDs) can also be specified. For example "sg_inq /dev/sda"
+will work in the 2.6 series kernels. From lk 2.6.6 other SCSI "char"
+device names may be used as well (e.g. "/dev/st0m").
+.PP
+The number of bytes output by \fI\-\-hex\fR and \fI\-\-raw\fR is 36 bytes
+or the number given to \fI\-\-len=LEN\fR (or \fI\-\-maxlen=LEN\fR). That
+number is reduced if the "resid" returned by the HBA indicates less bytes
+were sent back from \fIDEVICE\fR.
+.PP
+The \fIDEVICE\fR is opened with a read\-only flag (e.g. in Unix with the
+O_RDONLY flag).
+.SH ATA DEVICES
+There are two major types of ATA devices: non\-packet devices (e.g. ATA
+disks) and packet devices (ATAPI). The majority of ATAPI devices are
+CD/DVD/BD drives in which the ATAPI transport carries the MMC set (i.e.
+a SCSI command set). Further, both types of ATA devices can be connected
+to a host computer via a "SCSI" (or some other) transport. When an
+ATA disk is controlled via a SCSI (or non\-ATA) transport then two
+approaches are commonly used: tunnelling (e.g. STP in Serial Attached
+SCSI (SAS)) or by emulating a SCSI device (e.g. with a SCSI to
+ATA translation layer, see SAT at www.t10.org ). Even when the
+physical transport to the host computer is ATA (especially in the
+case of SATA) the operating system may choose to put a SAT
+layer in the driver "stack" (e.g. libata in Linux).
+.PP
+The main identifying command for any SCSI device is an INQUIRY. The
+corresponding command for an ATA non\-packet device is IDENTIFY DEVICE
+while for an ATA packet device it is IDENTIFY PACKET DEVICE.
+.PP
+When this utility is invoked for an ATAPI device (e.g. a CD/DVD/BD
+drive with "sg_inq /dev/hdc") then a SCSI INQUIRY is sent to the
+device and if it responds then the response to decoded and output and
+this utility exits. To see the response for an ATA IDENTIFY PACKET
+DEVICE command add the \fI\-\-ata\fR option (e.g. "sg_inq \-\-ata /dev/hdc).
+.PP
+This utility doesn't decode the response to an ATA IDENTIFY (PACKET)
+DEVICE command, hdparm does a good job at that. The '\-HHH' option has
+been added for use with either the '\-\-ata' or '\-\-page=ai'
+option to produce a format acceptable to "hdparm \-\-Istdin".
+An example: 'sg_inq \-\-ata \-HHH /dev/hdc | hdparm \-\-Istdin'. See hdparm.
+.SH EXIT STATUS
+The exit status of sg_inq is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using \fI\-\-old\fR (or \fI\-O\fR) as the first option.
+.TP
+\fB\-36\fR
+only requests 36 bytes of response data for an INQUIRY. Furthermore even
+if the device indicates in its response it can supply more data, a
+second (longer) INQUIRY is not performed. This is a paranoid setting.
+Equivalent to '\-\-len=36' in the OPTIONS section.
+.TP
+\fB\-a\fR
+fetch the ATA Information VPD page [0x89]. Equivalent to '\-\-page=ai' in
+the OPTIONS section. This page is defined in SAT (see at www.t10.org).
+.TP
+\fB\-A\fR
+Assume given \fIDEVICE\fR is an ATA or ATAPI device.
+Equivalent to \fI\-\-ata\fR in the OPTIONS section.
+.TP
+\fB\-b\fR
+decodes the Block Limits VPD page [0xb0].  Equivalent to '\-\-page=bl' in
+the OPTIONS section. This page is defined in SBC\-2 (see www.t10.org).
+.TP
+\fB\-B\fR=\fI0|1\fR
+equivalent to \fI\-\-block=0|1\fR in OPTIONS section.
+.TP
+\fB\-c\fR
+set the Command Support Data (CmdDt) bit (defaults to clear(0)). Used in
+ conjunction with the \fI\-o=OPCODE_PG\fR option to specify the SCSI command
+opcode to query. Equivalent to \fI\-\-cmddt\fR in the OPTIONS section.
+.TP
+\fB\-cl\fR
+lists the command data for all supported commands (followed by the command
+name) by looping through all 256 opcodes. This option uses the CmdDt bit
+which is now obsolete. See the sg_opcodes(8) utility.
+Equivalent to '\-\-cmddt \-\-cmddt' in the OPTIONS section.
+.TP
+\fB\-d\fR
+decodes depending on context. If \fI\-e\fR option is given, or any option
+that implies \fI\-e\fR (e.g. '\-i' or '\-p=80'), then this utility attempts
+to decode the indicated VPD page.  Otherwise the version descriptors (if any)
+are listed following a standard INQUIRY response. In the version descriptors
+sense, equivalent to \fI\-\-descriptors\fR in the OPTIONS section.
+.TP
+\fB\-e\fR
+enable (i.e. sets) the Vital Product Data (EVPD) bit (defaults to clear(0)).
+Used in conjunction with the \fI\-p=VPD_PG\fR option to specify the VPD page
+to fetch. If \fI\-p=VPD_PG\fR is not given then VPD page 0 (list supported
+VPD pages) is assumed.
+.TP
+\fB\-h\fR
+outputs INQUIRY response in hex rather than trying to decode it.
+Equivalent to \fI\-\-hex\fR in the OPTIONS section.
+.TP
+\fB\-H\fR
+same action as \fI\-h\fR.
+Equivalent to \fI\-\-hex\fR in the OPTIONS section.
+.TP
+\fB\-i\fR
+decodes the Device Identification VPD page [0x83]. Equivalent to \fI\-\-id\fR
+in the OPTIONS section. This page is made up of several "designation
+descriptors". If \fI\-h\fR is given then each descriptor header is decoded
+and the identifier itself is output in hex. To see the whole VPD 0x83 page
+response in hex use '\-p=83 \-h'.
+.TP
+\fB\-I\fR=\fIFN\fR
+equivalent to \fI\-\-inhex=FN\fR in the OPTIONS section.
+.TP
+\fB\-m\fR
+decodes the Management network addresses VPD page [0x85]. Equivalent
+to '\-\-page=mna' in the OPTIONS section.
+.TP
+\fB\-M\fR
+decodes the Mode page policy VPD page [0x87].  Equivalent to '\-\-page=mpp'
+in the OPTIONS section.
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-o\fR=\fIOPCODE_PG\fR
+used in conjunction with the \fI\-e\fR or \fI\-c\fR option. If neither given
+then the \fI\-e\fR option assumed. When the \fI\-e\fR option is also
+given (or assumed) then the argument to this option is the VPD page number.
+The argument is interpreted as hexadecimal and is expected to be in the range
+0 to ff inclusive. Only VPD page 0 is decoded and it lists supported VPD pages
+and their names (if known). To decode the mandatory device identification
+page (0x83) use the \fI\-i\fR option. A now obsolete usage is when the
+\fI\-c\fR option is given in which case the argument to this option is assumed
+to be a command opcode number. Recent SCSI draft standards have moved this
+facility to a separate command (see sg_opcodes(8)). Defaults to 0 so if
+\fI\-e\fR is given without this option then VPD page 0 is output.
+.TP
+\fB\-p\fR=\fIVPD_PG\fR
+same action as \fI\-o=OPCODE_PG\fR option described in the previous entry.
+Since the opcode value with the CmdDt is now obsolete, the main use of this
+option is to specify the VPD page number. The argument is interpreted as
+hexadecimal and is expected to be in the range 0 to ff inclusive.
+Defaults to 0 so if \fI\-e\fR is given without this option then VPD page 0
+is output.
+.TP
+\fB\-P\fR
+decodes the Unit Path Report VPD page [0xc0] which is EMC specific.
+Equivalent to '\-\-page=upr' in the OPTIONS section.
+.TP
+\fB\-r\fR
+outputs the response in binary to stdout.  Equivalent to \fI\-\-raw\fR in
+the OPTIONS section.  Can be used twice (i.e. '\-rr' (and '\-HHH' has
+same effect)) and if used with the \fI\-A\fR or \fI\-a\fR option yields
+output with the same format as "cat /proc/ide/hd<x>/identify" so that it
+can then be piped to "hdparm \-\-Istdin".
+.TP
+\fB\-s\fR
+decodes the SCSI Ports VPD page [0x88].
+Equivalent to '\-\-page=sp' in the OPTIONS section.
+.TP
+\fB\-u\fR
+equivalent to '\-\-export' in the OPTIONS section.
+.TP
+\fB\-v\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.TP
+\fB\-x\fR
+decodes the Extended INQUIRY data VPD [0x86] page.
+Equivalent to '\-\-page=ei' in the OPTIONS section.
+.TP
+\fB\-?\fR
+output usage message and exit. Ignore all other parameters.
+.SH EXAMPLES
+The examples in this page use Linux device names. For suitable device
+names in other supported Operating Systems see the sg3_utils(8) man page.
+.PP
+To view the standard inquiry response use without options:
+.PP
+   sg_inq /dev/sda
+.PP
+Some SCSI devices include version descriptors indicating the various
+SCSI standards and drafts they support. They can be viewed with:
+.PP
+   sg_inq \-d /dev/sda
+.PP
+Modern SCSI devices include Vital Product Data (VPD)pages which can be
+viewed with the SCSI INQUIRY command. To list the supported VPD
+pages (but not their contents) try:
+.PP
+   sg_inq \-e /dev/sda
+.PP
+Some VPD pages can be read with the sg_inq utility but a newer utility
+called sg_vpd specializes in showing their contents. The sdparm utility
+can also be used to show the contents of VPD pages.
+.PP
+Further examples of sg_inq together with some typical output can be found
+on http://sg.danny.cz/sg/sg3_utils.html web page.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2001\-2016 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_opcodes(8), sg_vpd(8), sdparm(8), hdparm(8), sgdiag(scsirastools)
diff --git a/sg3_utils/doc/sg_logs.8 b/sg3_utils/doc/sg_logs.8
new file mode 100644
index 0000000..2356d03
--- /dev/null
+++ b/sg3_utils/doc/sg_logs.8
@@ -0,0 +1,436 @@
+.TH SG_LOGS "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_logs \- access log pages with SCSI LOG SENSE command
+.SH SYNOPSIS
+.B sg_logs
+[\fI\-\-All\fR] [\fI\-\-all\fR] [\fI\-\-brief\fR] [\fI\-\-control=PC\fR]
+[\fI\-\-enumerate\fR] [\fI\-\-enum_vendor\fR] [\fI\-\-filter=FL\fR]
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-in=FN\fR] [\fI\-\-list\fR]
+[\fI\-\-maxlen=LEN\fR] [\fI\-\-name\fR] [\fI\-\-no_inq\fR] [\fI\-\-page=PG\fR]
+[\fI\-\-paramp=PP\fR] [\fI\-\-pcb\fR] [\fI\-\-ppc\fR] [\fI\-\-raw\fR]
+[\fI\-\-readonly\fR] [\fI\-\-reset\fR] [\fI\-\-select\fR] [\fI\-\-sp\fR]
+[\fI\-\-temperature\fR] [\fI\-\-transport\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.PP
+.B sg_logs
+[\fI\-a\fR] [\fI\-A\fR] [\fI\-b\fR] [\fI\-c=PC\fR] [\fI\-e\fR] [\fI\-E\fR]
+[\fI\-f=FL\fR] [\fI\-h\fR] [\fI\-H\fR] [\fI\-i=FN\fR] [\fI\-l\fR] [\fI\-L\fR]
+[\fI\-m=LEN\fR] [\fI\-n\fR] [\fI\-p=PG\fR] [\fI\-paramp=PP\fR]
+[\fI\-pcb\fR] [\fI\-ppc\fR] [\fI\-r\fR] [\fI\-R\fR] [\fI\-select\fR]
+[\fI\-sp\fR] [\fI\-t\fR] [\fI\-T\fR] [\fI\-v\fR] [\fI\-V\fR] [\fI\-?\fR]
+[\fI\-x\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends a SCSI LOG SENSE command to the \fIDEVICE\fR and then
+outputs the response. The LOG SENSE command is used to fetch log pages. Known
+log pages can be decoded. When the \fI\-\-reset\fR and/or \fI\-\-select\fR
+option is given then a SCSI LOG SELECT command is issued.
+.PP
+In SPC\-4 revision 5 a subpage code was introduced to both the LOG SENSE and
+LOG SELECT command. At the same time a page code field was introduced to the
+to the LOG SELECT command. The log subpage code can range from 0 to 255 (0xff)
+inclusive. The subpage code value 255 can be thought of as a wildcard.
+.PP
+This utility supports two command line syntaxes, the preferred one is shown
+first in the synopsis and explained in this section. A later section on the
+old command line syntax outlines the second group of options.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-A\fR, \fB\-\-All\fR
+fetch and decode all the log pages and subpages supported by the \fIDEVICE\fR.
+This requires a two stage process: first the "supported log pages and
+subpages" log page is fetched, then for each entry in the response, the
+corresponding log page (or subpage) is fetched and displayed. Note that there
+are many SCSI devices that do not support LOG SENSE subpages and respond
+to this option with an illegal request sense key.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+outputs all the log pages supported by the \fIDEVICE\fR. This requires a two
+stage process: first the "supported log pages" log page is fetched, then for
+each entry in the response, the corresponding log page is fetched and
+displayed. When used twice (e.g. '\-aa') all log pages and subpages are
+fetched.
+.TP
+\fB\-b\fR, \fB\-\-brief\fR
+shorten the amount of output for some log pages. For example the Tape
+Alert log page only outputs parameters whose flags are set when
+\fI\-\-brief\fR is given.
+.TP
+\fB\-c\fR, \fB\-\-control\fR=\fIPC\fR
+accepts 0, 1, 2 or 3 for the \fIPC\fR argument:
+.br
+  \fB0\fR : current threshold values
+.br
+  \fB1\fR : current cumulative values
+.br
+  \fB2\fR : default threshold values
+.br
+  \fB3\fR : default cumulative values
+.br
+The default value is 1 (i.e. current cumulative values).
+.TP
+\fB\-e\fR, \fB\-\-enumerate\fR
+this option is used to output information held in internal tables about
+known log pages including their names and acronyms. If given \fIDEVICE\fR
+is ignored. When given once (e.g. '\-e') all known pages are listed, sorted
+in ascending acronym order. When given twice, vendor pages are excluded.
+When given three times, all known pages are listed, sorted in ascending
+numeric order listed; when given four times, vendor pages are excluded from
+the numeric order.
+.br
+The \fI\-\-filter=FL\fR and \fI\-\-verbose\fR options modify the output
+of the enumeration.
+.TP
+\fB\-E\fR, \fB\-\-enum_vendor\fR
+this option is used to output information held in internal tables about
+known vendor specific log pages including their names and acronyms.
+.TP
+\fB\-f\fR, \fB\-\-filter\fR=\fIFL\fR
+\fIFL\fR is either a parameter code when \fIDEVICE\fR is given, or a
+peripheral device type (pdt) (or other) if \fI\-\-enumerate\fR is given.
+.br
+In the parameter code case \fIFL\fR is a value between 0 and 65535 (0xffff)
+and only the parameter section matching that code is output.
+The \fB\-\-hex\fR option outputs log parameter in hexadecimal rather than
+decoding it. If the \fB\-\-hex\fR option is used twice then the leading
+address on each line of hex is removed. If the \fB\-\-raw\fR option is
+given then the log parameter is output in binary.
+Most log pages contain one or more log parameters. Examples of those that
+don't are those pages that list supported log pages.
+.br
+In the \fI\-\-enumerate\fR case, when \fIFL\fR >= zero it is taken as a
+pdt value and only log pages associated with that pdt plus generic pages
+listed in SPC are enumerated. If \fIFL\fR is \-1 then the filter does
+nothing which is the same as not giving this option; when \fIFL\fR is \-2
+then only generic pages listed in SPC are enumerated. If \fIFL\fR is \-10
+then only generic direct access like (e.g. disk) pages are enumerated. If
+\fIFL\fR is \-11 then only generic tape like pages (e.g. includes ADC)
+are enumerated.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+The default action is to decode known mode page numbers (and subpage numbers)
+into text. When this option is used once, the response is output in
+hexadecimal. When used twice, each line of hex has the ASCII equivalent shown
+to the right. When used three times, the hex has no leading address nor
+trailing ASCII making it suitable to be placed in a file (or piped). That
+file might later be used by another invocation using the \fI\-\-in=FN\fR
+option.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIFN\fR
+This option may be used in two different contexts. One is with the
+\fI\-\-select\fR to send a LOG SELECT command to the given \fIDEVICE\fR;
+see the LOG SELECT section below.
+.br
+The other context is with no \fIDEVICE\fR argument given in which case
+the contents of \fIFN\fR are decoded as if it were the response of a LOG
+SENSE command (i.e. a log page). For decoding the page and subpage numbers
+are taken from \fIFN\fR while the device type is either generic (i.e. from
+SPC) or the value given by \fI\-\-filter=FL\fR.
+.br
+\fIFN\fR is treated as a file name (or '\-' for stdin) which contains ASCII
+hexadecimal or binary representing a log page. The hexadecimal should be
+arranged as 1 or 2 digits representing a byte each of which is whitespace or
+comma separated. Anything from and including a hash mark to the end of line
+is ignored. If the \fI\-\-raw\fR option is also given then \fIFN\fR is
+treated as binary.
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+lists the names of all logs sense pages supported by this device. This is
+done by reading the "supported log pages" log page. When used
+twice (e.g. '\-ll') lists the names of all logs sense pages and subpages
+supported by this device. There is a list of common log page codes below.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+sets the "allocation length" field in the LOG SENSE cdb. The is the maximum
+length in bytes that the response will be. Without this option (or \fILEN\fR
+equal to 0) this utility first fetches the 4 byte response then does a second
+access with the length indicated in the first (4 byte) response. Negative
+values and 1 for \fILEN\fR are not accepted. \fILEN\fR cannot exceed
+65535 (0xffff).  Responses can be quite large (e.g. the background scan
+results log page) and this option can be used to limit the amount of
+information returned.
+.TP
+\fB\-n\fR, \fB\-\-name\fR
+decode some log pages into 'name=value' entries, one per line. The name
+contains no space and may be abbreviated and the value is decimal unless
+prefixed by '0x'. Nesting is indicated by leading spaces. This form
+is meant to be relatively easy to parse.
+.TP
+\fB\-x\fR, \fB\-\-no_inq\fR
+suppresses the output of information obtained from an initial call to the
+INQUIRY command for the standard response. The default (assuming some other
+options that suppress this output are also not given) is to output several
+device identification strings.
+.br
+If this option is given twice (or more) then no INQUIRY command is sent
+hence there will be no device identification string output either. Also the
+peripheral device type (PDT) field will not be obtained so this utility will
+not be able to differentiate between some log pages that are device
+dependent. It will assume a PDT of 0 (i.e. a disk).
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPG\fR
+log page name/number to access. \fIPG\fR is either an acronym, a page number,
+or a page, subpage number pair. Available acronyms can be listed with the
+\fI\-\-enumerate\fR option. Page (0 to 63) and subpage (0 to 255) numbers
+are comma separated. They are decimal unless a hexadecimal indication is
+given. A hexadecimal number can be specified by a leading "0x" or a
+trailing "h".
+.br
+A few acronyms specify a range of subpage values in which case the acronym
+may be followed by a comma then a subpage number. This method can also be
+used to fetch the Supported subpages log page (e.g. \-\-page=temp,0xff).
+.TP
+\fB\-P\fR, \fB\-\-paramp\fR=\fIPP\fR
+\fIPP\fR is the parameter pointer value to place in a field of that name in
+the LOG SENSE cdb. A decimal number in the range 0 to 65535 (0xffff) is
+expected. When a value greater than 0 is given the \fI\-\-ppc\fR option
+should be selected. The default value is 0.
+.TP
+\fB\-q\fR, \fB\-\-pcb\fR
+show Parameter Control Byte settings (only relevant when log parameters
+being output in ASCII).
+.TP
+\fB\-Q\fR, \fB\-\-ppc\fR
+sets the Parameter Pointer Control (PPC) bit in the LOG SENSE cdb. Default
+is 0 (i.e. cleared). This bit was made obsolete in SPC\-4 revision 18.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the response in binary to stdout. Error messages and warnings are
+output to stderr.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag). The
+default action is to try and open \fIDEVICE\fR read\-write then if that
+fails try to open again with read\-only. However when a read\-write open
+succeeds there may still be unwanted actions on the close (e.g. some OSes
+try to do a SYNCHRONIZE CACHE command). So this option forces a read\-only
+open on \fIDEVICE\fR and if it fails, this utility will exit. Note that
+options like \fI\-\-select\fR most likely need a read\-write open.
+.TP
+\fB\-R\fR, \fB\-\-reset\fR
+use SCSI LOG SELECT command (PCR bit set) to reset the all log pages (or
+the given page). Exactly what is reset depends on the accompanying SP
+bit (i.e. \fI\-\-sp\fR option which defaults to 0) and the
+\fIPC\fR ("page control") value (which defaults to 1). Supplying this option
+implies the \fI\-\-select\fR option as well. This option seems to clear error
+counter log pages but leaves pages like self\-test results, start\-stop cycle
+counter and temperature log pages unaffected. This option may be required to
+clear log pages if a counter reaches its maximum value since the log page in
+which the counter is found will remain "stuck" until something is done.
+.TP
+\fB\-S\fR, \fB\-\-select\fR
+use a LOG SELECT command. The default action (i.e. when neither this option
+nor \fI\-\-reset\fR is given) is to do a LOG SENSE command. See the LOG
+SELECT section.
+.TP
+\fB\-s\fR, \fB\-\-sp\fR
+sets the Saving Parameters (SP) bit. Default is 0 (i.e. cleared). When set
+this instructs the device to store the current log page parameters (as
+indicated by the DS and TSD parameter codes) in some non\-volatile location.
+Hence the log parameters will be preserved across power cycles. This option
+is typically not needed, especially if the GLTSD flag is clear in the
+control mode page as this instructs the device to periodically save all
+saveable log parameters to non\-volatile locations.
+.TP
+\fB\-t\fR, \fB\-\-temperature\fR
+outputs the temperature. First looks in the temperature log page and if
+that is not available tries the Informational Exceptions log page which
+may also have the current temperature (especially on older disks).
+.TP
+\fB\-T\fR, \fB\-\-transport\fR
+outputs the transport ('Protocol specific port') log page. Equivalent to
+setting '\-\-page=18h'.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level of verbosity. When used with \fI\-\-enumerate\fR, in the
+list of known log page names, those that have no associated decode logic
+are followed by "[hex only]".
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string then exit.
+.SH LOG SELECT
+The LOG SELECT command can be used to reset certain parameters to vendor
+specific defaults, save them to non-volatile storage (i.e. the media), or
+supply new page contents. This command has changed between SPC\-3 and SPC\-4
+with the addition of the Page and Subpage Code fields which can only be
+non zero when the Parameter list length is zero.
+.PP
+The \fI\-\-select\fR option is required to issue a LOG SELECT command. If
+the \fI\-\-in=FN\fR option is not given (or \fIFN\fR is effectively empty)
+then the Parameter list length field is set to zero. If the \fI\-\-in=FN\fR
+option is is given then its decoded data is placed in the data\-out buffer
+and its length in bytes is placed in the Parameter list length field.
+.PP
+Other options that are active with the LOG SELECT command are
+\fI\-\-control=PC\fR, \fI\-\-reset\fR (which sets the PCR bit) and
+\fI\-\-sp\fR.
+.SH
+APPLICATION CLIENT
+This is the name of a log page that acts as a container for data provided
+by the user. An application client is a SCSI term for the program that issues
+commands to a SCSI initiator (often known as a Host Bus Adapter (HBA)). So,
+for example, this utility is a SCSI application client.
+.PP
+The Application Client log page has 64 log parameters with parameters codes
+0 to 63. Each can hold 252 bytes of user binary data. That 252 bytes (or
+less) of user data, with a 4 byte prefix (for a total of 256 bytes) can be
+provided with the \fI\-\-in=FN\fR option. A typical prefix would
+be '0,n,83,fc'. The "n" is the parameter code in hex so the last log
+parameter would be '0,3f,83,fc'. That log parameter could be read back at
+some later time with '\-\-page=0xf \-\-filter=0x<n>'.
+.SH NOTES
+This utility will usually do a double fetch of log pages with the SCSI LOG
+SENSE command. The first fetch requests a 4 byte response (i.e. place 4 in
+the "allocation length" field in the cdb). From that response it can
+calculate the actual length of the response which is what it asks for
+on the second fetch. This is typical practice in SCSI and guaranteed to
+work in the standards. However some older devices don't comply. For
+those devices using the \fI\-\-maxlen=LEN\fR option will do a single fetch.
+A value of 252 should be a safe starting point.
+.PP
+Various log pages hold information error rates, device temperature,
+start stop cycles since device produced and the results of the last
+20 self tests. Self tests can be initiated by the sg_senddiag(8) utility.
+The smartmontools package provides much of the information found with
+sg_logs in a form suitable for monitoring the health of SCSI disks and
+tape drives.
+.PP
+The simplest way to find which log pages can be decoded by this utility is
+to use the \fI\-\-enumerate\fR option. Some page names are known but there
+is no decode logic; such cases have "[hex only]" after the log page name
+when the \fI\-\-verbose\fR option is given with \fI\-\-enumerate\fR.
+.SH EXIT STATUS
+The exit status of sg_logs is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using '\-\-old' (or '\-O) as the first option.
+.PP
+Options with arguments or with two or more letters can have an extra '\-'
+prepended. For example: both '\-pcb' and '\-\-pcb' are acceptable.
+.TP
+\fB\-a\fR
+outputs all the log pages supported by the device.
+Equivalent to \fI\-\-all\fR in the main description.
+.TP
+\fB\-A\fR
+outputs all the log pages and subpages supported by the device.
+Equivalent to '\-\-all \-\-all' in the main description.
+.TP
+\fB\-c\fR=\fIPC\fR
+Equivalent to \fI\-\-control=PC\fR in the main description.
+.TP
+\fB\-e\fR
+enumerate internal tables to show information about known log pages.
+Equivalent to \fI\-\-enumerate\fR in the main description.
+.TP
+\fB\-E\fR
+enumerate internal tables to show information about known vendor specific
+log pages. Equivalent to \fI\-\-enum_vendor\fR in the main description.
+.TP
+\fB\-h\fR
+suppresses decoding of known log sense pages and prints out the
+response in hex instead.
+.TP
+\fB\-i\fR=\fIFN\fR
+\fIFN\fR is treated as a file name (or '\-' for stdin) which contains ASCII
+hexadecimal representing a log page that will be sent as parameter data of a
+LOG SELECT command. See the LOG SELECT section.
+.TP
+\fB\-H\fR
+same action as '\-h' in this section and equivalent to \fI\-\-hex\fR in
+the main description.
+.TP
+\fB\-l\fR
+lists the names of all logs sense pages supported by this device.
+Equivalent to \fI\-\-list\fR in the main description.
+.TP
+\fB\-L\fR
+lists the names of all logs sense pages and subpages supported by this
+device. Equivalent to '\-\-list \-\-list' in the main description.
+.TP
+\fB\-m\fR=\fILEN\fR
+request only \fILEN\fR bytes of response data. Default is 0 which is
+interpreted as all that is available. \fILEN\fR is decimal unless it has
+a leading '0x' or trailing 'h'.  Equivalent to \fI\-\-maxlen=LEN\fR in
+the main description.
+.TP
+\fB\-n\fR
+Equivalent to \fI\-\-name\fR in the main description.
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-p\fR=\fIPG\fR
+log page code to access. \fIPG\fR is either an acronym, a page number, or
+a page, subpage pair. Available acronyms can be listed with the
+\fI\-\-enumerate\fR option. Page (0 to 3f) and subpage (0 to ff) numbers
+are comma separated. The numbers are assumed to be hexadecimal.
+.TP
+\fB\-paramp\fR=\fIPP\fR
+\fIPP\fR is the parameter pointer value (in hex) to place in command.
+Should be a number between 0 and ffff inclusive.
+.TP
+\fB\-pcb\fR
+show Parameter Control Byte settings (only relevant when log parameters
+being output in ASCII).
+.TP
+\fB\-ppc\fR
+sets the Parameter Pointer Control (PPC) bit. Default is 0 (i.e. cleared).
+.TP
+\fB\-r\fR
+use SCSI LOG SELECT command (PCR bit set) to reset the all log pages (or
+the given page). Equivalent to \fI\-\-reset\fR in the main description.
+.TP
+\fB\-R\fR
+Equivalent to \fI\-\-readonly\fR in the main description.
+.TP
+\fB\-select\fR
+use a LOG SELECT command. Equivalent to \fI\-\-select\fR in the main
+description.
+.TP
+\fB\-sp\fR
+sets the Saving Parameters (SP) bit. Default is 0 (i.e. cleared).
+Equivalent to \fI\-\-sp\fR in the main description.
+.TP
+\fB\-t\fR
+outputs the temperature. Equivalent to \fI\-\-temperature\fR in the main
+description.
+.TP
+\fB\-T\fR
+outputs the transport ('Protocol specific port') log page. Equivalent
+to \fI\-\-transport\fR in the main description.
+.TP
+\fB\-v\fR
+increase level of verbosity.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.TP
+\fB\-x\fR
+suppress the INQUIRY command. Equivalent to \fI\-\-no_inq\fR in the main
+description.
+.TP
+\fB\-?\fR
+output usage message then exit.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2002\-2016 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B smartctl(smartmontools), sg_senddiag(8)
diff --git a/sg3_utils/doc/sg_luns.8 b/sg3_utils/doc/sg_luns.8
new file mode 100644
index 0000000..4d14ca6
--- /dev/null
+++ b/sg3_utils/doc/sg_luns.8
@@ -0,0 +1,304 @@
+.TH SG_LUNS "8" "June" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_luns \- send SCSI REPORT LUNS command or decode given LUN
+.SH SYNOPSIS
+.B sg_luns
+[\fI\-\-decode\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-linux\fR]
+[\fI\-\-lu_cong\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-quiet\fR] [\fI\-\-raw\fR]
+[\fI\-\-readonly\fR] [\fI\-\-select=SR\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.PP
+.B sg_luns
+\fI\-\-test=ALUN\fR [\fI\-\-hex\fR] [\fI\-\-lu_cong\fR] [\fI\-\-verbose\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+In the first form shown in the SYNOPSIS this utility sends the SCSI REPORT
+LUNS command to the \fIDEVICE\fR and outputs the response. The response
+should be a list of LUNs ("a LUN inventory") for the I_T nexus associated
+with the \fIDEVICE\fR. Roughly speaking that is all LUNs that share the
+target device that the REPORT LUNS command is sent through. In the SPC\-3
+and SPC\-4 SCSI standards support for the REPORT LUNS command is mandatory.
+.PP
+When the \fI\-\-test=ALUN\fR option is given (the second form in the
+SYNOPSIS), then the \fIALUN\fR value is decoded as outlined in SAM\-3,
+SAM\-4 and SAM\-5 (revision 13, section 4.7) .
+.PP
+Where required below the first form shown in the SYNOPSIS is called "device
+mode" and the second form is called "test mode".
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-d\fR, \fB\-\-decode\fR
+decode LUNs into their component parts, as described in the LUN section
+of SAM\-3, SAM\-4 and SAM\-5.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+[device mode] when given once this utility will output the SCSI
+response (i.e. the data\-out buffer) to the REPORT LUNS command in ASCII
+hex then exit. When given twice it causes \fI\-\-decode\fR to output
+component fields in hex rather than decimal.
+.br
+[test mode] when this option is given, then decoded component fields of
+\fIALUN\fR are output in hex.
+.TP
+\fB\-l\fR, \fB\-\-linux\fR
+this option is only available in Linux. After the T10 representation of
+each 64 bit LUN (in 16 hexadecimal digits), if this option is given then
+to the right, in square brackets, is the Linux LUN integer in decimal.
+If the \fI\-\-hex\fR option is given twice (e.g. \-HH) as well then the
+Linux LUN integer is output in hexadecimal.
+.TP
+\fB\-L\fR, \fB\-\-lu_cong\fR
+this option is only considered with \fI\-\-decode\fR. When given once
+then the list of LUNs is decoded as if the LU_CONG bit was set in
+each LU's coresponding INQUIRY response. When given twice the list of
+LUNs is decoded as if the LU_CONG bit was clear in each LU's coresponding
+INQUIRY response. When this option is not given and \fI\-\-decode\fR is
+given then an INQUIRY is sent to the \fIDEVICE\fR and the setting of
+its LU_CONG bit is used to decode the list of LUNs.
+.br
+[test mode] decode \fIALUN\fR as if the LU_CONG bit is set in its
+corresponding standard INQUIRY response. In other words treat \fIALUN\fR
+as if it is a conglomerate LUN. If not given (or given twice) then decode
+\fIALUN\fR as if the LU_CONG bit is clear.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in
+the cdb's "allocation length" field. If not given (or \fILEN\fR is zero)
+then 8192 is used. The maximum allowed value of \fILEN\fR is 1048576.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+output only the ASCII hex rendering of each report LUN, one per line.
+Without the \fI\-\-quiet\fR option, there is header information printed
+before the LUN listing.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the SCSI response (i.e. the data\-out buffer) in binary (to stdout).
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-s\fR, \fB\-\-select\fR=\fISR\fR
+\fISR\fR is placed in the SELECT REPORT field of the SCSI REPORT LUNS
+command. The default value is 0. Hexadecimal values may be given with
+a leading "0x" or a trailing "h". For detailed information see the
+REPORT LUNS command in SPC (most recent is SPC\-4 revision 37 in section
+6.33). To simplify, for the I_T nexus associated with the \fIDEVICE\fR, the
+meanings of the \fISR\fR values defined to date for SPC\-4 are:
+.br
+  \fB0\fR : most luns excluding well known logical unit numbers
+.br
+  \fB1\fR : well known logical unit numbers
+.br
+  \fB2\fR : all luns accessible to this I_T nexus
+.br
+  \fB0x10\fR : only accessible administrative luns
+.br
+  \fB0x11\fR : administrative luns plus non-conglomerate luns (see SPC\-4)
+.br
+  \fB0x12\fR : if \fIDEVICE\fR is an administrative LU, then report its
+.br
+         lun plus its subsidiary luns
+.PP
+For \fISR\fR values 0x10 and 0x11, the \fIDEVICE\fR must be either LUN 0 or
+the REPORT LUNS well known logical unit. Values between 0xf8 and
+0xff (inclusive) are vendor specific, other values are reserved. This
+utility will accept any value between 0 and 255 (0xff) for \fISR\fR .
+.TP
+\fB\-t\fR, \fB\-\-test\fR=\fIALUN\fR
+\fIALUN\fR is assumed to be a hexadecimal number in ASCII hex or the
+letter 'L' followed by a decimal number (see below). The hexadecimal number
+can be up to 64 bits in size (i.e. 16 hexadecimal digits) and is padded to
+the right if less than 16 hexadecimal digits are given (e.g.
+\fI\-\-test=0122003a\fR represents T10 LUN 0122003a00000000). \fIALUN\fR
+may be prefixed by '0x' or '0X' (e.g. the previous example could have been
+\fI\-\-test=0x0122003a\fR). \fIALUN\fR may also be given with spaces or
+tabs between each byte (or other grouping) but then \fIALUN\fR would need
+to be surrounded by single or double quotes.
+In the decimal number case (i.e. following a 'L') that number is assumed
+to be a Linux "word flipped" LUN which is converted into a T10 LUN
+representation and printed. In both cases the number is interpreted as a
+LUN and decoded as if the \fI\-\-decode\fR option had been given.
+Also when \fIALUN\fR is a hexadecimal number it can have a trailing 'L'
+in which case the corresponding Linux "word flipped" LUN value is output.
+The LUN is decoded in all cases.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+The SCSI REPORT LUNS command is important for Logical Unit (LU) discovery.
+After a target device is discovered (usually via some transport specific
+mechanism), a REPORT LUNS command should either be sent to LUN 0 (which
+is Peripheral device addressing method with bus_id=0 and target/lun=0)
+or to the REPORT LUNS well known LUN (i.e. 0xc101000000000000). SAM\-5
+requires that one of these responds with an inventory of LUNS that are
+contained in this target device.
+.PP
+In test mode, if the \fI\-\-hex\fR option is given once then in the decoded
+output, some of the component fields are printed in hex with leading zeros.
+The leading zeros are to indicate the size of the component field. For
+example: in the Peripheral device addressing method (16 bits overall), the
+bus ID is 6 bits wide and the target/LUN field is 8 bits wide; so both are
+shown with two hex digits (e.g. bus_id=0x02, target=0x3a).
+.SH EXAMPLES
+Typically by the time user space programs get to run, SCSI LUs have been
+discovered. In Linux the lsscsi utility lists the LUs that are currently
+present. The LUN of a device (LU) is the fourth element in the tuple at the
+beginning of each line. Below we see a target (or "I_T Nexus": "6:0:0") has
+two LUNS: 1 and 49409. If 49409 is converted into T10 LUN format it is
+0xc101000000000000 which is the REPORT LUNS well known LUN.
+.PP
+  # lsscsi \-g
+.br
+  [6:0:0:1]    disk    Linux    scsi_debug       0004  /dev/sdb   /dev/sg1
+.br
+  [6:0:0:2]    disk    Linux    scsi_debug       0004  /dev/sdc   /dev/sg2
+.br
+  [6:0:0:49409]wlun    Linux    scsi_debug       0004  \-          /dev/sg3
+.PP
+We could send a REPORT LUNS command (with \fISR\fR 0x0, 0x1 or 0x2) to any
+of those file device nodes and get the same result. Below we use /dev/sg1 :
+.PP
+  # sg_luns /dev/sg1
+.br
+  Lun list length = 16 which imples 2 lun entry
+.br
+  Report luns [select_report=0x0]:
+.br
+      0001000000000000
+.br
+      0002000000000000
+.PP
+That is a bit noisy so cut down the clutter with \fI\-\-quiet\fR:
+.PP
+  # sg_luns \-q /dev/sg1
+.br
+  0001000000000000
+.br
+  0002000000000000
+.PP
+Now decode that LUN into its component parts:
+.PP
+  # sg_luns \-d \-q /dev/sg1
+.br
+  0001000000000000
+.br
+        Peripheral device addressing: lun=1
+.br
+  0002000000000000
+.br
+        Peripheral device addressing: lun=2
+.PP
+Now use \fI\-\-select=1\fR to find out if there are any well known
+LUNs:
+.PP
+  # sg_luns \-q \-s 1 /dev/sg1
+.br
+  c101000000000000
+.PP
+So how many LUNs do we have all together (associated with the current
+I_T Nexus):
+.PP
+  # sg_luns \-q \-s 2 /dev/sg1
+.br
+  0001000000000000
+.br
+  0002000000000000
+.br
+  c101000000000000
+.PP
+  # sg_luns \-q \-s 2 \-d /dev/sg1
+.br
+  0001000000000000
+.br
+        Peripheral device addressing: lun=1
+.br
+  0002000000000000
+.br
+        Peripheral device addressing: lun=1
+.br
+  c101000000000000
+.br
+        REPORT LUNS well known logical unit
+.PP
+The following example uses the \fI\-\-linux\fR option and is not available
+in other operating systems. The extra number in square brackets is the
+Linux version of T10 LUN shown at the start of the line.
+.PP
+  # sg_luns \-q \-s 2 \-l /dev/sg1
+.br
+  0001000000000000    [1]
+.br
+  0002000000000000    [2]
+.br
+  c101000000000000    [49409]
+.PP
+Now we use the \fI\-\-test=\fR option to decode LUNS input on the command
+line (rather than send a REPORT LUNS command and act on the response):
+.PP
+  # sg_luns \-\-test=0002000000000000
+.br
+  Decoded LUN:
+.br
+    Peripheral device addressing: lun=2
+.PP
+  # sg_luns \-\-test="c1 01"
+.br
+  Decoded LUN:
+.br
+    REPORT LUNS well known logical unit
+.PP
+  # sg_luns \-t 0x023a004b \-H
+.br
+  Decoded LUN:
+.br
+    Peripheral device addressing: bus_id=0x02, target=0x3a
+.br
+    >>Second level addressing:
+.br
+      Peripheral device addressing: lun=0x4b
+.PP
+The next example is Linux specific as we try to find out what the
+Linux LUN 49409 translates to in the T10 world:
+.PP
+  # sg_luns \-\-test=L49409
+.br
+  64 bit LUN in T10 preferred (hex) format:  c1 01 00 00 00 00 00 00
+.br
+  Decoded LUN:
+.br
+    REPORT LUNS well known logical unit
+.PP
+And the mapping between T10 and Linux LUN representations can be done the
+other way:
+.PP
+  # sg_luns \-t c101L
+.br
+  Linux 'word flipped' integer LUN representation: 49409
+.br
+  Decoded LUN:
+.br
+    REPORT LUNS well known logical unit
+.br
+.SH EXIT STATUS
+The exit status of sg_luns is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq(8)
diff --git a/sg3_utils/doc/sg_map.8 b/sg3_utils/doc/sg_map.8
new file mode 100644
index 0000000..5cbb4c2
--- /dev/null
+++ b/sg3_utils/doc/sg_map.8
@@ -0,0 +1,182 @@
+.TH SG_MAP "8" "May 2013" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+sg_map \- displays mapping between Linux sg and other SCSI devices
+.SH SYNOPSIS
+.B sg_map
+[\fI\-a\fR] [\fI-h\fR] [\fI\-i\fR] [\fI\-n\fR] [\fI\-scd\fR] [\fI\-sd\fR]
+[\fI\-sr\fR] [\fI\-st\fR] [\fI\-V\fR] [\fI\-x\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sometimes it is difficult to determine which SCSI device a sg device
+name (e.g. /dev/sg0) refers to. This command loops through the
+sg devices and finds the corresponding SCSI disk, cdrom or tape
+device name (if any). Scanners are an example of SCSI devices
+that have no alternate SCSI device name apart from their sg device
+name.
+.PP
+This utility is deprecated and has not been updated for years, only very
+obvious bugs will be fixed. Unless a very old version of Linux is being
+used (e.g.  2.4 series or earlier), then please use a utility like lsscsi(8)
+or the facilities offered by udev(8).
+.SH OPTIONS
+.TP
+\fB\-a\fR
+assume the sg devices have alphabetical device names and loop
+through /dev/sga, /dev/sgb, etc. Default is numeric scan.
+Note that sg device nodes with an alphabetical index have been
+deprecated since the Linux kernel 2.2 series.
+.TP
+\fB\-h\fR
+print usage message then exit.
+.TP
+\fB\-i\fR
+in addition do a standard INQUIRY and output vendor, product and revision
+strings for devices that are found.
+.TP
+\fB\-n\fR
+assume the sg devices have numeric device names and loop
+through /dev/sg0, /dev/sg1, etc. Default is numeric scan
+.TP
+\fB\-scd\fR
+display mappings to SCSI cdrom device names of the form
+/dev/scd0, /dev/scd1 etc
+.TP
+\fB\-sd\fR
+display mappings to SCSI disk device names
+.TP
+\fB\-sr\fR
+display mappings to SCSI cdrom device names of the form
+/dev/sr0, /dev/sr1 etc
+.TP
+\fB\-st\fR
+display mappings to SCSI tape device names
+.TP
+\fB\-V\fR
+print out version string then exit (without further ado).
+.TP
+\fB\-x\fR
+after each active sg device name is displayed there are
+five digits: <host_number> <bus> <scsi_id> <lun> <scsi_type>
+.SH NOTES
+If no options starting with "\-s" are given then the mapping to
+all SCSI disk, cdrom and tape device names is shown.
+.PP
+If the device file system (devfs) is present a line noting
+this is output. The "native" devfs scsi hierarchy makes the
+relationship between a sg device name and any corresponding
+disk, cdrom or tape device name easy to establish. This
+replaces the need for this command. However many applications
+will continue to look for Linux SCSI device names in their
+traditional places. [Devfs supplies a compatibility daemon
+called devfsd whose default configuration adds back the
+Linux device names in their traditional positions.
+.PP
+Quite often the mapping information can be derived by
+observing the output of the command: "cat /proc/scsi/scsi".
+However if devices have been added since boot this can
+be deceptive.
+.PP
+In the Linux kernel 2.6 series something close to the mapping
+shown by this utility can be found by analysing sysfs. The
+main difference is that sysfs analysis will show the mapping
+between sg nodes and other SCSI device nodes in terms of
+major and minor numbers. While major 8, minor 16 will usually
+be /dev/sdb this is not necessarily so. Facilities associated
+with udev may assign major 8, minor 16 some other device node
+name. This version of sg_map has been extended to cope with
+sparse disk device node names of the form "/dev/sd<str>"
+where <str> can be one of [a\-z,aa\-zz,aaa\-zzz]. See the sg_map26
+utility for a more precise way (i.e. less directory scanning)
+for mapping between sg device names and higher level names;
+including finding user defined names.
+.PP
+This utility was written at a time when hotplugging of SCSI devices
+was not supported in Linux. It used a simple algorithm to scan sg
+device nodes in ascending numeric or alphabetical order, stopping
+after there were 5 consecutive errors.
+.PP
+In the Linux kernel 2.6 series, this utility uses sysfs to find which
+sg device nodes are active and only checks those. Hence there can be
+large "holes" in the numbering of sg device nodes (e.g. after an
+adapter has been removed) and still all active sg device nodes will
+be listed. This utility assumes that sg device nodes are named using
+the normal conventions and searches from /dev/sg0 to /dev/sg4095
+inclusive.
+.SH EXAMPLES
+.PP
+My system has a SCSI disk, a cd writer and a dvd player:
+.br
+   $ sg_map
+.br
+   # Note: the devfs pseudo file system is present
+.br
+   /dev/sg0  /dev/sda
+.br
+   /dev/sg1  /dev/sr0
+.br
+   /dev/sg2  /dev/sr1
+.PP
+In order to find which sg device name corresponds to the disk:
+.br
+   $ sg_map \-sd
+.br
+   # Note: the devfs pseudo file system is present
+.br
+   /dev/sg0  /dev/sda
+.br
+   /dev/sg1
+.br
+   /dev/sg2
+.PP
+The "\-x" option gives the following output:
+.br
+   sg_map \-x
+.br
+   # Note: the devfs pseudo file system is present
+.br
+   /dev/sg0  1 0 1 0  0  /dev/sda
+.br
+   /dev/sg1  2 0 4 0  5  /dev/sr0
+.br
+   /dev/sg2  2 0 6 0  5  /dev/sr1
+.PP
+When a SCSI scanner is added the output becomes:
+.br
+   $ sg_map
+.br
+   # Note: the devfs pseudo file system is present
+.br
+   /dev/sg0  /dev/sda
+.br
+   /dev/sg1  /dev/sr0
+.br
+   /dev/sg2  /dev/sr1
+.br
+   /dev/sg3
+.PP
+By process of elimination /dev/sg3 must be the scanner.
+.SH EXIT STATUS
+The exit status of sg_map is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2000\-2013 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_map26(8)
+,
+.B scsi_info(8)
+,
+.B scsidev(8)
+,
+.B devfsd(8)
+,
+.B lsscsi(8)
+,
+.B udev(7)
diff --git a/sg3_utils/doc/sg_map26.8 b/sg3_utils/doc/sg_map26.8
new file mode 100644
index 0000000..cb826e2
--- /dev/null
+++ b/sg3_utils/doc/sg_map26.8
@@ -0,0 +1,161 @@
+.TH SG_MAP26 "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_map26 \- map SCSI generic (sg) device to corresponding device names
+.SH SYNOPSIS
+.B sg_map26
+[\fI\-\-dev_dir=DIR\fR] [\fI\-\-given_is=\fR0|1] [\fI\-\-help\fR]
+[\fI\-\-result=\fR0|1|2|3] [\fI\-\-symlink\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Maps a special file (block or char) associated with a SCSI device
+to the corresponding SCSI generic (sg) device, or vice versa.
+Can also be given a sysfs file, for example '/sys/block/sda'
+or '/sys/block/sda/dev'.
+.PP
+Rather than map to or from a sg device, the sysfs file name
+matching a given device special file (or vice versa) can be
+requested. This is done with '\-\-result=2' and '\-\-result=3'.
+This feature works on ATA devices (e.g. 'dev/hdc') as well
+as SCSI devices.
+.PP
+In this utility, "mapped" refers to finding the relationship between
+a SCSI generic (sg) node and the higher level SCSI device name; or
+vice versa. For example '/dev/sg0' may "map" to '/dev/sda'.
+Mappings may not exist, if a relevant module is not loaded, for
+example. Also there are SCSI devices that can only be accessed via a sg
+node (e.g. SAF\-TE and some SES devices).
+.PP
+In this utility, "matching" refers to different representations of
+the same device accessed via the same driver. For example, '/dev/hdc'
+and '/sys/block/hdc' usually refer to the same device and thus would
+be considered matching. A related example is that '/dev/cdrom'
+and '/dev/hdc' are also considered matching if '/dev/cdrom' is a
+symlink to '/dev/hdc'.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-d\fR, \fB\-\-dev_dir\fR=\fIDIR\fR
+where \fIDIR\fR is the directory to search for resultant device special
+files in (or symlinks to same). Only active when '\-\-result=0' (the
+default) or '\-\-result=2'. If this option is not given and \fIDEVICE\fR is
+a device special file then the directory part of \fIDEVICE\fR is assumed.
+If this option is not given and \fIDEVICE\fR is a sysfs name, then if
+necessary '/dev' is assumed as the directory.
+.TP
+\fB\-g\fR, \fB\-\-given_is\fR=0 | 1
+specifies the \fIDEVICE\fR is either a device special file (when the
+argument is 0), or a sysfs 'dev' file (when the argument is 1). The parent
+directory of a sysfs 'dev' file is also accepted (e.g.
+either '/sys/block/sda/dev' or '/sys/block/sda' are accepted). Usually
+there is no need to give this option since this utility first checks for
+special files (or symlinks to special files) and if not, assumes it
+has been given a sysfs 'dev' file (or its parent). Generates an error
+if given and disagrees with variety of \fIDEVICE\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-r\fR, \fB\-\-result\fR=0 | 1 | 2 | 3
+specifies what variety of file (or files) that this utility tries to find.
+The default is a "mapped" device special file, when the argument is 0.
+When the argument is 1, this utility tries to find the "mapped" sysfs node
+name. When the argument is 2, this utility tries to find the "matching"
+device special file. When the argument is 3, this utility tries to find
+the "matching" sysfs node name.
+.TP
+\fB\-s\fR, \fB\-\-symlink\fR
+when a device special file is being sought (i.e. when '\-\-result=0' (the
+default) or '\-\-result=2') then also look for symlinks to that device
+special file in the same directory.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+This utility is designed for the Linux 2.6 (and later) kernel series.
+It uses special file major and minor numbers (and whether the special
+is block or character) together with sysfs to do its mapping or
+matching. In the absence of any other information, device special
+files are assumed to be in the '/dev' directory while sysfs is
+assumed to be mounted at '/sys'. Device names in sysfs are
+predictable, given the corresponding major and minor number of
+the device. However, due to udev rules, the name of device
+special files can be anything the user desires (e.g. '/dev/sda'
+could be named '/dev/my_boot_disk'). When trying to
+find a resultant device special file, this utility uses the major
+and minor numbers (and whether a block or char device is sought)
+to search the device directory.
+.PP
+This utility only shows one relationship at a time. To get an
+overview of all SCSI devices, with special file names and optionally
+the "mapped" sg device name, see the lsscsi utility.
+.SH EXAMPLES
+Assume sg2 maps to sdb while dvd, cdrom and hdc are all matching.
+.PP
+  # sg_map26 /dev/sg2
+.br
+  /dev/sdb
+.PP
+  # sg_map26 /dev/sdb
+.br
+  /dev/sg2
+.PP
+  # sg_map26 \-\-result=0 /dev/sdb
+.br
+  /dev/sg2
+.PP
+  # sg_map26 \-\-result=3 /dev/sdb
+.br
+  /sys/block/sda
+.PP
+  # sg_map26 \-\-result=1 /dev/sdb
+.br
+  /sys/class/scsi_generic/sg0
+.PP
+Now look at '/dev/hdc' and friends
+.PP
+  # sg_map26 /dev/hdc
+.br
+  <error: a hd device does not map to a sg device>
+.PP
+  # sg_map26 \-\-result=3 /dev/hdc
+.br
+  /sys/block/hdc
+.PP
+  # sg_map26 \-\-result=2 /dev/hdc
+.br
+  /dev/hdc
+.PP
+  # sg_map26 \-\-result=2 \-\-symlink /dev/hdc
+.br
+  /dev/cdrom
+.br
+  /dev/dvd
+.br
+  /dev/hdc
+.PP
+  # sg_map26 \-\-result=2 \-\-symlink /sys/block/hdc
+.br
+  /dev/cdrom
+.br
+  /dev/dvd
+.br
+  /dev/hdc
+.SH EXIT STATUS
+The exit status of sg_map26 is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2005\-2012 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B udev(7), lsscsi(lsscsi)
diff --git a/sg3_utils/doc/sg_modes.8 b/sg3_utils/doc/sg_modes.8
new file mode 100644
index 0000000..2db42be
--- /dev/null
+++ b/sg3_utils/doc/sg_modes.8
@@ -0,0 +1,287 @@
+.TH SG_MODES "8" "January 2015" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+sg_modes \- reads mode pages with SCSI MODE SENSE command
+.SH SYNOPSIS
+.B sg_modes
+[\fI\-\-all\fR] [\fI\-\-control=PC\fR] [\fI\-\-dbd\fR] [\fI\-\-dbout\fR]
+[\fI\-\-examine\fR] [\fI\-\-flexible\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-list\fR] [\fI\-\-llbaa\fR] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-page=PG[,SPG]\fR] [\fI\-\-raw\fR] [\fI\-R\fR] [\fI\-\-six\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fIDEVICE\fR]
+.PP
+.B sg_modes
+[\fI\-6\fR] [\fI\-a\fR] [\fI\-A\fR] [\fI\-c=PC\fR] [\fI\-d\fR] [\fI\-D\fR]
+[\fI\-e\fR] [\fI\-f\fR] [\fI\-h\fR] [\fI\-H\fR] [\fI\-l\fR] [\fI\-L\fR]
+[\fI\-m=LEN\fR] [\fI\-p=PG[,SPG]\fR] [\fI\-r\fR] [\fI\-subp=SPG\fR]
+[\fI\-v\fR] [\fI\-V\fR] [\fI\-?\fR] [\fIDEVICE\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends a MODE SENSE SCSI command to the \fIDEVICE\fR and
+outputs the response. There is a 6 byte and 10 byte (cdb) variant of
+the MODE SENSE command, this utility defaults to the 10 byte variant.
+.PP
+This utility decodes mode page headers and block descriptors but outputs
+the contents of each mode page in hex. It also has no facility to change
+the mode page contents or block descriptor data. Mode page contents are
+decoded and can be changed by the
+.B sdparm
+utility.
+.PP
+This utility supports two command line syntaxes, the preferred one is
+shown first in the synopsis and explained in this section. A later
+section on the old command line syntax outlines the second group of
+options.
+.PP
+If no page is given (and \fI\-\-list\fR is not selected) then \fI\-\-all\fR
+is assumed. The \fI\-\-all\fR option requests all mode pages (but not
+subpages) in a single response.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+output all the mode pages reported by the \fIDEVICE\fR. This is what the
+page code 63 (0x3f) is defined to do. When used once, mode subpages are
+not fetched. When used twice (e.g. '\-aa'), all mode pages and subpages
+are requested which is equivalent to '\-\-page=63,255'.
+.TP
+\fB\-c\fR, \fB\-\-control\fR=\fIPC\fR
+\fIPC\fR is the page control value. Up to four different versions of each
+page are held by the device:
+.br
+  \fB0\fR : current values (i.e. those active at present)
+.br
+  \fB1\fR : changeable values
+.br
+  \fB2\fR : default values (i.e. the manufacturer's settings)
+.br
+  \fB3\fR : saved values
+.br
+The changeable values are bit masks showing which fields could be changed
+with a MODE SELECT. The saved values will be re\-instated the next time
+the device is power cycled or reset. If this option is not given then
+current values [0] are assumed.
+.TP
+\fB\-d\fR, \fB\-\-dbd\fR
+disable block descriptors. By default, block descriptors (usually
+one (for disks) or none) are returned in a MODE SENSE response. This option
+sets the "disable block descriptors" (DBD) bit in the cdb which instructs
+the device not to return any block descriptors in its response. Older
+devices may not support this setting and may return an "illegal request"
+sense key; alternatively they may ignore it. Oddly the Reduced Block Command
+set (RBC) requires this bit set.
+.TP
+\fB\-D\fR, \fB\-\-dbout\fR
+disable outputting block descriptors. Irrespective of whether block
+descriptors are present in the response or not, they are not output.
+.TP
+\fB\-e\fR, \fB\-\-examine\fR
+examine each mode page in the range 0 through to 62 (inclusive).
+If some response is given then print out the mode page name or
+number (in hex) if the name is not known.
+.TP
+\fB\-f\fR, \fB\-\-flexible\fR
+Some devices, bridges and/or drivers attempt crude translations between
+MODE SENSE 6 and 10 byte commands without correcting the response. This
+will cause the response to be mis\-interpreted (usually with an error saying
+the response is malformed). With this option, the length of the response
+is checked, and if it looks wrong, the response is then decoded as if the
+other mode sense (cdb length) was sent.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+The default action is to decode known mode page numbers (and subpage
+numbers) into text. When this option is used once, the response is output
+in hexadecimal to stdout. When this option is used twice, mode page numbers
+and page control values are output in hex.
+.br
+When this option is used three times, the full response to the MODE SENSE
+command is output in hex to stdout without any decoding. This form can
+be redirected to a file (or piped) and then used 'sdparm \-\-inhex=' to
+decode.
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+lists all common page and subpage codes and their names that are found in
+the command set that matches the peripheral type of the given \fIDEVICE\fR.
+If no \fIDEVICE\fR and no \fI\-\-page=PG\fR is given then the common page and
+subpage codes and their names are listed for SBC (e.g. a disk). If no
+\fIDEVICE\fR is given and a \fI\-\-page=PG\fR is given then the
+common page and subpage codes and their names are listed for the command set
+whose peripheral device type matches the value given to \fIPG\fR. For
+example 'sg_mode \-\-list \-\-page=1' lists the command mode pages and
+subpages for tape devices. Additionally if a sub_page_code is given then it
+is interpreted as a transport identifier and command transport specific mode
+page codes and their names are listed following the main mode page list.
+Other options are ignored.
+.TP
+\fB\-L\fR, \fB\-\-llbaa\fR
+set the Long LBA Accepted (LLBAA) bit in the MODE SENSE (10) cdb. This
+bit is not defined in the MODE SENSE (6) cdb so setting the '\-L'
+and '\-\-six' options is reported as an error. When set the \fIDEVICE\fR
+may respond with 16 byte block descriptors as indicated by
+the 'LongLBA' field in the response. In most cases setting this option
+is not needed.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+The \fILEN\fR argument is the maximum response length in bytes. It is
+the 'allocation length' field in the cdb. When not given (or \fILEN\fR is
+zero) then the allocation length field is set to 4096 for MODE SENSE (10)
+or 252 for MODE SENSE (6). The \fILEN\fR argument must be non\-negative
+and no greater than 65535 for MODE SENSE (10) and not greater than 255
+for MODE SENSE (6).
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPG\fR
+page code to fetch. The \fIPG\fR is assumed to be a decimal value unless
+prefixed by '0x' or has a trailing 'h'. It should be a value between 0
+and 63 (inclusive). When not given and a default is required then
+a value of 63 (0x3f), which fetches all mode pages, is used.
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPG,SPG\fR
+page code and subpage code values to fetch. Both arguments are assumed
+to be decimal unless flagged as hexadecimal. The page code should be
+between 0 and 63 inclusive. The subpage code should be between 0 and 255
+inclusive. The default value for the subpage code is 0.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the response in binary to stdout. Error messages and warnings, if
+any, are sent to stderr. When this option is used twice (e.g. '\-rr')
+then has the same action as '\-R'
+.TP
+\fB\-R\fR
+output the selected mode page to stdout a byte per line. Each line contains
+two hexadecimal digits (e.g. "3e"). Useful as input (after editing) to
+the sg_wr_mode(8) utility.
+.TP
+\fB\-6\fR, \fB\-\-six\fR
+by default this utility sends a 10 byte MODE SENSE command to
+the \fIDEVICE\fR. However some SCSI devices only support 6 byte MODE SENSE
+commands (e.g. SCSI\-2 tape drives). This parameter forces the use
+of 6 byte MODE SENSE commands.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string then exit.
+.SH NOTES
+If the normal sg_modes utility fails with "illegal command
+operation code" then try the '\-\-six' (or '\-6') option.
+.PP
+This utility performs a SCSI INQUIRY command to determine the peripheral
+type of the device (e.g. 0 \-> Direct Access Device (disk)) prior to
+sending a MODE SENSE command. This helps in decoding the block
+descriptor and mode pages.
+.PP
+This utility opens \fIDEVICE\fR with a read\-only flag (e.g. in Unix with
+the O_RDONLY flag).
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be a SCSI
+generic (sg) device. In the 2.6 series block devices (e.g. SCSI disks
+and DVD drives) can also be specified. For example "sg_modes \-a /dev/sda"
+will work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_modes is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using '\-\-old' (or '\-O) as the first option.
+.TP
+\fB\-6\fR
+by default this utility sends a 10 byte MODE SENSE command to
+the \fIDEVICE\fR. This parameter forces the use of 6 byte MODE SENSE commands.
+See \fI\-\-six\fR in the main description.
+.TP
+\fB\-a\fR
+see \fI\-\-all\fR in the main description.
+.TP
+\fB\-A\fR
+output all the mode pages and subpages supported by the \fIDEVICE\fR. Same
+as '\-\-all \-\-all' in the new syntax.
+.TP
+\fB\-c\fR=\fIPC\fR
+\fIPC\fR is the page control value. See \fB\-\-control\fR=\fIPC\fR in
+the main description.
+.TP
+\fB\-d\fR
+see \fB\-\-dbd\fR in the main description.
+.TP
+\fB\-D\fR
+see \fB\-\-dbout\fR in the main description.
+.TP
+\fB\-e\fR
+see \fB\-\-examine\fR in the main description.
+.TP
+\fB\-f\fR
+see \fB\-\-flexible\fR in the main description.
+.TP
+\fB\-h\fR
+The default action is to decode known mode page numbers (and subpage
+numbers) into text. With this option mode page numbers (and subpage
+numbers) are output in hexadecimal.
+.TP
+\fB\-H\fR
+same action as the '\-h' option.
+.TP
+\fB\-l\fR
+see \fB\-\-list\fR in the main description.
+.TP
+\fB\-L\fR
+see \fB\-\-llbaa\fR in the main description.
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-m\fR=\fILEN\fR
+see \fB\-\-maxlen\fR=\fILEN\fR in the main description.
+.TP
+\fB\-p\fR=\fIPG\fR
+\fIPG\fR is page code to fetch. Should be a hexadecimal number between 0
+and 3f inclusive (0 to 63 decimal). The default value when required is
+3f (fetch all mode pages).
+.TP
+\fB\-p\fR=\fIPG,SPG\fR
+page code and subpage code values to fetch. The page code should be a
+hexadecimal number between 0 and 3f inclusive. The subpage code should
+be a hexadecimal number between 0 and ff inclusive. The default value
+for the subpage code is 0.
+.TP
+\fB\-r\fR
+output the selected mode page to stdout a byte per line. Each line contains
+two hexadecimal digits (e.g. "3e"). Useful as input (after editing) to
+the sg_wr_mode(8) utility.
+.TP
+\fB\-subp\fR=\fISPG\fR
+sub page code to fetch. Should be a hexadecimal number between 0 and
+0xff inclusive. The default value is 0.
+.TP
+\fB\-v\fR
+increase verbosity of output.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.TP
+\fB\-?\fR
+output usage message then exit. Ignore all other parameters.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2000\-2015 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sdparm(8), sg_wr_mode(8), sginfo(8),
+.B sgmode(scsirastools), scsiinfo(net), scu(net),
+.B seatools(seagate)
+.PP
+All these utilities offer some facility to change mode page (or block
+descriptor) parameters.
diff --git a/sg3_utils/doc/sg_opcodes.8 b/sg3_utils/doc/sg_opcodes.8
new file mode 100644
index 0000000..c950811
--- /dev/null
+++ b/sg3_utils/doc/sg_opcodes.8
@@ -0,0 +1,245 @@
+.TH SG_OPCODES "8" "December 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_opcodes \- report supported SCSI commands or task management functions
+.SH SYNOPSIS
+.B sg_opcodes
+[\fI\-\-alpha\fR] [\fI\-\-compact\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-no-inquiry\fR] [\fI\-\-opcode=OP\fR] [\fI\-\-raw\fR] [\fI\-\-rctd\fR]
+[\fI\-\-repd\fR] [\fI\-\-sa=SA\fR] [\fI\-\-tmf\fR] [\fI\-\-unsorted\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.PP
+.B sg_opcodes
+[\fI\-a\fR] [\fI\-c\fR] [\fI\-n\fR] [\fI\-o=OP\fR] [\fI\-q\fR] [\fI\-R\fR]
+[\fI\-s=SA\fR] [\fI\-t\fR] [\fI\-u\fR] [\fI\-v\fR] [\fI\-V\fR] [\fI\-?\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends a SCSI REPORT SUPPORTED OPERATION CODES or a REPORT
+SUPPORTED TASK MANAGEMENT FUNCTIONS command to the \fIDEVICE\fR and then
+outputs the response. The default action is to report supported operation
+codes. In this mode it will either list all supported commands or give
+detailed information on a specific command identified by the
+\fI\-\-opcode=OP\fR option (perhaps with additional information from the
+\fI\-\-sa=SA\fR option).
+.PP
+The name of a SCSI command depends on its peripheral device type (e.g. a
+disk). The REPORT SUPPORTED OPERATION CODES and REPORT SUPPORTED TASK
+MANAGEMENT FUNCTIONS commands are not supported in the MMC command set for
+CD and DVD devices. This utility does an INQUIRY to obtain the peripheral
+device type and prints out the vendor, product and revision strings.
+.PP
+A similar facility to query supported operation codes previously was available
+via the CmdDt bit in the SCSI INQUIRY command (see sg_inq(8)). However that
+facility was made obsolete and replaced by the REPORT SUPPORTED OPERATION
+CODES command in SPC\-3 (revision 4) during February 2002.
+.PP
+This utility supports two command line syntaxes, the preferred one is
+shown first in the synopsis and explained in this section. A later section
+on the old command line syntax outlines the second group of options.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-a\fR, \fB\-\-alpha\fR
+when all supported commands are being listed there is no requirement for
+the device server (i.e. the \fIDEVICE\fR) to sort the list of commands. When
+this option is given the list of supported commands is sorted by
+name (alphabetically). When this option and the \fB\-\-unsorted\fR option are
+both _not_ given then the list of supported commands is sorted
+numerically (first by operation code and then by service action).
+.TP
+\fB\-c\fR, \fB\-\-compact\fR
+some command names, especially those associated with some service actions,
+are getting longer. This may cause line wrap in the one line per command
+mode on some terminals. When this option is given the opcode and service
+action fields are combined into a single field with the service action,
+prefixed by a comma shown directly after the opcode. If there is no service
+action associated with the command, then the comma and the service action
+are not shown after the opcode. The CDB size field is not shown when this
+option is given.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+outputs the usage message summarizing command line options
+then exits. Ignores \fIDEVICE\fR if given.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+outputs the response in ASCII hexadecimal to stdout.
+.TP
+\fB\-n\fR, \fB\-\-no-inquiry\fR
+Prior to calling a SCSI REPORT SUPPORTED OPERATION CODES or a REPORT
+SUPPORTED TASK MANAGEMENT FUNCTIONS command, a SCSI INQUIRY command
+is performed. The reason is to determine the peripheral device type (pdt)
+of the \fIDEVICE\fR as this is helpful in translating operation codes
+to the command names. By default this utility prints a summary of INQUIRY
+command response on stdout. If this option (or the \fI\-\-raw\fR option)
+is given then that summary is not printed on stdout.
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-o\fR, \fB\-\-opcode\fR=\fIOP\fR
+the \fIDEVICE\fR will be queried for the given operation code ( i.e. the
+\fIOP\fR value) which is the first byte of a SCSI command. \fIOP\fR is
+decimal unless prefixed by "0x" or it has a trailing "h". \fIOP\fR should
+be in the range 0 to 255 (0xff) inclusive. When this option is not given
+then all available SCSI commands supported by the \fIDEVICE\fR are listed.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the response in binary to stdout. Error messages and warnings, if
+any, are sent to stderr.
+.TP
+\fB\-R\fR, \fB\-\-rctd\fR
+set report command timeout descriptor (RCTD) bit in the cdb. The response
+may or may not contain command timeout descriptors. If available they are
+output. If supported there are two values: a nominal command timeout
+and a recommended command timeout. Both have units of seconds. A value
+of zero means that no timeout is indicated and this is shown in
+the corresponding decoded output as "\-".
+.TP
+\fB\-q\fR, \fB\-\-repd\fR
+set read extended parameter data (REPD) bit in the report task management
+functions cdb. 16 bytes rather than the default 4 bytes expected in the
+response. This was added in SPC\-4 (revision 26).
+.TP
+\fB\-s\fR, \fB\-\-sa\fR=\fISA\fR
+the \fIDEVICE\fR will be queried for a command with the given service
+action (i.e. the \fISA\fR value). Used in conjunction with the
+\fI\-\-opcode=OP\fR option. If this option is not given, \fI\-\-opcode=OP\fR
+is given and the command in question does have a service action then a value
+of 0 will be assumed. \fISA\fR is decimal and expected to be in the range 0
+to 65535 (0xffff) inclusive.
+.TP
+\fB\-t\fR, \fB\-\-tmf\fR
+list supported task management functions. This is done with the SCSI REPORT
+SUPPORTED TASK MANAGEMENT FUNCTIONS command.  When this option is chosen
+the \fI\-\-alpha\fR, \fI\-\-opcode=OP\fR, \fI\-\-rctd\fR, \fI\-\-sa=SA\fR
+and \fI\-\-unsorted\fR options are ignored.
+.TP
+\fB\-u\fR, \fB\-\-unsorted\fR
+when all supported commands are being listed there is no requirement for
+the device server (i.e. the \fIDEVICE\fR) to sort the list of commands. When
+this option is given the list of supported commands is in the order given by
+the \fIDEVICE\fR. When this option is not given the supported commands
+are sorted numerically (first by operation code and then by service action).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string then exit.
+.SH NOTES
+As of SPC\-4 revision 7a the recognized task management functions are:
+abort set, abort task set, clear ACA, clear task set, I_T nexus reset,
+logical unit reset, query task, target reset and wakeup.
+As of SPC\-4 revision 26 target reset and wakeup have been made obsolete
+while query task set and query asynchronous event notification have been
+added.
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be a SCSI
+generic (sg) device. In the 2.6 series block devices (e.g. SCSI disks
+and DVD drives) can also be specified. For example "sg_opcodes /dev/sda"
+will work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_opcodes is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using '\-\-old' (or '\-O) as the first option.
+.TP
+\fB\-a\fR
+sort command alphabetically. Equivalent to \fI\-\-alpha\fR in main
+description.
+.TP
+\fB\-n\fR
+don't print a summary of the SCSI INQUIRY response on stdout.
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-o\fR=\fIOP\fR
+the \fIDEVICE\fR will be queried for the given operation code (i.e.
+\fIOP\fR) which is the first byte of a SCSI command. \fIOP\fR is
+hexadecimal and expected to be in the range 0 to ff inclusive.
+When this option is not given then all available SCSI commands supported
+by the \fIDEVICE\fR are listed.
+.TP
+\fB\-q\fR
+set the read extended parameter data (REPD) bit in report TMF cdb.
+Equivalent to \fI\-\-repd\fR in main description.
+.TP
+\fB\-R\fR
+set the report command timeout descriptor (RCTD) bit in cdb. Equivalent
+to \fI\-\-rctd\fR in main description.
+.TP
+\fB\-s\fR=\fISA\fR
+the \fIDEVICE\fR will be queried for a command with the given service
+action (i.e. \fISA\fR). Used in conjunction with the \fI\-o=OP\fR
+option. If this option is not given, \fI\-o=OP\fR is given and the command
+in question does have a service action then a value of 0 will be assumed.
+\fISA\fR is hexadecimal and expected to be in the range 0 to ffff inclusive.
+.TP
+\fB\-t\fR
+list supported task management functions. Equivalent to \fI\-\-tmf\fR in
+the main description.
+.TP
+\fB\-u\fR
+output all supported commands in the order given by \fIDEVICE\fR.
+Equivalent to \fI\-\-unsorted\fR in main description.
+.TP
+\fB\-v\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.TP
+\fB\-?\fR
+output usage message. Ignore all other parameters.
+.SH EXAMPLES
+The examples in this page use Linux device names. For suitable device
+names in other supported Operating Systems see the sg3_utils(8) man page.
+.PP
+To see the information about a specific command give its operation
+code to the '\-\-op=' option. A command line invocation is shown first
+followed by a typical response:
+.PP
+   # sg_opcodes \-\-op=93h /dev/sdb
+.PP
+  Opcode=0x93
+.br
+  Command_name: Write same(16)
+.br
+  Command supported [conforming to SCSI standard]
+.br
+  Usage data: 93 e2 00 00 00 00 ff ff ff ff 00 00 ff ff 00 00
+.PP
+The next example shows the supported task management functions:
+.PP
+   # sg_opcodes \-\-tmf \-n /dev/sdb
+.PP
+Task Management Functions supported by device:
+.br
+    Abort task
+.br
+    Abort task set
+.br
+    Clear ACA
+.br
+    Clear task set
+.br
+    Logical unit reset
+.br
+    Query task
+.br
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2012 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq(sg3_utils)
diff --git a/sg3_utils/doc/sg_persist.8 b/sg3_utils/doc/sg_persist.8
new file mode 100644
index 0000000..96b7e9d
--- /dev/null
+++ b/sg3_utils/doc/sg_persist.8
@@ -0,0 +1,425 @@
+.TH SG_PERSIST "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_persist \- use SCSI PERSISTENT RESERVE command to access registrations
+and reservations
+.SH SYNOPSIS
+.B sg_persist
+[\fIOPTIONS\fR] \fIDEVICE\fR
+.PP
+.B sg_persist
+[\fIOPTIONS\fR] \fI\-\-device=DEVICE\fR
+.PP
+.B sg_persist
+\fI\-\-help\fR | \fI\-\-version\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility allows Persistent reservations and registrations to be
+queried and changed. Persistent reservations and registrations are
+queried by sub\-commands (called "service actions" in SPC\-4) of the
+SCSI PERSISTENT RESERVE IN (PRIN) command. Persistent reservations and
+registrations are changed by sub\-commands of the SCSI PERSISTENT RESERVE
+OUT (PROUT) command.
+.PP
+There is a two stage process to obtain a persistent reservation. First an
+application (an I_T nexus in standard's jargon) must register a reservation
+key. If that is accepted (and it should be unless some other I_T nexus has
+registered that key) then the application can try and reserve the device.
+The reserve operation must specify the reservation key and a "type" (see
+the \fI\-\-prout\-type=TYPE\fR option).
+.PP
+It is relatively safe to query the state of Persistent reservations and
+registrations. With no options this utility defaults to the READ KEYS
+sub\-command of the PRIN command. Other PRIN sub\-commands are
+READ RESERVATION, REPORT CAPABILITIES and READ FULL STATUS.
+.PP
+Before trying to change Persistent reservations and registrations users
+should be aware of what they are doing. The relevant sections of the SCSI
+Primary Commands document (i.e. SPC\-4 whose most recent draft is revision
+37 dated 17 May 2014) are sections 5.12 (titled "Reservations"),
+6.15 (for the PRIN command) and 6.16 (for the PROUT command). To safeguard
+against accidental use, the \fI\-\-out\fR option must be given when a
+PROUT sub\-command (e.g.  \fI\-\-register\fR) is used.
+.PP
+The older SCSI RESERVE and RELEASE commands (both 6 and 10 byte variants)
+are not supported by this utility. In SPC\-3, RESERVE and RELEASE are
+deprecated, replaced by Persistent Reservations. RESERVE and RELEASE
+have been removed from SPC\-4 and Annex B is provided showing how to
+convert to persistent reservation commands. See a utility
+called 'scsires' for support of the SCSI RESERVE and RELEASE commands.
+.PP
+The \fIDEVICE\fR is required by all variants of this utility apart
+from \fI\-\-help\fR. The \fIDEVICE\fR can be given either as an
+argument (typically but not necessarily the last one) or via
+the \fI\-\-device=DEVICE\fR option.
+.PP
+SPC\-4 does not use the term "sub\-command". It uses the term "service action"
+for this and for part of a field's name in the parameter block associated
+with the PROUT command (i.e. "service action reservation key"). To lessen
+the potential confusion the term "sub\-command" has been introduced.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The following options are sorted in alphabetical order, based on their
+long option name.
+.TP
+\fB\-l\fR, \fB\-\-alloc-length\fR=\fILEN\fR
+specify the allocation length of the PRIN command. \fILEN\fR is a hex value.
+By default this value is set to the size of the data\-in buffer (8192).
+This parameter is of use for verification that response to PRIN commands
+with various allocation lengths is per section 4.3.5.6 of SPC\-4 revision 18.
+Valid \fILEN\fR values are 0\-8192.
+.TP
+\fB\-C\fR, \fB\-\-clear\fR
+Clear is a sub\-command of the PROUT command. It releases the
+persistent reservation (if any) and clears all registrations from the
+device. It is required to supply a reservation key that is registered
+for this I_T_L nexus (identified by \fI\-\-param\-rk=RK\fR).
+.TP
+\fB\-d\fR, \fB\-\-device\fR=\fIDEVICE\fR
+\fIDEVICE\fR to send SCSI commands to. The \fIDEVICE\fR can either be
+provided via this option or via a freestanding argument. For example,
+these two: 'sg_persist \-\-device=/dev/sg2' and 'sg_persist /dev/sg2'
+are equivalent.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output a usage message showing main options. Use twice (e.g. '\-hh') for
+the other option and more help.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+the response to a valid PRIN sub\-command will be output in hexadecimal.
+By default (i.e. without this option) if the PRIN sub\-command is recognised
+then the response will be decoded as per SPC\-4. May be used more than
+once for more hex and less text.
+.TP
+\fB\-i\fR, \fB\-\-in\fR
+specify that a SCSI PERSISTENT RESERVE IN command is required. This
+is the default.
+.TP
+\fB\-n\fR, \fB\-\-no\-inquiry\fR
+the default action is to do a standard SCSI INQUIRY command and output
+make, product and revision strings plus the peripheral device type
+prior to executing a PRIN or PROUT command. With this option the
+INQUIRY command is skipped.
+.TP
+\fB\-o\fR, \fB\-\-out\fR
+specify that a SCSI PERSISTENT RESERVE OUT command is required.
+.TP
+\fB\-Y\fR, \fB\-\-param\-alltgpt\fR
+set the 'all target ports' (ALL_TG_PT) flag in the parameter block of the
+PROUT command. Only relevant for 'register' and 'register and ignore existing
+key' sub\-commands.
+.TP
+\fB\-Z\fR, \fB\-\-param\-aptpl\fR
+set the 'activate persist through power loss' (APTPL) flag in the parameter
+block of the PROUT command. Relevant for 'register', 'register and ignore
+existing key' and 'register and move' sub\-commands.
+.TP
+\fB\-K\fR, \fB\-\-param\-rk\fR=\fIRK\fR
+specify the reservation key found in the parameter block of the PROUT
+command. \fIRK\fR is assumed to be hex (up to 8 bytes long). Default value
+is 0. This option is needed by most PROUT sub\-commands.
+.TP
+\fB\-S\fR, \fB\-\-param\-sark\fR=\fISARK\fR
+specify the service action reservation key found in the parameter block
+of the PROUT command. \fISARK\fR is assumed to be hex (up to 8 bytes long).
+Default value is 0. This option is needed by some PROUT sub\-commands.
+.TP
+\fB\-P\fR, \fB\-\-preempt\fR
+Preempt is a sub\-command of the PROUT command. Preempts the existing
+persistent reservation (identified by \fI\-\-param\-sark=SARK\fR) with
+the registration key that is registered for this I_T_L nexus (identified
+by \fI\-\-param\-rk=RK\fR). If a new reservation is established as
+a result of the preemption then the supplied \fI\-\-prout\-type=TYPE\fR
+is used as the type for this new reservation.
+.TP
+\fB\-A\fR, \fB\-\-preempt\-abort\fR
+Preempt and Abort is a sub\-command of the PROUT command. Preempts
+the existing persistent reservation (identified by \fI\-\-param\-sark=SARK\fR)
+with the registration key that is registered for this I_T_L nexus (identified
+by \fI\-\-param\-rk=RK\fR). If a new reservation is established as
+a result of the preemption then the supplied \fI\-\-prout\-type=TYPE\fR
+is used as the type for this new reservation. ACA and other pending
+tasks are aborted.
+.TP
+\fB\-T\fR, \fB\-\-prout\-type\fR=\fITYPE\fR
+specify the PROUT command's 'type' argument. Required by
+the 'register\-move', 'reserve', 'release' and 'preempt (and abort)'
+sub\-commands. Valid \fITYPE\fR values: 1\-> write exclusive, 3\->
+exclusive access, 5\-> write exclusive \- registrants only, 6\->
+exclusive access \- registrants only, 7\-> write exclusive \- all registrants,
+8\-> exclusive access \- all registrants. Default value is 0 (which is
+an invalid type). Each "persistent reservation type" is explained in more
+detail in a subsection of that name in the read reservation section of
+the PRIN command (section 6.15.3.3 of SPC\-4 revision 37).
+.TP
+\fB\-s\fR, \fB\-\-read\-full\-status\fR
+Read Full Status is a sub\-command of the PRIN command. For each registration
+with the given SCSI device, it lists the reservation key and associated
+information. TransportIDs, if supplied in the response, are decoded.
+.TP
+\fB\-k\fR, \fB\-\-read\-keys\fR
+Read Keys is a sub\-command of the PRIN command. Lists all the reservation
+keys registered (i.e. registrations) with the given SCSI device. This is
+the default sub\-command for the SCSI PRIN command.
+.TP
+\fB\-y\fR, \fB\-\-readonly\fR
+Open \fIDEVICE\fR read\-only. May be useful with PRIN commands if there are
+unwanted side effects with the default read\-write open. When given twice
+is interpreted as forcing a read\-write open thus overriding the
+SG_PERSIST_IN_RDONLY environment variable if present.
+.TP
+\fB\-r\fR, \fB\-\-read\-reservation\fR
+Read Reservation is a sub\-command of the PRIN command. List information
+about the current holder of the reservation on the \fIDEVICE\fR. If there
+is no current reservation this will be noted. Information about the current
+holder of the reservation includes its reservation key, scope and type.
+.TP
+\fB\-s\fR, \fB\-\-read\-status\fR
+same as \fI\-\-read\-full\-status\fR.
+.TP
+\fB\-G\fR, \fB\-\-register\fR
+Register is a sub\-command of the PROUT command. It has 3 different
+actions depending on associated parameters. a) add a new registration
+with '\-\-param\-rk=0' and '\-\-param\-sark=<new_rk>'; b) Change an existing
+registration with '\-\-param\-rk=<old_rk>'
+and '\-\-param\-sark=<new_rk>'; or  c) Delete an existing registration
+with '\-\-param\-rk=<old_rk>' and '\-\-param\-sark=0'.
+.TP
+\fB\-I\fR, \fB\-\-register\-ignore\fR
+Register and Ignore Existing Key is a sub\-command of the PROUT command.
+Similar to \fI\-\-register\fR except that when changing a reservation key
+the old key is not specified. The '\-\-param\-sark=<new_rk>' option should
+also be given.
+.TP
+\fB\-M\fR, \fB\-\-register\-move\fR
+register (another initiator) and move (the reservation held by the current
+initiator to that other initiator) is a sub\-command of the PROUT command.
+It requires the transportID of the other initiator. [The standard uses the
+term I_T nexus but the point to stress is that there are two initiators
+(the one sending this command and another one) but only one logical unit.]
+The \fI\-\-prout\-type=TYPE\fR and \fI\-\-param\-rk=RK\fR options need to
+match that of the existing reservation while \fI\-\-param\-sark=SARK\fR
+option specifies the reservation key of the new (i.e. destination)
+registration.
+.TP
+\fB\-Q\fR, \fB\-\-relative\-target\-port\fR=\fIRTPI\fR
+relative target port identifier that reservation is to be moved to by
+PROUT 'register and move' sub\-command. \fIRTPI\fR is assumed to be hex
+in the range 0 to ffff inclusive. Defaults to 0 .
+.TP
+\fB\-L\fR, \fB\-\-release\fR
+Release is a sub\-command of the PROUT command. It releases the
+current persistent reservation. The \fI\-\-prout\-type=TYPE\fR
+and \fI\-\-param\-rk=RK\fR options, matching the reservation, must also be
+specified.
+.TP
+\fB\-z\fR, \fB\-\-replace\-lost\fR
+Replace Lost Reservation is a sub\-command of the PROUT command.  It "begins
+a recovery process for the lost persistent reservation that is managed by
+application clients". It also stops the device server terminating commands
+due to a lost persistent reservation. Options should be
+be '\-\-param\-rk=0' (or not given), '\-\-param\-sark=<new_rk>'
+and \fI\-\-prout\-type=TYPE\fR.
+.TP
+\fB\-c\fR, \fB\-\-report\-capabilities\fR
+Report Capabilities is a sub\-command of the PRIN command. It lists
+information about the aspects of persistent reservations that the
+\fIDEVICE\fR supports.
+.TP
+\fB\-R\fR, \fB\-\-reserve\fR
+Reserve is a sub\-command of the PROUT command. It creates a new
+persistent reservation (if permitted). The \fI\-\-prout\-type=TYPE\fR
+and \fI\-\-param\-rk=RK\fR options must also be specified.
+.TP
+\fB\-X\fR, \fB\-\-transport\-id\fR=\fITIDS\fR
+The \fITIDS\fR argument can take one of several forms. It can be a
+comma (or single space) separated list of ASCII hex bytes representing
+a single TransportID as defined in SPC\-4. They are usually 24 bytes
+long apart from in iSCSI. The \fITIDS\fR argument may be a transport
+specific form (e.g. "sas,5000c50005b32001" is clearer than and equivalent
+to the hex byte form: "6,0,0,0,5,0,c5,0,5,b3,20,1"). The \fITIDS\fR argument
+may be "\-" in which case one or more TransportIDs can be read from stdin.
+The \fITIDS\fR argument may be of the form "file=<name>" in which case
+one or more TransportIDs can be read from a file called <name>. See
+the "TRANSPORT IDs" section below for more information.
+.TP
+\fB\-U\fR, \fB\-\-unreg\fR
+optional when the PROUT register and move sub\-command is invoked. If given
+it will unregister the current initiator (I_T nexus) after the other initiator
+has been registered and the reservation moved to it. When not given the
+initiator (I_T nexus) that sent the PROUT command remains registered.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+print out cdb of issued commands prior to execution. If used twice
+prints out the parameter block associated with the PROUT command prior
+to its execution as well. If used thrice decodes given transportID(s)
+as well. To see the response to a PRIN command in low level form use
+the \fI\-\-hex\fR option.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string. Ignore all other parameters.
+.TP
+\fB\-?\fR
+output usage message. Ignore all other parameters.
+.SH TRANSPORT IDs
+TransportIDs are used in persistent reservations to identify initiators.
+The format of a TransportID differs depending on the type of transport
+being used. Their format is described in SPC\-4 (in draft revision 37 see
+section 7.6.4).
+.PP
+A TransportID is required for the PROUT 'register and move' sub\-command and
+the PROUT 'register' sub\-command can have zero, one or more TransportIDs.
+.PP
+When the \fI\-\-transport\-id=TIDS\fR option is given then the \fITIDS\fR
+argument may be a comma (or single space) separated list of ASCII hex bytes
+that represent a single TransportID as defined in SPC\-4. Alternatively the
+\fITIDS\fR argument may be a transport specific string starting with
+either "fcp,", "spi,", "sbp,", "srp,", "iqn", "sas," or "sop,". The "iqn"
+form is an iSCSI qualified name. Apart from "iqn" the other transport
+specific leadin string may be given in upper case (e.g. "FCP,").
+.PP
+The "fcp," form should be followed by 16 ASCII hex digits that represent an
+initiator's N_PORT_NAME (e.g. "fcp,10000000C9F3A571"). The "spi," form should
+be followed by "<scsi_address>,<relative_target_port_identifier>" (both
+decimal numbers). The "sbp," form should be followed by 16 ASCII hex digits
+that represent an initiator's EUI\-64 name. The "srp," form should be
+followed by 32 ASCII hex digits that represent an initiator port identifier.
+The "sas," form should be followed by 16 ASCII hex digits that represent an
+initiator's port SAS address (e.g. "sas,5000c50005b32001"). The "sop," form
+takes a hex number that represents a routing id.
+.PP
+There are two iSCSI qualified name forms. The shorter form contains the
+iSCSI name of the initiator
+port (e.g. "iqn.5886.com.acme.diskarrays\-sn\-a8675309"). The longer form
+adds the initiator session id (ISID in hex) separated by ",i,0x".
+For example "iqn.5886.com.acme.diskarrays\-sn\-a8675309,i,0x1234567890ab".
+On the command line to stop punctuation in an iSCSI name
+being (mis)\-interpreted by the shell, putting the option argument
+containing the iSCSI name in double quotes is advised. iSCSI names are
+encoded in UTF\-8 so if non (7 bit) ASCII characters appear in the
+iSCSI name on the command line, there will be difficulties if they are not
+encoded in UTF\-8. The locale can be changed temporarily by prefixing the
+command line invocation of sg_persist with "LANG=en_US.utf\-8" for example.
+.PP
+Alternatively the \fITIDS\fR argument may specify a file (or pipe) from which
+one or more TransportIDs may be read. If the \fITIDS\fR argument is "\-"
+then stdin (standard input) is read. If the \fITIDS\fR argument is of the
+form "file=<name>" than a file called <name> is read.
+A valid SPC\-4 TransportID is built from the transport specific string
+outlined in the previous paragraphs. The parsing of the data read is
+relatively simple. Empty lines are ignored. Everything from and including
+a "#" on a line is ignored. Leading spaces and tabs are ignored. There
+can be one transportID per line. The transportID can either be a comma,
+space or tab separated list of ASCII hex bytes that represent a
+TransportID as defined in SPC\-4. Padding with zero bytes to a minimum
+length of 24 bytes is performed if necessary. The transportID may also
+be transport specific string type discussed above.
+.PP
+In SPC\-3 the SPEC_I_PT bit set to one and TransportIDs were allowed for
+the PROUT register and ignore existing key sub\-command. In SPC\-4 that
+is disallowed yielding a CHECK CONDITION status with and ILLEGAL REQUEST
+sense key and an additional sense code set to INVALID FIELD IN PARAMETER
+LIST.
+.SH ENVIRONMENT VARIABLES
+Currently there is one recognised environment variable: SG_PERSIST_IN_RDONLY.
+If present and only if a PRIN command has been selected then the
+given \fIDEVICE\fR is opened read\-only (e.g. in Unix that is with the
+O_RDONLY flag). See the \fI\-\-readonly\fR option.
+.SH NOTES
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be
+a SCSI generic (sg) device. In the 2.6 series any SCSI device
+name (e.g. /dev/sdc, /dev/st1m or /dev/sg3) can be specified.
+For example "sg_persist \-\-read\-keys /dev/sdb"
+will work in the 2.6 series kernels.
+.PP
+The only scope for PROUT commands supported in the current draft of
+SPC\-4 is "LU_SCOPE". Hence there seems to be no point in offering an
+option to set scope to another value.
+.PP
+Most errors with the PROUT sub\-commands (e.g. missing or
+mismatched \fI\-\-prout\-type=TYPE\fR) will result in a RESERVATION
+CONFLICT status. This can be a bit confusing when you know there is
+only one (active) initiator: the "conflict" is with the SPC standard, not
+another initiator.
+.PP
+Some recent disks accept some PRIN and PROUT sub\-commands when the
+media is stopped. One exception was setting the APTPL flag (with
+the \fI\-\-param\-aptpl\fR option) during a key register operation,
+it complained if the disk one stopped. The error indicated it wanted
+the disk spun up and when that happened, the registration was
+successful.
+.SH EXAMPLES
+These examples use Linux device names. For suitable device names in
+other supported Operating Systems see the sg3_utils(8) man page.
+.PP
+Due to the various option defaults the simplest example executes
+the 'read keys' sub\-command of the PRIN command:
+.PP
+   sg_persist /dev/sdb
+.PP
+This is the same as the following (long\-winded) command:
+.PP
+   sg_persist \-\-in \-\-read\-keys \-\-device=/dev/sdb
+.PP
+To read the current reservation either the '\-\-read\-reservation' form or
+the shorter '\-r' can be used:
+.PP
+   sg_persist \-r /dev/sdb
+.PP
+To
+.B register
+the new reservation key 0x123abc the following could be used:
+.PP
+   sg_persist \-\-out \-\-register \-\-param\-sark=123abc /dev/sdb
+.PP
+Given the above registration succeeds, to
+.B reserve
+the \fIDEVICE\fR (with type 'write exclusive') the following
+could be used:
+.PP
+   sg_persist \-\-out \-\-reserve \-\-param\-rk=123abc
+.br
+              \-\-prout\-type=1 /dev/sdb
+.PP
+To
+.B release
+the reservation the following can be given (note that
+the \-\-param\-rk and \-\-prout\-type arguments must match those of the
+reservation):
+.PP
+   sg_persist \-\-out \-\-release \-\-param\-rk=123abc
+.br
+              \-\-prout\-type=1 /dev/sdb
+.PP
+Finally to
+.B unregister
+a reservation key (and not effect other
+registrations which is what '\-\-clear' would do) the command
+is a little surprising:
+.PP
+   sg_persist \-\-out \-\-register \-\-param\-rk=123abc /dev/sdb
+.PP
+Now have a close look at the difference between the register and
+unregister examples above.
+.PP
+An example file that is suitably formatted to pass transportIDs via
+a '\-\-transport\-id=file=transport_ids.txt' option can be found in the
+examples sub\-directory of the sg3_utils package. There is also a
+simple test script called sg_persist_tst.sh in the same directory.
+.PP
+The above sequence of commands was tested successfully on a Seagate Savvio
+10K.3 disk and a 1200 SSD both of which have SAS interfaces.
+.SH EXIT STATUS
+The exit status of sg_persist is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2014 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg3_utils(sg3_utils), scsires(internet)
diff --git a/sg3_utils/doc/sg_prevent.8 b/sg3_utils/doc/sg_prevent.8
new file mode 100644
index 0000000..3548ffb
--- /dev/null
+++ b/sg3_utils/doc/sg_prevent.8
@@ -0,0 +1,59 @@
+.TH SG_PREVENT "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_prevent \- send SCSI PREVENT ALLOW MEDIUM REMOVAL command
+.SH SYNOPSIS
+.B sg_prevent
+[\fI\-\-allow\fR] [\fI\-\-help\fR] [\fI\-\-prevent=PC\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI PREVENT ALLOW MEDIUM REMOVAL command to \fIDEVICE\fR.
+The default action of this utility is to prevent the removing or
+ejecting of the medium from a drive. This is done by ignoring the
+SCSI START STOP UNIT command (see sg_start) and ignoring the eject
+button on the drive when the user presses it. Drives that hold removable
+disks, tape cartridges or cd/dvd media typically implement this command.
+The definition of the "prevent" codes for this command differ between
+disks and tapes (covered by SBC\-3 and SSC\-3) and cd/dvd drives (covered
+by MMC\-5). The "prevent codes" described here are from MMC\-5.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-a\fR, \fB\-\-allow\fR
+allow medium removal. This is equivalent to setting to '\-\-prevent=2'.
+Cannot be used with \fI\-\-prevent=PC\fR option (i.e. either use
+no options (hence prevent removal), this option or \fI\-\-prevent=PC\fR).
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-p\fR, \fB\-\-prevent\fR=\fIPC\fR
+where \fIPC\fR is a prevent code value. Defined values are: 0 allows removal,
+1 prevents removal (default), 2 allows persistent removal while 3 prevents
+persistent removal. "Persistent" in this context means that the
+initiator (port) that successfully uses code 3 blocks other initiators (ports)
+from allowing removal. A "persistent prevent" state can be cleared by the
+owner allowing persistent removal (code 2) or a power cycle (or anything that
+resets the device (LU)) or some special commands (e.g. various service
+actions of Persistent Reserve Out, see SPC\-3).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH EXIT STATUS
+The exit status of sg_prevent is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2012 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_start(sg3_utils), sg_persist(sg3_utils)
diff --git a/sg3_utils/doc/sg_raw.8 b/sg3_utils/doc/sg_raw.8
new file mode 100644
index 0000000..16d6437
--- /dev/null
+++ b/sg3_utils/doc/sg_raw.8
@@ -0,0 +1,159 @@
+.TH SG_RAW "8" "November 2015" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_raw \- send arbitrary SCSI command to a device
+.SH SYNOPSIS
+.B sg_raw [\fIOPTIONS\fR] \fIDEVICE\fR CDB0 CDB1 ...
+.SH DESCRIPTION
+This utility sends an arbitrary SCSI command (between 6 and 256 bytes) to
+the \fIDEVICE\fR. There may be no associated data transfer; or data may be
+read from a file and sent to the \fIDEVICE\fR; or data may be received from
+the \fIDEVICE\fR and then displayed or written to a file. If supported
+by the pass through, bidirectional commands may be sent (i.e. containing
+both data to be sent to the \fIDEVICE\fR and received from the
+\fIDEVICE\fR).
+.PP
+The SCSI command may be between 6 and 256 bytes long. Each command byte is
+specified in plain hex format (00..FF) without a prefix or suffix. See
+EXAMPLES section below.
+.PP
+The commands pass through a generic SCSI interface which is implemented
+for several operating systems including Linux, FreeBSD and Windows.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-binary\fR
+Dump data in binary form, even when writing to stdout.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display usage information and exit.
+.TP
+\fB\-i\fR, \fB\-\-infile\fR=\fIIFILE\fR
+Read data from \fIIFILE\fR instead of stdin. This option is ignored if
+\fB\-\-send\fR is not specified.
+.TP
+\fB\-k\fR, \fB\-\-skip\fR=\fILEN\fR
+Skip the first \fILEN\fR bytes of the input file or stream. This option
+is ignored if \fB\-\-send\fR is not specified.
+.TP
+\fB\-n\fR, \fB\-\-nosense\fR
+Don't display SCSI Sense information.
+.TP
+\fB\-o\fR, \fB\-\-outfile\fR=\fIOFILE\fR
+Write data received from the \fIDEVICE\fR to \fIOFILE\fR. The data is
+written in binary. By default, data is dumped in hex format to stdout.
+If \fIOFILE\fR is '\-' then data is dumped in binary to stdout.
+This option is ignored if \fB\-\-request\fR is not specified.
+.TP
+\fB\-r\fR, \fB\-\-request\fR=\fIRLEN\fR
+Expect to receive up to \fIRLEN\fR bytes of data from the \fIDEVICE\fR.
+\fIRLEN\fR may be suffixed with 'k' to use kilobytes (1024 bytes) instead
+of bytes. \fIRLEN\fR is decimal unless it has a leading '0x' or a
+trailing 'h'.
+.br
+If \fIRLEN\fR is too small (i.e. either smaller than indicated by the
+cdb (typically the "allocation length" field) and/or smaller than the
+\fIDEVICE\fR tries to send back) then the HBA driver may complain. Making
+\fIRLEN\fR larger than required should cause no problems. Most
+SCSI "data\-in" commands return a data block that contains (in its early
+bytes) a length that the \fIDEVICE\fR would "like" to send back if
+the "allocation length" field in the cdb is large enough. In practice, the
+\fIDEVICE\fR will return no more bytes than indicated in the "allocation
+length" field of the cdb.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+Open \fIDEVICE\fR read\-only. The default (without this option) is to open
+it read\-write.
+.TP
+\fB\-s\fR, \fB\-\-send\fR=\fISLEN\fR
+Read \fISLEN\fR bytes of data, either from stdin or from a file, and send
+them to the \fIDEVICE\fR. In the SCSI transport, \fISLEN\fR becomes the
+length (in bytes) of the "data\-out" buffer. \fISLEN\fR is decimal unless
+it has a leading '0x' or a trailing 'h'.
+.br
+It is the responsibility of the user to make sure that the "data\-out"
+length implied or stated in the cdb matches \fISLEN\fR. Note that some
+common SCSI commands such as WRITE(10) have a "transfer length" field whose
+units are logical blocks (which are often 512 bytes long).
+.TP
+\fB\-t\fR, \fB\-\-timeout\fR=\fISEC\fR
+Wait up to \fISEC\fR seconds for command completion (default: 20).
+Note that if a command times out the operating system may start by
+aborting the command and if that is unsuccessful it may attempt
+to reset the device.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version and license information and exit.
+.SH NOTES
+The sg_inq utility can be used to send an INQUIRY command to a device
+to determine its peripheral device type (e.g. '1' for a streaming
+device (tape drive)) which determines which SCSI command sets a device
+should support (e.g. SPC and SSC). The sg_vpd utility probes the Vital
+Product Pages of a devices which may contain useful information.
+.PP
+The ability to send more than a 16 byte CDB (in some cases 12 byte CDB)
+may be restricted by the pass\-through interface, the low level driver
+or the transport. In the Linux series 3 kernels, the bsg driver can
+handle longer CDBs, block devices (e.g. /dev/sdc) accessed via the
+SG_IO ioctl cannot handle CDBs longer than 16 bytes, and the sg driver
+can handle longer CDBs from lk 3.17 .
+.PP
+The CDB command name defined by T10 for the given CDB is shown if
+the '\-vv' option is given. The command line syntax still needs to be
+correct, so /dev/null may be used for the \fIDEVICE\fR since the CDB
+command name decoding is done before the \fIDEVICE\fR is checked.
+.SH EXAMPLES
+These examples, apart from the last one, use Linux device names. For
+suitable device names in other supported Operating Systems see the
+sg3_utils(8) man page.
+.TP
+sg_raw /dev/scd0 1b 00 00 00 02 00
+Eject the medium in CD drive /dev/scd0.
+.TP
+sg_raw \-r 1k /dev/sg0 12 00 00 00 60 00
+Perform an INQUIRY on /dev/sg0 and dump the response data (up to
+1024 bytes) to stdout.
+.TP
+sg_raw \-s 512 \-i i512.bin /dev/sda 3b 02 00 00 00 00 00 02 00 00
+Showing an example of writing 512 bytes to a sector on a disk
+is a little dangerous. Instead this example will read i512.bin (assumed
+to be 512 bytes long) and use the SCSI WRITE BUFFER command to send
+it to the "data" buffer (that is mode 2). This is a safe operation.
+.TP
+sg_raw \-r 512 \-o o512.bin /dev/sda 3c 02 00 00 00 00 00 02 00 00
+This will use the SCSI READ BUFFER command to read 512 bytes from
+the "data" buffer (i.e. mode 2) then write it to the o512.bin file.
+When used in conjunction with the previous example, if both commands
+work then 'cmp i512.bin o512.bin' should show a match.
+.TP
+sg_raw \-\-infile=urandom.bin \-\-send=512 \-\-request=512 \-\-outfile=out.bin "/dev/bsg/7:0:0:0" 53 00 00 00 00 00 00 00 01 00
+This is a bidirectional XDWRITEREAD(10) command being sent via a Linux
+bsg device. Note that data is being read from "urandom.bin" and sent
+to the device (data\-out) while resulting data (data\-in) is placed
+in the "out.bin" file. Also note the length of both is 512 bytes
+which corresponds to the transfer length of 1 (block) in the cdb (i.e.
+the second last byte).
+.TP
+sg_raw.exe PhysicalDrive1 a1 0c 0e 00 00 00 00 00 00 e0 00 00
+This example is from Windows and shows a ATA STANDBY IMMEDIATE command
+being sent to PhysicalDrive1. That ATA command is contained within
+the SCSI ATA PASS\-THROUGH(12) command (see the SAT or SAT\-2 standard at
+http://www.t10.org). Notice that the STANDBY IMMEDIATE command does not
+send or receive any additional data, however if it fails sense data
+should be returned and displayed.
+.SH EXIT STATUS
+The exit status of sg_raw is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Ingo van Lil
+.SH "REPORTING BUGS"
+Report bugs to <inguin at gmx dot de>.
+.SH COPYRIGHT
+Copyright \(co 2001\-2015 Ingo van Lil
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq, sg_vpd, sg3_utils (sg3_utils), plscsi
diff --git a/sg3_utils/doc/sg_rbuf.8 b/sg3_utils/doc/sg_rbuf.8
new file mode 100644
index 0000000..2c2b772
--- /dev/null
+++ b/sg3_utils/doc/sg_rbuf.8
@@ -0,0 +1,178 @@
+.TH SG_RBUF "8" "January 2007" "sg3_utils\-1.23" SG3_UTILS
+.SH NAME
+sg_rbuf \- reads data using SCSI READ BUFFER command
+.SH SYNOPSIS
+.B sg_rbuf
+[\fI\-\-buffer=EACH\fR] [\fI\-\-dio\fR] [\fI\-\-help\fR] [\fI\-\-mmap\fR]
+[\fI\-\-quick\fR] [\fI\-\-size=OVERALL\fR] [\fI\-\-test\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.PP
+.B sg_rbuf
+[\fI\-b=EACH_KIB\fR] [\fI\-d\fR] [\fI\-m\fR] [\fI\-q\fR]
+[\fI\-s=OVERALL_MIB\fR] [\fI\-t\fR] [\fI\-v\fR] [\fI\-V\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This command reads data with the SCSI READ BUFFER command and then discards
+it. Typically the data being read is from a disk's memory cache. It is
+assumed that the data is sourced quickly (although this is not guaranteed
+by the SCSI standards) so that it is faster than reading data from the media.
+This command is designed for timing transfer speeds across a SCSI transport.
+.PP
+To fetch the data with a SCSI READ BUFFER command and optionally decode it
+see the sg_read_buffer utility. There is also a sg_write_buffer utility
+useful for downloading firmware amongst other things.
+.PP
+This utility supports two command line syntaxes, the preferred one is
+shown first in the synopsis and explained in this section. A later section
+on the old command line syntax outlines the second group of options.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-buffer\fR=\fIEACH\fR
+where \fIEACH\fR is the number of bytes to be transferred by each READ
+BUFFER command. The default is the actual available buffer size returned
+by the READ BUFFER (descriptor) command. The maximum is
+the same as the default, hence this argument can only be used to reduce the
+size of each transfer to less than the device's actual available buffer size.
+.TP
+\fB\-d\fR, \fB\-\-dio\fR
+use direct IO if available. This option is only available if the \fIDEVICE\fR
+is a sg driver device node (e.g. /dev/sg1). In this case the sg driver will
+attempt to configure the DMA from the SCSI adapter to transfer directly into
+user memory. This will eliminate the copy via kernel buffers. If not
+available then this will be reported and indirect IO will be done instead.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print usage message then exit.
+.TP
+\fB\-m\fR, \fB\-\-mmap\fR
+use memory mapped IO if available. This option is only available if the
+\fIDEVICE\fR is a sg driver device node (e.g. /dev/sg1). In this case the
+sg driver will attempt to configure the DMA from the SCSI adapter to transfer
+directly into user memory. This will eliminate the copy via kernel buffers.
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-q\fR, \fB\-\-quick\fR
+only transfer the data into kernel buffers (typically by DMA from the SCSI
+adapter card) and do not move it into the user space. This option is only
+available if the \fIDEVICE\fR is a sg driver device node (e.g. /dev/sg1).
+.TP
+\fB\-s\fR, \fB\-\-size\fR=\fIOVERALL\fR
+where \fIOVERALL\fR is the size of total transfer in bytes. The default is
+200 MiB (200*1024*1024 bytes). The actual number of bytes transferred may
+be slightly less than requested since all transfers are the same size (and
+an integer division is involved rounding towards zero).
+.TP
+\fB\-t\fR, \fB\-\-time\fR
+times the bulk data transfer component of this command. The elapsed time
+is printed out plus a MB/sec calculation. In this case "MB" is 1,000,000
+bytes. The gettimeofday() system call is used internally for the time
+calculation.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string then exit.
+.SH NOTES
+This command is typically used on modern SCSI disks which have a RAM cache
+in their drive electronics. If no IO to the magnetic media, or slower devices
+like flash RAM, is involved then the disk may be able to source data fast
+enough to saturate the bandwidth of the SCSI transport. The bottleneck may
+then be the DMA element in the HBA, the Linux drivers or the host machine's
+hardware (e.g. speed of RAM).
+.PP
+Various numeric arguments (e.g. \fIOVERALL\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.SH EXAMPLES
+.PP
+On the test system /dev/sg0 corresponds to a fast disk on a U2W SCSI
+bus (max 80 MB/sec). The disk specifications state that its cache is 4 MB.
+.br
+   $ time ./sg_rbuf /dev/sg0
+.br
+READ BUFFER reports: buffer capacity=3434944,
+.br
+    offset boundary=6
+.br
+Read 200 MiB (actual 199 MiB, 209531584 bytes),
+.br
+    buffer size=3354 KiB
+.br
+real 0m5.072s, user 0m0.000s, sys 0m2.280s
+.PP
+So that is approximately 40 MB/sec at 40 % utilization. Now with
+the addition of the "\-q" option this throughput improves and the
+utilization drops to 0%.
+.br
+   $ time ./sg_rbuf \-q /dev/sg0
+.br
+READ BUFFER reports: buffer capacity=3434944,
+.br
+    offset boundary=6
+.br
+Read 200 MiB (actual 199 MiB, 209531584 bytes),
+.br
+    buffer size=3354 KiB
+.br
+real 0m2.784s, user 0m0.000s, sys 0m0.000s
+.SH EXIT STATUS
+The exit status of sg_rbuf is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using '\-\-old' (or '\-O) as the first option.
+.TP
+\fB\-b\fR=\fIEACH_KIB\fR
+where \fIEACH_KIB\fR is the number of Kilobytes (i.e. 1024 byte units) to be
+transferred by each READ BUFFER command. Similar to the
+\fI\-\-buffer=EACH\fR option in the main description but the units are
+different.
+.TP
+\fB\-d\fR
+use direct IO if available. Equivalent to the \fI\-\-dio\fR option in the
+main description.
+.TP
+\fB\-m\fR
+use memory mapped IO if available. Equivalent to the \fI\-\-mmap\fR option
+in the main description.
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-q\fR
+only transfer the data into kernel buffers (typically by DMA from
+the SCSI adapter card) and do not move it into the user space.
+Equivalent to the \fI\-\-quick\fR option in the main description.
+.TP
+\fB\-s\fR=\fIOVERALL_MIB\fR
+where \fIOVERALL_MIB\fR is the size of total transfer in Megabytes (1048576
+bytes). Similar to the \fI\-\-size=OVERALL\fR option in the main description
+but the units are different.
+.TP
+\fB\-t\fR
+times the bulk data transfer component of this command. Equivalent to
+the \fI\-\-time\fR option in the main description.
+.TP
+\fB\-v\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2000\-2007 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_read_buffer, sg_write_buffer, sg_test_rwbuf(all in sg3_utils)
diff --git a/sg3_utils/doc/sg_rdac.8 b/sg3_utils/doc/sg_rdac.8
new file mode 100644
index 0000000..fed5e90
--- /dev/null
+++ b/sg3_utils/doc/sg_rdac.8
@@ -0,0 +1,44 @@
+.TH SG_RDAC "8" "April 2015" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+sg_rdac \- display or modify SCSI RDAC Redundant Controller mode page
+.SH SYNOPSIS
+.B sg_rdac
+[\fI\-6\fR] [\fI\-a\fR] [\fI\-f=LUN\fR] [\fI\-v\fR] [\fI\-V\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+sg_rdac displays or modifies the RDAC controller settings via the
+Redundant Controller mode page (0x2C). When modifying the settings it
+allows to transfer the ownership of individual drives to the
+controller the command was received on.
+.SH OPTIONS
+.TP
+\fB\-6\fR
+Use the 6 byte cdb variants of the SCSI MODE SENSE and MODE SELECT commands.
+The default action (in the absence of this option) is to use the 10 byte
+cdb variants.
+.TP
+\fB\-a\fR
+Transfer all (visible) devices
+.TP
+\fB\-f\fR=\fILUN\fR
+Transfer the device identified by \fILUN\fR. This command will only work
+if the controller supports 'Dual Active Mode' (aka active/active mode).
+.TP
+\fB\-v\fR
+be verbose
+.TP
+\fB\-V\fR
+print version string then exit
+.SH EXIT STATUS
+The exit status of sg_rdac is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Hannes Reinecke <hare at suse dot com>, based on sg_emc_trespass.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2006\-2015 Hannes Reinecke, Douglas Gilbert.
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg3_utils/doc/sg_read.8 b/sg3_utils/doc/sg_read.8
new file mode 100644
index 0000000..3f01134
--- /dev/null
+++ b/sg3_utils/doc/sg_read.8
@@ -0,0 +1,184 @@
+.TH SG_READ "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_read \- read multiple blocks of data, optionally with SCSI READ commands
+.SH SYNOPSIS
+.B sg_read
+[\fIblk_sgio=\fR0|1] [\fIbpt=BPT\fR] [\fIbs=BS\fR] [\fIcdbsz=\fR6|10|12|16]
+\fIcount=COUNT\fR [\fIdio=\fR0|1] [\fIdpo=\fR0|1] [\fIfua=\fR0|1]
+\fIif=IFILE\fR [\fImmap=\fR0|1] [\fIno_dxfer=\fR0|1] [\fIodir=\fR0|1]
+[\fIskip=SKIP\fR] [\fItime=TI\fR] [\fIverbose=VERB\fR] [\fI\-\-help\fR]
+[\fI\-\-version\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Read data from a Linux SCSI generic (sg) device, a block device or
+a normal file with each read command issued to the same offset or
+logical block address (lba). This can be used to test (or time) disk
+caching, SCSI (or some other) transport throughput, and/or SCSI
+command overhead.
+.PP
+When the \fICOUNT\fR value is positive, then up to \fIBPT\fR blocks are
+read at a time, until the \fICOUNT\fR is exhausted. Each read operation
+starts at the same lba which, if \fISKIP\fR is not given, is the
+beginning of the file or device.
+.PP
+The \fICOUNT\fR value may be negative when \fIIFILE\fR is a sg device
+or is a block device with 'blk_sgio=1' set. Alternatively 'bpt=0' may
+be given. In these cases |\fICOUNT\fR| "zero block" SCSI READ commands
+are issued. "Zero block" means "do nothing" for SCSI READ 10, 12 and
+16 byte commands (but not for the 6 byte variant). In practice "zero
+block" SCSI READ commands have low latency and so are one way to measure
+SCSI command overhead.
+.SH OPTIONS
+.TP
+\fBblk_sgio\fR=0 | 1
+The default action of this utility is to use the Unix read() command when
+the \fIIFILE\fR is a block device. In lk 2.6 many block devices can handle
+SCSI commands issued via the SG_IO ioctl. So when this option is set
+the SG_IO ioctl sends SCSI READ commands to \fIIFILE\fR if it is a block
+device.
+.TP
+\fBbpt\fR=\fIBPT\fR
+where \fIBPT\fR is the maximum number of blocks each read operation fetches.
+Fewer blocks will be fetched when the remaining \fICOUNT\fR is less than
+\fIBPT\fR. The default value for \fIBPT\fR is 128. Note that each read
+operation starts at the same lba (as given by \fIskip=SKIP\fR or 0).
+If 'bpt=0' then the \fICOUNT\fR is interpreted as the number of zero
+block SCSI READ commands to issue.
+.TP
+\fBbs\fR=\fIBS\fR
+where \fIBS\fR is the size (in bytes) of each block read. This
+.B must
+be the block size of the physical device (defaults to 512) if SCSI commands
+are being issued to \fIIFILE\fR.
+.TP
+\fBcdbsz\fR=6 | 10 | 12 | 16
+size of SCSI READ commands issued on sg device names, or block devices
+if 'blk_sgio=1' is given. Default is 10 byte SCSI READ cdbs.
+.TP
+\fBcount\fR=\fICOUNT\fR
+when \fICOUNT\fR is a positive number, read that number of blocks,
+typically with multiple read operations. When \fICOUNT\fR is negative then
+|\fICOUNT\fR| SCSI READ commands are performed requesting zero blocks
+to be transferred. This option is mandatory.
+.TP
+\fBdio\fR=0 | 1
+default is 0 which selects indirect IO. Value of 1 attempts direct
+IO which, if not available, falls back to indirect IO and notes this
+at completion. This option is only active if \fIIFILE\fR is an sg device.
+If direct IO is selected and /proc/scsi/sg/allow_dio
+has the value of 0 then a warning is issued (and indirect IO is performed)
+.TP
+\fBdpo\fR=0 | 1
+when set the disable page out (DPO) bit in SCSI READ commands is set.
+Otherwise the DPO bit is cleared (default).
+.TP
+\fBfua\fR=0 | 1
+when set the force unit access (FUA) bit in SCSI READ commands is set.
+Otherwise the FUA bit is cleared (default).
+.TP
+\fBif\fR=\fIIFILE\fR
+read from this \fIIFILE\fR. This argument must be given. If the \fIIFILE\fR
+is a normal file then it must be seekable (if (\fICOUNT\fR > \fIBPT\fR) or
+\fIskip=SKIP\fR is given). Hence stdin is not acceptable (and giving "\-"
+as the \fIIFILE\fR argument is reported as an error).
+.TP
+\fBmmap\fR=0 | 1
+default is 0 which selects indirect IO. Value of 1 causes memory mapped
+IO to be performed. Selecting both dio and mmap is an error. This option
+is only active if \fIIFILE\fR is an sg device.
+.TP
+\fBno_dxfer\fR=0 | 1
+when set then DMA transfers from the device are made into kernel buffers
+but no further (i.e. there is no second copy into the user space). The
+default value is 0 in which case transfers are made into the user space.
+When neither mmap nor dio is set then data transfer are copied via
+kernel buffers (i.e. a double copy). Mainly for testing.
+.TP
+\fBodir\fR=0 | 1
+when set opens an \fIIFILE\fR which is a block device with an additional
+O_DIRECT flag. The default value is 0 (i.e. don't open block devices
+O_DIRECT).
+.TP
+\fBskip\fR=\fISKIP\fR
+all read operations will start offset by \fISKIP\fR bs\-sized blocks
+from the start of the input file (or device).
+.TP
+\fBtime\fR=\fITI\fR
+When \fITI\fR is 0 (default) doesn't perform timing.
+When 1, times transfer and does throughput calculation, starting at the
+first issued command until completion. When 2, times transfer and does
+throughput calculation, starting at the second issued command until
+completion. When 3 times from third command, etc. An average number of
+commands (SCSI READs or Unix read()s) executed per second is also
+output.
+.TP
+\fBverbose\fR=\fIVERB\fR
+as \fIVERB\fR increases so does the amount of debug output sent to stderr.
+Default value is zero which yields the minimum amount of debug output.
+A value of 1 reports extra information that is not repetitive.
+.TP
+\fB\-\-help\fR
+Output the usage message then exit.
+.TP
+\fB\-\-version\fR
+Output the version string then exit.
+.SH NOTES
+Various numeric arguments (e.g. \fISKIP\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.PP
+Data usually gets to the user space in a 2 stage process: first the
+SCSI adapter DMAs into kernel buffers and then the sg driver copies
+this data into user memory.
+This is called "indirect IO" and there is a "dio" option to select
+"direct IO" which will DMA directly into user memory. Due to some
+issues "direct IO" is disabled in the sg driver and needs a
+configuration change to activate it. This is typically done with
+"echo 1 > /proc/scsi/sg/allow_dio". An alternate way to avoid the
+2 stage copy is to select memory mapped IO with 'mmap=1'.
+.SH SIGNALS
+The signal handling has been borrowed from dd: SIGINT, SIGQUIT and
+SIGPIPE output the number of remaining blocks to be transferred;
+then they have their default action.
+SIGUSR1 causes the same information to be output yet the copy continues.
+All output caused by signals is sent to stderr.
+.SH EXAMPLES
+.PP
+Let us assume that /dev/sg0 is a disk and we wish to time the disk's
+cache performance.
+.PP
+   sg_read if=/dev/sg0 bs=512 count=1MB mmap=1 time=2
+.PP
+This command will continually read 128  512 byte blocks from block 0.
+The "128" is the default value for 'bpt' while "block 0" is chosen
+because the 'skip' argument was not given. This will continue until
+1,000,000 blocks are read. The idea behind using 'time=2' is that the
+first 64 KiB read operation will involve reading the magnetic media
+while the remaining read operations will "hit" the disk's cache. The
+output of third command will look like this:
+.PP
+  time from second command to end was 4.50 secs, 113.70 MB/sec
+.br
+  Average number of READ commands per second was 1735.27
+.br
+  1000000+0 records in, SCSI commands issued: 7813
+.SH EXIT STATUS
+The exit status of sg_read is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2000\-2012 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+To time streaming media read or write time see
+.B sg_dd
+is in the sg3_utils package. The lmbench package contains
+.B lmdd
+which is also interesting.
+.B raw(8), dd(1)
diff --git a/sg3_utils/doc/sg_read_attr.8 b/sg3_utils/doc/sg_read_attr.8
new file mode 100644
index 0000000..153ae22
--- /dev/null
+++ b/sg3_utils/doc/sg_read_attr.8
@@ -0,0 +1,212 @@
+.TH SG_READ_ATTR "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_read_attr \- send SCSI READ ATTRIBUTE command
+.SH SYNOPSIS
+.B sg_read_attr
+[\fI\-\-cache\fR] [\fI\-\-enumerate\fR] [\fI\-\-ea=EA\fR]
+[\fI\-\-filter=FL\fR] [\fI\-\-first=FAI\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-in=FN\fR] [\fI\-\-lvn=LVN\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-pn=PN\fR]
+[\fI\-\-quiet\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-sa=SA\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI READ ATTRIBUTE command to \fIDEVICE\fR and outputs the data
+returned. This command is found in the SPC\-5 draft standard, revision
+8 (spc5r08.pdf).
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-c\fR, \fB\-\-cache\fR
+sets the CACHE bit in the READ ATTRIBUTE cdb. This instructs the device
+server to return cached attributes. By default that bit is cleared
+which instructs the device server not to return cached attributes.
+.TP
+\fB\-e\fR, \fB\-\-enumerate\fR
+enumerates all known attributes and service actions. Attributes include
+an identifier, length, format and a name as defined by T10. If \fIDEVICE\fR
+is given then it is ignored.
+.TP
+\fB\-E\fR, \fB\-\-ea\fR=\fIEA\fR
+where \fIEA\fR is an element address which is placed in the READ ATTRIBUTE
+cdb. This field is only found in SMC\-2 and SMC\-3 drafts for medium
+changers usually associated with tape libraries. By default this field
+is set to zero.
+.TP
+\fB\-f\fR, \fB\-\-filter\fR=\fIFL\fR
+where \fIFL\fR is an attribute identifier in the range 0 to 65535 or \-1.
+Attribute identifiers are typical given in hexadecimal in which case the
+hex number should be prefixed by "0x" ot has a trailing "h". "\-1" is
+the default value and means 'match all'; for all other values of \fIFL\fR
+on the matching attribute is output.
+.TP
+\fB\-F\fR, \fB\-\-first\fR=\fIFAI\fR
+where \fIFAI\fR is the "first attribute identifier" field in the cdb. It
+seems as though the intent of this field is that only attributes whose
+identifiers are equal to or greater than \fIFAI\fR are returned. The default
+value of \fIFAI\fR is zero. Attributes are returned in ascending identifier
+order.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output the response in hexadecimal to stdout. When used once the whole
+response is output in ASCII hexadecimal with a leading address (starting at
+0) on each line. When used twice each attribute descriptor in the response
+is output separately in hexadecimal. When used thrice the whole response is
+output in hexadecimal with no leading address (on each line).
+.br
+Output generated by '\-HHH' (or \fI\-\-hex\fR used three times) can be
+redirected to a file. That file will be in suitable format for \fI\-\-in=FN\fR
+to use in a later invocation.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIFN\fR
+\fIFN\fR is treated as a file name (or '\-' for stdin) which contains ASCII
+hexadecimal or binary representing the response to a READ ATTRIBUTE command
+with service action 0x0 (i.e (fetch) attribute values). When this option is
+given then \fIDEVICE\fR (if also given) is ignored.
+.br
+By default \fIFN\fR is assumed to contain ASCII hexadecimal arranged as
+bytes which a space, tab or comma delimited. All characters from (and
+including) "#" to the end of line are ignored. If the \fI\-\-raw\fR option
+is also given then \fIFN\fR is assumed to contain binary data. When the
+\fI\-\-raw\fR option is given then after processing the input the
+internal raw variable is reset to 0 so it has no effect on the output.
+.br
+Since the READ ATTRIBUTE response does not contain the service action number
+that it is a response to, then the \fI\-\-sa=SA\fR should be given (if not
+service action 0 (attribute values) is assumed.
+.TP
+\fB\-l\fR, \fB\-\-lvn\fR=\fILVN\fR
+where \fILVN\fR is placed in the "logical volume number" field of the cdb.
+The default value is zero which is required to be the logical volume number
+if the device only has one volume.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in
+the cdb's "allocation length" field. If not given (or \fILEN\fR is zero)
+then 8192 is used. The maximum allowed value of \fILEN\fR is 1048576.
+.TP
+\fB\-p\fR, \fB\-\-pn\fR=\fIPN\fR
+where \fIPN\fR is placed in the "partition number" field of the cdb. If
+the \fIDEVICE\fR only has one partition then its partition number must be
+zero. The default value of \fIPN\fR is zero.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+this option reduces the amount of information output. For example when
+used once (\fISA\fR=0), it suppresses the header line announcing the
+output of attributes; when used twice it suppresses the name of each
+attribute, leaving only the associated attribute values (or strings).
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the SCSI response (i.e. the data\-out buffer) in binary (to stdout).
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-s\fR, \fB\-\-sa\fR=\fISA\fR
+where \fISA\fR is placed on the "service action" field of the cdb. Values
+of 0 to 63 are accepted with a default of 0. spc5r08.pdf defines five
+service actions: 0 for attributes values ; 1 for an attribute list (names,
+not values), 2 for the logical volume list; 3 for the partition list; 4
+is restricted for SMC\-3; and 5 for the supported attribute list.
+.br
+Alternatively an acronym can be given for \fISA\fR. The acronym should be
+one of "av", "al", "lvl", "pn", "smc" or "sa" for service actions 0 to 5
+respectively. The acronyms can also be given in upper case.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+Only tape systems seem to implement the SCSI READ ATTRIBUTE command. The vast
+majority of its definition is in the SPC standard so other device types could
+use it.
+.PP
+Much of the information provided by READ ATTRIBUTE can also be found in
+pages returned by LOG SENSE (see the sg_logs utility) and in the VPD
+pages returned by the INQUIRY command.
+.SH EXAMPLES
+To list the attributes of a tape drive whose xxxx is /dev/sg1 the following
+could be used:
+.PP
+# sg_read_attr \-s al /dev/sg1
+.br
+Attribute list:
+.br
+  Remaining capacity in partition [MiB]
+.br
+  Maximum capacity in partition [MiB]
+.br
+  TapeAlert flags
+.br
+  Load count
+.br
+  MAM space remaining [B]
+.br
+  Assigning organization
+.br
+  Format density code
+.br
+  ...
+.PP
+To check the number of partitions:
+.PP
+# sg_read_attr \-s pl /dev/sg1
+.br
+Partition number list:
+.br
+  First partition number: 0
+.br
+  Number of partitions available: 2
+.PP
+And to see the attribute values (which is the default service action):
+.PP
+# sg_read_attr /dev/sg1
+.br
+Attribute values:
+.br
+  Remaining capacity in partition [MiB]: 1386103
+.br
+  Maximum capacity in partition [MiB]: 1386103
+.br
+  TapeAlert flags: 0
+.br
+  ....
+.PP
+To redirect the attribute values response to a file for later decoding:
+.PP
+# sg_read_attr \-HHH /dev/sg1 > av.hex
+.PP
+And later the response held in the av.hex file could be decoded with:
+.PP
+# sg_read_attr \-s av \-\-in=av.hex
+.br
+Attribute values:
+.br
+  Remaining capacity in partition [MiB]: 1386103
+.br
+  Maximum capacity in partition [MiB]: 1386103
+.br
+  TapeAlert flags: 0
+.br
+  ....
+.PP
+.SH EXIT STATUS
+The exit status of sg_read_attr is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2016 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_vpd,sg_logs(sg3_utils)
diff --git a/sg3_utils/doc/sg_read_block_limits.8 b/sg3_utils/doc/sg_read_block_limits.8
new file mode 100644
index 0000000..0ef2a52
--- /dev/null
+++ b/sg3_utils/doc/sg_read_block_limits.8
@@ -0,0 +1,56 @@
+.TH SG_READ_BLOCK_LIMITS "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_read_block_limits \- send SCSI READ BLOCK LIMITS command
+.SH SYNOPSIS
+.B sg_read_block_limits
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-raw\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send a SCSI READ BLOCK LIMITS command to \fIDEVICE\fR and outputs the
+response. This command is defined for tape (drives) and its description
+is found in the SSC documents at http://www.t10.org .
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output response in hex (rather than decode it).
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response in binary to stdout.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH EXIT STATUS
+The exit status of sg_read_block_limits is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH EXAMPLES
+It is usually okay to use no options. Here is an invocation (on the first
+line following the "#" command prompt) followed by some typical output:
+.PP
+   # sg_read_block_limits /dev/st0
+.br
+Read Block Limits results:
+.br
+        Minimum block size: 1 byte(s)
+.br
+        Maximum block size: 16777215 byte(s), 16383 KB, 15 MB
+.br
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2009\-2012 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg3_utils(sg3_utils)
diff --git a/sg3_utils/doc/sg_read_buffer.8 b/sg3_utils/doc/sg_read_buffer.8
new file mode 100644
index 0000000..fd9296c
--- /dev/null
+++ b/sg3_utils/doc/sg_read_buffer.8
@@ -0,0 +1,113 @@
+.TH SG_READ_BUFFER "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_read_buffer \- send SCSI READ BUFFER command
+.SH SYNOPSIS
+.B sg_read_buffer
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-id=ID\fR] [\fI\-\-length=LEN\fR]
+[\fI\-\-mode=MO\fR] [\fI\-\-offset=OFF\fR] [\fI\-\-raw\fR]
+[\fI\-\-readonly\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI READ BUFFER command to the \fIDEVICE\fR, and if there
+is a response either decodes it, prints it in hexadecimal or sends
+it in binary to stdout. If a response is received for a "descriptor"
+mode then, in the absence of \fI\-\-hex\fR and \fI\-\-raw\fR, it is
+decoded. Response for non\-descriptor modes are output in hexadecimal
+unless the \fI\-\-raw\fR option is given.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit. If used multiple times also prints
+the mode names and their acronyms.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output the response in hexadecimal. When given twice the response is
+output in hex with the corresponding representation in ASCII to the
+right of each line.
+.TP
+\fB\-i\fR, \fB\-\-id\fR=\fIID\fR
+this option sets the buffer id field in the cdb. \fIID\fR is a value between
+0 (default) and 255 inclusive.
+.TP
+\fB\-l\fR, \fB\-\-length\fR=\fILEN\fR
+where \fILEN\fR is the length, in bytes, that is placed in the "allocation
+length" field in the cdb. The default value is 4 (bytes). The device may
+respond with less bytes.
+.TP
+\fB\-m\fR, \fB\-\-mode\fR=\fIMO\fR
+this option sets the mode field in the cdb. \fIMO\fR is a value between
+0 (default) and 31 inclusive. Alternatively an abbreviation can be given.
+See the MODES section below. To list the available mode abbreviations use
+an invalid one (e.g. '\-\-mode=xxx'). As an example, to fetch the read
+buffer descriptor give '\-\-mode=desc' .
+.TP
+\fB\-o\fR, \fB\-\-offset\fR=\fIOFF\fR
+this option sets the buffer offset field in the cdb. \fIOFF\fR is a value
+between 0 (default) and 2**24\-1 . It is a byte offset.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+if a response is received then it is sent in binary to stdout.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH MODES
+Following is a list of READ BUFFER command settings for the MODE field.
+First is an acronym accepted by the \fIMO\fR argument of this utility.
+Following the acronym in square brackets are the corresponding decimal and
+hex values that may also be given for \fIMO\fR. The following are listed
+in numerical order.
+.TP
+hd  [0, 0x0]
+Combined header and data (obsolete in SPC\-4).
+.TP
+vendor  [1, 0x1]
+Vendor specific.
+.TP
+data  [2, 0x2]
+Data.
+.TP
+desc  [3, 0x3]
+Descriptor: yields 4 bytes that contain an offset boundary field (1 byte)
+and buffer capacity (3 bytes).
+.TP
+echo  [10, 0xa]
+Read data from echo buffer (was called "Echo buffer" in SPC\-3).
+.TP
+echo_desc  [11, 0xb]
+Echo buffer descriptor: yields 4 bytes of which the last (lowest) 13 bits
+represent the echo buffer capacity. The maximum echo buffer size is 4096
+bytes.
+.TP
+en_ex  [26, 0x1a]
+Enable expander communications protocol and Echo buffer. Made obsolete in
+SPC\-4.
+.TP
+err_hist  [28, 0x1c]
+Error history. Introduced in SPC\-4.
+.SH NOTES
+All numbers given with options are assumed to be decimal.
+Alternatively numerical values can be given in hexadecimal preceded by
+either "0x" or "0X" (or has a trailing "h" or "H").
+.SH EXIT STATUS
+The exit status of sg_read_buffer is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Luben Tuikov and Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2006\-2014 Luben Tuikov and Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_write_buffer(sg3_utils)
diff --git a/sg3_utils/doc/sg_read_long.8 b/sg3_utils/doc/sg_read_long.8
new file mode 100644
index 0000000..e1b1591
--- /dev/null
+++ b/sg3_utils/doc/sg_read_long.8
@@ -0,0 +1,102 @@
+.TH SG_READ_LONG "8" "November 2015" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_read_long \- send a SCSI READ LONG command
+.SH SYNOPSIS
+.B sg_read_long
+[\fI\-\-16\fR] [\fI\-\-correct\fR] [\fI\-\-help\fR] [\fI\-\-lba=LBA\fR]
+[\fI\-\-out=OF\fR] [\fI\-\-pblock\fR] [\fI\-\-readonly\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] [\fI\-\-xfer_len=BTL\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send SCSI READ LONG command to \fIDEVICE\fR. The read buffer is output in hex
+and ASCII to stdout or placed in a file. Note that the data returned includes
+the logical block data (typically 512 bytes for a disk) plus ECC
+information (whose format is proprietary) plus optionally other proprietary
+data. Note that the logical block data may be encoded or encrypted.
+.PP
+In SBC\-4 revision 7 the SCSI READ LONG (10 and 16 byte) commands were made
+obsolete. In the same revision all uses of SCSI WRITE LONG (10 and 16 byte)
+commands were made obsolete apart from the case in which the WR_UNCOR bit is
+set.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-S\fR, \fB\-\-16\fR
+uses a SCSI READ LONG(16) command. The default action is to use a SCSI
+READ LONG(10) command. The READ LONG(10) command has a 32 bit field for
+the lba while READ LONG(16) has a 64 bit field.
+.TP
+\fB\-c\fR, \fB\-\-correct\fR
+sets the 'CORRCT' bit in the SCSI READ LONG command. When set the data is
+corrected by the ECC before being transferred back to this utility. The
+default is to leave the 'CORRCT' bit clear in which case the data is
+not corrected.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the logical block address of the sector to read. Assumed
+to be in decimal unless prefixed with '0x' (or has a trailing 'h'). Defaults
+to lba 0. If the lba is larger than can fit in 32 bits then the \fI\-\-16\fR
+option should be used.
+.TP
+\fB\-o\fR, \fB\-\-out\fR=\fIOF\fR
+instead of outputting ASCII hex to stdout, send it in binary to the
+file called \fIOF\fR. If '\-' is given for \fIOF\fR then the (binary)
+output is sent to stdout. Note that all informative and error output is
+sent to stderr.
+.TP
+\fB\-p\fR, \fB\-\-pblock\fR
+sets the 'PBLOCK' bit in the SCSI READ LONG command. When set the
+physical block (plus ECC data) containing the requested logical block
+address is read. The default is to leave the 'PBLOCK' bit clear in
+which case the logical block (plus any ECC data) is read.
+.TP
+\fB\-r\fR, \fB\-\-readonly\fR
+opens the DEVICE read\-only rather than read\-write which is the
+default. The Linux sg driver needs read\-write access for the SCSI
+READ LONG command but other access methods may require read\-only
+access.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-x\fR, \fB\-\-xfer_len\fR=\fIBTL\fR
+where \fIBTL\fR is the byte transfer length (default to 520). If the
+given value (or the default) does not match the "long" block size of the
+device, the appropriate \fIBTL\fR is deduced from the error response and
+printed (to stderr). The idea is that the user will retry this utility
+with the correct transfer length.
+.SH NOTES
+If a defective block is found and its contents, if any, has been
+retrieved then "sg_reassign" could be used to map out the defective
+block. Associated with such an action the number of elements in
+the "grown" defect list could be monitored (with "sg_reassign \-\-grown")
+as the disk could be nearing the end of its useful lifetime.
+.PP
+Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.PP
+As a data point, Fujitsu uses a 54 byte ECC (per block) which is capable
+of correcting up to a single burst error or 216 bits "on the
+fly". [Information obtained from MAV20xxrc product manual.]
+.SH EXIT STATUS
+The exit status of sg_read_long is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2016 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_reassign, sg_write_long, sg_dd
diff --git a/sg3_utils/doc/sg_readcap.8 b/sg3_utils/doc/sg_readcap.8
new file mode 100644
index 0000000..9a8dc27
--- /dev/null
+++ b/sg3_utils/doc/sg_readcap.8
@@ -0,0 +1,174 @@
+.TH SG_READCAP "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_readcap \- send SCSI READ CAPACITY command
+.SH SYNOPSIS
+.B sg_readcap
+[\fI\-\-16\fR] [\fI\-\-brief\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-lba=LBA\fR] [\fI\-\-long\fR] [\fI\-\-pmi\fR] [\fI\-\-raw\fR]
+[\fI\-\-readonly\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.PP
+.B sg_readcap
+[\fI\-16\fR] [\fI\-b\fR] [\fI\-h\fR] [\fI\-H\fR] [\fI\-lba=LBA\fR]
+[\fI\-pmi\fR] [\fI\-r\fR] [\fI\-R\fR] [\fI\-v\fR] [\fI\-V\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+The normal action of the SCSI READ CAPACITY command is to fetch the number
+of blocks (and block size) from the \fIDEVICE\fR.
+.PP
+The SCSI READ CAPACITY command (both 10 and 16 byte cdbs) actually yield
+the block address of the last block and the block size. The number of
+blocks is thus one plus the block address of the last block (as blocks
+are counted origin zero (i.e. starting at block zero)). This is the source
+of many "off by one" errors.
+.PP
+The READ CAPACITY(16) response provides additional information not found in
+the READ CAPACITY(10) response. This includes protection and logical block
+provisioning information, plus the number of logical blocks per physical
+block. So even though the media size may not exceed what READ CAPACITY(10)
+can show, it may still be useful to examine the response to READ
+CAPACITY(16). Sadly there are horrible SCSI command set implementations in
+the wild that crash when the READ CAPACITY(16) command is sent to them.
+.PP
+Device capacity is the product of the number of blocks by the block size.
+This utility outputs this figure in bytes, MiB (1048576 bytes per MiB)
+and GB (1000000000 bytes per GB).
+.PP
+If sg_readcap is called without the \fI\-\-long\fR option then the 10 byte
+cdb version (i.e. READ CAPACITY (10)) is sent to the \fIDEVICE\fR. If the
+number of blocks in the response is reported as
+0xffffffff (i.e. (2**32 \- 1) ) and the \fI\-\-hex\fR option has not been
+given, then READ CAPACITY (16) is called and its response is output.
+.PP
+This utility supports two command line syntaxes, the preferred one is
+shown first in the synopsis and explained in this section. A later section
+on the old command line syntax outlines the second group of options.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+.TP
+\fB\-\-16\fR
+Use the 16 byte cdb variant of the READ CAPACITY command. See the '\-\-long'
+option.
+\fB\-b\fR, \fB\-\-brief\fR
+outputs two hex numbers (prefixed with '0x' and space separated)
+to stdout. The first number is the maximum number of blocks on the
+device (which is one plus the lba of the last accessible block). The
+second number is the size in bytes of each block. If the operation fails
+then "0x0 0x0" is written to stdout.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output the response to the READ CAPACITY command (either the 10 or 16
+byte cdb variant) in ASCII hexadecimal on stdout.
+.TP
+\fB\-L\fR, \fB\-\-lba\fR=\fILBA\fR
+used in conjunction with \fI\-\-pmi\fR option. This variant of READ CAPACITY
+will yield the last block address after \fILBA\fR prior to a delay. For a
+disk, given a \fILBA\fR it yields the highest numbered block on the same
+cylinder (i.e. before the heads need to move). \fILBA\fR is assumed to be
+decimal unless prefixed by "0x" or it has a trailing "h". Defaults to 0.
+This option was made obsolete in SBC\-3 revision 26.
+.TP
+\fB\-l\fR, \fB\-\-long\fR
+Use the 16 byte cdb variant of the READ CAPACITY command. The default
+action is to use the 10 byte cdb variant which limits the maximum
+block address to (2**32 \- 2). When a 10 byte cdb READ CAPACITY command
+is used on a device whose size is too large then a last block address
+of 0xffffffff is returned (if the device complies with SBC\-2 or later).
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-p\fR, \fB\-\-pmi\fR
+partial medium indicator: for finding the next block address prior to
+some delay (e.g. head movement). In the absence of this option, the
+total number of blocks and the block size of the device are output.
+Used in conjunction with the \fI\-\-lba=LBA\fR option. This option was
+made obsolete in SBC\-3 revision 26.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response in binary to stdout.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default for READ CAPACITY(16) is to open it read\-write. The default
+for READ CAPACITY(10) is to open it read\-only so this option does not
+change anything for this case.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+outputs version string then exits.
+.SH NOTES
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be a SCSI
+generic (sg) device. In the 2.6 series block devices (e.g. SCSI disks
+and DVD drives) can also be specified. For example "sg_readcap /dev/sda"
+and "sg_readcap /dev/hdd" (if /dev/hdd is a ATAPI CD/DVD device) will
+work in the 2.6 series kernels.
+.SH EXIT STATUS
+The exit status of sg_readcap is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using '\-\-old' (or '\-O) as the first option.
+.TP
+\fB\-16\fR
+Use the 16 byte cdb variant of the READ CAPACITY command.
+Equivalent to \fI\-\-long\fR in the main description.
+.TP
+\fB\-b\fR
+utility outputs two hex numbers (prefixed with '0x' and space separated) to
+stdout. The first number is the maximum number of blocks on the device (which
+is one plus the lba of the last accessible block). The second number is the
+size of each block. If the operation fails then "0x0 0x0" is written to
+stdout.  Equivalent to \fI\-\-brief\fR in the main description.
+.TP
+\fB\-h\fR
+output the usage message then exit. Giving the \fI\-?\fR option also outputs
+the usage message then exits.
+.TP
+\fB\-H\fR
+output the response to the READ CAPACITY command (either the 10 or 16
+byte cdb variant) in ASCII hexadecimal on stdout.
+.TP
+\fB\-lba\fR=\fILBA\fR
+used in conjunction with \fI\-pmi\fR option. This variant of READ CAPACITY
+will yield the last block address after \fILBA\fR prior to a delay.
+Equivalent to \fI\-\-lba=LBA\fR in the main description.
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-pmi\fR
+partial medium indicator: for finding the next block address prior to
+some delay (e.g. head movement). In the absence of this switch, the
+total number of blocks and the block size of the device are output.
+Equivalent to \fI\-\-pmi\fR in the main description.
+.TP
+\fB\-r\fR
+output response in binary (to stdout).
+.TP
+\fB\-R\fR
+Equivalent to \fI\-\-readonly\fR in the main description.
+.TP
+\fB\-v\fR
+verbose: print out cdb of issued commands prior to execution. '\-vv'
+and '\-vvv' are also accepted yielding greater verbosity.
+.TP
+\fB\-V\fR
+outputs version string then exits.
+.SH AUTHORS
+Written by Douglas Gilbert
+.SH COPYRIGHT
+Copyright \(co 1999\-2014 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq(sg3_utils)
diff --git a/sg3_utils/doc/sg_reassign.8 b/sg3_utils/doc/sg_reassign.8
new file mode 100644
index 0000000..33bfd5b
--- /dev/null
+++ b/sg3_utils/doc/sg_reassign.8
@@ -0,0 +1,145 @@
+.TH SG_REASSIGN "8" "January 2014" "sg3_utils\-1.38" SG3_UTILS
+.SH NAME
+sg_reassign \- send SCSI REASSIGN BLOCKS command
+.SH SYNOPSIS
+.B sg_reassign
+[\fI\-\-address=A,A...\fR] [\fI\-\-dummy\fR] [\fI\-\-eight=0|1\fR]
+[\fI\-\-grown\fR] [\fI\-\-help\fR] [\fI\-\-longlist=0|1\fR]
+[\fI\-\-primary\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send a SCSI REASSIGN BLOCKS command to \fIDEVICE\fR. Alternatively
+this utility can find the number of element in a "grown" or "primary"
+defect list with a SCSI READ DEFECT DATA (10) command. These SCSI commands
+are defined in SBC\-2 for direct access devices (e.g. a disk). Reassign
+blocks is designed to change the physical location of a logical block
+that is known or suspected to be defective to another area on the
+media. Disks are typically formatted with blocks held in reserve
+for this situation.
+.PP
+If neither the \fI\-\-grown\fR nor \fI\-\-primary\fR option is supplied
+then one or more addresses need to be given. If the address (or all of
+the addresses) fit into 4 bytes and '\-\-eight=1' is not given then the
+parameter block passed to \fIDEVICE\fR is made up of 4 byte logical block
+addresses. If any of the addresses need more than 4 bytes to
+represent (i.e. >= 2**32) or '\-\-eight=1' is given then the parameter block
+passed to \fIDEVICE\fR is made up of 8 byte logical block addresses.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-a\fR, \fB\-\-address\fR=\fIA,A...\fR
+where \fIA,A...\fR is a string of comma separated numbers. Each number
+is interpreted as decimal unless prefixed by '0x' or '0X' (or it has a
+trailing 'h' or 'H'). If multiple logical block addresses are given they
+must be separated by a comma or a (single) space. A string that contains
+any space separators needs to be quoted. At least one address must be given.
+.TP
+\fB\-a\fR, \fB\-\-address\fR=\-
+reads one or more logical block addresses from stdin. These may be comma,
+space, tab or linefeed (newline) separated. If a line contains "#" then
+the remaining characters on that line are ignored. Otherwise each non
+separator sequence of characters should resolve to a decimal number
+unless prefixed by '0x' or '0X' (or has a trailing 'h'). At least one
+address must be given. Lines should not be longer than 1023 bytes.
+.TP
+\fB\-d\fR, \fB\-\-dummy\fR
+prepare for but do not execute the SCSI REASSIGN BLOCKS command. Since
+the REASSIGN BLOCKS command is essentially irreversible, paranoid
+users may wish to check the invocation of this utility before reassigning
+defective blocks on a disk. Useful with '\-vv' for those who wish to
+view the parameter block that will accompany the command.
+.TP
+\fB\-e\fR, \fB\-\-eight\fR=0 | 1
+when value is 1 then it sets the 'LONGLBA' flag in the command indicating
+that the addresses in the associated parameter block are 8 byte quantities.
+When value is 0 then it clears the 'LONGLBA' flag in the command indicating
+that the addresses in the associated parameter block are 4 byte quantities.
+If this option is not given then 4 byte quantities are assumed unless one
+of the address is too large.
+.TP
+\fB\-g\fR, \fB\-\-grown\fR
+use the SCSI READ DEFECT DATA (10) command to determine the number of
+elements in the "grown defect list". When this option is given there
+is no reassignment of blocks (i.e. this utility is passive). When this
+option is given then the \fI\-\-address=\fR option is not permitted. See
+the discussion below concerning the relationship between reassigned blocks
+and the grown defect list. This list is sometimes referred to as the GLIST.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-l\fR, \fB\-\-longlist\fR=0 | 1
+sets the REASSIGN BLOCKS cdb field of the same name to the given value.
+Only 1000 addresses are permitted so there should be no need to specify
+a value of 1. The short list variant restricts the parameter block
+length to 2 ** 16 bytes (i.e. about 16000 4 byte addresses or 8000
+8 byte addresses). Added for completeness.
+.TP
+\fB\-p\fR, \fB\-\-primary\fR
+use the SCSI READ DEFECT DATA (10) command to determine the number of
+elements in the "primary defect list" which is established during the
+manufacturing process. When this option is given there is no reassignment
+of blocks (i.e. this utility is passive). When this option is given then
+the \fI\-\-address=\fR option is not permitted. This list is sometimes
+referred to as the PLIST.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+Note that if the ARRE field (for reads) and/or the AWRE field (for writes)
+are set in the "Read Write Error Recovery" mode page then recoverable read
+and/or write errors cause automatic reassignment of the defective block. The
+PER bit in the same mode page controls whether a RECOVERED ERROR sense key
+is reported on not (PER=1 implies do report). Irrespective of the ARRE, AWRE
+or PER field settings, the error counter log pages reflect any
+errors (recovered or otherwise). Whenever a block is reassigned, a new entry
+is added in the "grown" defect list. Apart from doing selftests (see
+sg_senddiag or smartmontools) regularly, monitoring the grown defect list of a disk is
+a reasonable metric of its health. If the grown list starts growing
+quickly that is an ominous sign. The best grown defect lists are empty
+ones. The number of elements in the grown defect list can be viewed with
+the \fI\-\-grown\fR option. The contents of the grown defect list can be
+viewed with the 'sginfo \-G' utility.
+.PP
+If an unrecoverable error is detected at a logical block address then
+REASSIGN BLOCKS is needed to reassign the block. Also if the ARRE and/or
+AWRE fields are clear and a recoverable error is detected then the
+logical block in question may be reassigned with this utility (otherwise
+the error counter log pages will continually be incremented for each
+recovered access).
+.PP
+The number of blocks held in reserve for the purposes of REASSIGN
+BLOCKS is vendor specific and may well be limited to the zone within
+the media where the original (defective) block lay. When this number
+is exhausted subsequent invocations of this utility may result in
+a sense key of hardware error and an additional sense of 'No defect
+spare location available'. The next step would be to reformat the
+disk (or get a replacement).
+.PP
+The SBC\-2 draft standard (revision 16) notes that when multiple addresses
+are given to the SCSI REASSIGN BLOCKS command and there is some failure
+at one of the later addresses then all addresses prior to that have already
+be reassigned. Care should be taken in such a case. Re\-executing the command
+with the same addresses will cause the earlier addresses to be reassigned
+again. At some stage the disk will run out of reserved locations.
+So unless a large number of addresses are involved it may be safer to
+reassign them one address at a time.
+.SH EXIT STATUS
+The exit status of sg_reassign is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2005\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_format,sginfo,sg_senddiag(all in sg3_utils), sdparm(sdparm),
+.B smartmontools(internet, sourceforge)
diff --git a/sg3_utils/doc/sg_referrals.8 b/sg3_utils/doc/sg_referrals.8
new file mode 100644
index 0000000..b19e400
--- /dev/null
+++ b/sg3_utils/doc/sg_referrals.8
@@ -0,0 +1,71 @@
+.TH SG_REFERRALS "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_referrals \- send SCSI REPORT REFERRALS command
+.SH SYNOPSIS
+.B sg_referrals
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-one-segment\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send the SCSI REPORT REFERRALS command to the \fIDEVICE\fR and outputs the
+response. This command was introduced in (draft) SBC\-3 revision 24 and
+devices that support referrals should support this command.
+.PP
+The default action is to decode the response for all user data segment
+referral descriptors. The amount of output can be reduced by the
+\fI\-\-lba\fR and \fI\-\-one-segment\fR options.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output response to this command in ASCII hex.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the Logical Block Address (LBA) in the first user
+data segment the \fIDEVICE\fR should report the referrals parameter
+data for.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in
+the cdb's "allocation length" field. If not given then 256 is used. 256 is
+enough space for the response header and user data segment descriptors.
+.TP
+\fB\-s\fR, \fB\-\-one-segment\fR
+report the user data segment of the segment spefified by the \fILBA\fR
+parameter only.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response in binary (to stdout).
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output). Additional output
+caused by this option is sent to stderr.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+For a discussion of referrals see section 4.25 of sbc3r25.pdf
+at http://www.t10.org (or the corresponding section of a later draft).
+.SH EXIT STATUS
+The exit status of sg_referrals is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert and Hannes Reinecke.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2009\-2014 Douglas Gilbert and Hannes Reinecke
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_vpd(8)
diff --git a/sg3_utils/doc/sg_rep_zones.8 b/sg3_utils/doc/sg_rep_zones.8
new file mode 100644
index 0000000..df734d0
--- /dev/null
+++ b/sg3_utils/doc/sg_rep_zones.8
@@ -0,0 +1,79 @@
+.TH SG_REP_ZONES "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_rep_zones \- send SCSI REPORT ZONES command
+.SH SYNOPSIS
+.B sg_rep_zones
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-raw\fR]
+[\fI\-\-readonly\fR] [\fI\-\-report=OPT\fR] [\fI\-\-start=LBA\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI REPORT ZONES command to \fIDEVICE\fR and outputs the data
+returned. This command is found in the ZBC draft standard, revision
+4c (zbc\-r04c.pdf).
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output the response in hexadecimal to stdout. When used once the whole
+response is output in ASCII hexadecimal with a leading address (starting at
+0) on each line. When used twice each zone descriptor in the response is
+output separately in hexadecimal. When used thrice the whole response is
+output in hexadecimal with no leading address (on each line).
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in
+the cdb's "allocation length" field. If not given (or \fILEN\fR is zero)
+then 8192 is used. The maximum allowed value of \fILEN\fR is 1048576.
+.TP
+\fB\-p\fR, \fB\-\-partial\fR
+set the PARTIAL bit in the cdb.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the SCSI response (i.e. the data\-out buffer) in binary (to stdout).
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-o\fR, \fB\-\-report\fR=\fIOPT\fR
+where \fIOPT\fR will become the contents of the REPORTING OPTION field
+in the cdb. The default value is 0 which means report a list of all zones.
+Some other values are 1 for list zones with a zone condition of empty; 2 for
+list zones with a zone condition of implicitly opened; 3 for list zones with
+a zone condition of explicitly opened; 4 for list zones with a zone condition
+of closed; 5 for list zones with a zone condition of full; 6 for list zones
+with a zone condition of read only; 7 for list zones with a zone condition of
+offline. Other values are 0x10 for list zones with RWP recommended set to
+true; 0x11 for list zones with non\-sequential write resource active set to
+true and 0x3f for list zones with a zone condition of not write pointer.
+.TP
+\fB\-s\fR, \fB\-\-start\fR=\fILBA\fR
+where \fILBA\fR is at the start or within the first zone to be reported. The
+default value is 0. If \fILBA\fR is not a zone start LBA then the preceding
+zone start LBA is used for reporting. Assumed to be in decimal unless
+prefixed with '0x' or has a trailing 'h' which indicate hexadecimal.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH EXIT STATUS
+The exit status of sg_rep_zones is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2014\-2016 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_reset_wp,sg_zone(sg3_utils)
diff --git a/sg3_utils/doc/sg_requests.8 b/sg3_utils/doc/sg_requests.8
new file mode 100644
index 0000000..ef17705
--- /dev/null
+++ b/sg3_utils/doc/sg_requests.8
@@ -0,0 +1,124 @@
+.TH SG_REQUESTS "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_requests \- send one or more SCSI REQUEST SENSE commands
+.SH SYNOPSIS
+.B sg_requests
+[\fI\-\-desc\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-num=NUM\fR] [\fI\-\-progress\fR] [\fI\-\-raw\fR] [\fI\-\-status\fR]
+[\fI\-\-time\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send SCSI REQUEST SENSE command to \fIDEVICE\fR and output the parameter
+data response which is expected to be in sense data format. Both fixed
+and descriptor sense data formats are supported.
+.PP
+Multiple REQUEST SENSE commands can be sent with the \fI\-\-num=NUM\fR
+option. This can be used for timing purposes or monitoring the progress
+indication.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-d\fR, \fB\-\-desc\fR
+sets the DESC bit in the REQUEST SENSE SCSI cdb. The \fIDEVICE\fR
+should return sense data in descriptor (rather than fixed) format. This
+will only occur if the \fIDEVICE\fR recognizes descriptor format (SPC\-3
+and later). If the device is pre SPC\-3 then setting a bit in a reserved
+field may cause a check condition status with an illegal request sense key,
+but will most likely be ignored.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output response in ASCII hexadecimal.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in the
+cdb's "allocation length" field. If not given (or \fILEN\fR is zero) then
+252 is used. The maximum value of \fILEN\fR is 255 (but SPC\-4 recommends 252).
+.TP
+\fB\-n\fR, \fB\-\-num\fR=\fINUM\fR
+perform \fINUM\fR SCSI REQUEST SENSE commands, stopping when either \fINUM\fR
+is reached or an error occurs. The default value for \fINUM\fR is 1 .
+.TP
+\fB\-p\fR, \fB\-\-progress\fR
+show progress indication (a percentage) if available. If \fI\-\-number=NUM\fR
+is given, \fINUM\fR is greater than 1 and an initial progress indication
+was detected then this utility waits 30 seconds before subsequent checks.
+Exits when \fINUM\fR is reached or there are no more progress indications.
+Ignores \fI\-\-hex\fR, \fI\-\-raw\fR and \fI\-\-time\fR options. See
+NOTES section below.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response in binary (to stdout).
+.TP
+\fB\-s\fR, \fB\-\-status\fR
+if the REQUEST SENSE command finished without error (as indicated by its
+SCSI status) then the contents of the parameter data are analysed as
+sense data and the exit status is set accordingly. The default
+action (i.e. when this option is not given) is to ignore the contents
+of the parameter data for the purposes of setting the exit status.
+Some types of error set a sense key of "NO SENSE" with non\-zero
+information in the additional sense code (e.g. the FAILURE PREDICTION
+THRESHOLD EXCEEDED group of codes); this results in an exit status
+value of 10. If the sense key is "NO SENSE" and both asc and ascq are
+zero then the exit status is set to 0 . See the sg3_utils(8) man page
+for exit status values.
+.TP
+\fB\-t\fR, \fB\-\-time\fR
+time the SCSI REQUEST SENSE command(s) and calculate the average number
+of operations per second.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+Additionally the response (if received) is output in ASCII\-HEX. Use
+this option multiple times for greater verbosity.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+In SCSI 1 and 2 the REQUEST SENSE command was very important for error
+and warning processing in SCSI. The autosense capability rendered this
+command almost superfluous.
+.PP
+However recent SCSI drafts (e.g. SPC\-4 rev 14 and SBC\-3 rev 14) increase
+the utility of the REQUEST SENSE command. Idle and standby (low) power
+conditions can be detected with this command.
+.PP
+The REQUEST SENSE command is not marked as mandatory in SPC\-3 (i.e. for
+all SCSI devices) but is marked as mandatory in SBC\-2 (i.e. for disks),
+SSC\-3 (i.e. for tapes) and MMC\-4 (i.e. for CD/DVD/HD\-DVD/BD drives).
+.PP
+The progress indication is optionally part of the sense data. When a prior
+command that takes a long time to complete (and typically precludes other
+media access commands) is still underway, the progress indication can be used
+to determine how long before the device returns to its normal state.
+.PP
+The SCSI FORMAT command for disks used with the IMMED bit set is an example
+of an operation that takes a significant amount of time and precludes other
+media access during that time. The IMMED bit set instructs the FORMAT command
+to return control to the application client once the format has commenced (see
+SBC\-3). Several long duration SCSI commands associated with tape drives also
+use the progress indication (see SSC\-3).
+.PP
+Early standards suggested that the SCSI TEST UNIT READY command be used for
+polling the progress indication. More recent standards seem to suggest
+the SCSI REQUEST SENSE command should be used instead.
+.PP
+The \fIDEVICE\fR is opened with a read\-only flag (e.g. in Unix with the
+O_RDONLY flag).
+.SH EXIT STATUS
+The exit status of sg_requests is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg3_utils
diff --git a/sg3_utils/doc/sg_reset.8 b/sg3_utils/doc/sg_reset.8
new file mode 100644
index 0000000..7ec309c
--- /dev/null
+++ b/sg3_utils/doc/sg_reset.8
@@ -0,0 +1,128 @@
+.TH SG_RESET "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_reset \- sends SCSI device, target, bus or host reset; or checks reset
+state
+.SH SYNOPSIS
+.B sg_reset
+[\fI\-\-bus\fR] [\fI\-\-device\fR] [\fI\-\-help\fR] [\fI\-\-host\fR]
+[\fI\-\-no-esc\fR] [\fI\-\-target\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+The sg_reset utility with no options (just a \fIDEVICE\fR) reports on the
+reset state (e.g. if a reset is underway) of the \fIDEVICE\fR. When given
+a \fI\-\-device\fR, \fI\-\-target\fR, \fI\-\-bus\fR or \fI\-\-host\fR
+option it requests a device, target, bus or host reset respectively.
+.PP
+A device reset is applied to the Logical Unit (LU) corresponding to
+\fIDEVICE\fR. It is most likely implemented by a Low level Driver (LLD)
+in Linux as a LOGICAL UNIT RESET task management function.
+.PP
+The ability to reset a SCSI target was added in Linux kernel 2.6.27 . A LLD
+may send Low level Drivers (LLDs) the I_T NEXUS RESET task management
+function. Alternatively it may use a transport mechanism to do the same
+thing (e.g. a hard reset on the link containing a SAS target).
+.PP
+In the Linux kernel 2.6 and 3 series this utility can be called on sd,
+sr (cd/dvd), st or sg device nodes; if the user has appropriate permissions.
+.PP
+Users of this utility can check whether a reset recovery is already underway
+before trying to send a new reset with this utility. Calling this utility
+with no options, just the \fIDEVICE\fR, will do such a check.
+.SH OPTIONS
+.TP
+\fB\-b\fR, \fB\-\-bus\fR
+attempt a SCSI bus reset. A bus reset is a SCSI Parallel Interface (SPI)
+concept not found in modern transports. A recent LLD may implement it as
+a series of resets on targets that might be considered as siblings to the
+target on the \fIDEVICE\fR path.
+.TP
+\fB\-d\fR, \fB\-\-device\fR
+attempt a SCSI device reset. This would typically involve sending a LOGICAL
+UNIT RESET task management function to \fIDEVICE\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-host\fR
+attempt a host reset. The "host" in this context is often called
+a Host Bus Adapter (HBA) and contains one or more SCSI initiators.
+.TP
+\fB\-N\fR, \fB\-\-no\-esc\fR
+without this option, if a device reset (\fI\-\-device\fR) fails then it
+will escalate to a target reset. And if a target reset (\fI\-\-target\fR)
+fails then it will escalate to a bus reset. And if a bus
+reset (\fI\-\-bus\fR) fails then it will escalate to a host reset. With this
+option only the requested reset is attempted. An alternate option name of
+\fI\-\-no-escalate\fR is also accepted.
+.TP
+\fB\-t\fR, \fB\-\-target\fR
+attempt a SCSI target reset. A SCSI target contains one or more LUs. This
+would typically involve sending a I_T NEXUS RESET task management function
+to \fIDEVICE\fR There may be a transport action that is equivalent (e.g.
+in SAS a hard reset on the link that contains the target).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the degree of verbosity (debug messages).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+prints the version string then exits.
+.SH NOTES
+The error recovery code within the Linux kernel (SCSI mid\-level) when faced
+with a SCSI command timing out and no response from the device (LU) does the
+following. First it tries a device reset and if that is not successful tries
+a target reset. If that is not successful it tries a bus reset. If that is
+not successful it tries a host reset. The "device,target,bus,host" order is
+the reset escalation that the \fI\-\-no-esc\fR option attempts to stop. In
+large storage configurations the escalation may be (very) undesirable.
+.PP
+This utility calls the SG_SCSI_RESET ioctl and as of lk 3.10.7 the
+\fI\-\-no-esc\fR option is not supported. Patches to implement this
+functionality may be accepted in lk 3.18 or 3.19 .
+.PP
+SAM\-4 and 5 define a hard reset, a LOGICAL UNIT RESET and a I_T NEXUS
+RESET. A hard reset is defined to be a power on condition, a microcode
+change or a transport reset event. LOGICAL UNIT RESET and I_T NEXUS
+RESET can be requested via task management functions (and support for
+LOGICAL UNIT RESET is mandatory). In Linux the SCSI subsystem leaves it up
+to the LLDs as to exactly what type (if any) of reset is performed.
+The "bus reset" is SCSI Parallel Interface (SPI) concept that may not map
+well to recent SCSI transports so it may be a dummy operation. A "host reset"
+attempts to re\-initialize the HBA that the request passes through en route
+to the \fIDEVICE\fR. Note that a "host reset" and a "bus reset" may cause
+collateral damage.
+.PP
+This utility does not allow individual SCSI commands to be aborted. SAM\-4
+defines ABORT TASK and ABORT TASK SET task management functions for that.
+.PP
+Prior to SAM\-3 there was a TARGET RESET task management function. And in
+SAM\-4 I_T NEXUS RESET appeared which seems closely related: the "I_T"
+stands for Initiator\-Target.
+.PP
+Transports may have their own types of resets not supported by this utility.
+For example SAS has a link reset in which both ends of a physical link (e.g.
+between a SAS expander and a SAS tape drive) renegotiate their connection.
+.PP
+Prior to version 0.57 of this utility the command line had short options
+only (e.g. \fI\-d\fR but not \fI\-\-device\fR). Also \fI\-h\fR invoked a host
+reset while in the current version \fI\-h\fR is equivalent to \fI\-\-help\fR
+and both \fI\-H\fR and \fI\-\-host\fR invoke a host reset. For backward
+compatibility define the environment variable SG3_UTILS_OLD_OPTS or
+SG_RESET_OLD_OPTS . In this case \fI\-h\fR will invoke a host reset and the
+output will be verbose as it was previously (equivalent to using the
+\fI\-\-verbose\fR option now).
+For example:
+.PP
+    SG_RESET_OLD_OPTS=1 sg_reset -h /dev/sg1
+.br
+sg_reset: starting host reset
+.br
+sg_reset: completed host reset
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH COPYRIGHT
+Copyright \(co 1999\-2014 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg3_utils/doc/sg_reset_wp.8 b/sg3_utils/doc/sg_reset_wp.8
new file mode 100644
index 0000000..036f80a
--- /dev/null
+++ b/sg3_utils/doc/sg_reset_wp.8
@@ -0,0 +1,51 @@
+.TH SG_RESET_WP "8" "November 2015" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_reset_wp \- send SCSI RESET WRITE POINTER command
+.SH SYNOPSIS
+.B sg_reset_wp
+[\fI\-\-all\fR] [\fI\-\-help\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+[\fI\-\-zone=ID\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI RESET WRITE POINTER command to the \fIDEVICE\fR. This command
+is found in the ZBC draft standard revision 4c (zbc\-r04c.pdf).
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+sets the ALL field in the cdb. This causes a reset write pointer operation of
+all open zones and full zones. When this option is given then the
+\fI\-\-zone=ID\fR option is ignored. Either this option or the
+\fI\-\-zone=ID\fR option is required.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-z\fR, \fB\-\-zone\fR=\fIID\fR
+where \fIID\fR is placed in the cdb's ZONE ID field. A zone id is a zone
+start logical block address (LBA). This causes a reset write pointer
+operation on the zone identified by the ZONE ID field. The default value is
+0. Either this option or the \fI\-\-all\fR option is required.
+\fIID\fR is assumed to be in decimal unless prefixed with '0x' or has a
+trailing 'h' which indicate hexadecimal.
+.SH EXIT STATUS
+The exit status of sg_reset_wp is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2014\-2015 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_rep_zones,sg_zone(sg3_utils)
diff --git a/sg3_utils/doc/sg_rmsn.8 b/sg3_utils/doc/sg_rmsn.8
new file mode 100644
index 0000000..294c85a
--- /dev/null
+++ b/sg3_utils/doc/sg_rmsn.8
@@ -0,0 +1,64 @@
+.TH SG_RMSN "8" "November 2012" "sg3_utils\-1.31" SG3_UTILS
+.SH NAME
+sg_rmsn \- send SCSI READ MEDIA SERIAL NUMBER command
+.SH SYNOPSIS
+.B sg_rmsn
+[\fI\-\-help\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send a SCSI READ MEDIA SERIAL NUMBER command to \fIDEVICE\fR and outputs
+the response.
+.PP
+This command is described in SPC\-3 found at www.t10.org . It was originally
+added to SPC\-3 in revision 11 (2003/2/12). It is not an mandatory command
+and the author has not seen any SCSI devices that support it.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+sends the serial number (if found) to stdout. This output may contain
+non\-printable characters (e.g. the serial number is padded with NULLs
+at the end so its length is a multiple of 4). The default action is
+to print the serial number out in ASCII\-HEX with ASCII characters to
+the right. All error messages are sent to stderr.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+opens the DEVICE read\-only rather than read\-write which is the
+default. The Linux sg driver needs read\-write access for the SCSI
+READ MEDIA SERIAL NUMBER command but other access methods may require
+read\-only access.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+Device identification information is also found in a standard INQUIRY
+response and its VPD pages (see sg_vpd). The relevant VPD pages are
+the "device identification page" (VPD page 0x83) and the "unit serial
+number" page (VPD page 0x80).
+.PP
+The MMC\-4 command set for CD/DVD/HD-DVD/BD drives has a "media serial number"
+feature (0x109) [and a "logical unit serial number" feature]. These
+can be viewed with sg_get_config.
+.SH EXIT STATUS
+The exit status of sg_rmsn is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2005\-2012 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_vpd(sg3_utils), sg_get_config(sg3_utils)
diff --git a/sg3_utils/doc/sg_rtpg.8 b/sg3_utils/doc/sg_rtpg.8
new file mode 100644
index 0000000..c4b8d5b
--- /dev/null
+++ b/sg3_utils/doc/sg_rtpg.8
@@ -0,0 +1,64 @@
+.TH SG_RTPG "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_rtpg \- send SCSI REPORT TARGET PORT GROUPS command
+.SH SYNOPSIS
+.B sg_rtpg
+[\fI\-\-decode\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-raw\fR]
+[\fI\-\-readonly\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send a SCSI REPORT TARGET PORT GROUPS command to \fIDEVICE\fR and
+outputs the response.
+.PP
+Target port group access is described in SPC\-3 and SPC\-4 found at
+www.t10.org . The most recent draft of SPC\-4 is revision 37 in which
+target port groups are described in section 5.15 .
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-d\fR, \fB\-\-decode\fR
+decodes the status code and asymmetric access state from each
+target port group descriptor returned. The default action is not
+to decode these values.
+.TP
+\fB\-e\fR, \fB\-\-extended\fR
+use extended header format for parameter data. This sets the PARAMETER DATA
+FORMAT field in the cdb to 1.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output response in hex (rather than partially or fully decode it).
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response in binary to stdout.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+The Report Target Port Groups command should be supported whenever the TPGS
+bits in a standard INQUIRY response are greater than zero. [View with
+sg_inq utility.]
+.SH EXIT STATUS
+The exit status of sg_rtpg is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2014 Christophe Varoqui and Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq(sg3_utils)
diff --git a/sg3_utils/doc/sg_safte.8 b/sg3_utils/doc/sg_safte.8
new file mode 100644
index 0000000..48c7e62
--- /dev/null
+++ b/sg3_utils/doc/sg_safte.8
@@ -0,0 +1,132 @@
+.TH SG_SAFTE "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_safte \- access SCSI Accessed Fault\-Tolerant Enclosure (SAF\-TE) device
+.SH SYNOPSIS
+.B sg_safte
+[\fI\-\-config\fR] [\fI\-\-devstatus\fR] [\fI\-\-encstatus\fR]
+[\fI\-\-flags\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-insertions\fR]
+[\fI\-\-raw\fR] [\fI\-\-usage\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Fetches enclosure status (via a SCSI READ BUFFER command).
+The \fIDEVICE\fR should be a SAF\-TE device which may be a storage
+array controller (INQUIRY peripheral device type 0xc) or a generic
+processor device (INQUIRY peripheral device type 0x3).
+.PP
+If no options are given (only the \fIDEVICE\fR argument) then the
+overall enclosure status as reported by the option
+.I
+\-\-config
+.R
+is reported.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long
+option name.
+.TP
+\fB\-c\fR, \fB\-\-config\fR
+will issues a
+.I
+Read Enclosure Configuration
+.R
+(READ BUFFER ID 0) cdb to the device, which returns a list of the
+enclosure hardware resources.
+.TP
+\fB\-d\fR, \fB\-\-devstatus\fR
+will issue a
+.I
+Read Device Slot Status
+.R
+(READ BUFFER ID 4) cdb to the device, which returns information about
+the current state of each drive or slot.
+.TP
+\fB\-s\fR, \fB\-\-encstatus\fR
+will issue a
+.I
+Read Enclosure Status
+.R
+(READ BUFFER ID 1) cdb to the device, which returns the operational
+state of the components.
+.TP
+\fB\-f\fR, \fB\-\-flags\fR
+will issue a
+.I
+Read Global Flags
+.R
+(READ BUFFER ID 5) cdb to the device, which read the most recent state
+of the global flags of the RAID processor device.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output the response to a READ BUFFER command in ASCII hex to stdout. If used
+once, output the response to the first READ BUFFER command (i.e. with
+buffer_id=0). This should be the enclosure configuration. If used twice (or
+more often), the response to subsequent READ BUFFER commands is output.
+.TP
+\fB\-i\fR, \fB\-\-insertions\fR
+will issue a
+.I
+Read Device Insertions
+.R
+(READ BUFFER ID 3) cdb to the device, which returns information about
+the number of times devices have been inserted whilst the RAID system
+was powered on.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the response to a READ BUFFER command in binary to stdout. If used
+once, output the response to the first READ BUFFER command (i.e. with
+buffer_id=0). This should be the enclosure configuration. If used twice (or
+more often), the response to subsequent READ BUFFER commands is output.
+.TP
+\fB\-u\fR, \fB\-\-usage\fR
+will issue a
+.I
+Read Usage Statistics
+.R
+(READ BUFFER ID 2) cdb to the device, which returns the information on
+total usage time and number of power\-on cycles of the RAID device.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+The implementation is based on the intermediate review document eg as
+found at
+.PP
+http://www.intel.com/design/servers/ipmi/saf\-te.htm
+.PP
+As the specification was never finalized this document serves as the
+de\-facto standard.
+.PP
+Similar functionality is provided by SPC\-4 SCSI Enclosure Services
+devices (Peripheral device type 0xd), which can be queried with the
+sg_ses utility.
+.SH EXAMPLES
+To view the configuration:
+.PP
+   sg_safte /dev/sg1
+.PP
+To view the device slot status:
+.PP
+   sg_safte \-\-devstatus /dev/sg1
+.PP
+.SH EXIT STATUS
+The exit status of sg_safte is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Hannes Reinecke and Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2012 Hannes Reinecke and Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq, sg_ses (in sg3_utils package); safte\-monitor (internet)
diff --git a/sg3_utils/doc/sg_sanitize.8 b/sg3_utils/doc/sg_sanitize.8
new file mode 100644
index 0000000..bed48c7
--- /dev/null
+++ b/sg3_utils/doc/sg_sanitize.8
@@ -0,0 +1,238 @@
+.TH SG_SANITIZE "8" "November 2015" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_sanitize \- remove all user data from disk with SCSI SANITIZE command
+.SH SYNOPSIS
+.B sg_sanitize
+[\fI\-\-ause\fR] [\fI\-\-block\fR] [\fI\-\-count=OC\fR] [\fI\-\-crypto\fR]
+[\fI\-\-desc\fR] [\fI\-\-early\fR] [\fI\-\-fail\fR] [\fI\-\-help\fR]
+[\fI\-\-invert\fR] [\fI\-\-ipl=LEN\fR] [\fI\-\-overwrite\fR]
+[\fI\-\-pattern=PF\fR] [\fI\-\-quick\fR] [\fI\-\-test=TE\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wait\fR] [\fI\-\-zero\fR]
+[\fI\-\-znr\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility invokes the SCSI SANITIZE command. This command was first
+introduced in the SBC\-3 revision 27 draft. The purpose of the sanitize
+operation is to alter the information in the cache and on the medium of a
+logical unit (e.g. a disk) so that the recovery of user data is not
+possible. If that user data cannot be erased, or is in the process of
+being erased, then the sanitize operation prevents access to that user
+data.
+.PP
+Once a SCSI SANITIZE command has successfully started, then user data from
+that disk is no longer available. Even if the disk is power cycled, the
+sanitize operation will continue after power is re\-instated until it is
+complete.
+.PP
+This utility requires either the \fI\-\-block\fR, \fI\-\-crypto\fR,
+\fI\-\-fail\fR or \fI\-\-overwrite\fR option. With the \fI\-\-block\fR,
+\fI\-\-crypto\fR or \fI\-\-overwrite\fR option the user is given 15 seconds
+to reconsider whether they wish to erase all the data on a disk, unless
+the \fI\-\-quick\fR option is given in which case the sanitize operation
+starts immediately. The disk's INQUIRY response strings are printed out just
+in case the wrong \fIDEVICE\fR has been given.
+.PP
+If the \fI\-\-early\fR option is given then this utility will exit soon
+after starting the SANITIZE command with the IMMED bit set. The user can
+monitor the progress of the sanitize operation with
+the "sg_request \-\-num=9999 \-\-progress" which sends a REQUEST SENSE
+command every 30 seconds. Otherwise if the \fI\-\-wait\fR option is given
+then this utility will wait until the SANITIZE command completes (or fails)
+and that can be many hours.
+.PP
+If neither the \fI\-\-early\fR nor \fI\-\-wait\fR option is given then
+the SANITIZE command is started with the IMMED bit set. After that this
+utility sends a REQUEST SENSE command every 60 seconds until there are
+no more progress indications.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long
+option name.
+.TP
+\fB\-A\fR, \fB\-\-ause\fR
+sets the AUSE bit in the cdb. AUSE is an acronym for "allow unrestricted
+sanitize exit". The default action is to leave the AUSE bit cleared.
+.TP
+\fB\-B\fR, \fB\-\-block\fR
+perform a "block erase" sanitize operation.
+.TP
+\fB\-c\fR, \fB\-\-count\fR=\fIOC\fR
+where \fIOC\fR is the "overwrite count" associated with the "overwrite"
+sanitize operation. \fIOC\fR can be a value between 1 and 31 and 1 is
+the default.
+.TP
+\fB\-C\fR, \fB\-\-crypto\fR
+perform a "cryptographic erase" sanitize operation.
+.TP
+\fB\-d\fR, \fB\-\-desc\fR
+sets the DESC field in the REQUEST SENSE command used for polling. By
+default this field is set to zero. A REQUEST SENSE polling loop is
+used after the SANITIZE command is issued (assuming that neither the
+\fI\-\-early\fR nor the \fI\-\-wait\fR option have been given) to check
+on the progress of this command as it can take some time.
+.TP
+\fB\-e\fR, \fB\-\-early\fR
+the default action of this utility is to poll the disk every 60 seconds to
+fetch the progress indication until the sanitize is finished. When this
+option is given this utility will exit "early" as soon as the SANITIZE
+command with the IMMED bit set to 1 has been acknowledged. This option and
+\fI\-\-wait\fR cannot both be given.
+.TP
+\fB\-F\fR, \fB\-\-fail\fR
+perform an "exit failure mode" sanitize operation. Typically requires the
+preceding SANITIZE command to have set the AUSE bit.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage information then exit.
+.TP
+\fB\-i\fR, \fB\-\-ipl\fR=\fILEN\fR
+set the initialization pattern length to \fILEN\fR bytes. By default it is
+set to the length of the pattern file (\fIPF\fR) or 4 if the \fI\-\-zero\fR
+option is given. Only active when the \fI\-\-overwrite\fR option is also
+given. It is the number of bytes from the \fIPF\fR file that will be used
+as the initialization pattern (if the \fI\-\-zero\fR option is not given).
+The minimum size is 1 byte and the maximum is the logical block size of the
+\fIDEVICE\fR (and not to exceed 65535). If \fILEN\fR exceeds the \fIPF\fR
+file size then the initialization pattern is padded with zeros.
+.TP
+\fB\-I\fR, \fB\-\-invert\fR
+set the INVERT bit in the overwrite service action parameter list. This
+only affects the "overwrite" sanitize operation. The default is a clear
+INVERT bit. When the INVERT bit is set then the initialization pattern
+is inverted between consecutive overwrite passes.
+.TP
+\fB\-O\fR, \fB\-\-overwrite\fR
+perform an "overwrite" sanitize operation. When this option is given then
+the \fI\-\-pattern=PF\fR or the \fI\-\-zero\fR option is required.
+.TP
+\fB\-p\fR, \fB\-\-pattern\fR=\fIPF\fR
+where \fIPF\fR is the filename of a file containing the initialization
+pattern required by an "overwrite" sanitize operation. The length of
+this file will be used as the length of the initialization pattern unless
+the \fI\-\-ipl=LEN\fR option is given. The length of the initialization
+pattern must be from 1 to the logical block size of the \fIDEVICE\fR.
+.TP
+\fB\-Q\fR, \fB\-\-quick\fR
+the default action (i.e. when the option is not given) is to give the user
+15 seconds to reconsider doing a sanitize operation on the \fIDEVICE\fR.
+When this option is given that step (i.e. the 15 second warning period)
+is skipped.
+.TP
+\fB\-T\fR, \fB\-\-test\fR=\fITE\fR
+set the TEST field in the overwrite service action parameter list. This
+only affects the "overwrite" sanitize operation. The default is to place
+0 in that field.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-w\fR, \fB\-\-wait\fR
+the default action (i.e. without this option and the \fI\-\-early\fR option)
+is to start the SANITIZE command with the IMMED bit set then poll for the
+progress indication with the REQUEST SENSE command until the sanitize
+operation is complete (or fails). When this option is given (and the
+\fI\-\-early\fR option is not given) then the SANITIZE command is started
+with the IMMED bit clear. For a large disk this might take hours. [A
+cryptographic erase operation could potentially be very quick.]
+.TP
+\fB\-z\fR, \fB\-\-zero\fR
+with an "overwrite" sanitize operation this option causes the initialization
+pattern to be zero (4 zeros are used as the initialization pattern). Cannot
+be used with the \fI\-\-pattern=PF\fR option. If this option is given
+twice (e.g. '\-zz') then 0xff is used as the initialization byte.
+.TP
+\fB\-Z\fR, \fB\-\-znr\fR
+sets ZNR bit (zoned no reset) in cdb. Introduced in the SBC\-4 revision 7
+draft.
+.SH NOTES
+The SCSI SANITIZE command is closely related to the ATA SANITIZE command,
+both are relatively new with the ATA command being the first one defined.
+The SCSI to ATA Translation (SAT) definition for the SCSI SANITIZE command
+appeared in the SAT\-3 revision 4 draft.
+.PP
+When a SAT layer is used to a (S)ATA disk then for OVERWRITE the
+initialization pattern must be 4 bytes long. So this means either the
+\fI\-\-zero\fR option may be given, or a pattern file (with the
+\fI\-\-pattern=PF\fR option) that is 4 bytes long or set to that
+length with the \fI\-\-ipl=LEN\fR option.
+.PP
+The SCSI SANITIZE command is related to the SCSI FORMAT UNIT command. It
+is likely that a block erase sanitize operation would take a similar
+amount of time as a format on the same disk (e.g. 9 hours for a 2 Terabyte
+disk). The primary goal of a format is the configuration of the disk at
+the end of a format (e.g. different logical block size or protection
+information added). Removal of user data is only a side effect of a format.
+With the SCSI SANITIZE command, removal of user data is the primary goal.
+If a sanitize operation is interrupted (e.g. the disk is power cycled)
+then after power up any remaining user data will not be available and the
+sanitize operation will continue. When a format is interrupted (e.g. the
+disk is power cycled) the drafts say very little about the state of the
+disk. In practice some of the original user data may remain and the format
+may need to be restarted.
+.PP
+Finding out whether a disk (SCSI or ATA) supports SANITIZE can be a
+challenge. If the user really needs to find out and no other information
+is available then try 'sg_sanitize \-\-fail \-vvv <device>' and observe
+the sense data returned may be the safest approach. Using the \fI\-\-fail\fR
+variant of this utility should have no effect unless it follows an already
+failed sanitize operation. If the SCSI REPORT SUPPORTED OPERATION CODES
+command (see sg_opcodes) is supported then using it would be a better
+approach for finding if sanitize is supported.
+.SH EXAMPLES
+These examples use Linux device names. For suitable device names in
+other supported Operating Systems see the sg3_utils(8) man page.
+.PP
+As a precaution if this utility is called with no options then apart from
+printing a usage message, nothing happens:
+.PP
+   sg_sanitize /dev/sdm
+.PP
+To do a "block erase" sanitize the \fI\-\-block\fR option is required.
+The user will be given a 15 second period to reconsider, the SCSI SANITIZE
+command will be started with the IMMED bit set, then this utility will
+poll for a progress indication with a REQUEST SENSE command until the
+sanitize operation is finished:
+.PP
+   sg_sanitize \-\-block /dev/sdm
+.PP
+To start a "block erase" sanitize and return from this utility once it is
+started (but not yet completed) use the \fI\-\-early\fR option:
+.PP
+   sg_sanitize \-\-block \-\-early /dev/sdm
+.PP
+If the 15 second reconsideration time is not required add the
+\fI\-\-quick\fR option:
+.PP
+   sg_sanitize \-\-block \-\-quick \-\-early /dev/sdm
+.PP
+To do an "overwrite" sanitize a pattern file may be given:
+.PP
+   sg_sanitize \-\-overwrite \-\-pattern=rand.img /dev/sdm
+.PP
+If the length of that "rand.img" is 512 bytes (a typically logical block
+size) then to use only the first 17 bytes (repeatedly) in the "overwrite"
+sanitize operation:
+.PP
+   sg_sanitize \-\-overwrite \-\-pattern=rand.img \-\-ipl=17 /dev/sdm
+.PP
+To overwrite with zeros use:
+   sg_sanitize \-\-overwrite \-\-zero /dev/sdm
+.SH EXIT STATUS
+The exit status of sg_sanitize is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page. Unless the \fI\-\-wait\fR option is given, the
+exit status may not reflect the success of otherwise of the format.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2011\-2015 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_requests(8), sg_format(8)
diff --git a/sg3_utils/doc/sg_sat_identify.8 b/sg3_utils/doc/sg_sat_identify.8
new file mode 100644
index 0000000..380e237
--- /dev/null
+++ b/sg3_utils/doc/sg_sat_identify.8
@@ -0,0 +1,116 @@
+.TH SG_SAT_IDENTIFY "8" "November 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_sat_identify \- send ATA IDENTIFY DEVICE command via SCSI to ATA
+Translation (SAT) layer
+.SH SYNOPSIS
+.B sg_sat_identify
+[\fI\-\-ck_cond\fR] [\fI\-\-extend\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-indent\fR] [\fI\-\-len=\fR{16|12}] [\fI\-\-packet\fR] [\fI\-\-raw\fR]
+[\fI\-\-readonly\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends either an ATA IDENTIFY DEVICE command or an ATA IDENTIFY
+PACKET DEVICE command to \fIDEVICE\fR and outputs the response. The devices
+that respond to these commands are ATA disks and ATAPI devices respectively.
+Rather than send these commands directly to the device they are sent via a
+SCSI transport which is assumed to contain a SCSI to ATA Translation (SAT)
+Layer (SATL). The SATL may be in an operating system driver, in host bus
+adapter firmware or in some external enclosure.
+.PP
+The SAT standard (SAT ANSI INCITS 431\-2007, prior draft: sat\-r09.pdf at
+www.t10.org) defines two SCSI "ATA PASS\-THROUGH" commands: one using a 16
+byte "cdb" and the other with a 12 byte cdb. This utility defaults to using
+the 16 byte cdb variant. SAT\-2 is also a standard: SAT\-2 ANSI INCITS
+465\-2010 and the draft prior to that is sat2r09.pdf . The SAT/-3 project
+has started and the most recent draft is sat3r01.pdf .
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-c\fR, \fB\-\-ck_cond\fR
+sets the CK_COND bit in the ATA PASS\-THROUGH SCSI cdb. The
+default setting is clear (i.e. 0). When set the SATL should yield a
+sense buffer containing a ATA Result descriptor irrespective of whether
+the command succeeded or failed. When clear the SATL should only yield
+a sense buffer containing a ATA Result descriptor if the command failed.
+.TP
+\fB\-e\fR, \fB\-\-extend\fR
+sets the EXTEND bit in the ATA PASS\-THROUGH SCSI cdb. The
+default setting is clear (i.e. 0). When set a 48 bit LBA command is sent
+to the device. This option has no effect when \fI\-\-len=12\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+outputs the usage message summarizing command line options
+then exits. Ignores \fIDEVICE\fR if given.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+outputs the ATA IDENTIFY (PACKET) DEVICE response in hex. The default
+action (i.e. without any '\-H' options) is to output the response in
+hex, grouped in 16 bit words (i.e. the ATA standard's preference).
+When given once, the response is output in ASCII hex bytes (i.e. the
+SCSI standard's preference). When given twice (i.e. '\-HH') the output
+is in hex, grouped in 16 bit words, the same as the default but without
+a header. When given thrice (i.e. '\-HHH') the output is in hex, grouped in
+16 bit words, in a format that is acceptable for 'hdparm \-\-Istdin' to
+process. '\-HHHH' simply outputs hex data bytes, space separated, 16 per
+line.
+.TP
+\fB\-i\fR, \fB\-\-indent\fR
+outputs the World Wide Name (WWN) of the device. This should be a NAA\-5
+64 bit number. It is output in hex prefixed with "0x". If not available
+then "0x0000000000000000" is output. The equivalent for a SCSI disk (i.e. its
+logical unit name) can be found with "sg_vpd \-ii".
+.TP
+\fB\-l\fR, \fB\-\-len\fR={16|12}
+this is the length of the SCSI cdb used for the ATA PASS\-THROUGH commands.
+The argument can either be 16 or 12. The default is 16. The larger cdb
+size is needed for 48 bit LBA addressing of ATA devices. On the other
+hand some SCSI transports cannot convey SCSI commands longer than 12 bytes.
+.TP
+\fB\-p\fR, \fB\-\-packet\fR
+send an ATA IDENTIFY PACKET DEVICE command (via the SATL). The default
+action is to send an ATA IDENTIFY DEVICE command.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the ATA IDENTIFY (PACKET) DEVICE response in binary. The output
+should be piped to a file or another utility when this option is used.
+The binary is sent to stdout, and errors are sent to stderr.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increases the level or verbosity.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string
+.SH NOTES
+Since the response to the IDENTIFY (PACKET) DEVICE command is very
+important for the correct use of an ATA(PI) device (and is typically the
+first command sent), a SATL should provide an ATA Information VPD page
+which contains the similar information.
+.PP
+The SCSI ATA PASS\-THROUGH (12) command's opcode is 0xa1 and it clashes with
+the MMC set's BLANK command used by cd/dvd writers. So a SATL in front
+of an ATAPI device that uses MMC (i.e. has peripheral device type 5)
+probably should treat opcode 0xa1 as a BLANK command and send it through
+to the cd/dvd drive. The ATA PASS\-THROUGH (16) command's opcode (0x85)
+does not clash with anything so it is a better choice.
+.PP
+Prior to Linux kernel 2.6.29 USB mass storage limited sense data to 18 bytes
+which made the \fB\-\-ck_cond\fR option yield strange (truncated) results.
+.SH EXIT STATUS
+The exit status of sg_sat_identify is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2006\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_vpd(sg3_utils), sg_inq(sg3_utils), sdparm(sdparm), hdparm(hdparm)
diff --git a/sg3_utils/doc/sg_sat_phy_event.8 b/sg3_utils/doc/sg_sat_phy_event.8
new file mode 100644
index 0000000..415f781
--- /dev/null
+++ b/sg3_utils/doc/sg_sat_phy_event.8
@@ -0,0 +1,109 @@
+.TH SG_SAT_PHY_EVENT "8" "November 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_sat_phy_event \- use ATA READ LOG EXT via a SAT pass\-through to fetch
+SATA phy event counters
+.SH SYNOPSIS
+.B sg_sat_phy_event
+[\fI\-\-ck_cond\fR] [\fI\-\-extend\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-ignore\fR] [\fI\-\-len=\fR{16|12}] [\fI\-\-raw\fR] [\fI\-\-reset\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends an ATA READ LOG EXT with the log page ("address") set to
+11h to \fIDEVICE\fR and outputs the response. Log page 11h is defined in
+the SATA 2.5 standard and contains phy event counters. Rather than send this
+command directly to the \fIDEVICE\fR, are sent via a SCSI transport which is
+assumed to contain a SCSI to ATA Translation (SAT) Layer (SATL). The SATL may
+be in an operating system driver, in host bus adapter firmware or in some
+external enclosure.
+.PP 
+The SAT standard (SAT ANSI INCITS 431\-2007, prior draft: sat\-r09.pdf at 
+www.t10.org) defines two SCSI "ATA PASS\-THROUGH" commands: one using a 16 
+byte "cdb" and the other with a 12 byte cdb. This utility defaults to using 
+the 16 byte cdb variant. SAT\-2 is also a standard: SAT\-2 ANSI INCITS 
+465\-2010 and the draft prior to that is sat2r09.pdf . The SAT-3 project has 
+started and the most recent draft is sat3r01.pdf .
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-c\fR, \fB\-\-ck_cond\fR
+sets the CK_COND bit in the ATA PASS\-THROUGH SCSI cdb. The
+default setting is clear (i.e. 0). When set the SATL should yield a
+sense buffer containing a ATA Result descriptor irrespective of whether
+the command succeeded or failed. When clear the SATL should only yield
+a sense buffer containing a ATA Result descriptor if the command failed.
+.TP
+\fB\-e\fR, \fB\-\-extend\fR
+sets the EXTEND bit in the ATA PASS\-THROUGH SCSI cdb. The
+default setting is clear (i.e. 0). When set a 48 bit LBA command is sent
+to the device. This option has no effect when \fI\-\-len=12\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+outputs the usage message summarizing command line options
+then exits. Ignores \fIDEVICE\fR if given.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+outputs the ATA READ LOG EXT response in hex. The default
+action (i.e. without any '\-H' options) is to output the response in
+hex, grouped in 16 bit words (i.e. the ATA standard's preference).
+When given once, the response is output in ASCII hex bytes (i.e. the
+SCSI standard's preference). When given twice (i.e. '\-HH') the output
+is in hex, grouped in 16 bit words, the same as the default but without
+a header.
+.TP
+\fB\-i\fR, \fB\-\-ignore\fR
+usually the phy counter identifier names are decoded. When this option is
+given, the numeric value of the identifier is output, the vendor flag, the
+data length (in bytes) and the corresponding value.
+.TP
+\fB\-l\fR, \fB\-\-len\fR={16|12}
+this is the length of the SCSI cdb used for the ATA PASS\-THROUGH commands.
+The argument can either be 16 or 12. The default is 16. The larger cdb
+size is needed for 48 bit LBA addressing of ATA devices. On the other
+hand some SCSI transports cannot convey SCSI commands longer than 12 bytes.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the ATA READ LOG EXT response in binary. The output
+should be piped to a file or another utility when this option is used.
+The binary is sent to stdout, and errors are sent to stderr.
+.TP
+\fB\-R\fR, \fB\-\-reset\fR
+reset the counters after the current values are returned, decoded and
+displayed.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increases the level or verbosity.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string
+.SH NOTES
+The SCSI ATA PASS\-THROUGH (12) command's opcode is 0xa1 and it clashes with
+the MMC set's BLANK command used by cd/dvd writers. So a SATL in front
+of an ATAPI device that uses MMC (i.e. has peripheral device type 5)
+probably should treat opcode 0xa1 as a BLANK command and send it through
+to the cd/dvd drive. The ATA PASS\-THROUGH (16) command's opcode (0x85)
+does not clash with anything so it is a better choice.
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be
+a SCSI generic (sg) device. In the 2.6 series block devices (e.g. disks
+and ATAPI DVDs) can also be specified. For example "sg_inq /dev/sda"
+will work in the 2.6 series kernels. From lk 2.6.6 other SCSI "char"
+device names may be used as well (e.g. "/dev/st0m"). Prior to lk 2.6.29
+USB mass storage limited sense data to 18 bytes which made the
+\fB\-\-ck_cond\fR option yield strange (truncated) results.
+.SH EXIT STATUS
+The exit status of sg_sat_identify is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2006\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_sat_identify,sg_sat_read_gplog(sg3_utils),
+.B smp_rep_phy_err_log(smp_utils), sdparm(sdparm), hdparm(hdparm)
diff --git a/sg3_utils/doc/sg_sat_read_gplog.8 b/sg3_utils/doc/sg_sat_read_gplog.8
new file mode 100644
index 0000000..1f0c750
--- /dev/null
+++ b/sg3_utils/doc/sg_sat_read_gplog.8
@@ -0,0 +1,114 @@
+.TH SG_SAT_READ_GPLOG "8" "April 2015" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+sg_sat_read_gplog \- use ATA READ LOG EXT command via a SCSI to ATA
+Translation (SAT) layer
+.SH SYNOPSIS
+.B sg_sat_read_gplog
+[\fI\-\-ck_cond\fR] [\fI\-\-count=CO\fR] [\fI\-\-dma\fR] [\fI\-\-help\fR]
+[\fI\-\-hex\fR] [\fI\-\-len=\fR{16|12}] [\fI\-\-log=\fRLA]
+[\fI\-\-page=\fRPN] [\fI\-\-readonly\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends an ATA READ LOG EXT or an ATA READ LOG DMA EXT command to
+the \fIDEVICE\fR. This command is used to read the general purpose log
+of (S)ATA disks (not ATAPI devices such as DVD driver). Rather than send the
+READ LOG (DMA) EXT command directly to the device it is sent via a SCSI
+transport which is assumed to contain a SCSI to ATA Translation (SAT)
+Layer (SATL). The SATL may be in an operating system driver, in host bus
+adapter (HBA) firmware or in some external enclosure.
+.PP
+This utility does not currently attempt to decode the response from the
+ATA disk, rather it outputs the response in ASCII hexadecimal grouped in
+16 bit words. Following ATA conventions those words are decoded little
+endian (note that SCSI commands use a big endian representation). In the
+future this utility may attempt to decode some log pages, perhaps using
+the \fI\-\-decode\fR option.
+.PP
+The SAT\-2 standard (SAT ANSI INCITS 465-2010, prior draft: sat2r09.pdf at
+www.t10.org) defines two SCSI "ATA PASS\-THROUGH" commands: one using a 16
+byte "cdb" and the other with a 12 byte cdb. This utility defaults to using
+the 16 byte cdb variant.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-C\fR, \fB\-\-ck_cond\fR
+sets the CK_COND bit in the ATA PASS\-THROUGH SCSI cdb. The
+default setting is clear (i.e. 0). When set the SATL should yield a
+sense buffer containing a ATA Result descriptor irrespective of whether
+the ATA command succeeded or failed. When clear the SATL should only yield
+a sense buffer containing a ATA Result descriptor if the ATA command failed.
+.TP
+\fB\-c\fR, \fB\-\-count\fR=\fICO\fR
+the number \fICO\fR is placed in the "count" field in the ATA READ
+LOG EXT command. This specified the number of 512-byte blocks of
+data to be read from the specified log.
+.TP
+\fB\-d\fR, \fB\-\-dma\fR
+use the ATA READ LOG DMA EXT command instead of ATA READ LOG EXT command.
+Some devices require this to return valid log data.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+outputs the usage message summarizing command line options then exits.
+Ignores \fIDEVICE\fR if given.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+when given once, the response is output in ASCII hexadecimal bytes. When
+given twice, then the response is grouped into 16 bit words using ATA
+conventions (i.e. little endian); this is the default output (i.e. when
+this option is not given). When given thrice (i.e. '\-HHH') the output
+is in hex, grouped in 16 bit words (without a leading offset and trailing
+ASCII on each line), in a format that is acceptable for 'hdparm \-\-Istdin'
+to process.
+.TP
+\fB\-L\fR, \fB\-\-log\fR=\fILA\fR
+the number \fILA\fR is known as the "log address" in the ATA standards and
+is placed in bits 7:0 of the "lba" field of the ATA READ LOG (DMA) EXT
+command. This specifies the log to be returned (See ATA-ACS for a detailed
+list of available log addresses). The default value placed in the "lba
+field is 0, returning the directory of available logs. The maximum value
+allowed for \fILOG\fR is 0xff.
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPN\fR
+the number \fIPN\fR is the page number (within the log address) and is
+placed in bits 32:16 of the "lba" field of the ATA READ LOG (DMA) EXT
+command. The default value placed in the "lba" field is 0. The maximum value
+allowed for \fILOG\fR is 0xffff.
+.TP
+\fB\-l\fR, \fB\-\-len\fR={16|12}
+this is the length of the SCSI cdb used for the ATA PASS\-THROUGH commands.
+The argument can either be 16 or 12. The default is 16. Some SCSI
+transports cannot convey SCSI commands longer than 12 bytes.
+.TP
+\fB\-r\fR, \fB\-\-readonly\fR
+causes the \fIDEVICE\fR to be opened with the read\-only flag (O_RDONLY in
+Unix). The default action is to open \fIDEVICE\fR with the read\-write
+flag (O_RDWR in Unix). In some cases sending power management commands to
+ATA disks are defeated by OS actions on the close() if the \fIDEVICE\fR was
+opened with the read\-write flag (e.g. the OS might think it needs to
+flush something to disk).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increases the level or verbosity.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string
+.SH NOTES
+Prior to Linux kernel 2.6.29 USB mass storage limited sense data to 18 bytes
+which made the \fB\-\-ck_cond\fR option yield strange (truncated) results.
+.SH EXIT STATUS
+The exit status of sg_sat_read_gplog is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Hannes Reinecke and Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2014\-2015 Hannes Reinecke, SUSE Linux GmbH
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_sat_identify(sg3_utils), sg_inq(sg3_utils), sdparm(sdparm),
+.B hdparm(hdparm)
diff --git a/sg3_utils/doc/sg_sat_set_features.8 b/sg3_utils/doc/sg_sat_set_features.8
new file mode 100644
index 0000000..d732c3d
--- /dev/null
+++ b/sg3_utils/doc/sg_sat_set_features.8
@@ -0,0 +1,112 @@
+.TH SG_SAT_SET_FEATURES "8" "November 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_sat_set_features \- use ATA SET FEATURES command via a SCSI to ATA
+Translation (SAT) layer
+.SH SYNOPSIS
+.B sg_sat_set_features
+[\fI\-\-count=CO\fR] [\fI\-\-ck_cond\fR] [\fI--extended\fR]
+[\fI\-\-feature=FEA\fR] [\fI\-\-help\fR] [\fI\-\-lba=LBA\fR]
+[\fI\-\-len=\fR{16|12}] [\fI\-\-readonly\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends an ATA SET FEATURES command to the \fIDEVICE\fR.
+This command is used to change settings of ATA non\-packet (i.e. disks) and
+packet devices (e.g. cd/dvd drives). Rather than send the SET FEATURES
+command directly to the device it is sent via a SCSI transport which is
+assumed to contain a SCSI to ATA Translation (SAT) Layer (SATL). The SATL
+may be in an operating system driver, in host bus adapter firmware or in
+some external enclosure.
+.PP
+The SAT standard (SAT ANSI INCITS 431\-2007, prior draft: sat\-r09.pdf at
+www.t10.org) defines two SCSI "ATA PASS\-THROUGH" commands: one using a 16
+byte "cdb" and the other with a 12 byte cdb. This utility defaults to using
+the 16 byte cdb variant. SAT\-2 is also a standard: SAT\-2 ANSI INCITS
+465\-2010 and the draft prior to that is sat2r09.pdf . The SAT-3 project has
+started and the most recent draft is sat3r05b.pdf .
+.PP
+The features can be read using the sg_sat_identify utility which uses either
+the ATA IDENTIFY DEVICE (for non\-packet devices) or the IDENTIFY PACKET
+DEVICE (for packet devices) command.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-c\fR, \fB\-\-count\fR=\fICO\fR
+the number \fICO\fR is placed in the "count" field in the ATA SET
+FEATURES command. Only some subcommands (a term used for the value
+placed in the "feature" field) require the count field to be set.
+The default value placed in the "count" field is 0.
+.TP
+\fB\-C\fR, \fB\-\-ck_cond\fR
+sets the CK_COND bit in the ATA PASS\-THROUGH SCSI cdb. The
+default setting is clear (i.e. 0). When set the SATL should yield a
+sense buffer containing a ATA Result descriptor irrespective of whether
+the ATA command succeeded or failed. When clear the SATL should only yield
+a sense buffer containing a ATA Result descriptor if the ATA command failed.
+.TP
+\fB\-e\fR, \fB\-\-extended\fR
+allow for extended LBA numbers (i.e. larger than 32 bits).
+This value is enabled automatically for large LBA numbers, but can be
+enabled explicitly even for low LBA numbers with this option.
+.TP
+\fB\-f\fR, \fB\-\-feature\fR=\fIFEA\fR
+the value \fIFEA\fR is placed in the "feature" field in the ATA SET
+FEATURES command. The term "subcommand" is sometimes used for this
+value. The default value placed in the "feature" field is 0 which
+is reserved and hence should not change anything. Two common examples
+are 2h to enable the write cache and 82h to disable it.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+outputs the usage message summarizing command line options
+then exits. Ignores \fIDEVICE\fR if given.
+.TP
+\fB\-L\fR, \fB\-\-lba\fR=\fILBA\fR
+the number \fILBA\fR is placed in the "lba" field of the ATA SET
+FEATURES command. Only some sub\-commands (a term used for the value
+placed in the "feature" field) require the lba field to be set. This
+value is typically not a "logical block address" as the acronym might
+imply.  The default value placed in the "lba" field is 0. The maximum value
+allowed for \fILBA\fR is 0xfffffffe (or 0xffffff if \fI\-\-len=\fR12).
+.TP
+\fB\-l\fR, \fB\-\-len\fR={16|12}
+this is the length of the SCSI cdb used for the ATA PASS\-THROUGH commands.
+The argument can either be 16 or 12. The default is 16. Some SCSI
+transports cannot convey SCSI commands longer than 12 bytes.
+.TP
+\fB\-r\fR, \fB\-\-readonly\fR
+causes the \fIDEVICE\fR to be opened with the read\-only flag (O_RDONLY in
+Unix). The default action is to open \fIDEVICE\fR with the read\-write
+flag (O_RDWR in Unix). In some cases sending power management commands to
+ATA disks are defeated by OS actions on the close() if the \fIDEVICE\fR was
+opened with the read\-write flag (e.g. the OS might think it needs to
+flush something to disk).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increases the level or verbosity.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string
+.SH NOTES
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be
+a SCSI generic (sg) device. In the 2.6 and 3 series block devices (e.g. disks
+and ATAPI DVDs) can also be specified. For example "sg_inq /dev/sda"
+will work in the 2.6 series kernels. From lk 2.6.6 other SCSI "char"
+device names may be used as well (e.g. "/dev/st0m"). Prior to lk 2.6.29
+USB mass storage limited sense data to 18 bytes which made the
+\fB\-\-ck_cond\fR option yield strange (truncated) results.
+.SH EXIT STATUS
+The exit status of sg_sat_set_features is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2007\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_sat_identify(sg3_utils), sg_inq(sg3_utils), sdparm(sdparm),
+.B hdparm(hdparm)
diff --git a/sg3_utils/doc/sg_scan.8.linux b/sg3_utils/doc/sg_scan.8.linux
new file mode 100644
index 0000000..0698000
--- /dev/null
+++ b/sg3_utils/doc/sg_scan.8.linux
@@ -0,0 +1,78 @@
+.TH SG_SCAN "8" "May 2013" "sg3_utils\-1.36" SG3_UTILS
+.SH NAME
+sg_scan \- scans sg devices (or SCSI/ATAPI/ATA devices) and prints
+results
+.SH SYNOPSIS
+.B sg_scan
+[\fI\-a\fR]
+[\fI\-i\fR]
+[\fI\-n\fR]
+[\fI\-w\fR]
+[\fI\-x\fR]
+[\fIDEVICE\fR]*
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+If no \fIDEVICE\fR names are given, sg_scan does a scan of the sg
+devices and outputs a line of information for each sg device that is
+currently bound to a SCSI device. If one or more \fIDEVICE\fRs are given
+only those devices are scanned.
+Each device is opened with the O_NONBLOCK flag so that the scan will
+not "hang" on any device that another process holds an O_EXCL lock on.
+.PP
+Any given \fIDEVICE\fR name is expected to comply
+with (to some extent) the Storage Architecture Model (SAM see www.t10.org).
+Any device names associated with the Linux SCSI subsystem (e.g. /dev/sda
+and /dev/st0m) are suitable. Devices names associated with ATAPI
+devices (e.g. most CD/DVD drives and ATAPI tape drives) are also suitable.
+If the device does not fall into the above categories then an ATA
+IDENTIFY command is tried.
+.PP
+In Linux 2.6 and 3 series kernels, the lsscsi utility may be helpful. Apart
+from providing more information (by data\-mining in the sysfs pseudo file
+system), it does not need root permissions to execute, as this utility
+would typically need.
+.SH OPTIONS
+.TP
+\fB\-a\fR
+do alphabetical scan (i.e. sga, sgb, sgc). Note that sg device nodes with
+an alphabetical index have been deprecated since the Linux kernel 2.2
+series.
+.TP
+\fB\-i\fR
+do a SCSI INQUIRY, output results in a second (indented) line. If the device
+is an ATA disk then output information from an ATA IDENTIFY command
+.TP
+\fB\-n\fR
+do numeric scan (i.e. sg0, sg1...) [default]
+.TP
+\fB\-w\fR
+use a read/write flag when opening sg device (default is read\-only)
+.TP
+\fB\-x\fR
+extra information output about queueing
+.SH NOTES
+This utility was written at a time when hotplugging of SCSI devices
+was not supported in Linux. It used a simple algorithm to scan sg
+device nodes in ascending numeric or alphabetical order, stopping
+after there were 4 consecutive errors.
+.PP
+In the Linux kernel 2.6 series, this utility uses sysfs to find which
+sg device nodes are active and only checks those. Hence there can be
+large "holes" in the numbering of sg device nodes (e.g. after an
+adapter has been removed) and still all active sg device nodes will
+be listed. This utility assumes that sg device nodes are named using
+the normal conventions and searches from /dev/sg0 to /dev/sg4095
+inclusive.
+.SH EXIT STATUS
+The exit status of sg_scan is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by D. Gilbert and F. Jansen
+.SH COPYRIGHT
+Copyright \(co 1999\-2013 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B lsscsi(8)
diff --git a/sg3_utils/doc/sg_scan.8.win32 b/sg3_utils/doc/sg_scan.8.win32
new file mode 100644
index 0000000..b87888a
--- /dev/null
+++ b/sg3_utils/doc/sg_scan.8.win32
@@ -0,0 +1,170 @@
+.TH SG_SCAN "8" "August 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_scan \- scan storage devices and map to volume names
+.SH SYNOPSIS
+.B sg_scan
+[\fI\-\-bus\fR]  [\fI\-\-help\fR] [\fI\-\-letter=VL\fR] [\fI\-\-scsi\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility scans for physical drives (a.k.a. "hard drives"), cd/dvd drives
+and tape drives and maps them to the corresponding volumes. There may be
+many, one or no corresponding volumes. There is one line output per device
+with identification strings to the right. Its purpose is to list the
+storage device names that can be used by other utilities in this package.
+.PP
+In later versions of Windows this utility may need to be "run as
+Administrator" for disks and other devices to be seen. If not those devices
+will simply not appear as calls to query them fail with access permission
+problems.
+.PP
+There is an optional SCSI adapter scan which may find additional storage
+devices other than the ones listed above. An example is a SCSI Enclosure
+Services (SES) device typically found in disk arrays. 
+.PP
+Storage and related devices can have several device names in Windows.
+Probably the most common in the volume name (e.g. "D:"). There is also
+a "class" device name, and this utility scans for three of
+them: "PhysicalDrive<n>", "CDROM<n>" and "TAPE<n>". <n> is an integer
+starting at 0 allocated in ascending order as devices are discovered (and
+sometimes rediscovered).
+.PP
+Some storage devices have a SCSI lower level device name which starts
+with a SCSI (pseudo) adapter name of the form "SCSI<n>:". To this is added
+sub\-addressing in the form of a "bus" number, a "target" identifier and
+a LUN (Logical Unit Number). The "bus" number is also known as a "PathId".
+These components are combined by the utility to make a device name of the
+form: "SCSI<n>:<bus>,<target>,<lun>". This utility allows the
+trailing ",<lun>" to be omitted in which case a LUN of zero is assumed. This
+lower level device name cannot often be used directly since Windows blocks
+attempts to use it if a class driver has "claimed" the device. There are
+SCSI device types (e.g. Automation/Drive interface type) for which there is
+no class driver. At least two transports ("bus types" in Windows jargin):
+USB and IEEE 1394 do not have a "scsi" device names of this form.
+.PP
+In keeping with DOS file system conventions, the various device names
+can be given in upper, lower or mixed case. Since "PhysicalDrive<n>" is
+tedious to write, a shortened form of "PD<n>" is permitted by all
+utilities in this package.
+.PP
+A single device (e.g. a disk) can have many device names! For
+example: "PDO" can also be "C:", "D:" and "SCSI0:0,1,0". The two volume names
+reflect that the disk has two partitions on it. Disk partitions that are
+not recognised by Windows are not usually given a volume name. However
+Vista does show a volume name for a disk which has no partitions recognised
+by it and when selected invites the user to format it (which is rather
+unfriendly to other OSes).
+.PP
+The scanning logic and output of this command changed significantly in
+sg3_utils version 1.27 . The SCSI adapter based scanned is now an
+optional extra.
+.PP
+For more information see the NOTES section below.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-bus\fR
+show the bus type (or transport) by which the device is attached to the
+operating systems. Two or more transports may be involved. For example,
+a SATA disk may be in the external enclosure connected to the computer via
+USB in which case the bus type is USB.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+outputs the usage message summarizing command line options
+then exits.
+.TP
+\fB\-l\fR, \fB\-\-letter\fR=\fIVL\fR
+normally a device that has multiple volume names has up to four listed. If
+there are more than that a "+" is added after the fourth. When this option
+is given the \fIVL\fR argument is assumed to be a volume name (i.e. 'C'
+to 'Z') and if found in the scan, only that volume name appears in the
+output. If there are novolume names in the output then \fIVL\fR was not
+found.
+.TP
+\fB\-s\fR, \fB\-\-scsi\fR
+do a SCSI adapter based scan after the normal storage device based scan.
+There is a blank line between the normal scan and the SCSI adapter based
+scan. If this option is given twice then only the SCSI adapter based scan
+is done.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increases the level or verbosity. Can be used multiple times to display
+more of the internal data, both in normal and error processing.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string
+.SH NOTES
+This utility does not support Windows 95, 98 and ME (and earlier Windows
+operating systems). The target Windows operating systems are currently
+Windows 2000, 2003, XP and Vista (and their variants).
+.PP
+When the \fI\-\-scsi\fR option is given the SCSI adapter tuple is followed
+by a list of two or three fields. First is "claimed=0|1" indicating whether
+a class driver has claimed the device. The next field is "pdt=<num>"
+where <num> is the "peripheral device type" as defined in the SCSI INQUIRY
+command (see SPC\-4 at http://www.t10.org). The <num> has a trailing "h" to
+indicate that it is hexadecimal. Sometimes a third field with the
+word "dubious" appears. This flags that what is supposed to be a SCSI
+INQUIRY command response has a badly formed "additional length" field.
+Thus the corresponding device is unlikely to be a native SCSI device.
+.PP
+The DOS device names given the the CreateFile() call all start with
+a "\\\\.\\" string. That can be given but if not will be supplied
+automatically.
+.PP
+Scanning devices that are hot unplugged and replugged often can be
+problematic, especially with the class device names. Each time a device is
+removed and re\-added it gets a larger class device name (e.g. "PD3"
+becomes "PD4" leaving "PD3" unused). This utility stops scanning class
+devices after it find 8 consecutive "holes".
+.SH EXAMPLES
+The following examples are from a laptop with an internal drive (SATA), a
+CD/DVD drive and a USB attached SATA disk. The latter disk has two volumes
+recognised by Windows.
+.PP
+  # sg_scan
+.br
+PD0     [C]     FUJITSU   MHY2160BH         0000  
+.br
+PD1     [DF]    WD        2500BEV External  1.05  WD\-WXE90     
+.br
+CDROM0  [E]     MATSHITA DVD/CDRW UJDA775  CB03
+.PP
+Now request bus types as well. BTW That is a SATA disk holding volume C:
+and there is a "Sata" bus type.
+.PP
+  # sg_scan \-b
+.br
+PD0     [C]     <Ata  >  FUJITSU   MHY2160BH         0000  
+.br
+PD1     [DF]    <Usb  >  WD        2500BEV External  1.05  WD\-WXE90
+.br
+CDROM0  [E]     <Atapi>  MATSHITA DVD/CDRW UJDA775  CB03
+.PP
+Now request a SCSI adapter scan as well.
+.PP
+  # sg_scan \-b \-s
+.br
+PD0     [C]     <Ata  >  FUJITSU   MHY2160BH         0000  
+.br
+PD1     [DF]    <Usb  >  WD        2500BEV External  1.05  WD\-WXE90     
+.br
+CDROM0  [E]     <Atapi>  MATSHITA DVD/CDRW UJDA775  CB03  
+.br
+   
+.br
+SCSI0:0,0,0   claimed=1 pdt=0h  FUJITSU   MHY2160BH         0000
+.br
+SCSI1:0,0,0   claimed=1 pdt=5h  MATSHITA  DVD/CDRW UJDA775  CB03
+.PP
+.SH EXIT STATUS
+The exit status of sg_scan is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2006\-2012 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg3_utils/doc/sg_senddiag.8 b/sg3_utils/doc/sg_senddiag.8
new file mode 100644
index 0000000..511ec7e
--- /dev/null
+++ b/sg3_utils/doc/sg_senddiag.8
@@ -0,0 +1,284 @@
+.TH SG_SENDDIAG "8" "April 2015" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+sg_senddiag \- performs a SCSI SEND DIAGNOSTIC command
+.SH SYNOPSIS
+.B sg_senddiag
+[\fI\-\-doff\fR] [\fI\-\-extdur\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-list\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR] [\fI\-\-pf\fR]
+[\fI\-\-raw=H,H...\fR] [\fI\-\-raw=\-\fR] [\fI\-\-selftest=ST\fR]
+[\fI\-\-test\fR] [\fI\-\-uoff\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+\fIDEVICE\fR
+.PP
+.B sg_senddiag
+[\fI\-doff\fR] [\fI\-e\fR] [\fI\-h\fR] [\fI\-H\fR] [\fI\-l\fR] [\fI\-pf\fR]
+[\fI\-raw=H,H...\fR] [\fI\-raw=\-\fR] [\fI\-s=ST\fR] [\fI\-t\fR]
+[\fI\-uoff\fR] [\fI\-v\fR] [\fI\-V\fR] [\fI\-?\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends a SCSI SEND DIAGNOSTIC command to the \fIDEVICE\fR. It
+can issue self\-tests, find supported diagnostic pages or send arbitrary
+diagnostic pages.
+.PP
+When the \fI\-\-list\fR option and a \fIDEVICE\fR are given then the utility
+sends a SCSI RECEIVE DIAGNOSTIC RESULTS command to fetch the response (i.e.
+the page numbers of supported diagnostic pages).
+.PP
+When the \fI\-\-list\fR option is given without a \fIDEVICE\fR then a list of
+diagnostic page names and their numbers, known by this utility, are listed.
+.PP
+This utility supports two command line syntax\-es, the preferred one is
+shown first in the synopsis and explained in this section. A later section
+on the old command line syntax outlines the second group of options.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-d\fR, \fB\-\-doff\fR
+set the Device Offline (DevOffL) bit (default is clear). Only significant
+when \fI\-\-test\fR option is set for the default self\-test. When set other
+operations on any logical units controlled by the this device server (target)
+may be affected (delayed) while a default self\-test is underway.
+.TP
+\fB\-e\fR, \fB\-\-extdur\fR
+outputs the expected extended self\-test duration. The duration is given in
+seconds (and minutes in parentheses). This figure is obtained from mode page
+0xa (i.e. the control mode page).
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+outputs response from RECEIVE DIAGNOSTIC RESULTS in hex rather than decode it.
+Only the Supported Diagnostic Pages diagnostic page (i.e. page_code=0) is
+decoded; other pages (e.g. those used by SES) are output in hex.
+.br
+If \fI\-\-hex\fR is used once, the hex output has a relative address at the
+start of each line. If \fI\-\-hex\fR is used twice, then ASCII is shown to
+the right of each line of hex. If \fI\-\-hex\fR is used three time or more,
+only the hex is output, in two character pairs (i.e. a byte) space separated
+and up to 16 bytes per line. This latter form, if placed in a file or piped
+through to another invocation, is suitable for the \fI\-\-raw=\-\fR option.
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+when a \fIDEVICE\fR is also given lists the names of all diagnostic pages
+supported by this device. The request is sent via a SEND DIAGNOSTIC
+command (with the "pF" bit set) and the response is fetched by a RECEIVE
+DIAGNOSTIC RESULTS command. When used in the absence of a \fI\-\-list\fR
+argument then a list of diagnostic page names and their numbers, known
+by this utility, are listed.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the value placed in the parameter list length field of a
+SEND DIAGNOSTIC command or in the allocation length field of a RECEIVE
+DIAGNOSTIC RESULTS command. This only occurs when the other options imply
+there will be data sent or received by the command. The default value
+is 4096 bytes. \fILEN\fR cannot exceed 65535 or 0xffff in hexadecimal.
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-P\fR, \fB\-\-page\fR=\fIPG\fR
+where \fIPG\fR is the RECEIVE DIAGNOSTIC RESULTS command page code field.
+If this option is given the PCV bit in that command is set. When this option
+is given then no SEND DIAGNOSTIC command is sent (unlike \fI\-\-list\fR).
+If \fIPG\fR is 0 then the response is decoded as if it is the SPC Supported
+Diagnostic pages diagnostic page. Other \fIPG\fR values (i.e. 1 to 255)
+have their responses output in hex.
+.TP
+\fB\-p\fR, \fB\-\-pf\fR
+set Page Format (PF) bit. By default it is clear (i.e. 0) unless the
+list \fI\-\-list\fR option is given in which case the Page Format
+bit is set (as required by SPC\-3).
+.TP
+\fB\-r\fR, \fB\-\-raw\fR=\fIH,H...\fR
+string of comma separated hex numbers each of which should resolve to
+a byte value (i.e. 0 to ff inclusive). A (single) space separated string
+of hex bytes is also allowed but the list needs to be in quotes. This
+sequence forms a diagnostic page to be sent with the SCSI SEND DIAGNOSTIC
+command. Mostly likely the \fI\-\-pf\fR option should also be given.
+.TP
+\fB\-r\fR, \fB\-\-raw=\-\fR
+reads sequence of bytes from stdin. The sequence may be comma, space, tab
+or linefeed (newline) separated. If a line contains "#" then the remaining
+characters on that line are ignored. Otherwise each non separator character
+should resolve to a byte value (i.e. 0 to ff inclusive). This sequence forms
+a diagnostic page to be sent with the SCSI SEND DIAGNOSTIC command. Mostly
+likely the \fI\-\-pf\fR option should also be given.
+.TP
+\fB\-s\fR, \fB\-\-selftest\fR=\fIST\fR
+where \fIST\fR is the self\-test code. The default value is 0 which is
+inactive. Some other values:
+.br
+  \fB1\fR : background short self\-test
+.br
+  \fB2\fR : background extended self\-test
+.br
+  \fB4\fR : aborts a (background) self\-test that is in progress
+.br
+  \fB5\fR : foreground short self\-test
+.br
+  \fB6\fR : foreground extended self\-test
+.br
+This option is mutually exclusive with default self\-test (i.e.
+can't have (\fIST\fR > 0) and \fI\-\-test\fR).
+.TP
+\fB\-t\fR, \fB\-\-test\fR
+sets the _default_ Self Test (SelfTest) bit. By default this is clear (0).
+The \fI\-\-selftest=ST\fR option should not be active together with this
+option. Both the \fI\-\-doff\fR and/or \fI\-\-uoff\fR options can be used
+with this option.
+.TP
+\fB\-u\fR, \fB\-\-uoff\fR
+set the Unit Offline (UnitOffL) bit (default is clear). Only significant
+when \fI\-\-test\fR option is set for the default self\-test. When set other
+operations on this logical unit may be affected (delayed) while a default
+self\-test is underway. Some devices (e.g. Fujitsu disks) do more tests
+when this bit is set.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string then exit.
+.SH NOTES
+All devices should support the default self\-test. The 'short' self\-test
+codes should complete in 2 minutes or less. The 'extended' self\-test
+codes' maximum duration is vendor specific (e.g. a little over 10 minutes
+with the author's disks). The foreground self\-test codes wait until they
+are completed while the background self\-test codes return immediately. The
+results of both foreground and background self\-test codes are placed in
+the 'self\-test results' log page (see sg_logs(8)). The SCSI command timeout
+for this utility is set to 60 minutes to allow for slow foreground extended
+self\-tests.
+.PP
+If the \fIDEVICE\fR is a disk then no file systems residing on that disk
+should be mounted during a foreground self\-test. The reason is that other
+SCSI commands may become queued behind the foreground self\-test and timeout.
+.PP
+When the \fI\-\-raw=H,H...\fR option is given then self\-tests should not
+be selected. However the \fB\-\-pf\fR (i.e. "page format") option should be
+given. The length of the diagnostic page to be sent is derived from the
+number of bytes given to the \fI\-\-raw=H,H...\fR option. The diagnostic
+page code (number) should be the first byte of the sequence (i.e. as
+dictated by SPC\-3 diagnostic page format). See the EXAMPLES section below.
+.PP
+Arbitrary diagnostic pages can be read (in hex) with the sg_ses(8)
+utility (not only those defined in SES\-2).
+.PP
+If the utility is used with no options (e.g. "sg_senddiag /dev/sg1")
+Then a degenerate SCSI SEND DIAGNOSTIC command is sent with zero
+in all its fields apart from the opcode. Some devices report this
+as an error while others ignore it. It is not entirely clear from
+SPC\-3 if it is invalid to send such a command.
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be a SCSI
+generic (sg) device. In the 2.6 series block devices (e.g. SCSI disks and
+DVD drives) can also be specified.
+.PP
+To access SCSI enclosures see the sg_ses(8) utility. sg_ses uses the
+SCSI SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS commands as outlined
+in the SES\-2 (draft) standard.
+.SH EXIT STATUS
+The exit status of sg_senddiag is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using '\-\-old' (or '\-O) as the first option.
+.TP
+\fB\-doff\fR
+set the Device Offline (DevOffL) bit (default is clear). Only significant
+when \fI\-t\fR option is set for the default self\-test. Equivalent to
+\fI\-\-doff\fR in the main description.
+.TP
+\fB\-e\fR
+outputs the expected extended self\-test duration. Equivalent to
+\fI\-\-extdur\fR in the main description.
+.TP
+\fB\-h\fR
+outputs response from RECEIVE DIAGNOSTIC RESULTS in hex rather than decode
+it.
+.TP
+\fB\-H\fR
+outputs response from RECEIVE DIAGNOSTIC RESULTS in hex rather than decode it.
+.TP
+\fB\-l\fR
+when a \fIDEVICE\fR is also given lists the names of all diagnostic
+pages supported by this device. The request is sent via a SEND DIAGNOSTIC
+command (with the "pf" bit set) and the response is fetched by a RECEIVE
+DIAGNOSTIC RESULTS command. When used in the absence of a \fIDEVICE\fR
+argument then a list of diagnostic page names and their numbers, known
+by this utility, are listed.
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-pf\fR
+set Page Format (PF) bit. By default it is clear (i.e. 0) unless
+the \fI\-l\fR option is given in which case the Page Format bit is set
+(as required by SPC\-3).
+.TP
+\fB\-raw\fR=\fIH,H...\fR
+string of comma separated hex numbers each of which should resolve to
+a byte value (i.e. 0 to ff inclusive). This sequence forms a diagnostic
+page to be sent with the SCSI SEND DIAGNOSTIC command. Mostly likely
+the \fI\-pf\fR option should also be given.
+.TP
+\fB\-raw=-\fR
+reads sequence of bytes from stdin. The sequence may be comma, space, tab
+or linefeed (newline) separated. If a line contains "#" then the remaining
+characters on that line are ignored. Otherwise each non separator character
+should resolve to a byte value (i.e. 0 to ff inclusive). This sequence forms
+a diagnostic page to be sent with the SCSI SEND DIAGNOSTIC command. Mostly
+likely the \fI\-pf\fR option should also be given.
+.TP
+\fB\-s\fR=\fIST\fR
+where \fIST\fR is the self\-test code. The default value is 0 which is
+inactive. A value of 1 selects a background short self\-test; 2 selects
+a background extended self\-test; 5 selects a foreground short self\-test;
+6 selects a foreground extended test. A value of 4 will abort
+a (background) self\-test that is in progress. This option is mutually
+exclusive with default self\-test (i.e. \fI\-t\fR).
+.TP
+\fB\-t\fR
+sets the _default_ Self Test (SelfTest) bit. By default this is clear (0).
+The \fI\-s=ST\fR option should not be active together with this option.
+Both the \fI\-doff\fR and/or \fI\-uoff\fR options can be used with this
+option.
+.TP
+\fB\-uoff\fR
+set the Unit Offline (UnitOffL) bit (default is clear). Equivalent to
+\fI\-\-uoff\fR in the main description.
+.TP
+\fB\-v\fR
+increase level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.TP
+\fB\-?\fR
+output usage message. Ignore all other parameters.
+.SH EXAMPLES
+The examples sub\-directory in the sg3_utils packages contains two example
+scripts that turn on the CJTPAT (jitter pattern) on some SAS disks (one
+script for each phy). One possible invocation for phy 1 is:
+.PP
+  sg_senddiag \-\-pf \-\-raw=\- /dev/sg2 < sdiag_sas_p1_cjtpat.txt
+.PP
+There is also an example script that turns on the IDLE pattern. Once a
+test pattern has been started it can be turned off by resetting the phy
+or with the STOP phy pattern function:
+.PP
+  sg_senddiag \-\-pf \-\-raw=\- /dev/sg2 < sdiag_sas_p1_stop.txt
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2003\-2015 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_ses(8), sg_logs(8), smartmontools(see net)
diff --git a/sg3_utils/doc/sg_ses.8 b/sg3_utils/doc/sg_ses.8
new file mode 100644
index 0000000..8d2d47b
--- /dev/null
+++ b/sg3_utils/doc/sg_ses.8
@@ -0,0 +1,594 @@
+.TH SG_SES "8" "December 2015" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_ses \- access a SCSI Enclosure Services (SES) device
+.SH SYNOPSIS
+.B sg_ses
+[\fI\-\-descriptor=DN\fR] [\fI\-\-dev\-slot\-num=SN\fR] [\fI\-\-eiioe=A_F\fR]
+[\fI\-\-filter\fR] [\fI\-\-get=STR\fR] [\fI\-\-hex\fR]
+[\fI\-\-index=IIA\fR | \fI\-\-index=TIA,II\fR] [\fI\-\-inner\-hex\fR]
+[\fI\-\-join\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-page=PG\fR] [\fI\-\-raw\fR]
+[\fI\-\-readonly\fR] [\fI\-\-sas\-addr=SA\fR] [\fI\-\-status\fR]
+[\fI\-\-verbose\fR] [\fI\-\-warn\fR] \fIDEVICE\fR
+.PP
+.B sg_ses
+[\fI\-\-byte1=B1\fR] [\fI\-\-clear=STR\fR] [\fI\-\-control\fR]
+[\fI\-\-data=H,H...\fR] [\fI\-\-descriptor=DN\fR]
+[\fI\-\-dev\-slot\-num=SN\fR] [\fI\-\-index=IIA\fR | \fI\-\-index=TIA,II\fR]
+[\fI\-\-mask\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-nickname=SEN\fR]
+[\fI\-\-nickid=SEID\fR]  [\fI\-\-page=PG\fR] [\fI\-\-readonly\fR]
+[\fI\-\-sas\-addr=SA\fR] [\fI\-\-set=STR\fR] [\fI\-\-verbose\fR]
+\fIDEVICE\fR
+.PP
+.B sg_ses
+[\fI\-\-enumerate\fR] [\fI\-\-list\fR] [\fI\-\-help\fR] [\fI\-\-version\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Fetches management information from a SCSI Enclosure Service (SES) device.
+This utility can also modify the state of a SES device. The \fIDEVICE\fR
+should be a SES device which may be a dedicated enclosure services
+processor in which case an INQUIRY response's Peripheral Device Type is
+13 [0xd]. Alternatively it may be attached to another type of SCSI
+device (e.g. a disk) in which case the EncServ bit is set in its INQUIRY
+response.
+.PP
+If the \fIDEVICE\fR argument is given with no options then the names of all
+diagnostic pages supported are listed. Most, but not necessarily all, of the
+named diagnostic pages are defined in the SES standards and drafts. The most
+recent reference for this utility is the draft SCSI Enclosure Services 3
+document T10/2149\-D Revision 11 at http://www.t10.org . Existing standards
+for SES and SES\-2 are ANSI INCITS 305\-1998 and ANSI INCITS 448\-2008
+respectively.
+.PP
+The first form shown in the synopsis is for fetching and decoding pages
+or fields from the SES \fIDEVICE\fR. Alternatively a fetched page may be
+output in hex or binary with the \fI\-\-hex\fR or \fI\-\-raw\fR options.
+.PP
+The second form in the synopsis is for modifying pages or fields held in
+the SES \fIDEVICE\fR. Changing the state of an enclosure (e.g. requesting
+the "ident" (locate) LED to flash on a disk carrier in an array) is typically
+done using a read\-modify\-write cycle. See the section on CHANGING STATE
+below.
+.PP
+The third form in the synopsis shows the options for providing command line
+help (i.e. usage information), listing out page and field information tables
+held by the utility (\fI\-\-enumerate\fR), or printing the version string
+of this utility.
+.PP
+There is a web page discussing this utility at
+http://sg.danny.cz/sg/sg_ses.html . Support for downloading microcode to
+a SES device has been placed in a separate utility called sg_ses_microcode.
+.PP
+In the following sections "page" refers to a diagnostic page, either
+fetched with a SCSI RECEIVE DIAGNOSTIC RESULTS command or sent to the
+\fIDEVICE\fR with a SCSI SEND DIAGNOSTIC command.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long
+option name.
+.TP
+\fB\-b\fR, \fB\-\-byte1\fR=\fIB1\fR
+some modifiable pages may need byte 1 (i.e. the second byte) set. In the
+Enclosure Control page, byte 1 contains the INFO, NON\-CRIT, CRIT and
+UNRECOV bits. In the Subenclosure String Out, Subenclosure Nickname Control
+and Download Microcode Control pages, byte 1 is the Subenclosure identifier.
+Active when the \fI\-\-control\fR and \fI\-\-data=H,H...\fR options are used
+and the default value is 0. If the \fI\-\-clear=STR\fR or \fI\-\-set=STR\fR
+option is used then the value read from byte 1 is written back to byte 1.
+\fIB1\fR is in decimal unless it is prefixed by '0x' or '0X' (or has a
+trailing 'h' or 'H').
+.TP
+\fB\-C\fR, \fB\-\-clear\fR=\fISTR\fR
+Used to clear an element field in the Enclosure Control or Threshold Out
+page. Must be used together with an indexing option to specify which element
+is to be changed. The Enclosure Control page is assumed if the
+\fI\-\-page=PG\fR option is not given. See the STR FORMAT section below.
+.TP
+\fB\-c\fR, \fB\-\-control\fR
+will send control information to the \fIDEVICE\fR via a SCSI SEND
+DIAGNOSTIC command. Cannot give both this option and \fI\-\-status\fR.
+The Enclosure Control, String Out, Threshold Out, Array Control (obsolete
+in SES\-2), Subenclosure String Out, Subenclosure Nickname Control and
+Download Microcode pages can be set currently. This option is assumed if
+either the \fI\-\-clear=STR\fR or \fI\-\-set=STR\fR option is given.
+.TP
+\fB\-d\fR, \fB\-\-data\fR=\fIH,H...\fR
+permits a string of comma separated (ASCII) hex bytes to be specified (limit
+1024). A (single) space separated string of hex bytes is also allowed but
+the list needs to be in quotes. This option allows the parameters to a
+control page to be specified. The string given should not include the first 4
+bytes (i.e. page code and length).
+.TP
+\fB\-d\fR, \fB\-\-data\fR=\-
+reads one or more data strings from stdin, limit 2048 bytes. stdin may
+provide ASCII hex as a comma separated list (i.e. as with the
+\fI\-\-data=H,H...\fR option). Additionally spaces, tabs and line feeds are
+permitted as separators from stdin . Stops reading stdin when an EOF is
+detected.
+.TP
+\fB\-d\fR, \fB\-\-data\fR=@\fIFN\fR
+reads one or more data strings from the file called \fIFN\fR, limit 2048
+bytes. Otherwise this option is the same as the previous item that reads
+from stdin.
+.TP
+\fB\-D\fR, \fB\-\-descriptor\fR=\fIDN\fR
+where \fIDN\fR is a descriptor name (string) as found in the Element
+Descriptor page. This is a medium level indexing alternative to the low
+level \fI\-\-index=\fR options. If the descriptor name contains a space then
+\fIDN\fR needs to be surrounded by quotes (single or double) or the space
+escaped (e.g. preceded by a backslash). See the DESCRIPTOR NAME, DEVICE SLOT
+NUMBER AND SAS ADDRESS section below.
+.TP
+\fB\-x\fR, \fB\-\-dev\-slot\-num\fR=\fISN\fR, \fB\-\-dsn\fR=\fISN\fR
+where \fISN\fR is a device slot number found in the Additional Element Status
+page. Only entries for FCP and SAS devices (with EIP=1) have device slot
+numbers. \fISN\fR must be a number in the range 0 to 255 (inclusive). 255 is
+used to indicate there is no corresponding device slot. This is a medium level
+indexing alternative to the low level \fI\-\-index=\fR options. See the
+DESCRIPTOR NAME, DEVICE SLOT NUMBER AND SAS ADDRESS section below.
+.TP
+\fB\-E\fR, \fB\-\-eiioe\fR=\fIA_F\fR
+\fIA_F\fR is either the string 'auto' or 'force'. There was some fuzziness
+in the interpretation of the 'element index' field in the Additional Element
+Status page between SES\-2 and SES\-3. The EIIOE bit was introduced to
+resolve the problem but not all enclosures have caught up.
+Using '\-\-eiioe=force' will decode this page as if the EIIOE bit is set.
+Using '\-\-eiioe=auto' will decode this page as if the EIIOE bit is set if
+the first element index in this page is 1 (in other words a heuristic to
+guess whether the EIIOE bit should be set or not).
+.br
+If the enclosure sets the EIIOE bit then this option has no effect. It is
+recommended that HP JBOD users set --eiioe=auto .
+.TP
+\fB\-e\fR, \fB\-\-enumerate\fR
+enumerate all known page names and SES elements when this option is given
+once. If \fI\-\-enumerate\fR is given twice, then the recognised acronyms for
+the \fI\-\-clear=STR\fR, \fI\-\-get=STR\fR and \fI\-\-set=STR\fR options are
+listed. The utility exits after listing this information (so most other
+options and \fIDEVICE\fR are ignored).
+.TP
+\fB\-f\fR, \fB\-\-filter\fR
+cuts down on the amount of output from the Enclosure Status page and the
+Additional Element Status page. When this option is given, any line which
+has all its binary flags cleared (i.e. 0) is filtered out (i.e.  ignored).
+If a line has some other value on it (e.g. a temperature) then it is output.
+When this option is used twice only elements associated with the "status=ok"
+field (in the Enclosure status page) are output. The \fI\-\-filter\fR option
+is useful for reducing the amount of output generated by the \fI\-\-join\fR
+option.
+.TP
+\fB\-G\fR, \fB\-\-get\fR=\fISTR\fR
+Used to read a field in a status element. Must be used together with a an
+indexing option to specify which element is to be read. By default the
+Enclosure Status page is read, the only other pages that can be read are the
+Threshold In and Additional Element Status pages. If a value is found it is
+output in decimal to stdout (by default) or in hexadecimal preceded by "0x"
+if the \fI\-\-hex\fR option is also given. See the STR FORMAT section below.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit. Since there is a lot of information,
+it is split into two pages. The most important is shown on the first page.
+Use this option twice (e.g. '\-hh') to output the second page. Note: the
+\fI\-\-enumerate\fR option might also be viewed as a help or usage type
+option. And like this option it has a "given twice" form: '\-ee'.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+If the \fI\-\-get=STR\fR option is given then output the value found (if
+any) in hexadecimal, with a leading "0x". Otherwise output the response
+in hexadecimal; with trailing ASCII if given once, without it if given
+twice, and simple hex if given three or more times. Ignored when all
+elements from several pages are being accessed. Also see the \fI\-\-raw\fR
+option which may be used with this option..
+.TP
+\fB\-I\fR, \fB\-\-index\fR=\fIIIA\fR
+where \fIIIA\fR is either an individual index (II) or an Element type
+abbreviation (A). See the INDEXES section below. If the \fI\-\-page=PG\fR
+option is not given then the Enclosure Status (or Control) page is assumed.
+May be used with the \fI\-\-join\fR option or one of the \fI\-\-clear=STR\fR,
+\fI\-\-get=STR\fR or \fI\-\-set=STR\fR options. To enumerate the available
+Element type abbreviations use the \fI\-\-enumerate\fR option.
+.TP
+\fB\-I\fR, \fB\-\-index\fR=\fITIA,II\fR
+where \fITIA,II\fR is an type header index (TI) or Element type
+abbreviation (A) followed by an individual index (II). See the INDEXES section
+below. If the \fI\-\-page=PG\fR option is not given then the Enclosure
+Status (or Control) page is assumed. May be used with the \fI\-\-join\fR
+option or one of the \fI\-\-clear=STR\fR, \fI\-\-get=STR\fR or
+\fI\-\-set=STR\fR options. To enumerate the available Element type
+abbreviations use the \fI\-\-enumerate\fR option.
+.TP
+\fB\-i\fR, \fB\-\-inner\-hex\fR
+the outer levels of a status page are decoded and printed out but the
+innermost level (e.g. the Element Status Descriptor) is output in hex. Also
+active with the Additional Element Status and Threshold In pages. Can be
+used with an indexing option and/or \fI\-\-join\fR options.
+.TP
+\fB\-j\fR, \fB\-\-join\fR
+group elements from the Element Descriptor, Enclosure Status and Additional
+Element Status pages. If this option is given twice then elements from the
+Threshold In page are also grouped. The order is dictated by the Configuration
+page. All elements are output unless one of the indexing options is given,
+in which case only the matching element and its associated fields are output.
+The \fI\-\-filter\fR option can be added to reduce the amount of output
+generated by this option. See the INDEXES and DESCRIPTOR NAME, DEVICE SLOT
+NUMBER AND SAS ADDRESS sections below.
+.TP
+\fB\-l\fR, \fB\-\-list\fR
+This option is equivalent to \fI\-\-enumerate\fR. See that option.
+.TP
+\fB\-M\fR, \fB\-\-mask\fR
+When modifying elements, the default action is a read (status element),
+mask, modify (based on \fI\-\-clear=STR\fR or \fI\-\-set=STR\fR) then write
+back as the control element. The mask step is new in sg_ses version 1.98
+and is based on what is allowable (and in the same location) in draft SES\-3
+revision 6. Those masks may evolve, as they have in the past. This option
+re\-instates the previous logic which was to ignore the mask step. The
+default action (i.e. without this option) is to perform the mask step in
+the read\-mask\-modify\-write sequence.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+\fILEN\fR is placed in the ALLOCATION LENGTH field of the SCSI RECEIVE
+DIAGNOSTIC RESULTS commands sent by the utility. It represents the maximum
+size of data the SES device can return (in bytes). It cannot exceed 65535
+and defaults to 65532 (bytes). Some systems may not permit such large sizes
+hence the need for this option. If \fILEN\fR is set to 0 then the default
+size is used.
+.TP
+\fB\-n\fR, \fB\-\-nickname\fR=\fISEN\fR
+where \fISEN\fR is the new Subenclosure Nickname. Only the first 32
+characters (bytes) of \fISEN\fR are used, if more are given they are
+ignored. See the SETTING SUBENCLOSURE NICKNAME section below.
+.TP
+\fB\-N\fR, \fB\-\-nickid\fR=\fISEID\fR
+where \fISEID\fR is the Subenclosure identifier that the new
+Nickname (\fISEN\fR) will be applied to. So \fISEID\fR must be an existing
+Subenclosure identifier. The default value is 0 which is the
+main enclosure.
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPG\fR
+where \fIPG\fR is a page abbreviation or code (a number). If \fIPG\fR
+starts with a digit it is assumed to be in decimal unless prefixed by
+0x for hex. Valid range is 0 to 255 (0x0 to 0xff) inclusive. Default is
+page 'sdp' which is page_code 0 (i.e. "Supported Diagnostic Pages") if
+no other options are given.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+outputs the chosen status page in ASCII hex in a format suitable for a
+later invocation using the \fI\-\-data=\fR option. A page less its first
+4 bytes (page code and length) is output. When used twice (e.g. \fI\-rr\fR)
+the full page contents is output in binary to stdout.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-A\fR, \fB\-\-sas\-addr\fR=\fISA\fR
+this is an indexing method for SAS end devices (e.g. SAS disks). The utility
+will try to find the element or slot in the Additional Element Status page
+whose SAS address matches \fISA\fR. For a SAS disk or tape that SAS address
+is its target port identifier for the port connected to that element or slot.
+Most SAS disks and tapes have two such target ports, usually numbered
+consecutively.
+.br
+SATA devices in a SAS enclosure often receive "manufactured" target port
+identifiers from a SAS expander; typically will a SAS address close to
+but different from the SAS address of the expander itself. Note that this
+manufactured target port identifier is different from a SATA disk's WWN.
+.br
+\fISA\fR is a hex number that is up to 8 digits long. It may have a
+leading '0x' or '0X' or a trailing 'h' or 'H'. This option is a medium level
+ indexing alternative to the low level \fI\-\-index=\fR options.
+See the DESCRIPTOR NAME, DEVICE SLOT NUMBER AND SAS ADDRESS section below.
+.TP
+\fB\-S\fR, \fB\-\-set\fR=\fISTR\fR
+Used to set an element field in the Enclosure Control or Threshold Out page.
+Must be used together with an indexing option to specify which element is to
+be changed. The Enclosure Control page is assumed if the \fI\-\-page=PG\fR
+option is not given. See the STR FORMAT section below.
+.TP
+\fB\-s\fR, \fB\-\-status\fR
+will fetch page from the \fIDEVICE\fR via a SCSI RECEIVE DIAGNOSTIC RESULTS
+command. In the absence of other options that imply modifying a page (e.g.
+\fI\-\-control\fR or \fI\-\-set=STR\fR) then \fI\-\-status\fR is assumed.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-w\fR, \fB\-\-warn\fR
+warn about certain irregularities with warnings sent to stderr. The join
+is a complex operation that relies on information from several pages to be
+synchronized. The quality of SES devices vary and to be fair, the
+descriptions from T10 drafts and standards have been tweaked several
+times (see the EIIOE bit) in order to clear up confusion.
+.SH INDEXES
+An enclosure can have information about its disk and tape drives plus other
+supporting components like power supplies spread across several pages.
+Addressing a specific element (overall or individual) within a page is
+complicated. This section describes low level indexing (i.e. choosing a
+single element (or a group of related elements) from a large number of
+elements). If available, the medium level indexing described in the
+following section (DESCRIPTOR NAME, DEVICE SLOT NUMBER AND SAS ADDRESS)
+might be simpler to use.
+.PP
+The Configuration page is key to low level indexing: it contains a list
+of "type headers", each of which contains an Element type (e.g. Array
+Device Slot), a Subenclosure identifier (0 for the primary enclosure) and
+a "Number of possible elements". Corresponding to each type header, the
+Enclosure Status page has one "overall" element plus "Number of possible
+elements" individual elements all of which have the given Element type. For
+some Element types the "Number of possible elements" will be 0 so the
+Enclosure Status page has only one "overall" element corresponding to that
+type header. The Element Descriptor page and the Threshold (In and Out)
+pages follow the same pattern as the Enclosure Status page.
+.PP
+The Additional Element Status page is a bit more complicated. It has
+entries for "Number of possible elements" of certain Element types. It
+does not have entries corresponding to the "overall" elements. To make
+the correspondence a little clearer each descriptor in this page optionally
+contains an "Element Index Present" (EIP) indicator. If EIP is set then each
+element's "Element Index" field refers to the position of the corresponding
+element in the Enclosure Status page.
+.PP
+Addressing a single overall element or a single individual element is done
+with two indexes: TI and II. Both are origin 0. TI=0 corresponds to the
+first type header entry which must be a Device Slot or Array Device Slot
+Element type (according to the SES\-2 standard). To address the corresponding
+overall instance, II is set to \-1, otherwise II can be set to the individual
+instance index. As an alternative to the type header index (TI), an Element
+type abbreviation (A) optionally followed by a number (e.g. "ps" refers to
+the first Power Supply Element type; "ps1" refers to the second) can be
+given.
+.PP
+One of two command lines variants can be used to specify indexes:
+\fI\-\-index=TIA,II\fR where \fITIA\fR is either an type header index (TI)
+or an Element type abbreviation (A) (e.g. "ps" or "ps1"). \fIII\fR is either
+an individual index or "\-1" to specify the overall element. The second
+variant is \fI\-\-index=IIA\fR where \fIIIA\fR is either an individual
+index (II) or an Element type abbreviation (A). When \fIIIA\fR is an
+individual index then the option is equivalent to \fI\-\-index=0,II\fR. When
+\fIIIA\fR is an Element type abbreviation then the option is equivalent to
+\fI\-\-index=A,\-1\fR.
+.PP
+To cope with vendor specific Element types (which should be in the range 128
+to 255) the Element type can be given as a number with a leading underscore.
+For example these are equivalent: \fI\-\-index=arr\fR and
+\fI\-\-index=_23\fR since the Array Device Slot Element type value is 23.
+Also \fI\-\-index=ps1\fR and \fI\-\-index=_2_1\fR are equivalent.
+.PP
+Another example: if the first type header in the Configuration page has
+has Array Device Slot Element type then \fI\-\-index=0,\-1\fR is
+equivalent to \fI\-\-index=arr\fR. Also \fI\-\-index=arr,3\fR is equivalent
+to \fI\-\-index=3\fR.
+.PP
+The \fI\-\-index=\fR options  can be used to reduce the amount of
+output (e.g. only showing the element associated with the second 12 volt
+power supply). They may also be used together with with the
+\fI\-\-clear=STR\fR, \fI\-\-get=STR\fR and \fI\-\-set=STR\fR options which
+are described in the STR section below.
+.SH DESCRIPTOR NAME, DEVICE SLOT NUMBER AND SAS ADDRESS
+The three options: \fI\-\-descriptor=DN\fR, \fI\-\-dev\-slot\-num=SN\fR
+and \fI\-\-sas\-addr=SA\fR allow medium level indexing, as an alternative
+to the low level \fI\-\-index=\fR options. Only one of the three options
+can be used in an invocation. Each of the three options implicitly set the
+\fI\-\-join\fR option since they need either the Element Descriptor page or
+the Additional Element Status page as well as the pages needed by the
+\fI\-\-index=\fR option.
+.PP
+These medium level indexing options need support from the SES device and
+that support is optional. For example the \fI\-\-descriptor=DN\fR needs
+the Element Descriptor page provided by the SES device however that is
+optional. Also the provided descriptor names need to be useful, and having
+descriptor names which are all "0" is not very useful. Also some
+elements (e.g. overall elements) may not have descriptor names.
+.PP
+These medium level indexing options can be used to reduce the amount of
+output (e.g. only showing the elements related to device slot number 3).
+They may also be used together with with the \fI\-\-clear=STR\fR,
+\fI\-\-get=STR\fR and \fI\-\-set=STR\fR options which are described in the
+following section. Note that even if a field can be set (e.g. "do not
+remove" (dnr)) and that field can be read back with \fI\-\-get=STR\fR
+confirming that change, the disk array may still ignore it (e.g. because it
+does not have the mechanism to lock the disk drawer).
+.SH STR FORMAT
+The \fISTR\fR operands of the \fI\-\-clear=STR\fR, \fI\-\-get=STR\fR and
+\fI\-\-set=STR\fR options all have the same structure. There are two forms:
+.br
+      <acronym>[=<value>]
+.br
+      <start_byte>:<start_bit>[:<num_bits>][=<value>]
+.PP
+The <acronym> is one of a list of common fields (e.g. "ident" and "fault")
+that the utility converts internally into the second form. The <start_byte>
+is usually in the range 0 to 3, the <start_bit> must be in the range 0 to
+7 and the <num_bits> must be in the range 1 to 64 (default 1). The
+number of bits are read in the left to right sense of the element tables
+shown in the various SES draft documents. For example the 8 bits of
+byte 2 would be represented as 2:7:8 with the most significant bit being
+2:7 and the least significant bit being 2:0 .
+.PP
+The <value> is optional but is ignored if provided to \fI\-\-get=STR\fR.
+For \fI\-\-set=STR\fR the default <value> is 1 while for \fI\-\-clear=STR\fR
+the default value is 0 . <value> is assumed to be decimal, hexadecimal
+values can be given in the normal fashion.
+.PP
+The supported list of <acronym>s can be viewed by using the
+\fI\-\-enumerate\fR option twice (or "\-ee").
+.SH CHANGING STATE
+This utility has various techniques for changing the state of a SES device.
+As noted above this is typically a read\-modify\-write type operation.
+Most modifiable pages have a "status" (or "in") page that can be read, and
+a corresponding "control" (or "out") page that can be written back to change
+the state of the enclosure.
+.PP
+The lower level technique provided by this utility involves outputting
+a "status" page in hex with \fI\-\-raw\fR. Then a text editor can be used
+to edit the hex (note: to change an Enclosure Control descriptor the SELECT
+bit needs to be set). Next the control page data can fed back with the
+\fI\-\-data=H,H...\fR option together with the \fI\-\-control\fR option;
+the \fI\-\-byte1=B1\fR option may need to be given as well.
+.PP
+Changes to the Enclosure Control page (and the Threshold Out page) can be
+done at a higher level. This involves choosing a page (the default in this
+case is the Enclosure Control page). Next choose an individual or overall
+element index (or name it with its Element Descriptor string). Then give
+the element's name (e.g. "ident" for RQST IDENT) or its position within that
+element (e.g. in an Array Device Slot Control element RQST IDENT is byte 2,
+bit 1 and 1 bit long ("2:1:1")). Finally a value can be given, if not the
+value for \fI\-\-set=STR\fR defaults to 1 and for \fI\-\-clear=STR\fR
+defaults to 0.
+.SH SETTING SUBENCLOSURE NICKNAME
+The format of the Subenclosure Nickname control page is different from its
+corresponding status page. The status page reports all Subenclosure
+Nicknames (and Subenclosure identifier 0 is the main enclosure) while the
+control page allows only one of them to be changed. Therefore using the
+\fB\-\-data\fR option technique to change a Subenclosure nickname is
+difficult (but still possible).
+.PP
+To simplify changing a Subenclosure nickname the \fI\-\-nickname=SEN\fR and
+\fI\-\-nickid=SEID\fR options have been added. If the \fISEN\fR string
+contains spaces or other punctuation, it should be quoted: surrounded by
+single or double quotes (or the offending characters escaped). If the
+\fI\-\-nickid=SEID\fR is not given then a Subenclosure identifier of 0 is
+assumed. As a guard the \fI\-\-control\fR option must also be given. If
+the \fI\-\-page=PG\fR option is not given then \fI\-\-page=snic\fR is
+assumed.
+.PP
+When \fI\-\-nickname=SEN\fR is given then the Subenclosure Nickname Status
+page is read to obtain the Generation Code field. That Generation Code
+together with no more than 32 bytes from the Nickname (\fISEN\fR) and the
+Subenclosure Identifier (\fISEID\fR) are written to the Subenclosure Nickname
+Control page.
+.PP
+There is an example of changing a nickname in the EXAMPLES section below.
+.SH NOTES
+This utility can be used to fetch arbitrary (i.e. non SES) diagnostic
+pages (using the SCSI READ DIAGNOSTIC command). To this end the
+\fI\-\-page=PG\fR and \fI\-\-hex\fR options would be appropriate. Arbitrary
+diagnostic pages can be sent to a device with the sg_senddiag utility.
+.PP
+The most troublesome part of the join operation is associating Additional
+Element Status descriptors correctly. At least one SES device vendor has
+misinterpreted the SES\-2 standard with its "element index" field. The
+code in this utility interprets the "element index" field as per the SES\-2
+standard and if that yields an inappropriate Element type, adjusts its
+indexing to follow that vendor's misinterpretation.
+.PP
+In draft SES\-3 revision 5 the "Door Lock" element name was changed to
+the "Door" (and an OPEN field was added to the status element). As a
+consequence the former 'dl' element type abbreviation has been changed
+to 'do'.
+.PP
+There is a related command set called SAF\-TE (SCSI attached fault\-tolerant
+enclosure) for enclosure (including RAID) status and control. SCSI devices
+that support SAF\-TE report "Processor" peripheral device type (0x3) in their
+INQUIRY response. See the sg_safte utility in this package or safte\-monitor
+on the Internet.
+.SH EXAMPLES
+Examples can also be found at http://sg.danny.cz/sg/sg_ses.html
+.PP
+The following examples use Linux device names. For suitable device names
+in other supported Operating Systems see the sg3_utils(8) man page.
+.PP
+To view the supported pages:
+.PP
+   sg_ses /dev/bsg/6:0:2:0
+.PP
+To view the Configuration Diagnostic page:
+.PP
+   sg_ses \-\-page=cf /dev/bsg/6:0:2:0
+.PP
+To view the Enclosure Status page:
+.PP
+   sg_ses \-\-page=es /dev/bsg/6:0:2:0
+.PP
+To get the (attached) SAS address of that device (which is held in the
+Additional Element Sense page (page 10)) printed on hex:
+.PP
+   sg_ses \-p aes \-D ArrayDevice07 \-G at_sas_addr \-H /dev/sg3
+.PP
+To collate the information in the Enclosure Status, Element Descriptor
+and Additional Element Status pages the \fI\-\-join\fR option can be used:
+.PP
+   sg_ses \-\-join /dev/sg3
+.PP
+This will produce a lot of output. To filter out lines that don't contain
+much information add the \fI\-\-filter\fR option:
+.PP
+   sg_ses \-\-join \-\-filter /dev/sg3
+.PP
+Fields in the various elements of the Enclosure Control and Threshold pages
+can be changed with the \fI\-\-clear=STR\fR and \fI\-\-set=STR\fR
+options. [All modifiable pages can be changed with the \fI\-\-raw\fR and
+\fI\-\-data=H,H...\fR options.] The following example looks at making
+the "ident" LED (also called "locate") flash on "ArrayDevice07" which is a
+disk (or more precisely the carrier drawer the disk is in):
+.PP
+   sg_ses \-\-index=7 \-\-set=2:1:1 /dev/sg3
+.PP
+If the Element Descriptor diagnostic page shows that "ArrayDevice07" is
+the descriptor name associated with element index 7 then this invocation
+is equivalent to the previous one:
+.PP
+   sg_ses \-\-descriptor=ArrayDevice07 \-\-set=2:1:1 /dev/sg3
+.PP
+Further the byte 2, bit 1 (for 1 bit) field in the Array Device Slot Control
+element is RQST IDENT for asking a disk carrier to flash a LED so it can
+be located. In this case "ident" (or "locate") is accepted as an acronym
+for that field:
+.PP
+   sg_ses \-\-descriptor=ArrayDevice07 \-\-set=ident /dev/sg3
+.PP
+To stop that LED flashing:
+.PP
+   sg_ses \-\-dev\-slot\-num=7 \-\-clear=ident /dev/sg3
+.PP
+The above assumes the descriptor name 'ArrayDevice07' corresponds to device
+slot number 7.
+.PP
+Now for an example of a more general but lower level technique for changing
+a modifiable diagnostic page. The String (In and Out) diagnostics page is
+relatively simple (compared with the Enclosure Status/Control page). However
+the use of this lower level technique is awkward involving three steps: read,
+modify then write. First check the current String (In) page contents:
+.PP
+   sg_ses \-\-page=str /dev/bsg/6:0:2:0
+.PP
+Now the "read" step. The following command will send the contents of the
+String page (from byte 4 onwards) to stdout. The output will be in ASCII
+hex with pairs of hex digits representing a byte, 16 pairs per line,
+space separated. The redirection puts stdout in a file called "t":
+.PP
+   sg_ses \-\-page=str \-\-raw /dev/bsg/6:0:2:0 > t
+.PP
+Then with the aid of the SES\-3 document (in revision 3: section 6.1.6)
+use your favourite editor to change t. The changes can be sent to the
+device with:
+.PP
+   sg_ses \-\-page=str \-\-control \-\-data=\- /dev/bsg/6:0:2:0 < t
+.PP
+If the above is successful, the String page should have been changed. To
+check try:
+.PP
+   sg_ses \-\-page=str /dev/bsg/6:0:2:0
+.PP
+To change the nickname on the main enclosure:
+.PP
+   sg_ses \-\-nickname='1st enclosure' \-\-control /dev/bsg/6:0:2:0
+.SH EXIT STATUS
+The exit status of sg_ses is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2015 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq, sg_safte, sg_senddiag, sg_ses_microcode, sg3_utils (sg3_utils);
+.B safte\-monitor (Internet)
diff --git a/sg3_utils/doc/sg_ses_microcode.8 b/sg3_utils/doc/sg_ses_microcode.8
new file mode 100644
index 0000000..3e4a9d2
--- /dev/null
+++ b/sg3_utils/doc/sg_ses_microcode.8
@@ -0,0 +1,246 @@
+.TH SG_SES_MICROCODE "8" "October 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_ses_microcode \- send microcode to a SCSI enclosure
+.SH SYNOPSIS
+.B sg_ses_microcode
+[\fI\-\-bpw=CS\fR] [\fI\-\-help\fR] [\fI\-\-id=ID\fR] [\fI\-\-in=FILE\fR]
+[\fI\-\-length=LEN\fR] [\fI\-\-mode=MO\fR] [\fI\-\-non\fR]
+[\fI\-\-offset=OFF\fR] [\fI\-\-skip=SKIP\fR] [\fI\-\-subenc=MS\fR]
+[\fI\-\-tlength=TLEN\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility attempts to download microcode to an enclosure (or one of its
+sub\-enclosures) associated with the \fIDEVICE\fR. The process for doing
+this is defined in the SCSI Enclosure Services (SES) standards and drafts
+maintained by the T10 committee.
+.PP
+The process is to send one or more sequences containing a SCSI SEND
+DIAGNOSTIC command followed optionally by a RECEIVE DIAGNOSTIC RESULTS
+command. The former sends a Download microcode Control diagnostic
+page (dpage) and the latter fetches a Download microcode status dpage which
+can be viewed as a report on the former command.
+.PP
+The default action (i.e. when the \fI\-\-mode=MO\fR option is not given)
+is to fetch the Download microcode status dpage and decode it. This does
+not require the microcode (firmware) itself so the \fI\-\-in=FILE\fR option
+is not required.
+.PP
+The most recent reference for this utility is the draft SCSI Enclosure
+Services 3 (SES\-3) document T10/2149\-D Revision 7 at http://www.t10.org .
+Existing standards for SES and SES\-2 are ANSI INCITS 305\-1998 and ANSI
+INCITS 448\-2008 respectively.
+.PP
+Most other support for SES in this package (apart from downloading
+microcode) can be found in the sg_ses utility. Another way of downloading
+firmware to a SCSI device is with the WRITE BUFFER command defined in
+SPC\-4, see the sg_write_buffer utility.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-bpw\fR=\fICS\fR
+where \fICS\fR is the chunk size in bytes and should be a multiple of 4.
+This will be the maximum number of bytes sent per SEND DIAGNOSTIC command.
+So if \fICS\fR is less than the effective length of the microcode then
+multiple SEND DIAGNOSTIC commands are sent, each taking the next chunk
+from the read data and increasing the buffer offset field in the Download
+microcode control dpage by the appropriate amount. The default is
+a chunk size of 0 which is interpreted as a very large number hence only
+one SEND DIAGNOSTIC command will be sent.
+.br
+The number in \fICS\fR can optionally be followed by ",act" or ",activate".
+In this case after the microcode has been successfully sent to the
+\fIDEVICE\fR, an additional Download microcode control dpage with its mode
+set to "Activate deferred microcode" [0xf] is sent.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit. If used multiple times also prints
+the mode names and their acronyms.
+.TP
+\fB\-i\fR, \fB\-\-id\fR=\fIID\fR
+this option sets the BUFFER ID field in the Download microcode control
+dpage. \fIID\fR is a value between 0 (default) and 255 inclusive.
+.TP
+\fB\-I\fR, \fB\-\-in\fR=\fIFILE\fR
+read data from file \fIFILE\fR that will be sent with the SEND DIAGNOSTIC
+command.  If \fIFILE\fR is '\-' then stdin is read until an EOF is
+detected (this is the same action as \fI\-\-raw\fR). Data is read from
+the beginning of \fIFILE\fR except in the case when it is a regular file
+and the \fI\-\-skip=SKIP\fR option is given.
+.TP
+\fB\-l\fR, \fB\-\-length\fR=\fILEN\fR
+where \fILEN\fR is the length, in bytes, of data to be written to the device.
+If not given (and the length cannot be deduced from \fI\-\-in=FILE\fR or
+\fI\-\-raw\fR) then defaults to zero. If the option is given and the length
+deduced from \fI\-\-in=FILE\fR or \fI\-\-raw\fR is less (or no data is
+provided), then bytes of 0xff are used as fill bytes.
+.TP
+\fB\-m\fR, \fB\-\-mode\fR=\fIMO\fR
+this option sets the MODE. \fIMO\fR is a value between
+0 (which is dmc_status and the default) and 255 inclusive. Alternatively
+an abbreviation can be given. See the MODES section below. To list the
+available mode abbreviations at run time give an invalid
+one (e.g. '\-\-mode=xxx') or use the '\-h' option.
+.TP
+\fB\-N\fR, \fB\-\-non\fR
+allow for non\-standard implementations that reset their Download microcode
+engine after a RECEIVE DIAGNOSTIC RESULTS command with the Download microcode
+status dpage is sent. When this option is given sending that command and
+dpage combination is avoided unless an error has already occurred.
+.TP
+\fB\-o\fR, \fB\-\-offset\fR=\fIOFF\fR
+this option sets the BUFFER OFFSET field in the Download microcode control
+dpage. \fIOFF\fR is a value between 0 (default) and 2**32\-1 . It is a
+byte offset.
+.TP
+\fB\-s\fR, \fB\-\-skip\fR=\fISKIP\fR
+this option is only active when \fI\-\-in=FILE\fR is given and \fIFILE\fR is
+a regular file, rather than stdin. Data is read starting at byte offset
+\fISKIP\fR to the end of file (or the amount given by \fI\-\-length=LEN\fR).
+If not given the byte offset defaults to 0 (i.e. the start of the file).
+.TP
+\fB\-S\fR, \fB\-\-subenc\fR=\fISEID\fR
+\fISEID\fR is the subenclosure identify. It defaults to 0 which is the
+primary enclosure identifier.
+.TP
+\fB\-t\fR, \fB\-\-tlength\fR=\fITLEN\fR
+\fITLEN\fR is the total length in bytes of the microcode to be (or being)
+downloaded. It defaults to 0 which is okay in most cases. This option is
+only needed when sections of microcode and being sent in separate invocations
+of this utility.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH MODES
+Following is a list accepted by the \fIMO\fR argument of this utility.
+First shown is an acronym followed in square brackets by the corresponding
+decimal and hex values that may also be given for \fIMO\fR.
+.TP
+dmc_status  [0, 0x0]
+Use RECEIVE DIAGNOSTIC RESULTS to fetch the Download microcode status dpage
+and print it out.
+.TP
+dmc_offs  [6, 0x6]
+Download microcode with offsets and activate.
+.TP
+dmc_offs_save  [7, 0x7]
+Download microcode with offsets, save, and activate.
+.TP
+dmc_offs_defer  [14, 0xe]
+Download microcode with offsets, save, and defer activate.
+.TP
+activate_mc  [15, 0xf]
+Activate deferred microcode. There is no follow-up RECEIVE DIAGNOSTIC RESULTS
+to fetch the Download microcode status dpage since the \fIDEVICE\fR might be
+resetting.
+.PP
+Apart from dmc_status, these are placed in the Download microcode mode
+field in the Download microcode control dpage. In the case of dmc_status
+the Download microcode status dpage is fetch with the RECEIVE DIAGNOSTIC
+RESULTS command and decoded.
+.SH WHEN THE DOWNLOAD FAILS
+Firstly, if it succeeds, this utility should stay silent and return.
+Typically vendors will change the "revision" string (which is 4 characters
+long) whenever they release new firmware. That can be seen in the response
+to a SCSI INQUIRY command, for example by using the sg_inq utility.
+It is possible that the device needs to be power cycled before the new
+microcode becomes active. Also if mode dmc_offs_defer [0xe] is used to
+download the microcode, then another invocation with activate_mc may
+be needed.
+.PP
+If something goes wrong, there will typically be messages printed out
+by this utility. The first thing to check is the microcode (firmware)
+file itself. Is it designed for the device model; has it been corrupted,
+and if downgrading (i.e. trying to re-instate older firmware), does
+the vendor allow that?
+.PP
+Getting new firmware on a device is a delicate operation that is not
+always well defined by T10's standards and drafts. One might speculate
+that they are deliberately vague. In testing this utility one vendor's
+interpretation of the standard was somewhat surprising. The \fI\-\-non\fR
+option was added to cope with their interpretation. So if the above
+suggestions don't help, try adding the \fI\-\-non\fR option.
+.SH NOTES
+This utility can handle a maximum size of 128 MB of microcode which
+should be sufficient for most purposes. In a system that is memory
+constrained, such large allocations of memory may fail.
+.PP
+The user should be aware that most operating systems have limits on the
+amount of data that can be sent with one SCSI command. In Linux this
+depends on the pass through mechanism used (e.g. block SG_IO or the sg
+driver) and various setting in sysfs in the Linux lk 2.6/3
+series (e.g. /sys/block/sda/queue/max_sectors_kb). Devices (i.e. logical
+units) also typically have limits on the maximum amount of data they can
+handle in one command. These two limitations suggest that modes
+containing the word "offset" together with the \fI\-\-bpw=CS\fR option
+are required as firmware files get larger and larger. And \fICS\fR
+can be quite small, for example 4096 bytes, resulting in many SEND
+DIAGNOSTIC commands being sent.
+.PP
+The exact error from the non\-standard implementation was a sense key of
+ILLEGAL REQUEST and an asc/ascq code of 0x26,0x0 which is "Invalid field in
+parameter list". If that is seen try again with the \fI\-\-non\fR option.
+.PP
+Downloading incorrect microcode into a device has the ability to render
+that device inoperable. One would hope that the device vendor verifies
+the data before activating it.
+.PP
+A long (operating system) timeout of 7200 seconds is set on each SEND
+DIAGNOSTIC command.
+.PP
+All numbers given with options are assumed to be decimal.
+Alternatively numerical values can be given in hexadecimal preceded by
+either "0x" or "0X" (or has a trailing "h" or "H").
+.SH EXAMPLES
+If no microcode/firmware file is given then this utility fetches and decodes
+the Download microcode status dpage which could possibly show another
+initiator in the process of updating the microcode. Even if that is
+happening, fetching the status page should not cause any problems:
+.PP
+  sg_ses_microcode /dev/sg3
+.br
+Download microcode status diagnostic page:
+.br
+  number of secondary subenclosures: 0
+.br
+  generation code: 0x0
+.br
+   subenclosure identifier: 0 [primary]
+.br
+     download microcode status: No download microcode operation in progress [0x0]
+.br
+     download microcode additional status: 0x0
+.br
+     download microcode maximum size: 1048576 bytes
+.br
+     download microcode expected buffer id: 0x0
+.br
+     download microcode expected buffer id offset: 0
+.PP
+The following sends new microcode/firmware to an enclosure. Sending a 1.5 MB
+file in one command caused the enclosure to lock up temporarily and did
+not update the firmware. Breaking the firmware file into 4 KB chunks (an
+educated guess) was more successful:
+.PP
+  sg_ses_microcode \-b 4k \-m dmc_offs_save \-I firmware.bin /dev/sg4
+.PP
+The firmware update occurred in the following enclosure power cycle. With
+a modern enclosure the Extended Inquiry VPD page gives indications in which
+situations a firmware upgrade will take place.
+.SH EXIT STATUS
+The exit status of sg_ses_microcode is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_ses, sg_write_buffer(sg3_utils)
diff --git a/sg3_utils/doc/sg_start.8 b/sg3_utils/doc/sg_start.8
new file mode 100644
index 0000000..690a6eb
--- /dev/null
+++ b/sg3_utils/doc/sg_start.8
@@ -0,0 +1,267 @@
+.TH SG_START "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_start \- send SCSI START STOP UNIT command: start, stop, load or eject
+medium
+.SH SYNOPSIS
+.B sg_start
+[\fI0\fR] [\fI1\fR] [\fI\-\-eject\fR] [\fI\-\-help\fR] [\fI\-\-fl=FL\fR]
+[\fI\-\-immed\fR] [\fI\-\-load\fR] [\fI\-\-loej\fR] [\fI\-\-mod=PC_MOD\fR]
+[\fI\-\-noflush\fR] [\fI\-\-pc=PC\fR] [\fI\-\-readonly\fR] [\fI\-\-start\fR]
+[\fI\-\-stop\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.PP
+.B sg_start
+[\fI\-\-eject\fR] [\fI\-\-fl=FL\fR] [\fI\-i\fR] [\fI\-\-imm=0|1\fR]
+[\fI\-\-load\fR] [\fI\-\-loej\fR] [\fI\-\-mod=PC_MOD\fR] [\fI\-\-noflush\fR]
+[\fI\-\-pc=PC\fR] [\fI\-r\fR] [\fI\-\-start\fR] [\fI\-\-stop\fR] [\fI\-v\fR]
+[\fI\-V\fR] [\fI0|1\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+sg_start sends a SCSI START STOP UNIT command to the \fIDEVICE\fR with
+the selected options. The most used options are \fI\-\-stop\fR to spin
+down a disk and \fI\-\-start\fR to spin up a disk. Using \fI\-\-start\fR
+on a disk that is already spinning is harmless. There is also finer grain
+control with "power condition": active, idle or standby. This is set
+with the \fI\-\-pc=PC\fR option. In some contexts the "stop" state can
+be considered an additional power condition.
+.PP
+Devices that contain removable media such as cd/dvds can use the
+\fI\-\-loej\fR option to load the medium when used in conjunction
+with \fI\-\-start\fR (i.e. load medium then spin up). Alternatively
+\fI\-\-loej\fR may be used to eject the medium when used in conjunction
+with \fI\-\-stop\fR (i.e. spin down then eject medium). More simply, the
+loading or ejecting of a removable medium can be requested with the
+\fI\-\-load\fR or \fI\-\-eject\fR' option.
+.PP
+If no option or argument is given then a \fI\-\-start\fR is assumed; as the
+utility's name suggests.
+.PP
+This utility supports two command line syntaxes, the preferred one is
+shown first in the synopsis and explained in this section. A later
+section on the old command line syntax outlines the second group of
+options.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB0\fR
+same action as \fI\-\-stop\fR.
+.TP
+\fB1\fR
+same action as \fI\-\-start\fR.
+.TP
+\fB\-e\fR, \fB\-\-eject\fR
+stop the medium and eject it from the drive. Only appropriate for a
+device with removable medium. Might be ignored (prevented), see below.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-f\fR, \fB\-\-fl\fR=\fIFL\fR
+sets the format layer number for the disc to "jump" to (defined in MMC\-5).
+Values of \fIFL\fR can be 0 to 3. When this option is chosen, the FL, LoEj
+and Start bits are set in the cdb as required by MMC\-5; thus the user does
+not need to set the \fI\-\-start\fR and/or \fI\-\-load\fR options.
+.TP
+\fB\-i\fR, \fB\-\-immed\fR
+sets the IMM bit on the START STOP UNIT command so this utility will
+return immediately and not wait for the media to complete the requested
+action. The default is to wait until the media to complete the requested
+action before returning.
+.TP
+\fB\-l\fR, \fB\-\-load\fR
+load the medium in the drive and start it. Only appropriate for a removable
+medium.
+.TP
+\fB\-L\fR, \fB\-\-loej\fR
+sets the LOEJ bit on the START STOP UNIT command. This loads the media when
+the unit is started or eject it when the unit is stopped (i.e.  works in
+conjunction with START bit in cdb). This option is ignored if 'pc > 0'.
+Default is off (i.e. don't attempt to load or eject media). If a start/start
+indication is not given (i.e. neither \fI\-\-start\fR nor \fI\-\-stop\fR)
+and this option is given then a load and start action is assumed.
+.TP
+\fB\-m\fR, \fB\-\-mod\fR=\fIPC_MOD\fR
+where \fIPC_MOD\fR is the 'power condition modifier' value. 0 to 15 (inclusive)
+are valid and 0 is the default. This  'power condition modifier' field in the
+cdb was added after sbc3r13.
+.TP
+\fB\-n\fR, \fB\-\-noflush\fR
+do not perform a flush to media (e.g. like SYNCHRONIZE CACHE does) before
+a variant of this utility that limits access to the media. Using the
+\fB\-\-stop\fR option is an example of something that limits access to the
+media. This 'noflush' field in the cdb was added after sbc3r13.
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-p\fR, \fB\-\-pc\fR=\fIPC\fR
+where \fIPC\fR is the 'power conditions' value. 0 to 15 (inclusive) are valid.
+Default value is 0. When '\-\-pc=0' then \fB\-\-eject\fR, \fB\-\-load\fR,
+\fB\-\-loej\fR, \fB\-\-start\fR and \fB\-\-stop\fR are active. Some common
+values are 1 for the "active" power condition (SBC); 2 for the idle power
+condition; 3 for the standby power condition; 5 for sleep power
+condition (MMC); 7 for LU_CONTROL (SBC), 0xa (decimal 10) for
+FORCE_IDLE_0 (SBC) and 0xb (decimal 11) for FORCE_STANDBY_0 (SBC). See recent
+SBC\-3, MMC\-5 and SAS drafts at www.t10.org for more information.
+.TP
+\fB\-r\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR in read\-only mode. Maybe required in Linux to stop a
+nuisance spin\-up if the \fIDEVICE\fR is an ATA disk. The nuisance spin\-up
+may occur at the end of this command negating the effect of the
+\fI\-\-stop\fR option.
+.TP
+\fB\-s\fR, \fB\-\-start\fR
+start (spin\-up) the \fIDEVICE\fR. This sets the START bit in the cdb. Using
+this option on an already started device is harmless. In the absence of
+other options, this option defaults (i.e. set the START cdb bit).
+.TP
+\fB\-S\fR, \fB\-\-stop\fR
+stop (spin\-down) the \fIDEVICE\fR. This clears the START bit in the cdb.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity. Can be used multiple times.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string then exit.
+.SH NOTES
+To avoid confusion, only one of \fI0\fR, \fI1\fR \fI\-\-eject\fR,
+\fI\-\-load\fR, \fI\-\-start\fR and \fI\-\-stop\fR should be given.
+.PP
+There is an associated "power condition" mode page (0x1a) in which timer
+values can be set for transitioning to either idle or standby state after
+a period of inactivity. The sdparm utility can be used to view the power
+condition mode page and if required change it. If a \fIDEVICE\fR is in either
+idle or standby power condition state then a REQUEST SENSE command (see
+the sg_requests utility) should yield a sense key of "no sense" and an
+additional sense code of "Low power condition on" on recent SCSI devices.
+.PP
+Ejection of removable media (e.g. 'sg_start \-\-eject /dev/hdd' where
+the \fIDEVICE\fR is an ATAPI cd/dvd drive) may be prevented by a prior
+SCSI PREVENT ALLOW MEDIUM REMOVAL command (see sg_prevent). In this
+case this utility should fail with an error generated by the device:
+illegal request / medium removal prevented. This can be overridden
+using sg_prevent or, for example, 'sdparm \-\-command=unlock /dev/hdd'.
+.PP
+The SCSI TEST UNIT READY command can be used to find out whether a
+\fIDEVICE\fR is ready to transfer data. If rotating media is stopped or
+still coming up to speed, then the TEST UNIT READY command will yield
+a "not ready" sense key and an more informative additional sense
+code. See the sg_turs utility.
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be a SCSI
+generic (sg) device. In the 2.6 series block devices (e.g. SCSI disks
+and DVD drives) can also be specified. For example "sg_start 0 /dev/sda"
+will work in the 2.6 series kernels.
+.PP
+In the Linux 2.6 series, especially with ATA disks, using this utility
+to stop (spin down) a disk may not be sufficient and other mechanisms
+will start the disk again some time later. The user might additionally
+mark the disk as "offline" with 'echo offline > /sys/block/sda/device/state'
+where sda is the block name of the disk. To restart the disk "offline"
+can be replaced with "running". Note that once the 'state' is set to
+offline, no SCSI commands can be sent to the device until it is set back
+to running. Also stopping a disk via a pass\-through
+interface (e.g. /dev/sg1 or /dev/bsg/1:0:0:0) may reduce unwanted side
+effects (such as restarting it again when this utility completes).
+.SH EXIT STATUS
+The exit status of sg_start is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using '\-\-old' (or '\-O) as the first option.
+.PP
+Note that the action of \fI\-\-loej\fR is slightly different in the older
+interface: when neither \fI\-\-start\fR nor \fI\-\-stop\fR (nor proxies
+for them) are given, \fI\-\-loej\fR performs an eject operation. In the
+same situation the newer interface will perform a load operation.
+.PP
+Earlier versions of sg_start had a '\-s' option to perform a SYNCHRONIZE
+CACHE command before the START STOP UNIT command was issued. According to
+recent SBC\-2 drafts this is done implicitly if required. Hence the '\-s'
+option has been dropped.
+.PP
+All options, other than '\-v' and '\-V', can be given with a single "\-".
+For example: "sg_start \-stop /dev/sda" and "sg_start \-\-stop /dev/sda"
+are equivalent. The single "\-" form is for backward compatibility.
+.TP
+\fB0\fR
+stop (spin\-down) \fIDEVICE\fR.
+.TP
+\fB1\fR
+start (spin\-up) \fIDEVICE\fR.
+.TP
+\fB\-\-eject\fR
+stop the medium and eject it from the drive.
+.TP
+\fB\-\-fl\fR=\fIFL\fR
+sets the format layer number for the disc to "jump" to (defined in MMC\-5).
+.TP
+\fB\-i\fR
+sets the IMM bit on the START STOP UNIT command so this utility will return
+immediately and not wait for the media to spin down. Same effect
+as '\-\-imm=1'. The default action (without this option or a '\-\-imm=1'
+option) is to wait until the media spins down before returning.
+.TP
+\fB\-\-imm\fR=\fI0|1\fR
+when the immediate bit is 1 then this utility returns immediately after the
+\fIDEVICE\fR has received the command. When this option is 0 (the default)
+then the utility returns once the command has completed its action (i.e. it
+waits until the device is started or stopped).
+.TP
+\fB\-\-load\fR
+load the medium in the drive and start it.
+.TP
+\fB\-\-loej\fR
+sets the LOEJ bit in the START STOP UNIT cdb. When a "start" operation is
+indicated, then a load and start is performed. When a "stop" operation is
+indicated, then a stop and eject is performed. When neither a "start"
+or "stop" operation is indicated does a stop and eject. [Note that the last
+action differs from the new interface in which the option of this name
+defaults to load and start.]
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-\-mod\fR=\fIPC_MOD\fR
+where \fIPC_MOD\fR is the 'power condition modifier' value. 0 to 15 (inclusive)
+are valid and 0 is the default. This field was added after sbc3r13.
+.TP
+\fB\-\-noflush\fR
+do not perform a flush to media (e.g. like SYNCHRONIZE CACHE does) before
+a variant of this utility that limits access to the media. Using the
+\fB\-\-stop\fR option is an example of something that limits access to the
+media. This field was added after sbc3r13.
+.TP
+\fB\-\-pc\fR=\fIPC\fR
+where \fIPC\fR is the 'power condition' value (in hex). 0 to f (inclusive)
+are valid. Default value is 0.
+.TP
+\fB\-r\fR
+see the \fI\-\-readonly\fR option above. May be useful for ATA disks.
+.TP
+\fB\-\-start\fR
+start (spin\-up) \fIDEVICE\fR.
+.TP
+\fB\-\-stop\fR
+stop (spin\-down) \fIDEVICE\fR. Same meaning as "0" argument.
+.TP
+\fB\-v\fR
+verbose: outputs SCSI command in hex to console before with executing
+it. '\-vv' and '\-vvv' are also accepted yielding greater verbosity.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.SH AUTHOR
+Written by K. Garloff and D. Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2002\-2012 Kurt Garloff, Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_prevent(sg3_utils), sg_requests(sg3_utils), sg_turs(sg3_utils)
+.B sdparm(sdparm)
diff --git a/sg3_utils/doc/sg_stpg.8 b/sg3_utils/doc/sg_stpg.8
new file mode 100644
index 0000000..187054a
--- /dev/null
+++ b/sg3_utils/doc/sg_stpg.8
@@ -0,0 +1,122 @@
+.TH SG_STPG "8" "January 2014" "sg3_utils\-1.38" SG3_UTILS
+.SH NAME
+sg_stpg \- send SCSI SET TARGET PORT GROUPS command
+.SH SYNOPSIS
+.B sg_stpg
+[\fI\-\-active\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-offline\fR]
+[\fI\-\-optimized\fR] [\fI\-\-raw\fR] [\fI\-\-standby\fR]
+[\fI\-\-state=S,S...\fR] [\fI\-\-tp=P,P...\fR] [\fI\-\-unavailable\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send a SCSI SET TARGET PORT GROUPS command to \fIDEVICE\fR. This utility
+has different modes depending on whether the \fI\-\-tp=\fR option is given.
+.PP
+If \fI\-\-tp=\fR is given then the SET TARGET PORT GROUPS command parameter
+block is built with a descriptor for each element in the list given to
+\fI\-\-tp=\fR. The corresponding asymmetric access state value is either
+taken from the \fI\-\-state=\fR list or, if that is not given, from one
+of the explicit state options (e.g. \fI\-\-unavailable\fR), used repeatedly
+if required.
+.PP
+If \fI\-\-tp=\fR is not given then a sequence of SCSI commands are sent to
+the \fIDEVICE\fR leading up to the SET TARGET PORT GROUPS command. First an
+INQUIRY is sent to fetch the device identification VPD page to find
+the (primary) target port group associated with \fIDEVICE\fR. Then a REPORT
+TARGET PORT GROUPS command is issued to find the current state and
+whether a transition to the requested state is supported. If so the
+SET TARGET PORT GROUPS command is sent.
+.PP
+Target port group access is described in SPC\-4 found at www.t10.org
+in sections 5.8 and 5.16 (in rev 36e dated 2012/8/24). The SET TARGET PORT
+GROUPS command is also described in section 6.45 of that document.
+.PP
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long
+option name.
+.TP
+\fB\-a\fR, \fB\-\-active\fR
+set active/non\-optimized state.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output response to the REPORT TARGET PORT GROUPS command in hex then exit.
+.TP
+\fB\-O\fR, \fB\-l\fR, \fB\-\-offline\fR
+set offline state. This is the appropriate state to set a target port
+to prior to removing the device.  Note that a relative target port identifier
+should be given with this state (rather than a target port group identifier
+that all other states take).
+.TP
+\fB\-o\fR, \fB\-\-optimized\fR
+set active/optimized state. If no other state options or \fI\-\-tp=\fR
+option are given then active/optimized is the default state.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output response to the REPORT TARGET PORT GROUPS command in binary to stdout
+then exit.
+.TP
+\fB\-s\fR, \fB\-\-standby\fR
+set standby state. Port group shall accept those commands listed
+for "unavailable" state plus LOG SELECT/SENSE, MODE SELECT/SENSE, RECEIVE
+DIAGNOSTIC RESULTS, SEND DIAGNOSTIC, PERSISTENT RESERVE IN/OUT commands.
+.TP
+\fB\-S\fR, \fB\-\-state\fR=\fIS,S...\fR
+specifies a comma separated list (one element of more) of states. Either
+a number or an abbreviation can be given. A number is assumed to be a
+decimal number unless it is prefixed by "0x" or has a trailing "h" in
+which case a hexadecimal value is assumed. Only the values 0, 1, 2, 3
+or 14 are accepted. The accepted abbreviations are "an", "ao", "o", "s"
+or "u"; which represent active/non\-optimized(1), active/optimized(0),
+offline(14), standby(2) or unavailable(3) respectively.
+.TP
+\fB\-t\fR, \fB\-\-tp\fR=\fIP,P...\fR
+specifies a comma separated list (one element of more). Each elements is
+either a target port group identifier (when the corresponding state is
+other than "offline") or a relative target port identifier (when the
+corresponding state is "offline"). Each element is assumed to be a
+decimal number unless it is prefixed by "0x" or has a trailing "h" in
+which case a hexadecimal value is assumed.
+.TP
+\fB\-u\fR, \fB\-\-unavailable\fR
+set unavailable state. Port group shall only accept INQUIRY, REPORT LUNS,
+REPORT/SET TARGET PORT GROUPS, REQUEST SENSE and READ/WRITE BUFFER commands.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+The SET TARGET PORT GROUPS command should be supported whenever the TPGS
+value in a standard INQUIRY response is 2 or 3. [View with sg_inq utility.]
+.PP
+Notice that the offline state is termed as a "secondary target port
+asymmetric access state" and takes a relative target port identifier (i.e.
+acts on a single target port). All the other states are termed as "primary
+target port asymmetric access states" and each takes a target port group
+identifier (i.e. acts on one or more target ports).
+.PP
+When \fI\-\-tp=\fR is given then the same number of elements should be
+given to the \fI\-\-state=\fR option. If more than one list element is
+given to \fI\-\-tp=\fR and an equal number of elements is _not_ given
+to the \fI\-\-state=\fR option, then if only one state is specified
+then it is repeated.
+.SH EXIT STATUS
+The exit status of sg_stpg is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2007\-2014 Hannes Reinecke, Christophe Varoqui and Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq, sg_rtpg (sg3_utils)
diff --git a/sg3_utils/doc/sg_sync.8 b/sg3_utils/doc/sg_sync.8
new file mode 100644
index 0000000..68035ab
--- /dev/null
+++ b/sg3_utils/doc/sg_sync.8
@@ -0,0 +1,97 @@
+.TH SG_SYNC "8" "July 2013" "sg3_utils\-1.37" SG3_UTILS
+.SH NAME
+sg_sync \- send SCSI SYNCHRONIZE CACHE command
+.SH SYNOPSIS
+.B sg_sync
+[\fI\-\-16\fR] [\fI\-\-count=COUNT\fR] [\fI\-\-group=GN\fR]
+[\fI\-\-help\fR] [\fI\-\-immed\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-sync\-nv\fR]
+[\fI\-\-timeout=SECS\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send SYNCHRONIZE CACHE(10) or SYNCHRONIZE CACHE(16) command to \fIDEVICE\fR.
+These commands are defined for SCSI block devices (see SBC\-3). If successful
+these commands make sure that any blocks whose latest versions are held in
+cache are written to (also termed as "synchronized with") the medium.
+.PP
+If the \fILBA\fR and \fICOUNT\fR arguments are both zero (their defaults)
+then all blocks in the cache are synchronized. If \fILBA\fR is greater than
+zero while \fICOUNT\fR is zero then blocks in the cache whose addresses are
+from and including \fILBA\fR to the highest lba on the device are
+synchronized. If both \fILBA\fR and \fICOUNT\fR are non zero then blocks in
+the cache whose addresses lie in the range \fILBA\fR to
+\fILBA\fR+\fICOUNT\fR\-1 inclusive are synchronized with the medium.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-S\fR, \fB\-\-16\fR
+performs a SYNCHRONIZE CACHE(16) command. Default is to perform a
+SYNCHRONIZE CACHE(10) command.
+.TP
+\fB\-c\fR, \fB\-\-count\fR=\fICOUNT\fR
+where \fICOUNT\fR is the number of blocks to synchronize from and including
+\fILBA\fR. Default value is 0. When 0 then all blocks in the cache from and
+including \fILBA\fR argument to the highest block address are synchronized.
+.TP
+\fB\-g\fR, \fB\-\-group\fR=\fIGN\fR
+where \fIGN\fR is the group number which can be between 0 and 31 inclusive.
+The default value is 0 . Group numbers are used to segregate data collected
+within the device. This is a new feature in SBC\-2 and can probably be
+ignored for the time being.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-i\fR, \fB\-\-immed\fR
+sets the IMMED bit in the SYNCHRONIZE CACHE command. This instructs the
+device, if the format of the command is acceptable, to return a GOOD
+status immediately rather than wait for the blocks in the cache to be
+synchronized with (i.e. written to) the medium.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the lowest logical block address in the cache to
+synchronize to the medium. Default value is 0 .
+.TP
+\fB\-s\fR, \fB\-\-sync\-nv\fR
+synchronize the (volatile) cache with the non\-volatile cache. Without this
+option (or if there is no non\-volatile cache in the device) the
+synchronization is with the medium. The SYNC_NV bit was made obsolete in
+SBC\-3 revision 35d.
+.TP
+\fB\-t\fR, \fB\-\-timeout\fR=\fISECS\fR
+where \fISECS\fR is the number of seconds the OS allows the SYNCHRONIZE
+CACHE(16) to complete before it tries to cancel the command. Cancelling
+commands (typically with the task management function "abort task") is
+best avoided. Note this option is only active together with the \fI\-\-16\fR
+option. The default timeout is 60 seconds for both SYNCHRONIZE CACHE(10)
+and SYNCHRONIZE CACHE(16). Note that timeout issues can be avoided with
+the \fI\-\-immed\fR option.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+With the SYNCHRONIZE CACHE(16) command \fILBA\fR can be up to 64 bits
+in size and \fICOUNT\fR up to 32 bits in size. With the SYNCHRONIZ
+CACHE(10) command \fILBA\fR can be up to 32 bits in size and \fICOUNT\fR
+up to 16 bits in size.
+.PP
+Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.SH EXIT STATUS
+The exit status of sg_sync is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_start(sg3_utils)
diff --git a/sg3_utils/doc/sg_test_rwbuf.8 b/sg3_utils/doc/sg_test_rwbuf.8
new file mode 100644
index 0000000..f30c351
--- /dev/null
+++ b/sg3_utils/doc/sg_test_rwbuf.8
@@ -0,0 +1,85 @@
+.TH SG_TEST_RWBUF "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sg_test_rwbuf \- test a SCSI host adapter by issuing dummy writes
+and reads
+.SH SYNOPSIS
+.B sg_test_rwbuf
+[\fI\-\-addrd=AR\fR] [\fI\-\-addwr=AW\fR] [\fI\-\-help\fR]
+[\fI\-\-quick\fR] \fI\-\-size=SZ\fR [\fI\-\-times=NUM\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.PP
+or an older deprecated format
+.B sg_test_rwbuf
+\fIDEVICE\fR \fISZ\fR [\fIAW\fR] [\fIAR\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+sg_test_rwbuf writes and reads back \fISZ\fR bytes to the internal buffer of
+\fIDEVICE\fR (e.g. /dev/sda or /dev/sg0). A pseudo random pattern is
+written to the data buffer on the device then read back. If the same pattern
+is found 'Success' is reported. If they do not match (checksums unequal) then
+this is reported and up to 24 bytes from the first point of mismatch are
+reported; the first line shows what was written and the second line shows
+what was received. For testing purposes, you can ask it to write \fIAW\fR or
+read \fIAR\fR additional bytes.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-r\fR, \fB\-\-addrd\fR=\fIAR\fR
+Read an additional \fIAR\fR bytes (than indicated by \fISZ\fR) from the data
+buffer. Checksum is performed over the first \fISZ\fR bytes.
+.TP
+\fB\-w\fR, \fB\-\-addwr\fR=\fIAW\fR
+Write an additional \fIAW\fR bytes (than indicated by \fISZ\fR) of zeros
+into the data buffer. Checksum is generated over the first \fISZ\fR bytes.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Print out a usage message the exit.
+.TP
+\fB\-q\fR, \fB\-\-quick\fR
+Perform a READ BUFFER descriptor command to find out the available data
+buffer length and offset, print them out then exit (without testing
+with write/read sequences).
+.TP
+\fB\-s\fR, \fB\-\-size\fR=\fISZ\fR
+where \fISZ\fR is the size of buffer in bytes to be written then read and
+checked. This number needs to be less than or equal to the size of the
+device's data buffer which can be seen from the \fI\-\-quick\fR option.
+Either this option or the \fI\-\-quick\fR option should be given.
+.TP
+\fB\-t\fR, \fB\-\-times\fR=\fINUM\fR
+where \fINUM\fR is the number of times to repeat the write/read to buffer
+test. Default value is 1 .
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase verbosity of output.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print version number (and data of last change) then exit.
+.SH NOTES
+The microcode in a SCSI device is _not_ modified by doing a WRITE BUFFER
+command with its mode set to "data" (0x2) as done by this utility. Therefore
+this utility is safe in that respect. [Mode values 0x4, 0x5, 0x6 and 0x7
+are the dangerous ones :\-)]
+.PP
+\fBWARNING\fR: If you access the device at the same time (e.g. because it's
+a hard disk with a mounted file system on it) the device's buffer may be
+used by the device itself for other data at the same time, and overwriting
+it may or may not cause data corruption! \fBHOWEVER\fR the SPC\-3 draft
+standard does state in its WRITE BUFFER command: "This command shall not
+alter any medium of the logical unit when data mode ... is specified". This
+implies that it _is_ safe to use this utility with devices that have mounted
+file systems on them.
+Following this theme further, a disk with active mounted file systems may cause
+the data read back to be different (due to caching activity) to what was written
+and hence a checksum error.
+.SH EXIT STATUS
+The exit status of sg_test_rwbuf is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by D. Gilbert and K. Garloff
+.SH COPYRIGHT
+Copyright \(co 2000\-2012 Douglas Gilbert, Kurt Garloff
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg3_utils/doc/sg_timestamp.8 b/sg3_utils/doc/sg_timestamp.8
new file mode 100644
index 0000000..c63588a
--- /dev/null
+++ b/sg3_utils/doc/sg_timestamp.8
@@ -0,0 +1,126 @@
+.TH SG_TIMESTAMP "8" "December 2015" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_timestamp \- report or set timestamp on SCSI device
+.SH SYNOPSIS
+.B sg_timestamp
+[\fI\-\-help\fR] [\fI\-\-milliseconds=MS\fR] [\fI\-\-origin\fR]
+[\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-seconds=SEC\fR] [\fI\-\-srep\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI REPORT TIMESTAMP or SET TIMESTAMP command to the \fIDEVICE\fR.
+These commands are found in the SPC\-5 draft standard revision
+7 (spc5r07.pdf).
+.PP
+If either the \fI\-\-milliseconds=MS\fR or \fI\-\-seconds=SEC\fR option is
+given (and both can't be given) then the SET TIMESTAMP command is sent;
+otherwise the REPORT TIMESTAMP command is sent.
+.PP
+The timestamp is sent and received from the \fIDEVICE\fR as the number of
+milliseconds since the epoch of 1970\-01\-01 00:00:00 UTC and is held in a 48
+bit unsigned integer. That same epoch is used by Unix machines, but they
+usually hold the number of seconds since that epoch. The Unix date command
+and especally its "+%s" format is useful in converting to and from
+timestamps and more humanly readable forms. See the EXAMPLES section below.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-m\fR, \fB\-\-milliseconds\fR=\fIMS\fR
+where \fIMS\fR is the number of milliseconds since 1970\-01\-01 00:00:00 UTC
+to set in the \fIDEVICE\fR with the SCSI SET TIMESTAMP command.
+.TP
+\fB\-o\fR, \fB\-\-origin\fR
+the REPORT TIMESTAMP returned parameter data contains a "timestamp origin"
+field. When this option is given, that field is decoded and printed out
+before the timestamp value is output. The default action (i.e. when the
+option is not given) is not to print out this decoded field.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the SCSI REPORT TIMESTAMP response (i.e. the data\-out buffer) in
+binary (to stdout). Note that the \fI\-\-origin\fR and \fI\-\-srep\fR
+options are ignored when this option is given. Also all error and
+verbose messages are output to stderr.
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only. The default action is to open the
+\fIDEVICE\fR read\-write.
+.TP
+\fB\-s\fR, \fB\-\-seconds\fR=\fISEC\fR
+where \fISEC\fR is the number of seconds since 1970\-01\-01 00:00:00 UTC
+to set in the \fIDEVICE\fR with the SCSI SET TIMESTAMP command. \fISEC\fR
+is multiplied by 1000 before being used in the SET TIMESTAMP command.
+.TP
+\fB\-S\fR, \fB\-\-srep\fR
+report the number of seconds since 1970\-01\-01 00:00:00 UTC. This is done
+by dividing by 1000 the value returned by the SCSI REPORT TIMESTAMP command.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH EXIT STATUS
+The exit status of sg_timestamp is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH NOTES
+The TCMOS and the SCSIP bits in the Control extension mode page (see sdparm)
+modify the actions of the timestamp held by a \fIDEVICE\fR.
+.PP
+Currently only the "Utilization usage rate based on date and time" parameters
+within the Utilization log page (sbc4r09.pdf) use timestamps. See the sg_logs
+utility. Vendor specific commands and pages may also be using timestamps.
+.SH EXAMPLES
+On Unix machines (e.g. Linux, FreeBSD and Solaris) the date command is useful
+when working with timestamps.
+.PP
+To fetch the timestamp from a \fIDEVICE\fR and display it in a humanly
+readable form the following could be used:
+.PP
+   # sg_timestamp \-S /dev/sdb
+.br
+1448993950
+.br
+   # date \-\-date="@1448993950"
+.br
+Tue Dec  1 13:19:10 EST 2015
+.br
+   # date \-R \-\-date="@1448993950"
+.br
+Tue, 01 Dec 2015 13:19:10 \-0500
+.PP
+The latter two date commands show different forms of the same date (i.e.
+1448993950 seconds since 1970\-01\-01 00:00:00 UTC). The sg_timestamp and
+date commands can be combined using backquotes:
+.PP
+   # date \-R \-\-date="@`sg_timestamp \-S /dev/sdc`"
+.br
+Wed, 16 Dec 2015 20:12:59 \-0500
+.PP
+To set the timestamp on the \fIDEVICE\fR to now (approximately) the
+following could be used:
+.PP
+   # date +%s
+.br
+1448993955
+.br
+   # sg_timestamp \-\-seconds=1448993955 /dev/sdb
+.PP
+Those two command lines could be combined into one by using backquotes:
+.PP
+   # sg_timestamp \-\-seconds=`date +%s` /dev/sdb
+.PP
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2015 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sdparm(sdparm), sg_logs(sg3_utils)
diff --git a/sg3_utils/doc/sg_turs.8 b/sg3_utils/doc/sg_turs.8
new file mode 100644
index 0000000..fc50c3b
--- /dev/null
+++ b/sg3_utils/doc/sg_turs.8
@@ -0,0 +1,116 @@
+.TH SG_TURS "8" "May 2014" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_turs \- send one or more SCSI TEST UNIT READY commands
+.SH SYNOPSIS
+.B sg_turs
+[\fI\-\-help\fR] [\fI\-\-number=NUM\fR] [\fI\-\-progress\fR] [\fI\-\-time\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.PP
+.B sg_turs
+[\fI\-n=NUM\fR] [\fI\-p\fR]  [\fI\-t\fR] [\fI\-v\fR] [\fI\-V\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility sends one or more SCSI TEST UNIT READY commands to the
+\fIDEVICE\fR. This may be useful for timing the per command overhead.
+Note that TEST UNIT READY has no associated data, just a 6 byte command
+and a returned SCSI status value.
+.PP
+This utility supports two command line syntaxes, the preferred one is
+shown first in the synopsis and explained in this section. A later section
+on the old command line syntax outlines the second group of options.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+print out the usage message then exit.
+.TP
+\fB\-n\fR, \fB\-\-number\fR=\fINUM\fR
+performs TEST UNIT READY \fINUM\fR times. If not given defaults to 1.
+These suffix multipliers are permitted: c C *1; w W *2; b B *512;
+k K KiB *1,024; KB *1,000; m M MiB *1,048,576; MB *1,000,000;
+g G GiB *1,073,741,824; and GB *1,000,000,000 . Also a suffix of the
+form "x<n>" multiplies the leading number by <n>. Alternatively a hex
+number may be given, prefixed by either '0x' or has a trailing 'h'.
+.TP
+\fB\-O\fR, \fB\-\-old\fR
+switch to older style options.
+.TP
+\fB\-p\fR, \fB\-\-progress\fR
+show progress indication (a percentage) if available. If \fI\-\-number=NUM\fR
+is given, \fINUM\fR is greater than 1 and an initial progress indication
+was detected then this utility waits 30 seconds before subsequent checks.
+Exits when \fINUM\fR is reached or there are no more progress indications.
+Ignores \fI\-\-time\fR option. See NOTES section below.
+.TP
+\fB\-t\fR, \fB\-\-time\fR
+after completing the requested number of TEST UNIT READY commands, outputs
+the total duration and the average number of commands executed per second.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase level or verbosity.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print version string then exit.
+.SH NOTES
+The progress indication is optionally part of the sense data. When a prior
+command that takes a long time to complete (and typically precludes other
+media access commands) is still underway, the progress indication can be used
+to determine how long before the device returns to its normal state.
+.PP
+The SCSI FORMAT command for disks used with the IMMED bit set is an example
+of an operation that takes a significant amount of time and precludes other
+media access during that time. The IMMED bit set instructs the FORMAT command
+to return control to the application client once the format has commenced (see
+SBC\-3). Several long duration SCSI commands associated with tape drives also
+use the progress indication (see SSC\-3).
+.PP
+The \fIDEVICE\fR is opened with a read\-only flag (e.g. in Unix with the
+O_RDONLY flag).
+.PP
+Early standards suggested that the SCSI TEST UNIT READY command be used for
+polling the progress indication. More recent standards seem to suggest
+the SCSI REQUEST SENSE command should be used instead.
+.SH EXIT STATUS
+The exit status of sg_turs is 0 when it is successful (e.g. in the case of
+a mechanical disk, it is spun up and ready to accept commands). For this
+utility the other exit status of interest is 2 corresponding to
+the "not ready" sense key. For other exit status values see the sg3_utils(8)
+man page.
+.SH OLDER COMMAND LINE OPTIONS
+The options in this section were the only ones available prior to sg3_utils
+version 1.23 . In sg3_utils version 1.23 and later these older options can
+be selected by either setting the SG3_UTILS_OLD_OPTS environment variable
+or using '\-\-old' (or '\-O) as the first option.
+.TP
+\fB\-n\fR=\fINUM\fR
+performs TEST UNIT READY \fINUM\fR times. If not given defaults to 1.
+Equivalent to \fI\-\-number=NUM\fR in the main description.
+.TP
+\fB\-N\fR
+switch to the newer style options.
+.TP
+\fB\-p\fR
+show progress indication (a percentage) if available.
+Equivalent to \fI\-\-progress\fR in the main description.
+.TP
+\fB\-t\fR
+after completing the requested number of TEST UNIT READY commands, outputs
+the total duration and the average number of commands executed per second.
+Equivalent to \fI\-\-time\fR in the main description.
+.TP
+\fB\-v\fR
+increase level of verbosity.
+.TP
+\fB\-V\fR
+print out version string then exit.
+.SH AUTHORS
+Written by D. Gilbert
+.SH COPYRIGHT
+Copyright \(co 2000\-2014 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq, sg_request (sg3_utils)
diff --git a/sg3_utils/doc/sg_unmap.8 b/sg3_utils/doc/sg_unmap.8
new file mode 100644
index 0000000..5e8e581
--- /dev/null
+++ b/sg3_utils/doc/sg_unmap.8
@@ -0,0 +1,132 @@
+.TH SG_UNMAP "8" "April 2014" "sg3_utils\-1.39" SG3_UTILS
+.SH NAME
+sg_unmap \- send SCSI UNMAP command (known as 'trim' in ATA specs)
+.SH SYNOPSIS
+.B sg_unmap
+[\fI\-\-anchor\fR] [\fI\-\-grpnum=GN\fR] [\fI\-\-help\fR] [\fI\-\-in=FILE\fR]
+[\fI\-\-lba=LBA,LBA...\fR] [\fI\-\-num=NUM,NUM...\fR] [\fI\-\-timeout=TO\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send a SCSI UNMAP command to \fIDEVICE\fR to unmap one or more logical
+blocks. This command was introduced in SBC\-3 revision 18 under the broad
+heading of "logical block provisioning". Logical blocks may also be unmapped
+by the SCSI WRITE SAME command; see the sg_write_same utility. The unmap
+capability is closely related to the ATA DATA SET MANAGEMENT command with
+the "Trim" bit set.
+.PP
+Logical blocks to be unmapped can be specified in one of two ways to this
+utility. One way is by supplying the start LBAs to the '\-\-lba=' option
+and the corresponding number(s) to unmap to the '\-\-num=' option. The
+other way is by putting start LBA and number to unmap pairs in a file whose
+name is given to the '\-\-in=' option. All values are assumed to be decimal
+unless prefixed by "0x" (or "0X") or have a trailing "h" (or "H") in which
+case they are interpreted as hexadecimal. Suffix multipliers are permitted
+on decimal values (e.g. '\-\-num=1m').
+.PP
+When the '\-\-lba=' option is given then the '\-\-num=' option must also be
+given. If one has a comma separated list as its argument then the other must
+have the same number of elements in its list. The arguments can use a single
+space as a separator but need to be in quotes or escaped to not be
+misinterpreted by the shell.
+.PP
+With the '\-\-in=FILE' option an even number of values must be found and are
+interpreted as pairs: the first value in each pair is a starting LBA and the
+second value is the number to unmap from that LBA. Everything from and
+including a "#" on a line is ignored as are blank lines. Values may be
+comma, space and tab separated or appear on separate lines. Each line should
+not exceed 1023 bytes in length.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-a\fR, \fB\-\-anchor\fR
+sets the 'Anchor' bit in the command (introduced in sbc3r22).
+.TP
+\fB\-g\fR, \fB\-\-grpnum\fR=\fIGN\fR
+sets the 'Group number' field to \fIGN\fR. Defaults to a value of zero.
+\fIGN\fR should be a value between 0 and 31.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-I\fR, \fB\-\-in\fR=\fIFILE\fR
+where \fIFILE\fR is a file name containing pairs of values. The first
+member of each pair is a starting LBA and the second member of the
+pair is the number of logical blocks to unmap from and including that
+starting LBA. Values are interpreted as decimal unless indicated
+otherwise. This option cannot be present with the '\-\-lba=' option.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA,LBA...\fR
+where \fILBA,LBA...\fR is a string of comma (or space) separated values
+that are interpreted as starting logical block addresses. Each number
+is interpreted as decimal unless prefixed by '0x' or '0X' (or it has a
+trailing 'h' or 'H'). An argument that contains any space separators needs
+to be quoted (or otherwise escaped). When this option is given then
+the '\-\-num=' option must also be given and they must contain the same
+number of elements in their arguments.
+.TP
+\fB\-n\fR, \fB\-\-num\fR=\fINUM,NUM...\fR
+where \fINUM,NUM...\fR is a string of comma (or space) separated values
+that are interpreted as a number of logical blocks to unmap. Each number
+is interpreted as decimal unless prefixed by '0x' or '0X' (or it has a
+trailing 'h' or 'H'). Note that 0 blocks is acceptable. An argument that
+contains any space separators needs to be quoted (or otherwise escaped).
+When this option is given then the '\-\-lba=' option must also be given
+and they must contain the same number of elements in their arguments.
+.TP
+\fB\-t\fR, \fB\-\-timeout\fR=\fITO\fR
+where \fITO\fR is a timeout value (in seconds) for the UNMAP command.
+The default value is 60 seconds.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+Some limits: an LBA can be up to 64 bits, a NUM up to 32 bits (imposed
+by structure of UNMAP SCSI command parameter data). The NUM is
+further constrained by the MAXIMUM UNMAP LBA COUNT field in the
+BLOCK LIMITS VPD page (0xb0). The maximum number of LBA,NUM pairs is
+limited to 128 by this utility and may be further constrained by the
+MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT field in the BLOCK LIMITS VPD
+page.
+.PP
+Since it is unclear how long the UNMAP command will take to execute
+a '\-\-timeout=" option has been provided. The default timeout
+period is 60 seconds. If all the logical blocks on a logical unit (e.g.
+a disk drive) are to be unmapped then the FORMAT UNIT SCSI command (see
+the sg_format utility) may be considered as an alternative.
+.PP
+Support for logical block provisioning is indicated by the LBPME bit in the
+response to the SCSI READ CAPACITY (16) command (see the sg_readcap utility).
+.PP
+In SBC\-3 revision 25 the LBPU and ANC_SUP bits where added to the
+Logical Block Provisioning VPD page. When LBPU is set it indicates that
+the device supports the UNMAP command. When the ANC_SUP bit is set it
+indicates the device supports anchored LBAs.
+.PP
+The SCSI UNMAP command does the "right thing" with respect to command
+queueing. However its ATA counterpart: the DATA SET MANAGEMENT command with
+the "Trim" bit set does not interact well with SATA queueing known as NCQ.
+To address this problem T13 have introduced a new command called SFQ DATA SET
+MANAGEMENT which also has a Trim bit.
+.SH EXAMPLES
+In the examples directory of the sg3_utils package there is a
+sg_unmap_example.txt file that shows the format that the '\-\-in='
+option accepts.
+.SH EXIT STATUS
+The exit status of sg_unmap is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2009\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_format,sg_get_lba_status,sg_readcap,sg_vpd,sg_write_same(sg3_utils)
diff --git a/sg3_utils/doc/sg_verify.8 b/sg3_utils/doc/sg_verify.8
new file mode 100644
index 0000000..aa9e2e5
--- /dev/null
+++ b/sg3_utils/doc/sg_verify.8
@@ -0,0 +1,206 @@
+.TH SG_VERIFY "8" "August 2013" "sg3_utils\-1.37" SG3_UTILS
+.SH NAME
+sg_verify \- invoke SCSI VERIFY command(s) on a block device
+.SH SYNOPSIS
+.B sg_verify
+[\fI\-\-16\fR] [\fI\-\-bpc=BPC\fR] [\fI\-\-count=COUNT\fR] [\fI\-\-dpo\fR]
+[\fI\-\-ebytchk=BCH\fR] [\fI\-\-group=GN\fR] [\fI\-\-help\fR]
+[\fI\-\-in=IF\fR] [\fI\-\-lba=LBA\fR] [\fI\-\-ndo=NDO\fR] [\fI\-\-quiet\fR]
+[\fI\-\-readonly\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+[\fI\-\-vrprotect=VRP\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends one or more SCSI VERIFY (10 or 16) commands to \fIDEVICE\fR. These SCSI
+commands are defined in the SBC\-2 (draft) standard at http://www.t10.org and
+SBC\-3 drafts.
+.PP
+When \fI\-\-ndo=NDO\fR is not given then the verify starts at the logical
+block address given by the \fI\-\-lba=LBA\fR option and continues for
+\fI\-\-count=COUNT\fR blocks. No more than \fI\-\-bpc=BPC\fR blocks are
+verified by each VERIFY command so if necessary multiple VERIFY commands are
+sent. Medium verification operations are performed by the \fIDEVICE\fR (e.g.
+assuming each block has additional EEC data, check this against the logical
+block contents). No news is good news (i.e. if there are no verify errors
+detected then no messages are sent to stderr and the Unix exit status is 0).
+.PP
+When \fI\-\-ndo=NDO\fR is given then the \fI\-\-bpc=BPC\fR option is
+ignored. A single VERIFY command is issued and a comparison starts at the
+logical block address given by the \fI\-\-lba=LBA\fR option and continues for
+\fI\-\-count=COUNT\fR blocks. The VERIFY command has an associated data\-out
+buffer that is \fINDO\fR bytes long. The contents of the data\-out buffer are
+obtained from the \fIFN\fR file (if \fI\-\-in=FN\fR is given) or from stdin.
+A comparison takes place between data\-out buffer and the logical blocks
+on the \fIDEVICE\fR. If the comparison is good then no messages are sent to
+stderr and the Unix exit status is 0. If the comparison fails then a sense
+buffer with a sense key of MISCOMPARE is returned; in this case the Unix exit
+status will be 14. Messages will be sent to stderr associated with MISCOMPARE
+sense buffer unless the \fI\-\-quiet\fR option is given.
+.PP
+In SBC\-3 revision 34 the BYTCHK field in all SCSI VERIFY commands was
+expanded from one to two bits. That required some changes in the options
+of this utility, see the section below on OPTION CHANGES.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long
+option name.
+.TP
+\fB\-S\fR, \fB\-\-16\fR
+uses a VERIFY(16) command (default VERIFY(10)). Even without this option,
+using an \fI\-\-lba=LBA\fR which is too large, will cause the utility
+to issue a VERIFY(16) command.
+.TP
+\fB\-b\fR, \fB\-\-bpc\fR=\fIBPC\fR
+this option is ignored if \fI\-\-ndo=NDO\fR is given. Otherwise \fIBPC\fR
+specifies the maximum number of blocks that will be verified by a single SCSI
+VERIFY command. The default value is 128 blocks which equates to 64 KB for a
+disk with 512 byte blocks. If \fIBPC\fR is less than \fICOUNT\fR then
+multiple SCSI VERIFY commands are sent to the \fIDEVICE\fR. For the default
+VERIFY(10) \fIBPC\fR cannot exceed 0xffff (65,535) while for VERIFY(16)
+\fIBPC\fR cannot exceed 0x7fffffff (2,147,483,647). For recent block
+devices (disks) this value may be constrained by the maximum transfer length
+field in the block limits VPD page.
+.TP
+\fB\-c\fR, \fB\-\-count\fR=\fICOUNT\fR
+where \fICOUNT\fR specifies the number of blocks to verify. The default value
+is 1 . If \fICOUNT\fR is greater than \fIBPC\fR (or its default value of 128)
+and \fINDO\fR is not given, 0 or less then multiple SCSI VERIFY commands are
+sent to the device. Otherwise \fICOUNT\fR becomes the contents of the
+verification length field of the SCSI VERIFY command issued. The
+.B sg_readcap
+utility can be used to find the maximum number of blocks that a block
+device (e.g. a disk) has.
+.TP
+\fB\-d\fR, \fB\-\-dpo\fR
+disable page out changes the cache retention priority of blocks read on
+the device's cache to the lowest priority. This means that blocks read by
+other commands are more likely to remain in the device's cache.
+.TP
+\fB\-E\fR, \fB\-\-ebytchk\fR=\fIBCH\fR
+sets the BYTCHK field to \fIBCH\fR overriding the value (1) set by the
+\fI\-\-ndo=NDO\fR option. Values of 1, 2 or 3 are accepted for \fIBCH\fR
+however sbc3r34 reserves the value 2. If this option is given then
+\fI\-\-ndo=NDO\fR must also be given. If \fIBCH\fR is 3 then \fICOUNT\fR
+must be 1 and \fINDO\fR should be the size of one logical block (plus the
+size of some or all of the protection infomation if \fIVRP\fR is greater
+than 0).
+.TP
+\fB\-g\fR, \fB\-\-group\fR=\fIGN\fR
+where \fIGN\fR becomes the contents of the group number field in the SCSI
+VERIFY(16) command. The default value for \fIGN\fR is 0. Note that this
+option is ignored for the SCSI VERIFY(10) command.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
+where \fIIF\fR is the name of a file from which \fINDO\fR bytes will be read
+and placed in the data\-out buffer. This is only done when the
+\fI\-\-ndo=NDO\fR option is given. If this option is not given then stdin
+is read. If \fIIF\fR is "\-" then stdin is also used.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR specifies the logical block address of the first block to
+start the verify operation. \fILBA\fR is assumed to be decimal unless prefixed
+by '0x' or a trailing 'h' (see below). The default value is 0 (i.e. the start
+of the device).
+.TP
+\fB\-n\fR, \fB\-\-ndo\fR=\fINDO\fR
+\fINDO\fR is the number of bytes to obtain from the \fIFN\fR file (if
+\fI\-\-in=FN\fR is given) or from stdin. Those bytes are placed in the
+data\-out buffer associated with the SCSI VERIFY command and \fINDO\fR
+is placed in the verification length field in the cdb. The default value
+for \fINDO\fR is 0 and the maximum value is dependant on the OS. If the
+\fI\-\-ebytchk=BCH\fR option is not given then the BYTCHK field in the cdb
+is set to 1.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+suppress the sense buffer messages associated with a MISCOMPARE sense key
+that would otherwise be sent to stderr. Still set the exit status to 14
+which is the sense key value indicating a MISCOMPARE .
+.TP
+\fB\-r\fR, \fB\-\-readonly\fR
+opens the DEVICE read\-only rather than read\-write which is the
+default. The Linux sg driver needs read\-write access for the SCSI
+VERIFY command but other access methods may require read\-only access.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-P\fR, \fB\-\-vrprotect\fR=\fIVRP\fR
+where \fIVRP\fR is the value in the vrprotect field in the VERIFY command
+cdb. It must be a value between 0 and 7 inclusive. The default value is
+zero.
+.SH BYTCHK
+BYTCHK is the name of a field (two bits wide) in the VERIFY(10) and
+VERIFY(16) commands. When set to 1 or 3 (sbc3r34 reserves the value 2) it
+indicates that associated with the SCSI VERIFY command, a data\-out buffer
+will be sent for the device (disk) to check. Using the \fI\-\-ndo=NDO\fR
+option sets the BYTCHK field to 1 and \fINDO\fR is the number of bytes
+placed in the data\-out buffer. Those bytes are obtained from stdin or
+\fIIF\fR (from the \fI\-\-in=FN\fR option). The \fI\-\-ebytchk=BCH\fR
+option may be used to override the BYTCHK field value of 1 with \fIBCH\fR.
+.PP
+The calculation of \fINDO\fR is left up to the user. Its value depends
+on the logical block size (which can be found with the sg_readcap utility),
+the \fICOUNT\fR and the \fIVRP\fR values. If the \fIVRP\fR is greater than
+0 then each logical block will contain an extra 8 bytes (at least) of
+protection information.
+.PP
+When the BYTCHK field is 0 then the verification process done by the
+device (disk) is vendor specific. It typically involves checking each
+block on the disk against its error correction codes (ECC) which is
+additional data also held on the disk.
+.PP
+Many Operating Systems put limits on the maximum size of the
+data\-out (and data\-in) buffer. For Linux at one time the limit was
+less than 1 MB but has been increased somewhat.
+.SH OPTION CHANGES
+Earlier versions of this utility had a \fI\-\-bytchk=NDO\fR option which
+set the BYTCHK bit and set the cdb verification length field to \fINDO\fR.
+The shorter form of that option was \fI\-B NDO\fR. For backward
+compatibility that option is still present but not documented. In its place
+is the \fI\-\-ndo=NDO\fR whose shorter form of \fI\-n NDO\fR.
+\fI\-\-ndo=NDO\fR sets the BYTCHK field to 1 unless that is overridden by
+the \fI\-\-ebytchk=BCH\fR.
+.SH NOTES
+Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.PP
+The amount of error correction and the number of retries attempted before a
+block is considered defective are controlled in part by the Verify Error
+Recovery mode page. A note in the SBC\-3 draft (rev 29 section 6.4.9 on the
+Verify Error Recovery mode page) advises that to minimize the number of
+checks (and hence have the most "sensitive" verify check) do the following
+in that mode page: set the EER bit to 0, the PER bit to 1, the DTE bit to 1,
+the DCR bit to 1, the verify retry count to 0 and the verify recovery time
+limit to 0. Mode pages can be modified with the
+.B sdparm
+utility.
+.PP
+The SCSI VERIFY(6) command defined in the SSC\-2 standard and later (i.e.
+for tape drive systems) is not supported by this utility.
+.SH EXIT STATUS
+The exit status of sg_verify is 0 when it is successful. When \fIBCH\fR is
+other than 0 then a comparison takes place and if it fails then the exit
+status is 14 which happens to be the sense key value of MISCOMPARE.
+Otherwise see the EXIT STATUS section in the sg3_utils(8) man page.
+.PP
+Earlier versions of this utility set an exit status of 98 when there was a
+MISCOMPARE.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2013 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sdparm(sdparm), sg_modes(sg3_utils), sg_readcap(sg3_utils),
+.B sg_inq(sg3_utils)
diff --git a/sg3_utils/doc/sg_vpd.8 b/sg3_utils/doc/sg_vpd.8
new file mode 100644
index 0000000..4e3e15d
--- /dev/null
+++ b/sg3_utils/doc/sg_vpd.8
@@ -0,0 +1,298 @@
+.TH SG_VPD "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_vpd \- fetch SCSI VPD page and/or decode its response
+.SH SYNOPSIS
+.B sg_vpd
+[\fI\-\-all\fR] [\fI\-\-enumerate\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-ident\fR] [\fI\-\-inhex=FN\fR] [\fI\-\-long\fR] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-page=PG\fR] [\fI\-\-quiet\fR] [\fI\-\-raw\fR] [\fI\-\-vendor=VP\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fIDEVICE\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility, when \fIDEVICE\fR is given, fetches a Vital Product Data (VPD)
+page and decodes it or outputs it in ASCII hexadecimal or binary. VPD pages
+are fetched with a SCSI INQUIRY command.
+.PP
+Alternatively the \fI\-\-inhex=FN\fR option can be given. In this case
+\fIFN\fR is assumed to be a file name ('\-' for stdin) containing ASCII
+hexadecimal representing a VPD page response. If the \fI\-\-raw\fR option
+is also given then binary input is assumed (rather than ASCII hexadecimal).
+.PP
+Probably the most important page is the Device Identification
+VPD page (page number: 0x83). Since SPC\-3, support for this page
+has been flagged as mandatory. This page can be fetched by
+using the \fI\-\-ident\fR option.
+.PP
+The reference document used for interpreting VPD pages (and the INQUIRY
+standard response) is T10/BSR INCITS 502 Revision 08 which is draft SPC\-5
+revision 08, 25 January 2016). It can be found at http://www.t10.org .
+.PP
+When no options are given, other than a \fIDEVICE\fR, then the "Supported
+VPD pages" (0x0) VPD page is fetched and decoded.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long
+option name.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+decode all VPD pages. When used with \fIDEVICE\fR the pages to be decoded
+are found in the "Supported VPD pages" VPD page. Pages that cannot be
+decoded are displayed in hex; add the \fI\-\-long\fR option to have ASCII
+displayed to the right of each line of hex.
+.br
+If this option is used with the \fI\-\-inhex=FN\fR option then the file
+\fIFN\fR is assumed to contain 1 or more VPD pages (in ASCII hex or binary).
+Decoding continues until the file is exhausted (or an error occurs). Sanity
+checks are aplied on each VPD page's length and the ascending order of VPD
+page numbers (required by SPC\-4) so bad data may be detected.
+.br
+If the \fI\-\-page=PG\fR option is also given then no VPD page whose page
+number is greater than \fIPG\fR (or its numeric equivalent) is decoded.
+.TP
+\fB\-e\fR, \fB\-\-enumerate\fR
+list the names of the known VPD pages, first the standard pages (i.e.
+those defined by T10), then the vendor specific pages. Each group is sorted
+in abbreviation order. The \fIDEVICE\fR and most other options are ignored
+and this utility exits after listing the VPD page names. May be used together
+with \fI\-\-page=PG\fR where \fIPG\fR is numeric. If so, it searches for the
+summary lines of all VPD pages whose number matches \fIPG\fR. May be used
+with \fI\-\-vendor=VP\fR to restrict output to known vendor specific pages
+for vendor/product \fIVP\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+outputs the usage message summarizing command line options then exits.
+Ignores \fIDEVICE\fR if given.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+outputs the requested VPD page in ASCII hexadecimal. Can be used multiple
+times, see section on the ATA information vpd page.
+.br
+To generate output suitable for placing in a file that can be used by a
+later invocation with the \fI\-\-inhex=FN\fR option, use the '\-HHHH'
+option (e.g. 'sg_vpd \-p di \-HHHH /dev/sg3 > dev_id.hex'). The
+reason '\-HHHH' is used is to flag that unadorned hexadecimal (without other
+text or address offsets) is sent to stdout.
+.TP
+\fB\-i\fR, \fB\-\-ident\fR
+decode the device identification (0x83) VPD page. When used once this option
+has the same effect as '\-\-page=di'. When use twice then the short form of
+the device identification VPD page's logical unit designator is decoded. In
+the latter case this option has the same effect as '\-\-quiet \-\-page=di_lu'.
+.TP
+\fB\-I\fR, \fB\-\-inhex\fR=\fIFN\fR
+\fIFN\fR is expected to be a file name (or '\-' for stdin) which contains
+ASCII hexadecimal or binary representing a VPD page (or a standard INQUIRY)
+response. This utility will then decode that response. It is preferable to
+also supply the \fI\-\-page=PG\fR option, if not this utility will attempt
+to guess which VPD page (or standard INQUIRY) the response is associated
+with. The hexadecimal should be arranged as 1 or 2 digits representing a
+byte each of which is whitespace or comma separated. Anything from and
+including a hash mark to the end of line is ignored. If the \fI\-\-raw\fR
+option is also given then \fIFN\fR is treated as binary.
+.TP
+\fB\-l\fR, \fB\-\-long\fR
+when decoding some VPD pages, give a little more output. For example the ATA
+Information VPD page only shows the signature (in hex) and the IDENTIFY
+(PACKET) DEVICE (in hex) when this option is given.
+.TP
+\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
+where \fILEN\fR is the (maximum) response length in bytes. It is placed in the
+cdb's "allocation length" field. If not given (or \fILEN\fR is zero) then
+252 is used (apart from the ATA Information VPD page which defaults to 572)
+and, if the response indicates this value is insufficient, another INQUIRY
+command is sent with a larger value in the cdb's "allocation length" field.
+If this option is given and \fILEN\fR is greater than 0 then only one INQUIRY
+command is sent. Since many simple devices implement the INQUIRY command
+badly (and do not support VPD pages) then the safest value to use for
+\fILEN\fR is 36. See the sg_inq man page for the more information.
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPG\fR
+where \fIPG\fR is the VPD page to be decoded or output. The \fIPG\fR argument
+can either be an abbreviation, a number or a pair or numbers/abbreviations
+separated by a comma. The VPD page abbreviations can be seen by using the
+\fI\-\-enumerate\fR option. If a number is given it is assumed to be decimal
+unless it has a hexadecimal indicator which is either a leading '0x' or a
+trailing 'h'. If one number is given then it is assumed to be a VPD page
+number. If two numbers (or abbreviations) are given then the second one is
+the same as \fIVP\fR (see the \fI\-\-vendor=VP\fR option). If this option
+is not given (nor '\-i', '\-l' nor '\-V') then the "Supported VPD pages" (0x0)
+VPD page is fetched and decoded. If \fIPG\fR is '\-1' or 'sinq' then the
+standard INQUIRY response is output. This option may also be used with the
+\fI\-\-enumerate\fR (see its description).
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+suppress the amount of decoding output.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+if not used with \fI\-\-inhex=FN\fR then output requested VPD page in binary.
+The output should be piped to a file or another utility when this option is
+used. The binary is sent to stdout, and errors are sent to stderr.
+.br
+if used with \fI\-\-inhex=FN\fR then the contents of \fIFN\fR is treated as
+binary.
+.TP
+\fB\-M\fR, \fB\-\-vendor\fR=\fIVP\fR
+where \fIVP\fR is a vendor (e.g. "sea" for Seagate) or vendor/product
+acronym (e.g. "hp3par" for the 3PAR array from HP). Many vendors have re-used
+the numbers at the beginning of the vendor specific VPD page range (e.g.
+page 0xc0) and this option is a way of selecting only those which are of
+interest. Using a \fIVP\fR of "xxx" will list the available acronyms.
+.br
+If this option is used with \fI\-\-page=PG\fR and \fIPG\fR is an acronym
+then this option is ignored. If \fIPG\fR is a number (e.g. 0xc0) then
+\fIVP\fR is used to choose the which vendor specific page (e.g. sharing
+page number 0xc0) to decode.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increases the level or verbosity.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print out version string then exit.
+.SH ATA INFORMATION VPD PAGE
+This VPD page (0x89 or 'ai') is defined by the SCSI to ATA Translation
+standard. It contains information about the SAT layer, the "signature" of
+the ATA device and the response to the ATA IDENTIFY (PACKET) DEVICE
+command. The latter part has 512 bytes of identity, capability and
+settings data which the hdparm utility is capable of decoding (so this
+utility doesn't decode it).
+.PP
+To unclutter the output for this page, the signature and the IDENTIFY (PACKET)
+DEVICE response are not output unless the \fI\-\-long\fR option (or
+\fI\-\-hex\fR or \fI\-\-raw\fR) are given. When the \fI\-\-long\fR option
+is given the IDENTIFY (PACKET) DEVICE response is output as 256 (16 bit)
+words as is the fashion for ATA devices. To see that response as a string of
+bytes use the '\-HH' option. To format the output suitable for hdparm to
+decode use either the '\-HHH' or '\-rr' option. For example if 'dev/sdb' is
+a SATA disk behind a SAT layer then this
+command: 'sg_vpd \-p ai \-HHH /dev/sdb | hdparm \-\-Istdin'
+should decode the ATA IDENTIFY (PACKET) DEVICE response.
+.SH NOTES
+Since some VPD pages (e.g. the Extended INQUIRY page) depend on settings
+in the standard INQUIRY response, then the standard INQUIRY response is
+output as a pseudo VPD page when \fIPG\fR is set to '\-1' or 'sinq'. Also
+the decoding of some fields (e.g. the Extended INQUIRY page's SPT field)
+is expanded when the '\-\-long' option is given using the standard INQUIRY
+response information (e.g. the PDT and the PROTECT fields).
+.PP
+In the 2.4 series of Linux kernels the \fIDEVICE\fR must be
+a SCSI generic (sg) device. In the 2.6 series block devices (e.g. disks
+and ATAPI DVDs) can also be specified. For example "sg_inq /dev/sda"
+will work in the 2.6 series kernels. From lk 2.6.6 other SCSI "char"
+device names may be used as well (e.g. "/dev/st0m").
+.PP
+The \fIDEVICE\fR is opened with a read\-only flag (e.g. in Unix with the
+O_RDONLY flag).
+.SH EXIT STATUS
+The exit status of sg_vpd is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH EXAMPLES
+The examples in this page use Linux device names. For suitable device
+names in other supported Operating Systems see the sg3_utils(8) man page.
+.PP
+To see the VPD pages that a device supports, use with no options. The
+command line invocation is shown first followed by a typical response:
+.PP
+   # sg_vpd /dev/sdb
+.br
+Supported VPD pages VPD page:
+.br
+  Supported VPD pages [sv]
+.br
+  Unit serial number [sn]
+.br
+  Device identification [di]
+.br
+  Extended inquiry data [ei]
+.br
+  Block limits (SBC) [bl]
+.PP
+To see the VPD page numbers associated with each supported page then
+add the '\-\-long' option to the above command line. To view a
+VPD page either its number or abbreviation can be given to
+the '\-\-page=' option. The page name abbreviations are shown within
+square brackets above. In the next example the Extended inquiry data
+VPD page is listed:
+.PP
+   # sg_vpd \-\-page=ei /dev/sdb
+.br
+extended INQUIRY data VPD page:
+.br
+  ACTIVATE_MICROCODE=0 SPT=0 GRD_CHK=0 APP_CHK=0 REF_CHK=0
+.br
+  UASK_SUP=0 GROUP_SUP=0 PRIOR_SUP=0 HEADSUP=1 ORDSUP=1 SIMPSUP=1
+.br
+  WU_SUP=0 CRD_SUP=0 NV_SUP=0 V_SUP=0
+.br
+  P_I_I_SUP=0 LUICLR=0 R_SUP=0 CBCS=0
+.br
+  Multi I_T nexus microcode download=0
+.br
+  Extended self\-test completion minutes=0
+.br
+  POA_SUP=0 HRA_SUP=0 VSA_SUP=0
+.PP
+To check if any protection types are supported by a disk use the '\-\-long'
+option on the Extended inquiry data VPD page:
+.PP
+   # sg_vpd \-\-page=ei \-\-long /dev/sdb
+.br
+   extended INQUIRY data VPD page:
+.br
+     ACTIVATE_MICROCODE=0
+.br
+     SPT=1 [protection types 1 and 2 supported]
+.br
+     GRD_CHK=1
+.br
+     ....
+.PP
+Search for the name (and acronym) of all pages that share VPD page number
+0xb0 .
+.PP
+   # sg_vpd \-\-page=0xb0 \-\-enumerate
+.br
+   Matching standard VPD pages:
+.br
+     bl         0xb0      Block limits (SBC)
+.br
+     oi         0xb0      OSD information
+.br
+     sad        0xb0      Sequential access device capabilities (SSC)
+.PP
+Some examples follow using the "\-\-all" option. Send an ASCII hexadecimal
+representation of all VPD pages to a file:
+.PP
+   # sg_vpd \-\-all \-HHHH /dev/sg3 > all_vpds.hex
+.PP
+At some later time that file could be decoded with:
+.PP
+   # sg_vpd \-\-all \-\-inhex=all_vpds.hex
+.PP
+To do the equivalent as the previous example but use a file containing
+binary:
+.PP
+   # sg_vpd \-\-all \-\-raw /dev/sg3 > all_vpds.bin
+.br
+   # sg_vpd \-\-all \-\-raw \-\-inhex=all_vpds.bin
+.PP
+Notice that "\-\-raw" must be given with the second (\-\-inhex) invocation
+to alert the utility that all_vpds.bin contains binary as it assumes ASCII
+hexadecimal by default. Next we only decode T10 specified VPD pages
+excluding vendor specific VPD pages that start at page number 0xc0:
+.PP
+   # sg_vpd \-\-all \-\-page=0xbf \-\-raw \-\-inhex=all_vpds.bin
+.PP
+Further examples can be found on the http://sg.danny.cz/sg/sg3_utils.html
+web page.
+.SH AUTHOR
+Written by Douglas Gilbert
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2006\-2016 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_inq(sg3_utils), sg3_utils(sg3_utils), sdparm(sdparm), hdparm(hdparm)
diff --git a/sg3_utils/doc/sg_wr_mode.8 b/sg3_utils/doc/sg_wr_mode.8
new file mode 100644
index 0000000..59f8346
--- /dev/null
+++ b/sg3_utils/doc/sg_wr_mode.8
@@ -0,0 +1,198 @@
+.TH SG_WR_MODE "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_wr_mode \- write (modify) SCSI mode page
+.SH SYNOPSIS
+.B sg_wr_mode
+[\fI\-\-contents=H,H...\fR] [\fI\-\-dbd\fR] [\fI\-\-force\fR]
+[\fI\-\-help\fR] [\fI\-\-len=\fR10|6\fR] [\fI\-\-mask=M,M...\fR]
+[\fI\-\-page=PG_H[,SPG_H]\fR] [\fI\-\-save\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Writes a modified mode page to \fIDEVICE\fR. Uses the SCSI MODE SENSE (6
+or 10 byte variant) command to fetch the existing mode data which includes
+a mode page (or subpage). It then combines that with the contents,
+potentially masked, and writes the modified mode page with the SCSI MODE
+SELECT (6 or 10 byte variant) command. This utility does not modify
+the block descriptor(s); if any block descriptors are fetched by the MODE
+SENSE command then the same block descriptors are written back with the
+following MODE SELECT command.
+.PP
+If a contents argument is not given then the various components (i.e.
+header, block descriptor(s) and mode page) of the "current" values of
+the existing mode page are printed out. In this case the mode page is
+not altered on the device.
+.PP
+If the contents are specified, and a mask is not specified, then the contents
+must match the existing mode page in various aspects unless the
+\fI\-\-force\fR option is given. These include length, mode page code and
+subpage code if applicable. If all is well then the contents string is
+written to \fIDEVICE\fR as the new mode page.
+.PP
+If both contents and mask strings are specified then only bit positions
+in the contents corresponding to set bits in the mask are taken while the
+existing mode page supplies bit positions corresponding to clear bits.
+When a mask is given then the mask and/or the contents may be shorter
+than the existing mode page. If the mask is shorter than the contents then
+the remaining bytes are taken from the contents. If the contents are shorter
+than the existing mode page then the remaining bytes are taken from the
+existing mod page.
+.PP
+The force option allows the contents string to be written as the new
+mode page without any prior checks on the existing mode page. This should
+only be required for vendor specific mode pages. The existing mode data
+is ignored apart from the block descriptors which can be suppressed with
+the \fI\-\-dbd\fR option if need be.
+.PP
+Changing individual fields in a mode page is probably more easily done
+with the sdparm utility. Fields can be identified by acronym or by a
+numerical descriptor.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-c\fR, \fB\-\-contents\fR=\fIH,H...\fR
+where \fIH,H...\fR is a string of comma separated hex numbers each of
+which should resolve to a byte value (i.e. 0 to ff inclusive). A (single)
+space separated string of hex numbers is also allowed but the list needs to
+be in quotes. This is the new contents of the mode page to be written to
+\fIDEVICE\fR, potentially filtered by the mask string.
+.TP
+\fB\-c\fR, \fB\-\-contents\fR=-
+reads contents string from stdin. The hex numbers in the string may be comma,
+space, tab or linefeed (newline) separated. If a line contains "#" then the
+remaining characters on that line are ignored. Otherwise each non separator
+character should resolve to a byte value (i.e. 0 to ff inclusive). This
+forms the new contents of the mode page to be written to \fIDEVICE\fR,
+potentially filtered by the mask string.
+.TP
+\fB\-d\fR, \fB\-\-dbd\fR
+disable block descriptors (DBD flag in cdb). Some device types include
+block descriptors in the mode data returned by a MODE SENSE command. If
+so the same block descriptors are written by the MODE SELECT command.
+This option instructs the MODE SENSE command not to return any block
+descriptors. This would be a sensible default for this utility apart
+from the fact that not all SCSI devices support the DBD bit in the cdb.
+.TP
+\fB\-f\fR, \fB\-\-force\fR
+force the contents string to be taken as the new mode page, or at least
+doesn't do checks on the existing mode page. Note that \fIDEVICE\fR may
+still reject the new contents for the mode page. Cannot be given with
+the \fI\-\-mask=M,M...\fR option.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-l\fR, \fB\-\-len\fR=10 | 6
+length of the SCSI commands (cdb) sent to \fIDEVICE\fR. The default is 10
+so 10 byte MODE SENSE and MODE SELECT commands are issued. Some old devices
+don't support the 10 byte variants hence this option.
+.TP
+\fB\-m\fR, \fB\-\-mask\fR=\fIM,M...\fR
+where \fIM,M...\fR is a string of comma separated hex numbers each of which
+should resolve to a byte value (i.e. 0 to ff inclusive). A (single) space
+separated string of hex numbers is also allowed but the list needs to be in
+quotes. The mask chooses (bit by bit) whether the new mode page comes from
+the contents (mask bit set) or from the existing mode page (mask bit clear).
+If the mask string is shorter than the contents string then the remaining
+bytes are taken from the contents string. If the contents string is shorter
+than the existing mode page then the remaining bytes are taken from the
+existing mode page (i.e. they are left unaltered).
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPG_H\fR
+where \fIPG_H\fR is the page code value to fetch and modify. The page code
+is in hex and should be between 0 and 3e inclusive. Notice that page code
+3f to fetch all mode pages is disallowed.
+.TP
+\fB\-p\fR, \fB\-\-page\fR=\fIPG_H,SPG_H\fR
+where \fIPG_H\fR is the page code value and \fISPG_H\fR is the subpage code
+value to fetch and modify. Both values are in hex. The subpage code should
+be between 0 and fe inclusive. Notice that subpage code ff to fetch all
+mode subpages (for a given mode page or all mode pages in the case of 3f,ff)
+is disallowed.
+.TP
+\fB\-s\fR, \fB\-\-save\fR
+changes the "saved" mode page when MODE SELECT is successful. By
+default (i.e. when \fI\-\-save\fR is not used) only the "current" mode page
+values are changed when MODE SELECT is successful. In this case the new mode
+page will stay in effect until the device is reset (e.g.  power cycled).
+When it restarts the "saved" values for the mode page will be re\-instated.
+So to make changes permanent use the \fI\-\-save\fR option.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+This utility does not check whether the contents string is trying to
+modify parts of the mode page which are changeable. The device should
+do that and if some part is not changeable then it should
+report: "Invalid field in parameter list".
+.PP
+Some mode pages are not saveable. If so an attempt to use the \fI\-\-save\fR
+option should cause an error to be reported from the device: "Illegal field
+in cdb".
+.PP
+The device is required to do various checks before it accepts a new
+mode page. If these checks fail then the mode page is not altered and
+either a "parameter list length error" or an "invalid field in
+parameter list" error is returned by the device in the sense data.
+.PP
+The recommended way to modify a mode page is to read it with a
+MODE SENSE, modify some part of it then write it back to the
+device with a MODE SELECT command. For example, reading an existing mode
+page can be accomplished with 'sg_modes \-p=1a \-r /dev/sdb > mp_1a.txt' (the
+power condition mode page). The mp_1a.txt file can be edited and then used
+as the contents string to this
+utility (e.g. 'sg_wr_mode \-p 1a \-s \-c \- /dev/sdb < mp_1a.txt').
+.PP
+Two fields differ between what is read from the device with MODE SENSE and
+what is written to the device with MODE SELECT:
+the mode data length is reserved (i.e. zero(es)) in a MODE
+SELECT command while the PS bit ((sub)page byte 0 bit 7) in each
+mode (sub)page is reserved (zero) in a MODE SELECT command.
+The PS bit given in the contents string is zeroed unless
+the \fI\-\-force\fR option is selected.
+.SH EXAMPLES
+This utility can be used together with the sg_modes utility. To re\-instate
+the default mode page values (i.e. the mode page values chosen by the
+manufacturer of the device) as both the current and saved mode page
+values the following sequence could be used:
+.PP
+  $ sg_modes \-\-control=2 \-\-page=1a \-r /dev/sda > t
+.br
+  $ sg_wr_mode \-\-page=1a \-\-contents=\- \-\-save /dev/sda < t
+.PP
+Next is an example of using a mask to modify the "idle condition counter"
+of the "power condition" mode page (0x1a) from 0x28 to 0x37. Note that the
+change is not saved so the "idle condition counter" will revert to 0x28
+after the next power cycle. The output from sg_modes is abridged.
+.PP
+ $ sg_modes \-\-page=1a /dev/hdc
+.br
+ >> Power condition (mmc), page_control: current
+.br
+ 00     1a 0a 00 03 00 00 00 28  00 00 01 2c
+.PP
+ $ sg_wr_mode \-p 1a \-c 0,0,0,0,0,0,0,37 \-m 0,0,0,0,0,0,0,ff /dev/hdc
+.PP
+ $ sg_modes \-p 1a /dev/hdc
+.br
+ >> Power condition (mmc), page_control: current
+.br
+ 00     1a 0a 00 03 00 00 00 37  00 00 01 2c
+.SH EXIT STATUS
+The exit status of sg_wr_mode is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2016 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sdparm(sdparm), sg_modes(sg3_utils), sginfo(sg3_utils)
diff --git a/sg3_utils/doc/sg_write_buffer.8 b/sg3_utils/doc/sg_write_buffer.8
new file mode 100644
index 0000000..67030c3
--- /dev/null
+++ b/sg3_utils/doc/sg_write_buffer.8
@@ -0,0 +1,215 @@
+.TH SG_WRITE_BUFFER "8" "February 2015" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+sg_write_buffer \- send SCSI WRITE BUFFER commands
+.SH SYNOPSIS
+.B sg_write_buffer
+[\fI\-\-bpw=CS\fR] [\fI\-\-help\fR] [\fI\-\-id=ID\fR] [\fI\-\-in=FILE\fR]
+[\fI\-\-length=LEN\fR] [\fI\-\-mode=MO\fR] [\fI\-\-offset=OFF\fR]
+[\fI\-\-raw\fR] [\fI\-\-skip=SKIP\fR] [\fI\-\-specific=MS\fR]
+[\fI\-\-timeout=TO\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends one or more SCSI WRITE BUFFER commands to \fIDEVICE\fR, along with data
+provided by the user. In some cases no data is required, or data can be read
+from the file given in the \fI\-\-in=FILE\fR option, or data is read from
+stdin when either \fI\-\-raw\fR or \fI\-\-in=\-\fR is given.
+.PP
+Some WRITE BUFFER command variants do not have associated data to send to the
+device. For example "activate_mc" activates deferred microcode that was sent
+via prior WRITE BUFFER commands. There is a different method used to download
+microcode to SES devices, see the sg_ses_microcode utility.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-b\fR, \fB\-\-bpw\fR=\fICS\fR
+where \fICS\fR is the chunk size in bytes. This will be the maximum number
+of bytes sent per WRITE BUFFER command. So if \fICS\fR is less than the
+effective length then multiple WRITE BUFFER commands are sent, each taking
+the next chunk from the read data and increasing the buffer offset field
+in the WRITE BUFFER command by the appropriate amount. The default is
+a chunk size of 0 which is interpreted as a very large number hence only
+one WRITE BUFFER command will be sent. This option should only be used with
+modes that "download microcode, with offsets ..."; namely either mode 0x6,
+0x7, 0xd or 0xe.
+.br
+The number in \fICS\fR can optionally be followed by ",act" or ",activate".
+In this case after WRITE BUFFER commands have been sent until the
+effective length is exhausted another WRITE BUFFER command with its mode
+set to "Activate deferred microcode mode" [mode 0xf] is sent.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit. If used multiple times also prints
+the mode names and their acronyms.
+.TP
+\fB\-i\fR, \fB\-\-id\fR=\fIID\fR
+this option sets the buffer id field in the cdb. \fIID\fR is a value between
+0 (default) and 255 inclusive.
+.TP
+\fB\-I\fR, \fB\-\-in\fR=\fIFILE\fR
+read data from file \fIFILE\fR that will be sent with the WRITE BUFFER
+command.  If \fIFILE\fR is '\-' then stdin is read until an EOF is
+detected (this is the same action as \fI\-\-raw\fR). Data is read from
+the beginning of \fIFILE\fR except in the case when it is a regular file
+and the \fI\-\-skip=SKIP\fR option is given.
+.TP
+\fB\-l\fR, \fB\-\-length\fR=\fILEN\fR
+where \fILEN\fR is the length, in bytes, of data to be written to the device.
+If not given (and the length cannot be deduced from \fI\-\-in=FILE\fR or
+\fI\-\-raw\fR) then defaults to zero. If the option is given and the length
+deduced from \fI\-\-in=FILE\fR or \fI\-\-raw\fR is less (or no data is
+provided), then bytes of 0xff are used as fill bytes.
+.TP
+\fB\-m\fR, \fB\-\-mode\fR=\fIMO\fR
+this option sets the MODE field in the cdb. \fIMO\fR is a value between
+0 (default) and 31 inclusive. Alternatively an abbreviation can be given.
+See the MODES section below. To list the available mode abbreviations at
+run time give an invalid one (e.g. '\-\-mode=xxx') or use the '\-hh' option.
+.TP
+\fB\-o\fR, \fB\-\-offset\fR=\fIOFF\fR
+this option sets the BUFFER OFFSET field in the cdb. \fIOFF\fR is a value
+between 0 (default) and 2**24\-1 . It is a byte offset.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+read data from stdin until an EOF is detected. This data is sent with
+the WRITE BUFFER command to \fIDEVICE\fR. The action of this option is the
+same as using '\-\-in=\-'.
+.TP
+\fB\-s\fR, \fB\-\-skip\fR=\fISKIP\fR
+this option is only active when \fI\-\-in=FILE\fR is given and \fIFILE\fR is
+a regular file, rather than stdin. Data is read starting at byte offset
+\fISKIP\fR to the end of file (or the amount given by \fI\-\-length=LEN\fR).
+If not given the byte offset defaults to 0 (i.e. the start of the file).
+.TP
+\fB\-S\fR, \fB\-\-specific\fR=\fIMS\fR
+\fIMS\fR is the MODE SPECIFIC field in the cdb. This is a 3\-bit field
+so the values 0 to 7 are accepted. This field was introduced in SPC\-4
+revision 32 and can be used to specify additional events that activate
+deferred microcode (when \fIMO\fR is 0xD).
+.TP
+\fB\-t\fR, \fB\-\-timeout\fR=\fITO\fR
+\fITO\fR is the command timeout (in seconds) for each WRITE BUFFER command
+issued by this utility. Its default value is 300 seconds (5 minutes) and
+should only be altered if this is not sufficient.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH MODES
+Following is a list of WRITE BUFFER command settings for the MODE field.
+First is an acronym accepted by the \fIMO\fR argument of this utility.
+Following the acronym in square brackets are the corresponding decimal and
+hex values that may also be given for \fIMO\fR. The following are listed
+in numerical order.
+.TP
+hd  [0, 0x0]
+Combined header and data (obsolete in SPC\-4).
+.TP
+vendor  [1, 0x1]
+Vendor specific.
+.TP
+data  [2, 0x2]
+Data (was called "Write Data" in SPC\-3).
+.TP
+dmc  [4, 0x4]
+Download microcode and activate (was called "Download microcode" in SPC\-3).
+.TP
+dmc_save  [5, 0x5]
+Download microcode, save, and activate (was called "Download microcode and
+save" in SPC\-3).
+.TP
+dmc_offs  [6, 0x6]
+Download microcode with offsets and activate (was called "Download microcode
+with offsets" in SPC\-3).
+.TP
+dmc_offs_save  [7, 0x7]
+Download microcode with offsets, save, and activate (was called "Download
+microcode with offsets and save" in SPC\-3).
+.TP
+echo  [10, 0xa]
+Write data to echo buffer (was called "Echo buffer" in SPC\-3).
+.TP
+dmc_offs_ev_defer  [13, 0xd]
+Download microcode with offsets, select activation events, save, and defer
+activate (introduced in SPC\-4).
+.TP
+dmc_offs_defer  [14, 0xe]
+Download microcode with offsets, save, and defer activate (introduced in
+SPC\-4).
+.TP
+activate_mc  [15, 0xf]
+Activate deferred microcode (introduced in SPC\-4).
+.TP
+en_ex  [26, 0x1A]
+Enable expander communications protocol and Echo buffer (obsolete in SPC\-4).
+.TP
+dis_ex  [27, 0x1B]
+Disable expander communications protocol (obsolete in SPC\-4).
+.TP
+deh  [28, 0x1C]
+Download application client error history (was called "Download application
+log" in SPC\-3).
+.SH NOTES
+If no \fI\-\-length=LEN\fR is given this utility reads up to 8 MiB of data
+from the given file \fIFILE\fR (or stdin). If a larger amount of data is
+required then the \fI\-\-length=LEN\fR option should be given.
+.PP
+The user should be aware that most operating systems have limits on the
+amount of data that can be sent with one SCSI command. In Linux this
+depends on the pass through mechanism used (e.g. block SG_IO or the sg
+driver) and various setting in sysfs in the Linux lk 2.6/3
+series (e.g. /sys/block/sda/queue/max_sectors_kb). Devices (i.e. logical
+units) also typically have limits on the maximum amount of data they can
+handle in one command. These two limitations suggest that modes
+containing the word "offset" together with the \fI\-\-bpw=CS\fR option
+are required as firmware files get larger and larger. And \fICS\fR
+can be quite small, for example 4096 bytes, resulting in many WRITE
+BUFFER commands being sent.
+.PP
+Attempting to download a microcode/firmware file that is too large may
+cause an error to occur in the pass-through layer (i.e. before the
+SCSI command is issued). In Linux such error reports can be obscure as
+in "pass through os error invalid argument". FreeBSD reports such
+errors well to the machine's console but returns a cryptic error message
+to this utility.
+.PP
+Downloading incorrect microcode into a device has the ability to render
+that device inoperable. One would hope that the device vendor verifies
+the data before activating it. If the SCSI WRITE BUFFER command is given
+values in its cdb (e.g. \fILEN\fR) that are inappropriate (e.g. too large)
+then the device should respond with a sense key of ILLEGAL REQUEST and
+an additional sense code of INVALID FIELD in CDB. If a WRITE BUFFER
+command (or a sequence of them) fails due to device vendor verification
+checks then it should respond with a sense key of ILLEGAL REQUEST and
+an additional sense code of COMMAND SEQUENCE ERROR.
+.PP
+All numbers given with options are assumed to be decimal.
+Alternatively numerical values can be given in hexadecimal preceded by
+either "0x" or "0X" (or has a trailing "h" or "H").
+.SH EXAMPLES
+The following sends new firmware to an enclosure. Sending a 1.5 MB
+file in one WRITE BUFFER command caused the enclosure to lock up
+temporarily and did not update the firmware. Breaking the firmware file
+into 4 KB chunks (an educated guess) was more successful:
+.PP
+  sg_write_buffer \-b 4k \-m dmc_offs_save \-I firmware.bin /dev/sg4
+.PP
+The firmware update occurred in the following enclosure power cycle. With
+a modern enclosure the Extended Inquiry VPD page gives indications in which
+situations a firmware upgrade will take place.
+.SH EXIT STATUS
+The exit status of sg_write_buffer is 0 when it is successful. Otherwise
+see the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Luben Tuikov and Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2006\-2015 Luben Tuikov and Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_read_buffer, sg_ses_microcode(sg3_utils)
diff --git a/sg3_utils/doc/sg_write_long.8 b/sg3_utils/doc/sg_write_long.8
new file mode 100644
index 0000000..2d5560d
--- /dev/null
+++ b/sg3_utils/doc/sg_write_long.8
@@ -0,0 +1,176 @@
+.TH SG_WRITE_LONG "8" "January 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_write_long \- send SCSI WRITE LONG command
+.SH SYNOPSIS
+.B sg_write_long
+[\fI\-\-16\fR] [\fI\-\-cor_dis\fR] [\fI\-\-help\fR] [\fI\-\-in=IF\fR]
+[\fI\-\-lba=LBA\fR] [\fI\-\-pblock\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] [\fI\-\-wr_uncor\fR] [\fI\-\-xfer_len=BTL\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Send the SCSI WRITE LONG (10 or 16 byte) command to \fIDEVICE\fR. The buffer
+to be written to the \fIDEVICE\fR is filled with
+.B 0xff
+bytes or read from the \fIIF\fR file. This buffer includes the logical
+data (e.g. 512 bytes) and the ECC bytes.
+.PP
+This utility can be used to generate a MEDIUM ERROR at a specific logical
+block address. This can be useful for testing error handling. Prior to
+such a test, the
+.B sg_dd
+utility could be used to copy the original contents of the logical
+block address to some safe location. After the test the
+.B sg_dd
+utility could be used to write back the original contents of the
+logical block address. An alternate strategy would be to read the "long"
+contents of the logical block address with
+.B sg_read_long
+utility prior to testing and restore it with this utility after testing.
+.PP
+.B Take care:
+If recoverable errors are being injected (e.g. only one or a few bits
+changed so that the ECC is able to correct the data) then care should
+be taken with the settings in the "read write error recovery" mode page.
+Specifically if the ARRE (for reads) and/or AWRE (for writes) are set
+then recovered errors will cause the lba to be reassigned (and the old
+location to be added to the grown defect list (PLIST)). This is not easily
+reversed and uses (one of the finite number of) the spare sectors set
+aside for this purpose. If in doubt it is probably safest to clear the
+ARRE and AWRE bits. These bits can be checked and modified with the
+sdparm utility.  For example: "sdparm \-c AWRE,ARRE /dev/sda" will clear
+the bits until the disk is power cycled.
+.PP
+In SBC\-4 revision 7 all uses of SCSI WRITE LONG (10 and 16 byte) commands
+were made obsolete apart from the case in which the WR_UNCOR bit is set.
+The SCSI READ LONG (10 and 16 byte) commands were made obsolete in the
+same revision.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-S\fR, \fB\-\-16\fR
+send a SCSI WRITE LONG (16) command to \fIDEVICE\fR. The default action (in
+the absence of this option) is to send a SCSI WRITE LONG (10) command.
+.TP
+\fB\-c\fR, \fB\-\-cor_dis\fR
+sets the correction disabled (i.e 'COR_DIS') bit. This inhibits various
+other mechanisms such as automatic block reallocation, error recovery
+and various informational exception conditions being triggered.
+This bit is relatively new in SBC\-3 .
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
+read data (binary) from file named \fIIF\fR and use it for the SCSI WRITE
+LONG command. If \fIIF\fR is "\-" then stdin is read. If this option is
+not given then 0xff bytes are used as fill.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the logical block address of the sector to overwrite.
+Defaults to lba 0 which is a dangerous block to overwrite on a disk that is
+in use. Assumed to be in decimal unless prefixed with '0x' or has a
+trailing 'h'. If \fILBA\fR is larger than can fit in 32 bits then the
+\fI\-\-16\fR option should be used.
+.TP
+\fB\-p\fR, \fB\-\-pblock\fR
+sets the physical block (i.e 'PBLOCK') bit. This instructs \fIDEVICE\fR
+to use the given data (unless \fI\-\-wr_uncor\fR is also given) to write
+to the physical block specified by \fILBA\fR. The default action
+is to write to the logical block corresponding to the given lba.
+This bit is relatively new in SBC\-3 .
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the degree of verbosity (debug messages).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+output version string then exit.
+.TP
+\fB\-w\fR, \fB\-\-wr_uncor\fR
+sets the "write uncorrected" (i.e 'WR_UNCOR') bit. This instructs the
+\fIDEVICE\fR to flag the given lba (or the physical block that contains it
+if \fI\-\-pblock\fR is also given) as having an unrecoverable error
+associated with it. Note: no data is transferred to \fIDEVICE\fR,
+other than the command (i.e. the cdb). In the absence of this option, the
+default action is to use the provided data or 0xff
+bytes (\fI\-\-xfer_len=BTL\fR in length) and write it to \fIDEVICE\fR.
+This bit is relatively new in SBC\-3 .
+.TP
+\fB\-x\fR, \fB\-\-xfer_len\fR=\fIBTL\fR
+where \fIBTL\fR is the byte transfer length (default to 520). If the
+given value (or the default) does not match the "long" block size of the
+device, nothing is written to \fIDEVICE\fR and the appropriate xfer_len value
+may be deduced from the error response which is printed (to stderr).
+.SH NOTES
+Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.PP
+The 10 byte SCSI WRITE LONG command limits the logical block address
+to a 32 bit quantity. For larger LBAs use the \fI\-\-16\fR option for the
+SCSI WRITE LONG (16) command.
+.SH EXAMPLES
+This section outlines setting up a block with corrupted data, checking the
+error condition, then restoring useful contents to that sector.
+.PP
+First, if the data in a sector is important, save it with the sg_read_long
+utility:
+.PP
+  sg_read_long \-\-lba=0x1234 \-\-out=0x1234_1.img \-x \fIBTL\fR /dev/sda
+.PP
+This utility may need to be executed several time in order to determine
+what the correct value for \fIBTL\fR is.
+Next use this utility to "corrupt" that sector. That might be done with:
+.PP
+  sg_write_long \-\-lba=0x1234 \-x \fIBTL\fR /dev/sda
+.PP
+This will write a sector (and ECC data) of 0xff bytes. Some disks may
+reject this (at least one of the author's does). Another approach is
+to copy the 0x1234_1.img file (to 0x1234_2.img in this example) and
+change some values with a hex editor. Then write the changed image with:
+.PP
+  sg_write_long \-\-lba=0x1234 \-\-in=0x1234_2.img \-x \fIBTL\fR /dev/sda
+.PP
+Yet another approach is to use the \fI\-\-wr_uncor\fR option, if supported:
+.PP
+  sg_write_long \-\-lba=0x1234 \-\-wr_uncor /dev/sda
+.PP
+Next we use the sg_dd utility to check that the sector is corrupted. Here is an
+example:
+.PP
+  sg_dd if=/dev/sda blk_sgio=1 skip=0x1234 of=. bs=512 count=1 verbose=4
+.PP
+Notice that the "blk_sgio=1" option is given. This is to make sure that
+the sector is read (and no others) and the error is fully reported.
+The "blk_sgio=1" option causes the SG_IO ioctl to be used by sg_dd rather
+than the block subsystem.
+.PP
+Finally we should restore sector 0x1234 to a non\-corrupted state. A sector
+full of zeros could be written with:
+.PP
+  sg_dd if=/dev/zero of=/dev/sda blk_sgio=1 seek=0x1234 bs=512 count=1
+.PP
+This will result in a sector (block) with 512 bytes of 0x0 without a
+MEDIUM ERROR since the ECC and associated data will be regenerated and
+thus well formed. The 'blk_sgio=1' option is even more important in this
+case as it may stop the block subsystem doing a read before write (since
+the read will most likely fail).
+Another approach is to write back the original contents:
+.PP
+  sg_write_long \-\-lba=0x1234 \-\-in=0x1234_1.img \-x \fIBTL\fR /dev/sda
+.PP
+.SH EXIT STATUS
+The exit status of sg_write_long is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Saeed Bishara. Further work by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2016 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_read_long, sg_dd (both in sg3_utils), sdparm(sdparm)
diff --git a/sg3_utils/doc/sg_write_same.8 b/sg3_utils/doc/sg_write_same.8
new file mode 100644
index 0000000..1ad6242
--- /dev/null
+++ b/sg3_utils/doc/sg_write_same.8
@@ -0,0 +1,321 @@
+.TH SG_WRITE_SAME "8" "February 2015" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+sg_write_same \- send SCSI WRITE SAME command
+.SH SYNOPSIS
+.B sg_write_same
+[\fI\-\-10\fR] [\fI\-\-16\fR] [\fI\-\-32\fR] [\fI\-\-anchor\fR]
+[\fI\-\-grpnum=GN\fR] [\fI\-\-help\fR] [\fI\-\-in=IF\fR] [\fI\-\-lba=LBA\fR]
+[\fI\-\-lbdata\fR] [\fI\-\-num=NUM\fR] [\fI\-\-ndob\fR] [\fI\-\-pbdata\fR]
+[\fI\-\-timeout=TO\fR] [\fI\-\-unmap\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] [\fI\-\-wrprotect=WPR\fR] [\fI\-\-xferlen=LEN\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+Send the SCSI WRITE SAME (10, 16 or 32 byte) command to \fIDEVICE\fR. This
+command writes the given block \fINUM\fR times to consecutive blocks on
+the \fIDEVICE\fR starting at logical block address \fILBA\fR.
+.PP
+The length of the block to be written multiple times is obtained from either
+the \fILEN\fR argument, or the length of the given input file \fIIF\fR,
+or by calling READ CAPACITY(16) on \fIDEVICE\fR. The contents of the
+block to be written are obtained from the input file \fIIF\fR or
+zeros are used. If READ CAPACITY(16) is called (which implies \fIIF\fR
+was not given) and the PROT_EN bit is set then an extra 8 bytes (i.e.
+more than the logical block size) of 0xff are sent. If READ CAPACITY(16)
+fails then READ CAPACITY(10) is used to determine the block size.
+.PP
+If neither \fI\-\-10\fR, \fI\-\-16\fR nor \fI\-\-32\fR is given then
+WRITE SAME(10) is sent unless one of the following conditions is met.
+If \fILBA\fR (plus \fINUM\fR) exceeds 32 bits, \fINUM\fR exceeds 65535,
+or the \fI\-\-unmap\fR option is given then WRITE SAME(16) is sent.
+The \fI\-\-10\fR, \fI\-\-16\fR and \fI\-\-32\fR options are mutually
+exclusive.
+.PP
+SBC\-3 revision 35d introduced a "no data\-out buffer" (NDOB) bit which, if
+set, bypasses the requirement to send a single block of data to the
+\fIDEVICE\fR together with the command. Only WRITE SAME (16 and 32 byte)
+support the NDOB bit. If given, a user block of zeros is assumed; if
+required, protection information of 0xffs is assumed.
+.PP
+In SBC\-3 revision 26 the UNMAP and ANCHOR bits were added to the
+WRITE SAME (10) command. Since the UNMAP bit has been in WRITE SAME (16)
+and WRITE SAME (32) since SBC\-3 revision 18, the lower of the two (i.e.
+WRITE SAME (16)) is the default when the \fI\-\-unmap\fR option is given.
+To send WRITE SAME (10) use the \fI\-\-10\fR option.
+.PP
+.B Take care:
+The WRITE SAME(10, 16 and 32) commands may interpret a \fINUM\fR of zero as
+write to the end of \fIDEVICE\fR. This utility defaults \fINUM\fR to 1 .
+The WRITE SAME commands have no IMMED bit so if \fINUM\fR is large (or
+zero) then an invocation of this utility could take a long time, potentially
+as long as a FORMAT UNIT command. In such situations the command timeout
+value \fITO\fR may need to be increased from its default value of 60
+seconds. In SBC\-3 revision 26 the WSNZ (write same no zero) bit was added
+to the Block Limits VPD page [0xB0]. If set the WRITE SAME commands will not
+accept a \fINUM\fR of zero. The same SBC\-3 revision added the "Maximum
+Write Same Length" field to the Block Limits VPD page.
+.PP
+The Logical Block Provisioning VPD page [0xB2] contains the LBWS and
+LBW10 bits. If LBWS is set then WRITE SAME (16) supports the UNMAP bit.
+If LBWS10 is set then WRITE SAME (10) supports the UNMAP bit. If either
+LBWS or LBWS10 is set and the WRITE SAME (32) is supported then WRITE
+SAME (32) supports the UNMAP bit. This is as of SBC\-3 revision 26.
+.PP
+As a precaution against an accidental 'sg_write_same /dev/sda' (for example)
+overwriting LBA 0 on /dev/sda with zeros, at least one of the
+\fI\-\-in=IF\fR, \fI\-\-lba=LBA\fR or \fI\-\-num=NUM\fR options must be
+given. Obviously this utility can destroy a lot of user data so check the
+options carefully.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-R\fR, \fB\-\-10\fR
+send a SCSI WRITE SAME (10) command to \fIDEVICE\fR. The ability to
+set the \fI\-\-unmap\fR (and \fI\-\-anchor\fR) options to this command
+was added in SBC\-3 revision 26.
+.TP
+\fB\-S\fR, \fB\-\-16\fR
+send a SCSI WRITE SAME (16) command to \fIDEVICE\fR.
+.TP
+\fB\-T\fR, \fB\-\-32\fR
+send a SCSI WRITE SAME (32) command to \fIDEVICE\fR.
+.TP
+\fB\-a\fR, \fB\-\-anchor\fR
+sets the ANCHOR bit in the cdb. Introduced in SBC\-3 revision 22.
+That draft requires the \fI\-\-unmap\fR option to also be specified.
+.TP
+\fB\-g\fR, \fB\-\-grpnum\fR=\fIGN\fR
+sets the 'Group number' field to \fIGN\fR. Defaults to a value of zero.
+\fIGN\fR should be a value between 0 and 31.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
+read data (binary) from file named \fIIF\fR and use it as the data out
+buffer for the SCSI WRITE SAME command. The length of the data out buffer
+is \fI\-\-xferlen=LEN\fR or, if that is not given, the length of the \fIIF\fR
+file. If \fIIF\fR is "\-" then stdin is read. If this option is not given
+then 0x00 bytes are used as fill with the length of the data out buffer
+obtained from \fI\-\-xferlen=LEN\fR or by calling READ CAPACITY(16 or 10).
+If the response to READ CAPACITY(16) has the PROT_EN bit set then data
+out buffer size is modified accordingly with the last 8 bytes set to 0xff.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the logical block address to start the WRITE SAME command.
+Defaults to lba 0 which is a dangerous block to overwrite on a disk that is
+in use. Assumed to be in decimal unless prefixed with '0x' or has a
+trailing 'h'.
+.TP
+\fB\-L\fR, \fB\-\-lbdata\fR
+sets the LBDATA bit in the WRITE SAME cdb. This bit was made obsolete in
+sbc3r32 in September 2012.
+.TP
+\fB\-N\fR, \fB\-\-ndob\fR
+sets the NDOB bit in the WRITE SAME (16 and 32 byte) commands. Default is to
+clear this bit. When this option is given then \fI\-\-in=IF\fR is not allowed
+and \fI\-\-xferlen=LEN\fR can only be given if \fILEN\fR is 0 .
+.TP
+\fB\-n\fR, \fB\-\-num\fR=\fINUM\fR
+where \fINUM\fR is the number of blocks, starting at \fILBA\fR, to write the
+data out buffer to. The default value for \fINUM\fR is 1. The value corresponds
+to the 'Number of logical blocks' field in the WRITE SAME cdb.
+.br
+Note that a value of 0 in \fINUM\fR may be interpreted as write the data out
+buffer on every block starting at \fILBA\fR to the end of the \fIDEVICE\fR.
+If the WSNZ bit (introduced in sbc3r26, January 2011) in the Block Limits VPD
+page is set then the value of 0 is disallowed, yielding an Invalid request
+sense key.
+.TP
+\fB\-P\fR, \fB\-\-pbdata\fR
+sets the PBDATA bit in the WRITE SAME cdb. This bit was made obsolete in
+sbc3r32 in September 2012.
+.TP
+\fB\-t\fR, \fB\-\-timeout\fR=\fITO\fR
+where \fITO\fR is the command timeout value in seconds. The default value is
+60 seconds. If \fINUM\fR is large (or zero) a WRITE SAME command may require
+considerably more time than 60 seconds to complete.
+.TP
+\fB\-U\fR, \fB\-\-unmap\fR
+sets the UNMAP bit in the WRITE SAME(10, 16 and 32) cdb. See UNMAP section
+below.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the degree of verbosity (debug messages).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+output version string then exit.
+.TP
+\fB\-w\fR, \fB\-\-wrprotect\fR=\fIWPR\fR
+sets the "Write protect" field in the WRITE SAME cdb to \fIWPR\fR. The
+default value is zero. \fIWPR\fR should be a value between 0 and 7.
+When \fIWPR\fR is 1 or greater, and the disk's protection type is 1 or
+greater, then 8 extra bytes of protection information are expected or
+generated (to place in the command's data out buffer).
+.TP
+\fB\-x\fR, \fB\-\-xferlen\fR=\fILEN\fR
+where \fILEN\fR is the data out buffer length. Defaults to the length of
+the \fIIF\fR file or, if that is not given, then the READ CAPACITY(16 or 10)
+command is used to find the 'Logical block length in bytes'. That figure
+may be increased by 8 bytes if the \fIDEVICE\fR's protection type is 1 or
+greater and the WRPROTECT field (see \fI\-\-wrprotect=WPR\fR) is 1 or
+greater. If both this option and the \fIIF\fR option are given and
+\fILEN\fR exceeds the length of the \fIIF\fR file then \fILEN\fR is the
+data out buffer length with zeros used as pad bytes.
+.SH UNMAP
+Logical block provisioning is a new term introduced in SBC\-3 revision 25
+for the ability to mark blocks as unused. For large storage arrays, it is a
+way to provision less physical storage than the READ CAPACITY command reports
+is available, potentially allocating more physical storage when WRITE
+commands require it. For flash memory (e.g. SSD drives) it is a way of
+potentially saving power (and perhaps access time) when it is known large
+sections (or almost all) of the flash memory is not in use. SSDs need wear
+levelling algorithms to have acceptable endurance and typically over
+provision to simplify those algorithms; hence they typically contain more
+physical flash storage than their logical size would dictate.
+.PP
+Support for logical block provisioning is indicated by the LBPME bit being
+set in the READ CAPACITY(16) command response (see the sg_readcap utility).
+That implies at least one of the UNMAP or WRITE SAME(16) commands is
+implemented. If the UNMAP command is implemented then
+the "Maximum unmap LBA count" and "Maximum unmap block descriptor count"
+fields in the Block Limits VPD page should both be greater than zero. The
+READ CAPACITY(16) command response also contains a LBPRZ bit which if set
+means that if unmapped blocks are read then zeros will be returned for the
+data (and if protection information is active, 0xff bytes are returned for
+that). In SBC\-3 revision 27 the same LBPRZ bit was added to the Logical
+Block Provisioning VPD page.
+.PP
+In SBC\-3 revision 25 the LBPU and ANC_SUP bits where added to the
+Logical Block Provisioning VPD page. When LBPU is set it indicates that
+the device supports the UNMAP command (see the sg_unmap utility). When the
+ANC_SUP bit is set it indicates the device supports anchored LBAs.
+.PP
+When the UNMAP bit is set in the cdb then the data out buffer is also sent.
+Additionally the data section of that data out buffer should be full of 0x0
+bytes while the data protection block, 8 bytes at the end if present, should
+be set to 0xff bytes. If these conditions are not met and the LBPRZ bit is
+set then the UNMAP bit is ignored and the data out buffer is written to the
+\fIDEVICE\fR as if the UNMAP bit was zero. In the absence of the
+\fI\-\-in=IF\fR option, this utility will attempt build a data out buffer
+that meets the requirements for the UNMAP bit in the cdb to be acted on by
+the \fIDEVICE\fR.
+.PP
+Logical blocks may also be unmapped by the SCSI UNMAP and FORMAT UNIT
+commands (see the sg_unmap and sg_format utilities).
+.PP
+The unmap capability in SCSI is closely related to the ATA DATA SET
+MANAGEMENT command with the "Trim" bit set. That ATA trim capability does
+not interact well with SATA command queueing known as NCQ. T13 have
+introduced a new command called the SFQ DATA SET MANAGEMENT command also
+with a the "Trim" bit to address that problem. The SCSI WRITE SAME with
+the UNMAP bit set and the UNMAP commands do not have any problems with
+SCSI queueing.
+.SH NOTES
+Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.PP
+In Linux, prior to lk 3.17, the sg driver did not support cdb sizes greater
+than 16 bytes. Hence a device node like /dev/sg1 which is associated with
+the sg driver would fail with this utility if the \fI\-\-32\fR option was
+given (or implied by other options). The bsg driver with device nodes like
+/dev/bsg/6:0:0:1 does support cdb sizes greater than 16 bytes since its
+introduction in lk 2.6.28 .
+.SH EXIT STATUS
+The exit status of sg_write_same is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH EXAMPLES
+One simple usage is to write blocks of zero from (and including) a given LBA:
+.PP
+  sg_write_same \-\-lba=0x1234 \-\-num=63 /dev/sdc
+.PP
+Since \fI\-\-xferlen=LEN\fR has not been given, then this utility will
+call the READ CAPACITY command on /dev/sdc to determine the number
+of bytes in a logical block.  Let us assume that is 512 bytes. Since
+\fI\-\-in=IF\fR is not given a block of zeros is assumed. So 63 blocks
+of zeros (each block containing 512 bytes) will be written from (and
+including) LBA 0x1234 . Note that only one block of zeros is passed
+to the SCSI WRITE SAME command in the data out buffer (as required by
+SBC\-3).
+.PP
+A similar example follows but in this case the blocks
+are "unmapped" ("trimmed" in ATA speak) rather than zeroed:
+.PP
+  sg_write_same \-\-unmap \-L 0x1234 \-n 63 /dev/sdc
+.PP
+Note that if the LBPRZ bit in the READ CAPACITY(16) response is set (i.e.
+LPPRZ is an acronym for logical block provisioning read zeros) then these
+two examples do the same thing, at least seen from the point of view of
+subsequent reads.
+.PP
+This utility can also be used to write protection information (PI) on disks
+formatted with a protection type greater than zero. PI is 8 bytes of extra
+data appended to the user data of a logical block: the first two bytes are a
+CRC (the "guard"), the next two bytes are the "application tag" and the last
+four bytes are the "reference tag". With protection types 1 and 2 if the
+application tag is 0xffff then the guard should not be checked (against the
+user data).
+.PP
+In this example we assume the logical block size (of the user data) is 512
+bytes and the disk has been formatted with protection type 1. Since we are
+going to modify LBA 2468 then we take a copy of it first:
+.PP
+  dd if=/dev/sdb skip=2468 bs=512 of=2468.bin count=1
+.PP
+The following command line sets the user data to zeros and the PI to 8
+0xFF bytes on LBA 2468:
+.PP
+  sg_write_same \-\-lba=2468 /dev/sdb
+.PP
+Reading back that block should be successful because the application tag
+is 0xffff which suppresses the guard (CRC) check (which would otherwise be
+wrong):
+.PP
+  dd if=/dev/sdb skip=2468 bs=512 of=/dev/null count=1
+.PP
+Now an attempt is made to create a binary file with zeros in the user data,
+0x0000 in the application tag and 0xff bytes in the other two PI fields. It
+is awkward to create 0xff bytes in a file (in Unix) as the "tr" command
+below shows:
+.PP
+  dd if=/dev/zero bs=1 count=512 of=ud.bin
+.br
+  tr "\\000" "\\377" < /dev/zero | dd bs=1 of=ff_s.bin count=8
+.br
+  cat ud.bin ff_s.bin > lb.bin
+.br
+  dd if=/dev/zero bs=1 count=2 seek=514 conv=notrunc of=lb.bin
+.PP
+The resulting file can be viewed with 'hexdump \-C lb.bin' and should
+contain 520 bytes. Now that file can be written to LBA 2468 as follows:
+.PP
+  sg_write_same \-\-lba=2468 wrprotect=3 \-\-in=lb.bin /dev/sdb
+.PP
+Note the \fI\-\-wrprotect=3\fR rather than being set to 1, since we want
+the WRITE SAME command to succeed even though the PI data now indicates
+the user data is corrupted. When an attempt is made to read the LBA, an
+error should occur:
+.PP
+  dd if=/dev/sdb skip=2468 bs=512 of=/dev/null count=1
+.PP
+dd errors are not very expressive, if dmesg is checked there should be
+a line something like this: "[sdb]  Add. Sense: Logical block guard check
+failed". The block can be corrected by doing a "sg_write_same \-\-lba=1234
+/dev/sdb" again or restoring the original contents of that LBA:
+.PP
+  dd if=2468.bin bs=512 seek=2468 of=/dev/sdb conv=notrunc count=1
+.PP
+Hopefully the dd command would never try to truncate the output file when
+it is a block device.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2009\-2015 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_format,sg_get_lba_status,sg_readcap,sg_vpd,sg_unmap(sg3_utils)
diff --git a/sg3_utils/doc/sg_write_verify.8 b/sg3_utils/doc/sg_write_verify.8
new file mode 100644
index 0000000..195fa1a
--- /dev/null
+++ b/sg3_utils/doc/sg_write_verify.8
@@ -0,0 +1,191 @@
+.TH "WRITE AND VERIFY" "8" "July 2014" "sg3_utils\-1.40" SG3_UTILS
+.SH NAME
+sg_write_and_verify \- send the SCSI WRITE AND VERIFY command
+.SH SYNOPSIS
+.B sg_write_verify
+[\fI\-\-16\fR] [\fI\-\-bytchk=BC\fR] [\fI\-\-dpo\fR] [\fI\-\-group=GN\fR]
+[\fI\-\-help\fR] [\fI\-\-ilen=ILEN\fR] [\fI\-\-in=IF\fR] \fI\-\-lba=LBA\fR
+[\fI\-\-num=NUM\fR] [\fI\-\-repeat\fR] [\fI\-\-timeout=TO\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wrprotect=WP\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+Send a SCSI WRITE AND VERIFY (10) or (16) command to \fIDEVICE\fR. The
+data to be written is read from the \fIIF\fR file or, in its absence, a
+buffer full of 0xff bytes is used. The length of the data\-out buffer sent
+with the command is \fIILEN\fR bytes or, if that is not given, then it is
+the length of the \fIIF\fR file.
+.PP
+The write operation is to the \fIDEVICE\fR's medium (optionally to its cache)
+starting at logical block address \fILBA\fR for \fINUM\fR logical blocks.
+After the write to medium is performed a verify operation takes place which
+may viewed as a medium read (with appropriate checks) but without the data
+being returned. Additionally, if \fIBS\fR is set to one, the data read back
+from the medium in the verify operation is compared to the original data\-out
+buffer.
+.PP
+The relationship between the number of logical blocks to be written (i.e.
+\fINUM\fR) and the length (in bytes) of the data\-out buffer (i.e.
+\fIILEN\fR) may be simply found by multiplying the former by the logical
+block size. However if the \fIDEVICE\fR has protection information (PI)
+then it becomes a bit more complicated. Hence the calculation is left to
+the user with the default \fIILEN\fR, in the absence of the \fIIF\fR file,
+being set to \fINUM\fR * 512.
+.PP
+For sending large amounts of data to contiguous logical blocks, a single
+WRITE AND VERIFY command may not be appropriate (e.g. due to operating
+system limitations). In such cases see the REPEAT section below.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+The options are arranged in alphabetical order based on the long option name.
+.TP
+\fB\-S\fR, \fB\-\-16\fR
+Send a WRITE AND VERIFY(16) command. The default is to send a WRITE AND
+VERIFY(10) command unless \fILBA\fR or \fINUM\fR are too large for the
+10 byte variant.
+.TP
+\fB\-b\fR, \fB\-\-bytchk\fR=\fIBC\fR
+where \fIBC\fR is the value to place in the command's BYTCHK field. Values
+between 0 and 3 (inclusive) are accepted. The default is value is 0 which
+implies only a write to the medium then a verify operation are performed. The
+only other value T10 defines currently is 1 which does performs an additional
+comparison between the data\-out buffer that was used by the write operation
+and the contents of the logical blocks read back from the medium.
+.TP
+\fB\-d\fR, \fB\-\-dpo\fR
+Set the DPO (disable page out) bit in the command. The default is to leave
+it clear.
+.TP
+\fB\-g\fR, \fB\-\-group\fR=\fIGN\fR
+where \fIGN\fR is the value to place in the command's GROUP NUMBER field.
+Values between 0 and 31 (inclusive) are accepted. The default is value is 0.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-I\fR, \fB\-\-ilen\fR=\fIILEN\fR
+where \fIILEN\fR is the number of bytes that will be placed in the data\-out
+buffer. If the \fIIF\fR file is given then no more than \fIILEN\fR bytes
+are read from that file. If the \fIIF\fR file does not contain \fIILEN\fR
+bytes then an error is reported. If the  \fIIF\fR file is not given then
+a data\-out buffer with \fIILEN\fR bytes of 0xff is sent.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIIF\fR
+read data (binary) from file named \fIIF\fR. If \fIIF\fR is "\-" then
+stdin is used. This data will become the data\-out buffer and will be written
+to the \fIDEVICE\fR's medium. If \fIBC\fR is 1 then that data\-out buffer
+will be held until after the verify operation and compared to the data read
+back from the medium.
+.TP
+\fB\-l\fR, \fB\-\-lba\fR=\fILBA\fR
+where \fILBA\fR is the logical block address to start the write to medium.
+Assumed to be in decimal unless prefixed with '0x' or has a trailing 'h'.
+Must be provided.
+.TP
+\fB\-n\fR, \fB\-\-num\fR=\fINUM\fR
+where \fINUM\fR is the number of blocks, starting at \fILBA\fR, to write
+to the medium. The default value for \fINUM\fR is 1.
+.TP
+\fB\-R\fR, \fB\-\-repeat\fR
+this option will continue to do WRITE AND VERIFY commands until the \fIIF\fR
+file is exhausted. This option requires both the \fI\-\-ilen=ILEN\fR and
+\fI\-\-in=IF\fR options to be given. Each command starts at the next logical
+block address and is for no more than \fINUM\fR blocks. The last command may
+be shorter with the number of blocks scaled as required. If there are
+residue bytes a warning is sent to stderr. See the REPEAT section.
+.TP
+\fB\-t\fR, \fB\-\-timeout\fR=\fITO\fR
+where \fITO\fR is the command timeout value in seconds. The default value is
+60 seconds. If \fINUM\fR is large then command may require considerably more
+time than 60 seconds to complete.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the degree of verbosity (debug messages).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+output version string then exit.
+.TP
+\fB\-w\fR, \fB\-\-wrprotect\fR=\fIWP\fR
+set the WRPROTECT field in the cdb to \fIWP\fR. The default value is 0 which
+implies no protection information is sent (along with the user data) in the
+data\-out buffer.
+.SH REPEAT
+For data sizes around a megabyte and larger, it may be appropriate to send
+multiple SCSI WRITE AND VERIFY commands due to operating system
+limitations (e.g. pass\-through SCSI interfaces often limit the amount
+of data that can be passed with a SCSI command). With this utility the
+mechanism for doing that is the \fI\-\-repeat\fR option.
+.PP
+In this mode the \fI\-\-ilen=ILEN\fR and \fI\-\-in=IF\fR options must be
+given. The \fIILEN\fR and \fINUM\fR values are treated as a per SCSI command
+parameters. Up to \fIILEN\fR bytes will be read from the \fIIF\fR file
+continually until it is exhausted. If the \fIIF\fR file is stdin, reading
+continues until an EOF is detected. The data read from each iteration becomes
+the data\-out buffer for a new WRITE AND VERIFY command.
+.PP
+The last read from the file (or stdin) may read less than \fIILEN\fR bytes
+in which case the number of logical blocks sent to the last WRITE AND VERIFY
+is scaled back accordingly. If there is a residual number of bytes left
+after that scaling then that is reported to stderr.
+.PP
+If an error occurs then that is reported to stderr and via the exit status
+and the utility stops at that point.
+.SH NOTES
+Other SCSI WRITE commands have a Force Unit Access (FUA) bit but that is
+set (implicitly) by WRITE AND VERIFY commands hence there is no option to set
+it. The data\-out buffer may still additionally be placed in the
+\fIDEVICE\fR's cache and setting the DPO bit is a hint not to do that.
+.PP
+Normal SCSI WRITEs can be done with the ddpt and the sg_dd utilities. The
+SCSI WRITE SAME command can be done with the sg_write_same utility while
+the SCSI COMPARE AND WRITE command (sg_compare_and_write utility) offers
+a "test and set" facility.
+.PP
+Various numeric arguments (e.g. \fILBA\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.SH EXIT STATUS
+The exit status of sg_write_verify is 0 when it is successful. If the verify
+operation fails that is typically indicated with a medium error which leads
+to an exit status of 3.
+.PP
+If \fIBC\fR is set to 1 and the comparison it causes fails this utility will
+indicate the miscompare with an exit status of 14. For other exit status
+values see the EXIT STATUS section in the sg3_utils(8) man page.
+.SH EXAMPLES
+To start with, a simple example: write 1 block of data held in file t.bin
+that is 512 bytes long then write that block to LBA 0x1234 on /dev/sg4 .
+.PP
+  # sg_write_verify \-\-lba=0x1234 \-\-in=t.bin /dev/sg4
+.PP
+Since '\-\-num=' is not given then it defaults to 1. Further the \fIILEN\fR
+value is obtained from the file size of t.bin . To additionally do a
+data\-out comparison to the read back data:
+.PP
+  # sg_write_verify -l 0x1234 -i t.bin --bytchk=1 /dev/sg4
+.PP
+The ddpt command can do copies between SCSI devices using READ and WRITE
+commands. However, currently it has no facility to promote those WRITES
+to WRITE AND VERIFY commands. Using a pipe, that could be done like this:
+.PP
+  # ddpt if=/dev/sg2 bs=512 bpt=8 count=11 of=- |
+.br
+sg_write_verify \-\-in=\- \-l 0x567 \-n 8 \-\-ilen=4096 \-\-repeat /dev/sg4
+.PP
+Both ddpt and sg_write_verify are configured for segments of 8 512 byte
+logical blocks. Since 11 logical blocks are read then first 8 logical blocks
+are copied followed by a copy of the remaining 3 blocks. Since it is assumed
+that there is no protection information then the data\-in and data\-out
+buffers will be 4096 bytes each. For sg_write_verify this needs to be stated
+explicitly with the \-\-ilen=4096 option.
+.SH AUTHORS
+Bruno Goncalves and Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B ddpt(in a package of that name), sg_compare_and_write(8), sg_dd(8),
+.B sg_write_same(8)
diff --git a/sg3_utils/doc/sg_xcopy.8 b/sg3_utils/doc/sg_xcopy.8
new file mode 100644
index 0000000..15b5510
--- /dev/null
+++ b/sg3_utils/doc/sg_xcopy.8
@@ -0,0 +1,367 @@
+.TH SG_XCOPY "8" "March 2015" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+sg_xcopy \- copy data to and from files and devices using SCSI EXTENDED
+COPY (XCOPY)
+.SH SYNOPSIS
+.B sg_xcopy
+[\fIbs=BS\fR] [\fIconv=CONV\fR] [\fIcount=COUNT\fR] [\fIibs=BS\fR]
+[\fIif=IFILE\fR] [\fIiflag=FLAGS\fR] [\fIobs=BS\fR] [\fIof=OFILE\fR]
+[\fIoflag=FLAGS\fR] [\fIseek=SEEK\fR] [\fIskip=SKIP\fR] [\fI\-\-help\fR]
+[\fI\-\-version\fR]
+.PP
+[\fIbpt=BPT\fR] [\fIcat=\fR0|1] [\fIdc=\fR0|1]
+[\fIid_usage=\fR{hold|discard|disable}] [\fIlist_id=ID\fR] [\fIprio=PRIO\fR]
+[\fItime=\fR0|1] [\fIverbose=VERB\fR] [\fI\-\-on_dst|\-\-on_src\fR]
+[\fI\-\-verbose\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Copy data to and from any files. Specialized for "files" that are Linux SCSI
+devices that support the SCSI EXTENDED COPY (XCOPY) command.
+.PP
+During the draft stages of SPC\-4 the T10 committee has expanded the XCOPY
+command so that it now has two variants: "LID1" (for a List Identifier
+length of 1 byte) and "LID4" (for a List Identifier length of 4 bytes).
+This utility supports the older, LID1 variant which is also found in SPC\-3
+and earlier.  While the LID1 variant in SPC\-4 is command level (binary)
+compatible with XCOPY as defined in SPC\-3, some of the command naming has
+changed. This utility uses the older, SPC\-3 XCOPY names.
+.PP
+This utility
+has similar syntax and semantics to
+.B dd(1)
+but with no "conversions" is supported.
+.PP
+The first group in the synopsis above are "standard" Unix
+.B dd(1)
+operands. The second group are extra options added by this utility.
+Both groups are defined below in combined, alphabetical order.
+.PP
+By default the XCOPY command is sent to \fIOFILE\fR. This can be changed
+with the \fI\-\-on_src\fR or \fIiflag=xflag\R options which cause the XCOPY
+command to be sent to \fIIFILE\fR instead. Also see the section on
+ENVIRONMENT VARIABLES.
+.PP
+The ddpt utility supports the same xcopy(LID1) functionality as this utility
+with the same options and flags. Additionally ddpt supports a subset of
+xcopy(LID4) functionality variously called "xcopy version 2, lite" or ODX.
+ODX is a market name and stands for Offloaded Data Xfer (i.e. transfer).
+.SH OPTIONS
+.TP
+\fBbpt\fR=\fIBPT\fR
+each IO transaction will be made using \fIBPT\fR blocks (or less if near
+the end of the copy). Default is 128 for block sizes less that 2048
+bytes, otherwise the default is 32. So for bs=512 the reads and writes
+will each convey 64 KiB of data by default (less if near the end of the
+transfer or memory restrictions). When cd/dvd drives are accessed, the
+block size is typically 2048 bytes and bpt defaults to 32 which again
+implies 64 KiB transfers.
+.TP
+\fBbs\fR=\fIBS\fR
+where \fIBS\fR
+.B must
+be the block size of the physical device (if either the input or output
+files are accessed via SCSI commands). Note that this differs from
+.B dd(1)
+which permits \fIBS\fR to be an integral multiple. Defaults to the
+device block size.
+.TP
+\fBcat\fR={0|1}
+sets the SCSI EXTENDED COPY command segment descriptor CAT bit to 0 or
+1 (default: 0). The CAT bit (in conjunction with the PAD bit) controls
+the handling of residual data. See section
+.B HANDLING OF RESIDUAL DATA
+for details.
+.TP
+\fBdc\fR={0|1}
+sets the SCSI EXTENDED COPY command segment descriptor DC bit to 0 or
+1 (default: 0). The DC bit controls whether \fICOUNT\fR
+refers to the source (\fIdc=0\fR) or the target (\fIdc=1\fR) descriptor.
+.TP
+\fBconv\fR=\fBCONV\fR
+all \fBCONV\fR arguments are ignored.
+.TP
+\fBcount\fR=\fICOUNT\fR
+copy \fICOUNT\fR blocks from \fIIFILE\fR to \fIOFILE\fR. Default is the
+minimum (\fIIFILE\fR if \fIdc=0\fR or \fIOFILE\fR if \fIdc=1\fR)
+number of blocks that SCSI devices report from SCSI READ CAPACITY
+commands or that block devices (or their partitions) report. Normal
+files are not probed for their size. If \fIskip=SKIP\fR or
+\fIskip=SEEK\fR are given and the count is derived (i.e. not
+explicitly given) then the derived count is scaled back so that the
+copy will not overrun the device. If the file name is a block device
+partition and \fICOUNT\fR is not given then the size of the partition
+rather than the size of the whole device is used. If \fICOUNT\fR is
+not given (or \fIcount=\-1\fR) and cannot be derived then an error
+message is issued and no copy takes place.
+.TP
+\fBibs\fR=\fIBS\fR
+if given must be the same as \fIBS\fR given to 'bs=' option.
+.TP
+\fBid_usage\fR={hold|discard|disable}
+sets the SCSI EXTENDED COPY command parameter list field called LIST ID
+USAGE to 0 if the argument is 'hold', to 2 if the argument is 'discard',
+or to '3' if the argument is 'disable'.
+.br
+If the device has the ability to hold data (as indicated by "held data
+limit" being greater than zero) then \fIid_usage\fR defaults to 'hold'
+otherwise it defaults to 'discard'.
+.TP
+\fBif\fR=\fIIFILE\fR
+read from \fIIFILE\fR instead of stdin. If \fIIFILE\fR is '\-' then stdin
+is read. Starts reading at the beginning of \fIIFILE\fR unless \fISKIP\fR
+is given.
+.TP
+\fBiflag\fR=\fIFLAGS\fR
+where \fIFLAGS\fR is a comma separated list of one or more flags outlined
+below.  These flags are associated with \fIIFILE\fR and are ignored when
+\fIIFILE\fR is stdin.
+.TP
+\fBlist_id\fR=\fIID\fR
+sets the SCSI EXTENDED COPY command parameter list field called LIST
+IDENTIFIER to \fIID\fR. \fIID\fR should be a value between 0 and
+255 (inclusive). \fIID\fR usually defaults to 1 unless
+\fIid_usage=disable\fR in which case it defaults to 0.
+.TP
+\fBobs\fR=\fIBS\fR
+if given must be the same as \fIBS\fR given to 'bs=' option.
+.TP
+\fBof\fR=\fIOFILE\fR
+write to \fIOFILE\fR instead of stdout. If \fIOFILE\fR is '\-' then writes
+to stdout.  If \fIOFILE\fR is /dev/null then no actual writes are performed.
+If \fIOFILE\fR is '.' (period) then it is treated the same way as
+/dev/null (this is a shorthand notation). If \fIOFILE\fR exists then it
+is _not_ truncated; it is overwritten from the start of \fIOFILE\fR
+unless 'oflag=append' or \fISEEK\fR is given.
+.TP
+\fBoflag\fR=\fIFLAGS\fR
+where \fIFLAGS\fR is a comma separated list of one or more flags outlined
+below.  These flags are associated with \fIOFILE\fR and are ignored when
+\fIOFILE\fR is /dev/null, '.' (period), or stdout.
+.TP
+\fBprio\fR=\fIPRIO\fR
+sets the SCSI EXTENDED COPY command parameter list field called PRIORITY
+to \fIPRIO\fR.  The default value is 1.
+.TP
+\fBseek\fR=\fISEEK\fR
+start writing \fISEEK\fR bs\-sized blocks from the start of \fIOFILE\fR.
+Default is block 0 (i.e. start of file).
+.TP
+\fBskip\fR=\fISKIP\fR
+start reading \fISKIP\fR bs\-sized blocks from the start of \fIIFILE\fR.
+Default is block 0 (i.e. start of file).
+.TP
+\fBtime\fR={0|1}
+when 1, times transfer and does throughput calculation, outputting the
+results (to stderr) at completion. When 0 (default) doesn't perform timing.
+.TP
+\fBverbose\fR=\fIVERB\fR
+as \fIVERB\fR increases so does the amount of debug output sent to stderr.
+Default value is zero which yields the minimum amount of debug output.
+A value of 1 reports extra information that is not repetitive. A value
+2 reports cdbs and responses for SCSI commands that are not repetitive
+(i.e. other that READ and WRITE). Error processing is not considered
+repetitive. Values of 3 and 4 yield output for all SCSI commands (and
+Unix read() and write() calls) so there can be a lot of output.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+outputs usage message and exits.
+.TP
+\fB\-\-on_dst\fR
+send the XCOPY command to the output file/device (i.e. \fIOFILE\fR). This is
+the default unless overridden by the \fI\-\-on_src\fR or \fIiflag=xflag\fR
+options. Also see the section below on ENVIRONMENT VARIABLES.
+.TP
+\fB\-\-on_src\fR
+send the XCOPY command to the input file/device (i.e. \fIIFILE\fR).
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+equivalent to \fIverbose=1\fR. When used twice, equivalent to
+\fIverbose=2\fR, etc.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+outputs version number information and exits.
+.SH FLAGS
+Here is a list of flags and their meanings:
+.TP
+append
+causes the O_APPEND flag to be added to the open of \fIOFILE\fR. For regular
+files this will lead to data appended to the end of any existing data.
+Cannot be used together with the \fIseek=SEEK\fR option as they conflict.
+The default action of this utility is to overwrite any existing data
+from the beginning of the file or, if \fISEEK\fR is given, starting at
+block \fISEEK\fR. Note that attempting to 'append' to a device file (e.g.
+a disk) will usually be ignored or may cause an error to be reported.
+.TP
+excl
+causes the O_EXCL flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR.
+.TP
+flock
+after opening the associated file (i.e. \fIIFILE\fR and/or \fIOFILE\fR)
+an attempt is made to get an advisory exclusive lock with the flock()
+system call. The flock arguments are "FLOCK_EX | FLOCK_NB" which will
+cause the lock to be taken if available else a "temporarily unavailable"
+error is generated. An exit status of 90 is produced in the latter case
+and no copy is done.
+.TP
+null
+has no affect, just a placeholder.
+.TP
+pad
+sets the SCSI EXTENDED COPY command segment descriptor PAD bit. The
+PAD bit (in conjunction with the CAT bit) controls the handling of
+residual data.(See section
+.B HANDLING OF RESIDUAL DATA
+for details.
+.TP
+xcopy
+has no affect; for compatibility with ddpt.
+.SH HANDLING OF RESIDUAL DATA
+The \fIpad\fR and \fIcat\fR bits control the handling of residual
+data. As the data can be specified either in terms of source or target
+block size and both might have different block sizes residual data is
+likely to happen in these cases.
+If both block sizes are identical these bits have no effect as
+residual data will not occur.
+.PP
+If none of these bits are set, the EXTENDED COPY command will be
+aborted with additional sense 'UNEXPECTED INEXACT SEGMENT'.
+.PP
+If only the \fIcat\fR bit is set the residual data will be retained
+and made available for subsequent segment descriptors. Residual data
+will be discarded for the last segment descriptor.
+.PP
+If the \fIpad\fR bit is set for the source descriptor only, any
+residual data for both source or destination will be discarded.
+.PP
+If the \fIpad\fR bit is set for the target descriptor only any
+residual source data will be handled as if the \fIcat\fR bit is set,
+but any residual destination data will be padded to make a whole block
+transfer.
+.PP
+If the \fIpad\fR bit is set for both source and target any residual
+source data will be discarded, and any residual destination data will
+be padded.
+.SH ENVIRONMENT VARIABLES
+If the command line invocation does not explicitly (and unambiguously)
+indicate whether the XCOPY SCSI command should be sent to \fIIFILE\fR (i.e.
+the source) or \fIOFILE\fR (i.e. the destination) then a check is
+made for the presence of the XCOPY_TO_SRC and XCOPY_TO_DST environment
+variables. If either one exists (but not both) then it indicates where
+the SCSI XCOPY command will be sent. By default the XCOPY command is
+sent to \fIOFILE\fR.
+.SH RETIRED OPTIONS
+Here are some retired options that are still present:
+.TP
+append=0 | 1
+when set, equivalent to 'oflag=append'. When clear the action is
+to overwrite the existing file (if it exists); this is the default.
+See the 'append' flag.
+.SH NOTES
+Copying data behind an Operating System's back can cause problems. In the
+case of Linux, users should look at this link:
+http://linux\-mm.org/Drop_Caches
+.br
+This command sequence may be useful:
+.br
+  sync; echo 3 > /proc/sys/vm/drop_caches
+.PP
+Various numeric arguments (e.g. \fISKIP\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.PP
+The \fICOUNT\fR, \fISKIP\fR and \fISEEK\fR arguments can take 64 bit
+values (i.e. very big numbers). Other values are limited to what can fit in
+a signed 32 bit number.
+.PP
+All informative, warning and error output is sent to stderr so that
+dd's output file can be stdout and remain unpolluted. If no options
+are given, then the usage message is output and nothing else happens.
+.PP
+If a device supports xcopy operations then it should set the 3PC
+field (3PC stands for Third Party Copy) in its standard INQUIRY response.
+This utility will attempt a xcopy operation irrespective of the value
+in the 3PC field but if it is zero (cleared) one would expect the
+xcopy operation to fail.
+.PP
+The status of the SCSI EXTENDED COPY command can be queried with
+.B sg_copy_results(sg3_utils)
+.PP
+Currently only block\-to\-block transfers are implemented; \fIIFILE\fR
+and \fIOFILE\fR must refer to a SCSI block device.
+.PP
+No account is taken of partitions so, for example, /dev/sbc2, /dev/sdc,
+/dev/sg2, and /dev/bsg/3:0:0:1 would all refer to the same thing: the
+whole logical unit (i.e. the whole disk) starting at LBA 0. So any
+partition indication (e.g. /dev/sdc2) is ignored. The user should set
+\fISKIP\fR,  \fISEEK\fR and \fICOUNT\fR with information obtained
+from a command like 'fdisk \-l \-u /dev/sdc' to account for partitions.
+.PP
+XCOPY (LID1) capability has been added to the ddpt utility which is in
+a package of the same name. The ddpt utility will run on other
+OSes (e.g. FreeBSD and Windows) while sg_xcopy only runs on Linux. Also
+ddpt permits the arguments to \fIibs=\fR and \fIibs=\fR to be different.
+.SH EXAMPLES
+Copy 2M of data from the start of one device to another:
+.PP
+# sg_xcopy if=/dev/sdo of=/dev/sdp count=2048 list_id=2 dc=1
+.br
+sg_xcopy: if=/dev/sdo skip=0 of=/dev/sdp seek=0 count=1024
+.br
+Start of loop, count=1024, bpt=65535, lba_in=0, lba_out=0
+.br
+sg_xcopy: 1024 blocks, 1 command
+.PP
+Check the status of the EXTENDED COPY command:
+.PP
+# sg_copy_results \-\-status \-\-list_id=2 /dev/sdp
+.br
+Receive copy results (copy status):
+    Held data discarded: Yes
+    Copy manager status: Operation completed without errors
+    Segments processed: 1
+    Transfer count units: 0
+    Transfer count: 0
+.SH SIGNALS
+The signal handling has been borrowed from dd: SIGINT, SIGQUIT and
+SIGPIPE output the number of remaining blocks to be transferred and
+the records in + out counts; then they have their default action.
+SIGUSR1 causes the same information to be output yet the copy continues.
+All output caused by signals is sent to stderr.
+.SH EXIT STATUS
+The exit status of sg_xcopy is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.PP
+An additional exit status of 90 is generated if the flock flag is given
+and some other process holds the advisory exclusive lock.
+.SH AUTHORS
+Written by Hannes Reinecke and Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2000\-2015 Hannes Reinecke and Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+There is a web page discussing sg_dd at http://sg.danny.cz/sg/sg_dd.html
+.PP
+A POSIX threads version of this utility called
+.B sgp_dd
+is in the sg3_utils package. Another version from that package is called
+.B sgm_dd
+and it uses memory mapped IO to speed transfers from sg devices.
+.PP
+The lmbench package contains
+.B lmdd
+which is also interesting. For moving data to and from tapes see
+.B dt
+which is found at http://www.scsifaq.org/RMiller_Tools/index.html
+.PP
+To change mode parameters that effect a SCSI device's caching and error
+recovery see
+.B sdparm(sdparm)
+.PP
+See also
+.B dd(1), sg_copy_results(sg3_utils), ddrescue(GNU), ddpt,ddptctl(ddpt)
diff --git a/sg3_utils/doc/sg_zone.8 b/sg3_utils/doc/sg_zone.8
new file mode 100644
index 0000000..44a0cd6
--- /dev/null
+++ b/sg3_utils/doc/sg_zone.8
@@ -0,0 +1,60 @@
+.TH SG_ZONE "8" "November 2015" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_zone \- send SCSI OPEN, CLOSE or FINISH ZONE command
+.SH SYNOPSIS
+.B sg_zone
+[\fI\-\-all\fR] [\fI\-\-close\fR] [\fI\-\-finish\fR] [\fI\-\-help\fR]
+[\fI\-\-open\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-zone=ID\fR]
+\fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI OPEN ZONE, CLOSE ZONE or FINISH ZONE command to the \fIDEVICE\fR.
+These commands are found in the ZBC draft standard revision
+4c (zbc\-r04c.pdf).
+.PP
+One and only one of the \fI\-\-open\fR, \fI\-\-close\fR and \fI\-\-finish\fR
+options can be chosen.
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+sets the ALL field in the cdb.
+.TP
+\fB\-c\fR, \fB\-\-close\fR
+causes the CLOSE ZONE command to be sent to the \fIDEVICE\fR.
+.TP
+\fB\-f\fR, \fB\-\-finish\fR
+causes the FINISH ZONE command to be sent to the \fIDEVICE\fR.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-o\fR, \fB\-\-open\fR
+causes the OPEN ZONE command to be sent to the \fIDEVICE\fR.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.TP
+\fB\-z\fR, \fB\-\-zone\fR=\fIID\fR
+where \fIID\fR is placed in the cdb's ZONE ID field. A zone id is a zone
+start logical block address (LBA). The default value is 0. \fIID\fR is
+assumed to be in decimal unless prefixed with '0x' or has a trailing 'h'
+which indicate hexadecimal.
+.SH EXIT STATUS
+The exit status of sg_zone is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2014\-2015 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B sg_rep_zones,sg_reset_wp(sg3_utils)
diff --git a/sg3_utils/doc/sginfo.8 b/sg3_utils/doc/sginfo.8
new file mode 100644
index 0000000..77448a0
--- /dev/null
+++ b/sg3_utils/doc/sginfo.8
@@ -0,0 +1,325 @@
+.TH SGINFO "8" "January 2014" "sg3_utils\-1.38" SG3_UTILS
+.SH NAME
+sginfo \- access mode page information for a SCSI (or ATAPI) device
+.SH SYNOPSIS
+.B sginfo
+[\fIOPTIONS\fR]
+[\fIDEVICE\fR]
+[\fIREPLACEMENT_PARAMETERS\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+sginfo is a port of the Linux
+.B scsiinfo
+program by Eric Youngdale. It uses SCSI generic (sg) devices; however in
+some cases the high level device name (i.e. sd, sr, st, osst, or hd) can
+also be used. The primary role of this program is to access mode page
+information. If permitted, mode page information can be altered. In
+addition information from the INQUIRY and READ DEFECTS commands are also
+available.
+.PP
+This utility is in legacy mode, only obvious bugs will be fixed. Options
+like \fI\-l\fR (to list devices) are broken in recent versions of
+Linux (e.g. 2.6 series and later); the lsscsi(8) utility can be used
+instead. Also mode pages are not being updated as http://www.t10.org
+adds and modifies mode page fields. Those interested in SCSI mode pages
+may find the
+.B sdparm
+utility more up to date and easier use, especially for changing parameters.
+.PP
+Four sets of values are maintained by a SCSI device for each mode
+page: current (active), default (manufacturer's supplied values),
+saved (values that are retained if the SCSI device is powered down),
+and changeable (mask indicating those values that can be changed).
+By default when a mode page is displayed the current values are
+shown. This can be overridden by "\-M" (defaults), "\-S" (saved)
+or "\-m" (modifiable (i.e. changeable)).
+.PP
+Many mode pages are decoded: for disks (see SBC\-2), for CD/DVDs (see
+MMC\-2/3/4/5), for tapes (see SSC\-2) and for enclosures (see SES\-2).
+Some mode pages common to all SCSI peripheral device types are defined
+in SPC\-4 (primary commands). A decoded mode page has its field names
+in the first column and the corresponding value in the second column.
+A "hex" mode page (and subpage) has its byte position in the first
+column (in hex and starting at 0x2) and the corresponding hex value
+in the second column. Decoded pages can be viewed with the '\-t' option
+or with a specific option (e.g. 'c' for the caching mode page).
+Naturally decoded pages must be supplied by the \fIDEVICE\fR and
+recognised by this program. If supported by the device, decoded pages
+may be modified. All mode pages (and subpages) that the device supports
+can be viewed in hex (and potentially modified) via the "\-u" option
+.PP
+If no options are given that will cause mode page(s) or INQUIRY data
+to be printed out, then a brief INQUIRY response is output. This
+includes the vendor, product and revision level of the device.
+.SH OPTIONS
+.TP
+\fB\-6\fR
+Perform 6 byte MODE SENSE and MODE SELECT commands; by default the
+10 byte variants are used.
+.TP
+\fB\-a\fR
+Display some INQUIRY data and the unit serial number followed by
+all mode pages reported by the device. It is similar to
+the '\-t 0x3f' option. If the mode page is known then it is output
+in decoded form otherwise it is output in hexadecimal.
+.TP
+\fB\-A\fR
+Display some INQUIRY data and the unit serial number followed by
+all mode pages and all mode subpages reported by the device.
+It is similar to the '\-t 0x3f,0xff' option. If a mode (sub)page
+is known then it is output in decoded form otherwise it is output in
+hexadecimal.
+.TP
+\fB\-c\fR
+Access information in the Caching mode page.
+.TP
+\fB\-C\fR
+Access information in the Control mode Page.
+.TP
+\fB\-d\fR
+Display defect lists (default format: index).
+.TP
+\fB\-D\fR
+Access information in the Disconnect\-Reconnect mode page.
+.TP
+\fB\-e\fR
+Access information in the Error Recovery mode page.
+.TP
+\fB\-E\fR
+Access information in the Control Extension mode page.
+.TP
+\fB\-f\fR
+Access information in the Format Device mode page.
+.TP
+\fB\-F\fR\fIarg\fR
+Format of the defect lists:
+                \-Flogical  \- logical block addresses (32 bit)
+                \-Flba64    \- logical block addresses (64 bit)
+                \-Fphysical \- physical blocks
+                \-Findex    \- defect bytes from index
+                \-Fhead     \- sort by head
+.br
+Used in conjunction with "\-d" or "\-G". If a format is not given "index" is
+assumed.
+.TP
+\fB\-g\fR
+Access information in the Rigid Disk Drive Geometry mode page.
+.TP
+\fB\-G\fR
+Display grown defect list (default format: index).
+.TP
+\fB\-i\fR
+Display the response to a standard INQUIRY command.
+.TP
+\fB\-I\fR
+Access the Informational Exceptions mode page.
+.TP
+\fB\-l\fR
+Deprecated. Only use in old versions of Linux (e.g. 2.4 and
+earlier). Please use lsscsi(8) in the Linux 2.6 series and
+later. List known SCSI devices on the system.
+.TP
+\fB\-n\fR
+Access information in the Notch and Partition mode page.
+.TP
+\fB\-N\fR
+Negate (i.e. stop) mode page changes being placed in the "saved"
+page (by default changes go to the current and the saved page).
+Only active when used together with '\-R'.
+.TP
+\fB\-P\fR
+Access information in the Power Condition mode page.
+.TP
+\fB\-r\fR
+Display all raw (or primary) SCSI device names visible in the /dev
+directory. Examples are /dev/sda, /dev/st1 and /dev/scd2. Does not
+list sg device names so devices such as a SCSI enclosure which only
+have an sg device name are not listed.
+.TP
+\fB\-s\fR
+Display information in the unit serial number page which is a
+INQUIRY command variant.
+.TP
+\fB\-t\fR \fIPN\fR[,\fISPN\fR]
+Display information from mode page number \fIPN\fR (and optionally sub
+page number \fISPN\fR) in decoded format (if known, otherwise in hex form).
+\fIPN\fR is a mode page number in a decimal number from 0 to 63 inclusive.
+\fISPN\fR is the mode subpage number and is assumed to be 0 if not given.
+\fISPN\fR is a decimal number from 1 to 255 inclusive. A page number of 63
+returns all pages supported by the device in ascending order except for
+page 0 which, if present, is last. Page 0 is vendor specific and not
+necessarily in mode page format. Alternatively hex values can be given for
+both \fIPN\fR and \fISPN\fR (both prefixed by '0x').
+.TP
+\fB\-T\fR
+Trace commands to obtain more verbose output (for debugging). When used once
+SCSI commands are shown (in hex) and any errors from these SCSI commands are
+spelt out (i.e.  with a decoded and raw sense buffer). When used twice, the
+additional data sent with mode select and the response from mode sense are
+shown (in hex).
+.TP
+\fB\-u\fR \fIPN\fR[,\fISPN\fR]
+Display information from mode page number \fIPN\fR (and optionally \fISPN\fR)
+in hex form. \fIPN\fR is a mode page number in a decimal number from 0 to 63
+inclusive. \fISPN\fR is the mode subpage number and is assumed to be 0 if
+not given. \fISPN\fR is a decimal number from 1 to 255 inclusive. A page
+number of 63 returns all pages supported by the device in ascending order
+except for page 0 which, if present, is last. Page 0 is vendor specific and
+not necessarily in mode page format. Alternatively hex values can be given
+for both \fIPN\fR and \fISPN\fR (both prefixed by '0x'). For example 63 and
+0x3f are equivalent.
+.TP
+\fB\-v\fR
+Display version string then exit. [N.B. This option increases verbosity for
+most other utilities in this package as outlined in 'man 8 sg3_utils'.
+This odd usage is for backward compatibility with the scsiinfo utility.]
+.TP
+\fB\-V\fR
+Access information in the Verify Error Recovery mode page. [N.B. This
+option prints the version string then exits in most other utilities in
+this package as outlined in 'man 8 sg3_utils'. This odd usage is for
+backward compatibility with the scsiinfo utility.]
+.TP
+\fB\-z\fR
+do a single fetch for mode pages (over\-estimating the expected length
+of the returned response). The default action is to do a double
+fetch, the first fetch is to find the response length that could be
+returned. Devices that closely adhere to SCSI standards should not
+require this option, some real world devices do require it.
+.SH ADVANCED OPTIONS
+Only one of the following three options can be specified.
+None of these three implies the current values are returned.
+.TP
+\fB\-m\fR
+Display modifiable fields instead of current values
+.TP
+\fB\-M\fR
+Display manufacturer's defaults instead of current values
+.TP
+\fB\-S\fR
+Display saved defaults instead of current values
+.PP
+The following are advanced options, not generally suited for most users:
+.TP
+\fB\-X\fR
+Display output values in a list. Make them suitable for editing and
+being given back to the '\-R' (replace command).
+.TP
+\fB\\-R\fR
+Replace parameters \- best used with \-X (expert use only)
+.SH CHANGING MODE PAGE PARAMETERS
+Firstly you should know what you are doing before changing existing
+parameters. Taking the control page as an example, first list it out
+normally (e.g. "sginfo \-C /dev/sda") and
+decide which parameter is to be changed (note its position relative
+to the other lines output). Then execute the same sginfo command with
+the "\-X" option added; this will output the parameter values in a
+single row in the same relative positions as the previous command. Now
+execute "sginfo \-CXR /dev/sda ..." with the "..." replaced by the
+single row of values output by the previous command, with the relevant
+parameter changed. Here is a simplified example:
+.PP
+   $ sginfo \-C /dev/sda
+.br
+   Control mode page (0xa)
+.br
+   \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
+.br
+   TST                        0
+.br
+   D_SENSE                    0
+.br
+   GLTSD                      1
+.br
+   RLEC                       0
+.PP
+[Actually the Control page has more parameters that shown above.] Next
+output those parameters in single line form:
+.PP
+   $ sginfo \-CX /dev/sda
+.br
+   0 0 1 0
+.PP
+Let us assume that the GLTSD bit is to be cleared. The command that
+will clear it is:
+.PP
+   $ sginfo \-CXR /dev/sda 0 0 0 0
+.PP
+The same number of parameters output by the "\-CX" command needs to be
+placed at the end of the "\-CXR" command line (after the device name).
+Now check that the change took effect:
+.PP
+   $ sginfo \-C /dev/sda
+.br
+   Control mode page (0xa)
+.br
+   \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
+.br
+   TST                        0
+.br
+   D_SENSE                    0
+.br
+   GLTSD                      0
+.br
+   RLEC                       0
+.PP
+When a mode page is "replaced" the default action is to change both the
+current page and the saved page. [For some reason versions of sginfo and
+scsiinfo prior to 2.0 did not change the "saved" page.] To change only
+the current mode page but not the corresponding saved page use the "\-N"
+option.
+.PP
+.SH GENERATING SCRIPT FILES AND HEX PAGES
+The "\-aX" or "\-AX" option generates output suitable for a script file.
+Mode pages are output in list format (after the INQUIRY and serial
+number) one page per line. To facilitate running the output as (part
+of) a script file to assert chosen mode page values, each line is
+prefixed by "sginfo \-t \fIPN\fR[,\fISPN\fR] \-XR ". When such a script
+file is run, it will have the effect of re\-asserting the mode
+page values to what they were when the "\-aX" generated the output.
+.PP
+All mode pages (and subpages) supported by the device can be accessed via
+the \-t and \-u options. To see all
+mode pages supported by the device use "\-u 63". [To see all mode pages
+and all subpages use "\-u 63,255".] To list the control mode page in
+hex (mode page index in the first column and the corresponding byte
+value in the second column) use "\-u 0xa". Mode pages (subpage code == 0)
+start at index position 2 while subpages start at index position 4.
+If the "\-Xu ..." option is used then a list a hex values each value
+prefixed by "@" is output. Mode (sub)page values can then be modified
+with the "\-RXu ..." option.
+.PP
+.SH RESTRICTIONS
+The SCSI MODE SENSE command yields block descriptors as well as a mode
+page(s). This utility ignores block descriptors and does not display
+them. The "disable block descriptor" switch (DBD) in the MODE SENSE command
+is not set since some devices yield errors when it is set. When mode page
+values are being changed (the "\-R" option), the same block descriptor
+obtained by reading the mode page (i.e. via a MODE SENSE command) is sent
+back when the mode page is written (i.e. via a MODE SELECT command).
+.PP
+.SH REFERENCES
+SCSI (draft) standards can be found at http://www.t10.org . The relevant
+documents are SPC\-4 (mode pages common to all device types),
+SBC\-2 (direct access devices [e.g. disks]), MMC\-4 (CDs and DVDs) and
+SSC\-2 (tapes).
+.PP
+.SH AUTHORS
+Written by Eric Youngdale, Michael Weller, Douglas Gilbert, Kurt Garloff,
+Thomas Steudten
+.PP
+.SH HISTORY
+scsiinfo version 1.0 was released by Eric Youngdale on 1st November 1993.
+The most recent version of scsiinfo is version 1.7 with the last patches
+by Michael Weller. sginfo is derived from scsiinfo and uses the sg
+interface to get around the 4 KB buffer limit in scsiinfo that cramped
+the display of defect lists especially. sginfo was written by Douglas
+Gilbert with patches from Kurt Garloff. This manpage corresponds with
+version 2.25 of sginfo.
+.PP
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B lsscsi(lsscsi), scsiinfo(internet); sg_modes, sg_inq, sg_vpd (sg3_utils),
+.B sdparm(sdparm)
diff --git a/sg3_utils/doc/sgm_dd.8 b/sg3_utils/doc/sgm_dd.8
new file mode 100644
index 0000000..d4e0378
--- /dev/null
+++ b/sg3_utils/doc/sgm_dd.8
@@ -0,0 +1,273 @@
+.TH SGM_DD "8" "February 2015" "sg3_utils\-1.41" SG3_UTILS
+.SH NAME
+sgm_dd \- copy data to and from files and devices, especially SCSI
+devices
+.SH SYNOPSIS
+.B sgm_dd
+[\fIbs=BS\fR] [\fIcount=COUNT\fR] [\fIibs=BS\fR] [\fIif=IFILE\fR]
+[\fIiflag=FLAGS\fR] [\fIobs=BS\fR] [\fIof=OFILE\fR] [\fIoflag=FLAGS\fR]
+[\fIseek=SEEK\fR] [\fIskip=SKIP\fR] [\fI\-\-help\fR] [\fI\-\-version\fR]
+.PP
+[\fIbpt=BPT\fR] [\fIcdbsz=\fR6|10|12|16] [\fIdio=\fR0|1] [\fIsync=\fR0|1]
+[\fItime=\fR0|1] [\fIverbose=VERB\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Copy data to and from any files. Specialized for "files" that are
+Linux SCSI generic (sg) devices and raw devices. Uses memory mapped
+transfers on sg devices. Similar syntax and semantics to
+.B dd(1)
+but does not perform any conversions.
+.PP
+Will only perform memory mapped transfers when \fIIFILE\fR or \fIOFILE\fR
+are SCSI generic (sg) devices.
+.PP
+If both \fIIFILE\fR and \fIOFILE\fR are sg devices then memory mapped
+transfers are performed on \fIIFILE\fR. If no other flags are specified
+then indirect IO is performed on \fIOFILE\fR. If 'oflag=dio' is given then
+direct IO is attempted on \fIOFILE\fR. If direct IO is not available, then
+this utility falls back to indirect IO and reports this at the end of the
+copy.
+.PP
+The first group in the synopsis above are "standard" Unix
+.B dd(1)
+operands. The second group are extra options added by this utility.
+Both groups are defined below.
+.SH OPTIONS
+.TP
+\fBbpt\fR=\fIBPT\fR
+each IO transaction will be made using \fIBPT\fR blocks (or less if
+near the end of the copy). Default is 128 for block sizes less that 2048
+bytes, otherwise the default is 32. So for bs=512 the reads and writes
+will each convey 64 KiB of data by default (less if near the end of the
+transfer or memory restrictions). When cd/dvd drives are accessed, the
+block size is typically 2048 bytes and bpt defaults to 32 which again
+implies 64 KiB transfers.
+.TP
+\fBbs\fR=\fIBS\fR
+where \fIBS\fR
+.B must
+be the block size of the physical device. Note that this differs from
+.B dd(1)
+which permits \fIBS\fR to be an integral multiple. Default is 512 which
+is usually correct for disks but incorrect for cdroms (which normally
+have 2048 byte blocks). For this utility the maximum size of each individual
+IO operation is \fIBS\fR * \fIBPT\fR bytes.
+.TP
+\fBcdbsz\fR=6 | 10 | 12 | 16
+size of SCSI READ and/or WRITE commands issued on sg device names.
+Default is 10 byte SCSI command blocks (unless calculations indicate
+that a 4 byte block number may be exceeded, in which case it defaults
+to 16 byte SCSI commands).
+.TP
+\fBcount\fR=\fICOUNT\fR
+copy \fICOUNT\fR blocks from \fIIFILE\fR to \fIOFILE\fR. Default is the
+minimum (of \fIIFILE\fR and \fIOFILE\fR) number of blocks that sg devices
+report from SCSI READ CAPACITY commands or that block devices (or their
+partitions) report. Normal files are not probed for their size. If
+\fIskip=SKIP\fR or \fIskip=SEEK\fR are given and the count is derived (i.e.
+not explicitly given) then the derived count is scaled back so that the
+copy will not overrun the device. If the file name is a block device
+partition and \fICOUNT\fR is not given then the size of the partition rather
+than the size of the whole device is used. If \fICOUNT\fR is not given and
+cannot be derived then an error message is issued and no copy takes place.
+.TP
+\fBdio\fR=0 | 1
+permits direct IO to be selected on the write\-side (i.e. on \fIOFILE\fR).
+Only allowed when the read\-side (i.e. \fIIFILE\fR) is a sg device. When
+1 there may be a "zero copy" copy (i.e. mmap\-ed transfer on the read into
+the user space and direct IO from there on the write, potentially two DMAs
+and no data copying from the CPU). Default is 0.
+The same action as 'dio=1' is also available with 'oflag=dio'.
+.TP
+\fBibs\fR=\fIBS\fR
+if given must be the same as \fIBS\fR given to 'bs=' option.
+.TP
+\fBif\fR=\fIIFILE\fR
+read from \fIIFILE\fR instead of stdin. If \fIIFILE\fR is '\-' then stdin
+is read. Starts reading at the beginning of \fIIFILE\fR unless \fISKIP\fR
+is given.
+.TP
+\fBiflag\fR=\fIFLAGS\fR
+where \fIFLAGS\fR is a comma separated list of one or more flags outlined
+below.  These flags are associated with \fIIFILE\fR and are ignored when
+\fIIFILE\fR is stdin.
+.TP
+\fBobs\fR=\fIBS\fR
+if given must be the same as \fIBS\fR given to 'bs=' option.
+.TP
+\fBof\fR=\fIOFILE\fR
+write to \fIOFILE\fR instead of stdout. If \fIOFILE\fR is '\-' then writes
+to stdout. If \fIOFILE\fR is /dev/null then no actual writes are performed.
+If \fIOFILE\fR is '.' (period) then it is treated the same way as
+/dev/null (this is a shorthand notation). If \fIOFILE\fR exists then it
+is _not_ truncated; it is overwritten from the start of \fIOFILE\fR
+unless 'oflag=append' or \fISEEK\fR is given.
+.TP
+\fBoflag\fR=\fIFLAGS\fR
+where \fIFLAGS\fR is a comma separated list of one or more flags outlined
+below.  These flags are associated with \fIOFILE\fR and are ignored when
+\fIOFILE\fR is /dev/null, '.' (period), or stdout.
+.TP
+\fBseek\fR=\fISEEK\fR
+start writing \fISEEK\fR bs\-sized blocks from the start of \fIOFILE\fR.
+Default is block 0 (i.e. start of file).
+.TP
+\fBskip\fR=\fISKIP\fR
+start reading \fISKIP\fR bs\-sized blocks from the start of \fIIFILE\fR.
+Default is block 0 (i.e. start of file).
+.TP
+\fBsync\fR=0 | 1
+when 1, does SYNCHRONIZE CACHE command on \fIOFILE\fR at the end of the
+transfer. Only active when \fIOFILE\fR is a sg device file name.
+.TP
+\fBtime\fR=0 | 1
+when 1, times transfer and does throughput calculation, outputting the
+results (to stderr) at completion. When 0 (default) doesn't perform timing.
+.TP
+\fBverbose\fR=\fIVERB\fR
+as \fIVERB\fR increases so does the amount of debug output sent to stderr.
+Default value is zero which yields the minimum amount of debug output.
+A value of 1 reports extra information that is not repetitive. A value
+2 reports cdbs and responses for SCSI commands that are not repetitive
+(i.e. other that READ and WRITE). Error processing is not considered
+repetitive. Values of 3 and 4 yield output for all SCSI commands (and
+Unix read() and write() calls) so there can be a lot of output.
+.TP
+\fB\-\-help\fR
+outputs usage message and exits.
+.TP
+\fB\-\-version\fR
+outputs version number information and exits.
+.SH FLAGS
+Here is a list of flags and their meanings:
+.TP
+append
+causes the O_APPEND flag to be added to the open of \fIOFILE\fR. For normal
+files this will lead to data appended to the end of any existing data.
+Cannot be used together with the \fIseek=SEEK\fR option as they conflict.
+The default action of this utility is to overwrite any existing data
+from the beginning of the file or, if \fISEEK\fR is given, starting at
+block \fISEEK\fR. Note that attempting to 'append' to a device file (e.g.
+a disk) will usually be ignored or may cause an error to be reported.
+.TP
+dio
+is only active with oflag (i.e. 'oflag=dio'). Its action is described in
+the 'dio=1' option description above.
+.TP
+direct
+causes the O_DIRECT flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR. This flag requires some memory alignment on IO. Hence user
+memory buffers are aligned to the page size. Has no effect on sg, normal
+or raw files.
+.TP
+dpo
+set the DPO bit (disable page out) in SCSI READ and WRITE commands. Not
+supported for 6 byte cdb variants of READ and WRITE. Indicates that
+data is unlikely to be required to stay in device (e.g. disk) cache.
+May speed media copy and/or cause a media copy to have less impact
+on other device users.
+.TP
+dsync
+causes the O_SYNC flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR. The "d" is prepended to lower confusion with the 'sync=0|1'
+option which has another action (i.e. a synchronisation to media at the
+end of the transfer).
+.TP
+excl
+causes the O_EXCL flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR.
+.TP
+fua
+causes the FUA (force unit access) bit to be set in SCSI READ and/or WRITE
+commands. This only has effect with sg devices. The 6 byte variants
+of the SCSI READ and WRITE commands do not support the FUA bit.
+Only active for sg device file names.
+.TP
+null
+has no affect, just a placeholder.
+.SH RETIRED OPTIONS
+Here are some retired options that are still present:
+.TP
+fua=0 | 1 | 2 | 3
+force unit access bit. When 3, fua is set on both \fIIFILE\fR and
+\fIOFILE\fR; when 2, fua is set on \fIIFILE\fR; when 1, fua is set on
+\fIOFILE\fR; when 0 (default), fua is cleared on both. See the 'fua' flag.
+.SH NOTES
+A raw device must be bound to a block device prior to using sgm_dd.
+See
+.B raw(8)
+for more information about binding raw devices. To be safe, the sg device
+mapping to SCSI block devices should be checked with the lsscsi utility
+before use.
+.PP
+Raw device partition information can often be found with
+.B fdisk(8)
+[the "\-ul" argument is useful in this respect].
+.PP
+Various numeric arguments (e.g. \fISKIP\fR) may include multiplicative
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section
+in the sg3_utils(8) man page.
+.PP
+The count, skip and seek parameters can take 64 bit values (i.e. very
+big numbers). Other values are limited to what can fit in a signed
+32 bit number.
+.PP
+Data usually gets to the user space in a 2 stage process: first the
+SCSI adapter DMAs into kernel buffers and then the sg driver copies
+this data into user memory (write operations reverse this sequence).
+With memory mapped transfers a kernel buffer reserved by sg is memory
+mapped (see the
+.B mmap(2)
+system call) into the user space. When this is done
+the second (redundant) copy from kernel buffers to user space is
+not needed. Hence the transfer is faster and requires less "grunt"
+from the CPU.
+.PP
+All informative, warning and error output is sent to stderr so that
+dd's output file can be stdout and remain unpolluted. If no options
+are given, then the usage message is output and nothing else happens.
+.PP
+For sg devices this utility issues SCSI READ and WRITE (SBC) commands which
+are appropriate for disks and reading from CD/DVD/BD drives. Those commands
+are not formatted correctly for tape devices so sgm_dd should not be used
+on tape devices.
+.PP
+This utility stops the copy if any error is encountered. For more
+advanced "copy on error" logic see the
+.B sg_dd
+utility (and its 'coe' flag).
+.SH EXAMPLES
+.PP
+See the examples given in the man page for
+.B sg_dd(8).
+.SH SIGNALS
+The signal handling has been borrowed from dd: SIGINT, SIGQUIT and
+SIGPIPE output the number of remaining blocks to be transferred and
+the records in + out counts; then they have their default action.
+SIGUSR1 causes the same information to be output yet the copy continues.
+All output caused by signals is sent to stderr.
+.SH EXIT STATUS
+The exit status of sgm_dd is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page. Since this utility works at a higher level
+than individual commands, and there are 'coe' and 'retries' flags,
+individual SCSI command failures do not necessary cause the process
+to exit.
+.SH AUTHORS
+Written by Douglas Gilbert and Peter Allworth.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2000\-2015 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+The simplest variant of this utility is called
+.B sg_dd.
+A POSIX threads version of this utility called
+.B sgp_dd
+is in the sg3_utils package. The lmbench package contains
+.B lmdd
+which is also interesting.
+.B dd(1), ddpt(ddpt), raw(8)
diff --git a/sg3_utils/doc/sgp_dd.8 b/sg3_utils/doc/sgp_dd.8
new file mode 100644
index 0000000..6d148f5
--- /dev/null
+++ b/sg3_utils/doc/sgp_dd.8
@@ -0,0 +1,316 @@
+.TH SGP_DD "8" "November 2012" "sg3_utils\-1.35" SG3_UTILS
+.SH NAME
+sgp_dd \- copy data to and from files and devices, especially SCSI
+devices
+.SH SYNOPSIS
+.B sgp_dd
+[\fIbs=BS\fR] [\fIcount=COUNT\fR] [\fIibs=BS\fR] [\fIif=IFILE\fR]
+[\fIiflag=FLAGS\fR] [\fIobs=BS\fR] [\fIof=OFILE\fR] [\fIoflag=FLAGS\fR]
+[\fIseek=SEEK\fR] [\fIskip=SKIP\fR] [\fI\-\-help\fR] [\fI\-\-version\fR]
+.PP
+[\fIbpt=BPT\fR] [\fIcoe=\fR0|1] [\fIcdbsz=\fR6|10|12|16] [\fIdeb=VERB\fR]
+[\fIdio=\fR0|1] [\fIsync=\fR0|1] [\fIthr=THR\fR] [\fItime=\fR0|1]
+[\fIverbose=VERB\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Copy data to and from any files. Specialised for "files" that are
+Linux SCSI generic (sg) and raw devices. Similar syntax and semantics to
+.B dd(1)
+but does not perform any conversions. Uses POSIX threads to increase
+the amount of parallelism. This improves speed in some cases.
+.PP
+The first group in the synopsis above are "standard" Unix
+.B dd(1)
+operands. The second group are extra options added by this utility.
+Both groups are defined below.
+.SH OPTIONS
+.TP
+\fBbpt\fR=\fIBPT\fR
+each IO transaction will be made using \fIBPT\fR blocks (or less if
+near the end of the copy). Default is 128 for block sizes less that 2048
+bytes, otherwise the default is 32. So for bs=512 the reads and writes
+will each convey 64 KiB of data by default (less if near the end of the
+transfer or memory restrictions). When cd/dvd drives are accessed, the
+block size is typically 2048 bytes and bpt defaults to 32 which again
+implies 64 KiB transfers.
+.TP
+\fBbs\fR=\fIBS\fR
+where \fIBS\fR
+.B must
+be the block size of the physical device. Note that this differs from
+.B dd(1)
+which permits 'bs' to be an integral multiple of the actual device block
+size. Default is 512 which is usually correct for disks but incorrect for
+cdroms (which normally have 2048 byte blocks).
+.TP
+\fBcdbsz\fR=6 | 10 | 12 | 16
+size of SCSI READ and/or WRITE commands issued on sg device names.
+Default is 10 byte SCSI command blocks (unless calculations indicate
+that a 4 byte block number may be exceeded, in which case it defaults
+to 16 byte SCSI commands).
+.TP
+\fBcoe\fR=0 | 1
+set to 1 for continue on error. Only applies to errors on sg devices.
+Thus errors on other files will stop sgp_dd. Default is 0 which
+implies stop on any error. See the 'coe' flag for more information.
+.TP
+\fBcount\fR=\fICOUNT\fR
+copy \fICOUNT\fR blocks from \fIIFILE\fR to \fIOFILE\fR. Default is the
+minimum (of \fIIFILE\fR and \fIOFILE\fR) number of blocks that sg devices
+report from SCSI READ CAPACITY commands or that block devices (or their
+partitions) report. Normal files are not probed for their size. If
+\fIskip=SKIP\fR or \fIskip=SEEK\fR are given and the count is deduced (i.e.
+not explicitly given) then that count is scaled back so that the copy will
+not overrun the device. If the file name is a block device partition and
+\fICOUNT\fR is not given then the size of the partition rather than the
+size of the whole device is used. If \fICOUNT\fR is not given and cannot be
+deduced then an error message is issued and no copy takes place.
+.TP
+\fBdeb\fR=\fIVERB\fR
+outputs debug information. If \fIVERB\fR is 0 (default) then there is
+minimal debug information and as \fIVERB\fR increases so does the amount
+of debug (max debug output when \fIVERB\fR is 9).
+.TP
+\fBdio\fR=0 | 1
+default is 0 which selects indirect IO. Value of 1 attempts direct
+IO which, if not available, falls back to indirect IO and notes this
+at completion. If direct IO is selected and /proc/scsi/sg/allow_dio
+has the value of 0 then a warning is issued (and indirect IO is performed)
+For finer grain control use 'iflag=dio' or 'oflag=dio'.
+.TP
+\fBibs\fR=\fIBS\fR
+if given must be the same as \fIBS\fR given to 'bs=' option.
+.TP
+\fBif\fR=\fIIFILE\fR
+read from \fIIFILE\fR instead of stdin. If \fIIFILE\fR is '\-' then stdin
+is read. Starts reading at the beginning of \fIIFILE\fR unless \fISKIP\fR
+is given.
+.TP
+\fBiflag\fR=\fIFLAGS\fR
+where \fIFLAGS\fR is a comma separated list of one or more flags outlined
+below.  These flags are associated with \fIIFILE\fR and are ignored when
+\fIIFILE\fR is stdin.
+.TP
+\fBobs\fR=\fIBS\fR
+if given must be the same as \fIBS\fR given to 'bs=' option.
+.TP
+\fBof\fR=\fIOFILE\fR
+write to \fIOFILE\fR instead of stdout. If \fIOFILE\fR is '\-' then writes
+to stdout.  If \fIOFILE\fR is /dev/null then no actual writes are performed.
+If \fIOFILE\fR is '.' (period) then it is treated the same way as
+/dev/null (this is a shorthand notation). If \fIOFILE\fR exists then it
+is _not_ truncated; it is overwritten from the start of \fIOFILE\fR
+unless 'oflag=append' or \fISEEK\fR is given.
+.TP
+\fBoflag\fR=\fIFLAGS\fR
+where \fIFLAGS\fR is a comma separated list of one or more flags outlined
+below.  These flags are associated with \fIOFILE\fR and are ignored when
+\fIOFILE\fR is /dev/null, '.' (period), or stdout.
+.TP
+\fBseek\fR=\fISEEK\fR
+start writing \fISEEK\fR bs\-sized blocks from the start of \fIOFILE\fR.
+Default is block 0 (i.e. start of file).
+.TP
+\fBskip\fR=\fISKIP\fR
+start reading \fISKIP\fR bs\-sized blocks from the start of \fIIFILE\fR.
+Default is block 0 (i.e. start of file).
+.TP
+\fBsync\fR=0 | 1
+when 1, does SYNCHRONIZE CACHE command on \fIOFILE\fR at the end of the
+transfer. Only active when \fIOFILE\fR is a sg device file name.
+.TP
+\fBthr\fR=\fITHR\fR
+where \fITHR\fR is the number or worker threads (default 4) that attempt to
+copy in parallel. Minimum is 1 and maximum is 16.
+.TP
+\fBtime\fR=0 | 1
+when 1, the transfer is timed and throughput calculation is
+performed, outputting the results (to stderr) at completion. When
+0 (default) no timing is performed.
+.TP
+\fBverbose\fR=\fIVERB\fR
+increase verbosity. Same as \fIdeb=VERB\fR. Added for compatibility with
+sg_dd and sgm_dd.
+.TP
+\fB\-\-help\fR
+outputs usage message and exits.
+.TP
+\fB\-\-version\fR
+outputs version number information and exits.
+.SH FLAGS
+Here is a list of flags and their meanings:
+.TP
+append
+causes the O_APPEND flag to be added to the open of \fIOFILE\fR. For normal
+files this will lead to data appended to the end of any existing data.
+Cannot be used together with the \fIseek=SEEK\fR option as they conflict.
+The default action of this utility is to overwrite any existing data
+from the beginning of the file or, if \fISEEK\fR is given, starting at
+block \fISEEK\fR. Note that attempting to 'append' to a device file (e.g.
+a disk) will usually be ignored or may cause an error to be reported.
+.TP
+coe
+continue on error. When given with 'iflag=', an error that is detected
+in a single SCSI command (typically 'bpt' blocks) is noted (by an error
+message sent to stderr), then zeros are substituted into the buffer
+for the corresponding write operation and the copy continues. Note that the
+.B sg_dd
+utility is more sophisticated in such error situations when 'iflag=coe'.
+When given with 'oflag=', any error reported by a SCSI WRITE command is
+reported to stderr and the copy continues (as if nothing went wrong).
+.TP
+dio
+request the sg device node associated with this flag does direct IO.
+If direct IO is not available, falls back to indirect IO and notes
+this at completion. If direct IO is selected and /proc/scsi/sg/allow_dio
+has the value of 0 then a warning is issued (and indirect IO is performed).
+.TP
+direct
+causes the O_DIRECT flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR. This flag requires some memory alignment on IO. Hence user
+memory buffers are aligned to the page size. Has no effect on sg, normal
+or raw files.
+.TP
+dpo
+set the DPO bit (disable page out) in SCSI READ and WRITE commands. Not
+supported for 6 byte cdb variants of READ and WRITE. Indicates that
+data is unlikely to be required to stay in device (e.g. disk) cache.
+May speed media copy and/or cause a media copy to have less impact
+on other device users.
+.TP
+dsync
+causes the O_SYNC flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR. The 'd' is prepended to lower confusion with the 'sync=0|1'
+option which has another action (i.e. a synchronisation to media at the
+end of the transfer).
+.TP
+excl
+causes the O_EXCL flag to be added to the open of \fIIFILE\fR and/or
+\fIOFILE\fR.
+.TP
+fua
+causes the FUA (force unit access) bit to be set in SCSI READ and/or WRITE
+commands. This only has effect with sg devices. The 6 byte variants
+of the SCSI READ and WRITE commands do not support the FUA bit.
+Only active for sg device file names.
+.TP
+null
+has no affect, just a placeholder.
+.SH RETIRED OPTIONS
+Here are some retired options that are still present:
+.TP
+coe=0 | 1
+continue on error is 0 (off) by default. When it is 1, it is
+equivalent to 'iflag=coe oflag=coe' described in the FLAGS section
+above.  Similar to 'conv=noerror,sync' in
+.B dd(1)
+utility. Default is 0 which implies stop on error. More advanced
+coe=1 processing on reads is performed by the sg_dd utility.
+.TP
+.TP
+fua=0 | 1 | 2 | 3
+force unit access bit. When 3, fua is set on both \fIIFILE\fR and
+\fIOFILE\fR; when 2, fua is set on \fIIFILE\fR;, when 1, fua is set on
+\fIOFILE\fR; when 0 (default), fua is cleared on both. See the 'fua' flag.
+.SH NOTES
+A raw device must be bound to a block device prior to using sgp_dd.
+See
+.B raw(8)
+for more information about binding raw devices. To be safe, the sg device
+mapping to SCSI block devices should be checked with 'cat /proc/scsi/scsi'
+before use.
+.PP
+Raw device partition information can often be found with
+.B fdisk(8)
+[the "\-ul" argument is useful in this respect].
+.PP
+Various numeric arguments (e.g. \fISKIP\fR) may include multiplicative 
+suffixes or be given in hexadecimal. See the "NUMERIC ARGUMENTS" section 
+in the sg3_utils(8) man page.
+.PP
+The \fICOUNT\fR, \fISKIP\fR and \fISEEK\fR arguments can take 64 bit
+values (i.e. very big numbers). Other values are limited to what can fit in
+a signed 32 bit number.
+.PP
+Data usually gets to the user space in a 2 stage process: first the
+SCSI adapter DMAs into kernel buffers and then the sg driver copies
+this data into user memory (write operations reverse this sequence).
+This is called "indirect IO" and there is a 'dio' option to select
+"direct IO" which will DMA directly into user memory. Due to some
+issues "direct IO" is disabled in the sg driver and needs a
+configuration change to activate it.
+.PP
+All informative, warning and error output is sent to stderr so that
+dd's output file can be stdout and remain unpolluted. If no options
+are given, then the usage message is output and nothing else happens.
+.PP
+Why use sgp_dd? Because in some cases it is twice as fast as dd
+(mainly with sg devices, raw devices give some improvement).
+Another reason is that big copies fill the block device caches
+which has a negative impact on other machine activity.
+.SH SIGNALS
+The signal handling has been borrowed from dd: SIGINT, SIGQUIT and
+SIGPIPE output the number of remaining blocks to be transferred and
+the records in + out counts; then they have their default action.
+SIGUSR1 causes the same information to be output yet the copy continues.
+All output caused by signals is sent to stderr.
+.SH EXAMPLES
+.PP
+Looks quite similar in usage to dd:
+.PP
+   sgp_dd if=/dev/sg0 of=t bs=512 count=1MB
+.PP
+This will copy 1 million 512 byte blocks from the device associated with
+/dev/sg0 (which should have 512 byte blocks) to a file called t.
+Assuming /dev/sda and /dev/sg0 are the same device then the above is
+equivalent to:
+.PP
+   dd if=/dev/sda of=t bs=512 count=1000000
+.PP
+although dd's speed may improve if bs was larger and count was
+correspondingly scaled. Using a raw device to do something similar on a
+ATA disk:
+.PP
+   raw /dev/raw/raw1 /dev/hda
+.br
+   sgp_dd if=/dev/raw/raw1 of=t bs=512 count=1MB
+.PP
+To copy a SCSI disk partition to an ATA disk partition:
+.PP
+   raw /dev/raw/raw2 /dev/hda3
+.br
+   sgp_dd if=/dev/sg0 skip=10123456 of=/dev/raw/raw2 bs=512
+.PP
+This assumes a valid partition is found on the SCSI disk at the given
+skip block address (past the 5 GB point of that disk) and that
+the partition goes to the end of the SCSI disk. An explicit count
+is probably a safer option.
+.PP
+To do a fast copy from one SCSI disk to another one with similar
+geometry (stepping over errors on the source disk):
+.PP
+   sgp_dd if=/dev/sg0 of=/dev/sg1 bs=512 coe=1
+.SH EXIT STATUS
+The exit status of sgp_dd is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page. Since this utility works at a higher level
+than individual commands, and there are 'coe' and 'retries' flags,
+individual SCSI command failures do not necessary cause the process
+to exit.
+.SH AUTHORS
+Written by Douglas Gilbert and Peter Allworth.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2000\-2012 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+A simpler, non\-threaded version of this utility but with more
+advanced "continue on error" logic is called
+.B sg_dd
+and is also found in the sg3_utils package. The lmbench package contains
+.B lmdd
+which is also interesting.
+.B raw(8), dd(1)
diff --git a/sg3_utils/examples/Makefile b/sg3_utils/examples/Makefile
new file mode 100644
index 0000000..2f02328
--- /dev/null
+++ b/sg3_utils/examples/Makefile
@@ -0,0 +1,124 @@
+SHELL = /bin/sh
+
+PREFIX=/usr/local
+INSTDIR=$(DESTDIR)/$(PREFIX)/bin
+MANDIR=$(DESTDIR)/$(PREFIX)/man
+
+CC = gcc
+LD = gcc
+
+EXECS = sg_simple1 sg_simple2 sg_simple3 sg_simple4 sg_simple16 \
+	sg_iovec_tst scsi_inquiry sg_excl sg_sense_test sg_simple5 \
+	sg__sat_identify sg__sat_phy_event sg__sat_set_features \
+	sg_sat_chk_power sg_sat_smart_rd_data
+
+EXTRAS = sg_queue_tst sgq_dd
+
+BSG_EXTRAS = bsg_queue_tst
+
+
+MAN_PGS = 
+MAN_PREF = man8
+
+LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+
+CFLAGS = -g -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+# CFLAGS = -g -O2 -Wall -iquote ../include -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS)
+# CFLAGS = -g -O2 -Wall -pedantic -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+
+LDFLAGS =
+
+LIBFILESOLD = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_io_linux.o
+LIBFILESNEW = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_pt_linux.o
+
+all: $(EXECS)
+
+extras: $(EXTRAS)
+
+bsg: $(BSG_EXTRAS)
+
+
+depend dep:
+	for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+	done > .depend
+
+clean:
+	/bin/rm -f *.o $(EXECS) $(EXTRAS) $(BSG_EXTRAS) core .depend
+
+sg_simple1: sg_simple1.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_simple2: sg_simple2.o
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_simple3: sg_simple3.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_simple4: sg_simple4.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_simple16: sg_simple16.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_iovec_tst: sg_iovec_tst.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+scsi_inquiry: scsi_inquiry.o
+	$(LD) -o $@ $(LDFLAGS) $^ 
+
+sg_excl: sg_excl.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_sense_test: sg_sense_test.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_simple5: sg_simple5.o $(LIBFILESNEW)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg__sat_identify: sg__sat_identify.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg__sat_phy_event: sg__sat_phy_event.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg__sat_set_features: sg__sat_set_features.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_sat_chk_power: sg_sat_chk_power.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_sat_smart_rd_data: sg_sat_smart_rd_data.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_queue_tst: sg_queue_tst.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^ 
+
+bsg_queue_tst: bsg_queue_tst.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^ 
+
+sgq_dd: sgq_dd.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^ 
+
+install: $(EXECS)
+	install -d $(INSTDIR)
+	for name in $^; \
+	 do install -s -o root -g root -m 755 $$name $(INSTDIR); \
+	done
+	install -d $(MANDIR)/$(MAN_PREF)
+	for mp in $(MAN_PGS); \
+	 do install -o root -g root -m 644 $$mp $(MANDIR)/$(MAN_PREF); \
+	 gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \
+	done
+
+uninstall:
+	dists="$(EXECS)"; \
+	for name in $$dists; do \
+	 rm -f $(INSTDIR)/$$name; \
+	done
+	for mp in $(MAN_PGS); do \
+	 rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \
+	done
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/sg3_utils/examples/Makefile.cplus b/sg3_utils/examples/Makefile.cplus
new file mode 100644
index 0000000..b884b29
--- /dev/null
+++ b/sg3_utils/examples/Makefile.cplus
@@ -0,0 +1,83 @@
+SHELL = /bin/sh
+
+PREFIX=/usr/local
+INSTDIR=$(DESTDIR)/$(PREFIX)/bin
+MANDIR=$(DESTDIR)/$(PREFIX)/man
+
+CC = g++
+LD = g++
+## CC = clang++
+## LD = clang++
+
+EXECS = sg_tst_excl sg_tst_excl2 sg_tst_excl3 sg_tst_context sg_tst_async
+
+EXTRAS =
+
+BSG_EXTRAS =
+
+
+MAN_PGS = 
+MAN_PREF = man8
+
+LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+
+CPPFLAGS = -std=c++11 -pthread -g -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+## CFLAGS = -g -O2 -W -Wall -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+# CFLAGS = -g -O2 -Wall -iquote ../include -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS)
+# CFLAGS = -g -O2 -Wall -pedantic -iquote ../include -D_REENTRANT $(LARGE_FILE_FLAGS)
+
+LDFLAGS = -std=c++11 -pthread
+
+LIBFILESOLD = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_io_linux.o
+LIBFILESNEW = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_pt_linux.o
+
+all: $(EXECS)
+
+extras: $(EXTRAS)
+
+
+depend dep:
+	for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+	done > .depend
+
+clean:
+	/bin/rm -f *.o $(EXECS) $(EXTRAS) $(BSG_EXTRAS) core .depend
+
+sg_tst_excl: sg_tst_excl.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_tst_excl2: sg_tst_excl2.o $(LIBFILESNEW)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_tst_excl3: sg_tst_excl3.o $(LIBFILESNEW)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_tst_context: sg_tst_context.o $(LIBFILESNEW)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+sg_tst_async: sg_tst_async.o $(LIBFILESOLD)
+	$(LD) -o $@ $(LDFLAGS) $^
+
+install: $(EXECS)
+	install -d $(INSTDIR)
+	for name in $^; \
+	 do install -s -o root -g root -m 755 $$name $(INSTDIR); \
+	done
+	install -d $(MANDIR)/$(MAN_PREF)
+	for mp in $(MAN_PGS); \
+	 do install -o root -g root -m 644 $$mp $(MANDIR)/$(MAN_PREF); \
+	 gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \
+	done
+
+uninstall:
+	dists="$(EXECS)"; \
+	for name in $$dists; do \
+	 rm -f $(INSTDIR)/$$name; \
+	done
+	for mp in $(MAN_PGS); do \
+	 rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \
+	done
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/sg3_utils/examples/Makefile.freebsd b/sg3_utils/examples/Makefile.freebsd
new file mode 100644
index 0000000..7b90aa9
--- /dev/null
+++ b/sg3_utils/examples/Makefile.freebsd
@@ -0,0 +1,63 @@
+SHELL = /bin/sh
+
+PREFIX=/usr/local
+INSTDIR=$(DESTDIR)/$(PREFIX)/bin
+MANDIR=$(DESTDIR)/$(PREFIX)/man
+
+CC = gcc
+LD = gcc
+
+EXECS = sg_simple5
+
+MAN_PGS =
+MAN_PREF = man8
+
+OS_FLAGS = -DSG_LIB_FREEBSD
+EXTRA_FLAGS = $(OS_FLAGS)
+
+# CFLAGS = -O2 -Wall -W $(EXTRA_FLAGS)
+CFLAGS = -g -O2 -Wall -W $(EXTRA_FLAGS)
+# CFLAGS = -g -O2 -Wall -W -pedantic -std=c99 $(EXTRA_FLAGS)
+
+CFLAGS_PTHREADS = -D_REENTRANT
+
+# there is no rule to make the following in the parent directory,
+# it is assumed they are already built.
+D_FILES = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_pt_freebsd.o
+
+LDFLAGS = -lcam
+# LDFLAGS = -v -lm
+
+all: $(EXECS)
+
+depend dep:
+	for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+	done > .depend
+
+clean:
+	/bin/rm -f *.o $(EXECS) core* .depend *.a *.la *.lo
+	/bin/rm -rf .libs
+
+sg_simple5: sg_simple5.o $(D_FILES)
+	$(LD) -o $@ $(LDFLAGS) $@.o $(D_FILES)
+
+install: $(EXECS)
+	install -d $(INSTDIR)
+	for name in $(EXECS); \
+	 do install -s -m 755 $$name $(INSTDIR); \
+	done
+	install -d $(MANDIR)/$(MAN_PREF)
+	for mp in $(MAN_PGS); \
+	 do install -m 644 $$mp $(MANDIR)/$(MAN_PREF); \
+	 gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \
+	done
+
+uninstall:
+	dists="$(EXECS)"; \
+	for name in $$dists; do \
+	 rm -f $(INSTDIR)/$$name; \
+	done
+	for mp in $(MAN_PGS); do \
+	 rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \
+	done
+
diff --git a/sg3_utils/examples/README b/sg3_utils/examples/README
new file mode 100644
index 0000000..0a17fb6
--- /dev/null
+++ b/sg3_utils/examples/README
@@ -0,0 +1,19 @@
+Building files in this directory depends on several files being already
+built in the ../lib directory. So to build files here, the ./configure
+needs to be executed in the parent directory followed by changing
+directory to the lib directory and calling 'make' there.
+Another way is to do a top level 'make' after the ./configure which
+will make the libraries followed by all the utilities in the src/
+directory.
+
+There is an brief explanation of each example in the README file in
+the main (i.e. this directory's parent) directory. There are also
+some notes at the top of each source file.
+
+
+Those files with the extension "cpp" are C++ examples that use facilities
+in C++11. They can be built by calling 'make -f Makefile.cplus'. A
+gcc/g++ compiler of 4.7.3 vintage or later will be required.
+
+Douglas Gilbert
+10th July 2014
diff --git a/sg3_utils/examples/bsg_queue_tst.c b/sg3_utils/examples/bsg_queue_tst.c
new file mode 100644
index 0000000..ae7e3d0
--- /dev/null
+++ b/sg3_utils/examples/bsg_queue_tst.c
@@ -0,0 +1,171 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* If the following fails the Linux kernel is probably too old */
+#include <linux/bsg.h>
+
+
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_linux_inc.h"
+
+/* This program was used to test SCSI mid level queue ordering.
+   The default behaviour is to "queue at head" which is useful for
+   error processing but not for streaming READ and WRITE commands.
+
+*  Copyright (C) 2010-2016 D. Gilbert
+*  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.
+
+   Invocation: bsg_queue_tst [-t] <bsg_device>
+        -t      queue at tail
+
+   Version 0.90 (20100324)
+
+*/
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+#define SDIAG_CMD_LEN 6
+#define SENSE_BUFFER_LEN 96
+
+#define EBUFF_SZ 256
+
+#ifndef BSG_FLAG_Q_AT_TAIL
+#define BSG_FLAG_Q_AT_TAIL 0x10
+#endif
+
+#ifndef BSG_FLAG_Q_AT_HEAD
+#define BSG_FLAG_Q_AT_HEAD 0x20
+#endif
+
+
+int main(int argc, char * argv[])
+{
+    int bsg_fd, k, ok;
+    unsigned char inqCmdBlk[INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char sdiagCmdBlk[SDIAG_CMD_LEN] =
+                                {0x1d, 0, 0, 0, 0, 0};
+    unsigned char inqBuff[16][INQ_REPLY_LEN];
+    struct sg_io_v4 io_hdr[16];
+    struct sg_io_v4 rio_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[16][SENSE_BUFFER_LEN];
+    int q_at_tail = 0;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-t", argv[k], 2))
+            ++q_at_tail;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'bsg_queue_tst [-t] <bsg_device>'\n"
+               "where:\n      -t   queue_at_tail (def: q_at_head)\n");
+        return 1;
+    }
+
+    /* An access mode of O_RDWR is required for write()/read() interface */
+    if ((bsg_fd = open(file_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "bsg_queue_tst: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+
+    for (k = 0; k < 16; ++k) {
+        /* Prepare INQUIRY command */
+        memset(&io_hdr[k], 0, sizeof(struct sg_io_v4));
+        io_hdr[k].guard = 'Q';
+        /* io_hdr[k].iovec_count = 0; */  /* memset takes care of this */
+        if (0 == (k % 3)) {
+            io_hdr[k].request_len = sizeof(sdiagCmdBlk);
+            io_hdr[k].request = (uint64_t)(long)sdiagCmdBlk;
+        } else {
+            io_hdr[k].request_len = sizeof(inqCmdBlk);
+            io_hdr[k].request = (uint64_t)(long)inqCmdBlk;
+            io_hdr[k].din_xfer_len = INQ_REPLY_LEN;
+            io_hdr[k].din_xferp = (uint64_t)(long)inqBuff[k];
+        }
+        io_hdr[k].response = (uint64_t)(long)sense_buffer[k];
+        io_hdr[k].max_response_len = SENSE_BUFFER_LEN;
+        io_hdr[k].timeout = 20000;     /* 20000 millisecs == 20 seconds */
+        io_hdr[k].usr_ptr = k;
+        /* default is to queue at head (in SCSI mid level) */
+        if (q_at_tail)
+            io_hdr[k].flags |= BSG_FLAG_Q_AT_TAIL;
+        else
+            io_hdr[k].flags |= BSG_FLAG_Q_AT_HEAD;
+
+        if (write(bsg_fd, &io_hdr[k], sizeof(struct sg_io_v4)) < 0) {
+            perror("bsg_queue_tst: bsg write error");
+            close(bsg_fd);
+            return 1;
+        }
+    }
+    /* sleep(3); */
+    for (k = 0; k < 16; ++k) {
+        memset(&rio_hdr, 0, sizeof(struct sg_io_v4));
+        rio_hdr.guard = 'Q';
+        if (read(bsg_fd, &rio_hdr, sizeof(struct sg_io_v4)) < 0) {
+            perror("bsg_queue_tst: bsg read error");
+            close(bsg_fd);
+            return 1;
+        }
+        /* now for the error processing */
+        ok = 0;
+        if (0 == rio_hdr.device_status)
+            ok = 1;
+        else {
+            switch (sg_err_category_sense((unsigned char *)(long)
+                    rio_hdr.response, rio_hdr.response_len)) {
+            case SG_LIB_CAT_CLEAN:
+                ok = 1;
+                break;
+            case SG_LIB_CAT_RECOVERED:
+                printf("Recovered error, continuing\n");
+                ok = 1;
+                break;
+            default: /* won't bother decoding other categories */
+                fprintf(stderr, "command error:\n");
+                sg_print_sense(NULL, (unsigned char *)(long)rio_hdr.response,
+                               rio_hdr.response_len, 1);
+                break;
+            }
+        }
+
+        if (ok) { /* output result if it is available */
+            /* if (0 == rio_hdr.pack_id) */
+            if (0 == (rio_hdr.usr_ptr % 3))
+                printf("SEND DIAGNOSTIC %d duration=%u\n",
+                       (int)rio_hdr.usr_ptr, rio_hdr.duration);
+            else
+                printf("INQUIRY %d duration=%u\n", (int)rio_hdr.usr_ptr,
+                       rio_hdr.duration);
+        }
+    }
+
+    close(bsg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/forwarded_sense.txt b/sg3_utils/examples/forwarded_sense.txt
new file mode 100644
index 0000000..98d8ab4
--- /dev/null
+++ b/sg3_utils/examples/forwarded_sense.txt
@@ -0,0 +1,16 @@
+# Test forwarded sense data. Values are in hex.
+# Invocation: 'sg_decode_sense -f forwarded_sense.txt'   [dpg 20110209]
+
+# descriptor header
+72 6 18 7 0 0 0 1c
+
+# Forwarded sense [len=12]
+c a 1 2
+72 6 18 7 0 0 0 0
+
+# Information [len=12]
+0 a 80 0 11 22 33 44 55 66 77 88
+
+# FRU [len=4]
+3 2 0
+ 99
diff --git a/sg3_utils/examples/reassign_addr.txt b/sg3_utils/examples/reassign_addr.txt
new file mode 100644
index 0000000..9d48d00
--- /dev/null
+++ b/sg3_utils/examples/reassign_addr.txt
@@ -0,0 +1,11 @@
+# This file is an example for the sg_reassign utility.
+# That utility can take one or more logical block addresses from stdin when
+# either the '--address=-" or "-a -" option is given on the command line.
+
+# To see logical block addresses placed in the command parameter block
+# without executing them command try something like:
+# 'sg_reassign --address=- --dummy -vv /dev/sda < reassign_addr.txt
+
+1,34,0x33,0X444  0x89abcde	0xdeadbeef  	# 6 lba's
+
+# dpg 20070130
diff --git a/sg3_utils/examples/ref_sense.txt b/sg3_utils/examples/ref_sense.txt
new file mode 100644
index 0000000..89bca5b
--- /dev/null
+++ b/sg3_utils/examples/ref_sense.txt
@@ -0,0 +1,7 @@
+# Test User data segment referral sense data. Values are in hex.
+# Invocation: 'sg_decode_sense -f ref_sense.txt'   [dpg 20110208]
+
+72,0,0,0,0,0,0     38
+b,36,1,0
+0,0,0,2,11,11,11,11,22,22,22,22,55,55,55,55,66,66,66,66   1,0,0,7,  2,0,0,8
+0,0,0,1,77,77,77,77,77,77,77,77,88,88,88,88,88,88,88,88,  3,0,0,5
diff --git a/sg3_utils/examples/scsi_inquiry.c b/sg3_utils/examples/scsi_inquiry.c
new file mode 100644
index 0000000..8022295
--- /dev/null
+++ b/sg3_utils/examples/scsi_inquiry.c
@@ -0,0 +1,128 @@
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <scsi/scsi.h>
+/* #include <scsi/scsi_ioctl.h> */ /* glibc hides this file sometimes */
+
+/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
+   device driver.
+*  Copyright (C) 1999 D. Gilbert
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This program does a SCSI inquiry command on the given device and
+   outputs some of the result. This program highlights the use of the
+   SCSI_IOCTL_SEND_COMMAND ioctl. This should be able to be applied to
+   any SCSI device file descriptor (not just one related to sg). [Whether
+   this is a good idea on a disk while it is mounted is debatable.
+   No detrimental effects when this was tested ...]
+
+Version 0.14 20011218
+*/
+
+
+typedef struct my_scsi_ioctl_command {
+        unsigned int inlen;  /* _excluding_ scsi command length */
+        unsigned int outlen;
+        unsigned char data[1];  /* was 0 but that's not ISO C!! */
+                /* on input, scsi command starts here then opt. data */
+} My_Scsi_Ioctl_Command;
+
+#define OFF (2 * sizeof(unsigned int))
+
+#ifndef SCSI_IOCTL_SEND_COMMAND
+#define SCSI_IOCTL_SEND_COMMAND 1
+#endif
+
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+#define INQUIRY_REPLY_LEN 96
+
+
+int main(int argc, char * argv[])
+{
+    int s_fd, res, k, to;
+    unsigned char inqCmdBlk [INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0,
+                                                INQUIRY_REPLY_LEN, 0};
+    unsigned char * inqBuff = (unsigned char *)
+                                malloc(OFF + sizeof(inqCmdBlk) + 512);
+    unsigned char * buffp = inqBuff + OFF;
+    My_Scsi_Ioctl_Command * ishp = (My_Scsi_Ioctl_Command *)inqBuff;
+    char * file_name = 0;
+    int do_nonblock = 0;
+    int oflags = 0;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == strcmp(argv[k], "-n"))
+            do_nonblock = 1;
+        else if (*argv[k] != '-')
+            file_name = argv[k];
+        else {
+            printf("Unrecognized argument '%s'\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'scsi_inquiry [-n] <scsi_device>'\n");
+        printf("     where: -n   open device in non-blocking mode\n");
+        printf("  Examples: scsi_inquiry /dev/sda\n");
+        printf("            scsi_inquiry /dev/sg0\n");
+        printf("            scsi_inquiry -n /dev/scd0\n");
+        return 1;
+    }
+
+    if (do_nonblock)
+        oflags = O_NONBLOCK;
+    s_fd = open(file_name, oflags | O_RDWR);
+    if (s_fd < 0) {
+        if ((EROFS == errno) || (EACCES == errno)) {
+            s_fd = open(file_name, oflags | O_RDONLY);
+            if (s_fd < 0) {
+                perror("scsi_inquiry: open error");
+                return 1;
+            }
+        }
+        else {
+            perror("scsi_inquiry: open error");
+            return 1;
+        }
+    }
+    /* Don't worry, being very careful not to write to a none-scsi file ... */
+    res = ioctl(s_fd, SCSI_IOCTL_GET_BUS_NUMBER, &to);
+    if (res < 0) {
+        /* perror("ioctl on scsi device, error"); */
+        printf("scsi_inquiry: not a scsi device\n");
+        return 1;
+    }
+
+    ishp->inlen = 0;
+    ishp->outlen = INQUIRY_REPLY_LEN;
+    memcpy(buffp, inqCmdBlk, INQUIRY_CMDLEN);
+    res = ioctl(s_fd, SCSI_IOCTL_SEND_COMMAND, inqBuff);
+    if (0 == res) {
+        to = (int)*(buffp + 7);
+        printf("    %.8s  %.16s  %.4s, byte_7=0x%x\n", buffp + 8,
+               buffp + 16, buffp + 32, to);
+    }
+    else if (res < 0)
+        perror("scsi_inquiry: SCSI_IOCTL_SEND_COMMAND err");
+    else
+        printf("scsi_inquiry: SCSI_IOCTL_SEND_COMMAND status=0x%x\n", res);
+
+    res = close(s_fd);
+    if (res < 0) {
+        perror("scsi_inquiry: close error");
+        return 1;
+    }
+    return 0;
+}
diff --git a/sg3_utils/examples/sdiag_sas_p0_cjtpat.txt b/sg3_utils/examples/sdiag_sas_p0_cjtpat.txt
new file mode 100644
index 0000000..7281092
--- /dev/null
+++ b/sg3_utils/examples/sdiag_sas_p0_cjtpat.txt
@@ -0,0 +1,12 @@
+# This is the hex for a SAS protocol specific diagnostic
+# page. It will attempt to put phy identifier 0 of the
+# given device into CJTPAT (jitter pattern) generation mode.
+# Physical transmission speed is 3 Gbps
+# N.B. This will turn the receiver off on phy id 0.
+#
+# Usage example: 'sg_senddiag --pf --raw=- /dev/sg2 < {this_file}'
+#
+3f,6,0,1c,0,1,2,9,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
diff --git a/sg3_utils/examples/sdiag_sas_p1_cjtpat.txt b/sg3_utils/examples/sdiag_sas_p1_cjtpat.txt
new file mode 100644
index 0000000..c82a558
--- /dev/null
+++ b/sg3_utils/examples/sdiag_sas_p1_cjtpat.txt
@@ -0,0 +1,13 @@
+# This is the hex for a SAS protocol specific diagnostic
+# page. It will attempt to put phy identifier 1 of the
+# given device into CJTPAT (jitter pattern) generation mode.
+# Physical transmission speed is 3 Gbps
+# See sdiag_sas_p1_stop.txt to turn off this test pattern.
+# N.B. This will turn the receiver off on phy id 1.
+#
+# Usage example: 'sg_senddiag --pf --raw=- /dev/sg2 < {this_file}'
+#
+3f,6,0,1c,1,1,2,9,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
diff --git a/sg3_utils/examples/sdiag_sas_p1_idle.txt b/sg3_utils/examples/sdiag_sas_p1_idle.txt
new file mode 100644
index 0000000..39091ce
--- /dev/null
+++ b/sg3_utils/examples/sdiag_sas_p1_idle.txt
@@ -0,0 +1,14 @@
+# This is the hex for a SAS protocol specific diagnostic
+# page. It will attempt to put phy identifier 1 of the
+# given device into IDLE (continuously transmit idle dwords) mode.
+# Physical transmission speed is 3 Gbps (last number on first
+# active line can be 8 for 1.5Gbps, 9 for 3Gbps and 10 for 6Gbps).
+# See sdiag_sas_p1_stop.txt to turn off this test pattern.
+# N.B. This will turn the receiver off on phy id 1.
+#
+# Usage example: 'sg_senddiag --pf --raw=- /dev/sg2 < {this_file}'
+#
+3f,6,0,1c,1,1,12,9,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
diff --git a/sg3_utils/examples/sdiag_sas_p1_stop.txt b/sg3_utils/examples/sdiag_sas_p1_stop.txt
new file mode 100644
index 0000000..55b95c3
--- /dev/null
+++ b/sg3_utils/examples/sdiag_sas_p1_stop.txt
@@ -0,0 +1,11 @@
+# This is the hex for a SAS protocol specific diagnostic
+# page. It will attempt to stop phy identifier 1 of the
+# given device producing a test pattern.
+# N.B. This should make phy id 1 usable for SAS protocols again.
+#
+# Usage example: 'sg_senddiag --pf --raw=- /dev/sg2 < {this_file}'
+#
+3f,6,0,1c,1,0,2,9,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0
diff --git a/sg3_utils/examples/sg__sat_identify.c b/sg3_utils/examples/sg__sat_identify.c
new file mode 100644
index 0000000..06ecfe6
--- /dev/null
+++ b/sg3_utils/examples/sg__sat_identify.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2006-2007 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program uses a ATA PASS-THROUGH (16) SCSI command to package
+   an ATA IDENTIFY DEVICE (A1h) command. If the '-p' option is given,
+   it will package an ATA IDENTIFY PACKET DEVICE (Ech) instead (for
+   ATAPI device like cd/dvd drives) See http://www.t10.org
+   SAT draft at time of writing: sat-r08a.pdf
+
+   Invocation: sg__sat_identify [-p] [-v] [-V] <device>
+
+   With SAT, the user can find out whether a device is an ATA disk or
+   an ATAPI device. The ATA Information VPD page contains a "command
+   code" field in byte 56. Its values are either ECh for a (s/p)ATA
+   disk, A1h for a (s/p)ATAPI device, or 0 for unknown.
+
+*/
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
+
+#define ATA_IDENTIFY_DEVICE 0xec
+#define ATA_IDENTIFY_PACKET_DEVICE 0xa1
+#define ID_RESPONSE_LEN 512
+
+#define EBUFF_SZ 256
+
+static char * version_str = "1.02 20070130";
+
+static void usage()
+{
+    fprintf(stderr, "Usage: "
+          "sg__sat_identify [-p] [-v] [-V] <device>\n"
+          "  where: -p    do IDENTIFY PACKET DEVICE (def: IDENTIFY "
+          "DEVICE) command\n"
+          "         -v    increase verbosity\n"
+          "         -V    print version string and exit\n\n"
+          "Performs a IDENTIFY (PACKET) DEVICE ATA command via a SAT "
+          "pass through\n");
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok;
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char inBuff[ID_RESPONSE_LEN];
+    unsigned char sense_buffer[32];
+    int do_packet = 0;
+    int verbose = 0;
+    int extend = 0;
+    int chk_cond = 0;   /* set to 1 to read register(s) back */
+    int protocol = 4;   /* PIO data-in */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+    int t_length = 2;   /* 0 -> no data transferred, 2 -> sector count */
+    const unsigned char * cucp;
+
+    memset(inBuff, 0, sizeof(inBuff));
+    for (k = 1; k < argc; ++k) {
+        if (0 == strcmp(argv[k], "-p"))
+            ++do_packet;
+        else if (0 == strcmp(argv[k], "-v"))
+            ++verbose;
+        else if (0 == strcmp(argv[k], "-vv"))
+            verbose += 2;
+        else if (0 == strcmp(argv[k], "-vvv"))
+            verbose += 3;
+        else if (0 == strcmp(argv[k], "-V")) {
+            fprintf(stderr, "version: %s\n", version_str);
+            exit(0);
+        } else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        usage();
+        return 1;
+    }
+
+    if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg__sat_identify: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+
+    /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+    aptCmdBlk[6] = 1;   /* sector count */
+    aptCmdBlk[14] = (do_packet ? ATA_IDENTIFY_PACKET_DEVICE :
+                                 ATA_IDENTIFY_DEVICE);
+    aptCmdBlk[1] = (protocol << 1) | extend;
+    aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
+                   (byte_block << 2) | t_length;
+    if (verbose) {
+        fprintf(stderr, "    ata pass through(16) cdb: ");
+        for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
+            fprintf(stderr, "%02x ", aptCmdBlk[k]);
+        fprintf(stderr, "\n");
+    }
+
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(aptCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = ID_RESPONSE_LEN;
+    io_hdr.dxferp = inBuff;
+    io_hdr.cmdp = aptCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg__sat_identify: SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        if (verbose)
+            sg_chk_n_print3(">>> ATA_16 command", &io_hdr, 1);
+        /* check for ATA Return Descriptor */
+        cucp = sg_scsi_sense_desc_find(io_hdr.sbp, io_hdr.sb_len_wr,
+                                       SAT_ATA_RETURN_DESC);
+        if (cucp && (cucp[3])) {
+            if (cucp[3] & 0x4) {
+                printf("error in returned FIS: aborted command\n");
+                printf("    try again with%s '-p' option\n",
+                       (do_packet ? "out" : ""));
+                break;
+            }
+        }
+        ok = 1;         /* not sure what is happening so output response */
+        if (0 == verbose) {
+            printf(">>> Recovered error on ATA_16, may have failed\n");
+            printf("    Add '-v' for more information\n");
+        }
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok) { /* output result if it is available */
+        printf("Response for IDENTIFY %sDEVICE ATA command:\n",
+               (do_packet ? "PACKET " : ""));
+        dWordHex((const unsigned short *)inBuff, 256, 0,
+                 sg_is_big_endian());
+    }
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg__sat_phy_event.c b/sg3_utils/examples/sg__sat_phy_event.c
new file mode 100644
index 0000000..b21b3c9
--- /dev/null
+++ b/sg3_utils/examples/sg__sat_phy_event.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2006-2007 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program uses a ATA PASS-THROUGH (16) SCSI command defined
+   by SAT to package an ATA READ LOG EXT (2Fh) command to fetch
+   log page 11h. That page contains SATA phy event counters.
+   For SAT see http://www.t10.org [draft prior to standard: sat-r09.pdf]
+   For ATA READ LOG EXT command see ATA-8/ACS at www.t13.org .
+   For SATA phy counter definitions see SATA 2.5 .
+
+   Invocation: sg_sat_phy_event [-v] [-V] <device>
+
+*/
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return Descriptor */
+
+#define ATA_READ_LOG_EXT 0x2f
+#define SATA_PHY_EVENT_LPAGE 0x11
+#define READ_LOG_EXT_RESPONSE_LEN 512
+
+#define EBUFF_SZ 256
+
+static const char * version_str = "1.00 20070507";
+
+static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"ignore", no_argument, 0, 'i'},
+        {"raw", no_argument, 0, 'r'},
+        {"reset", no_argument, 0, 'R'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void usage()
+{
+    fprintf(stderr, "Usage: "
+          "sg_sat_phy_event [--help] [--hex] [--raw] [--reset] [--verbose]\n"
+          "                        [--version] DEVICE\n"
+          "  where:\n"
+          "    --help|-h       print this usage message then exit\n"
+          "    --hex|-H        output response in hex bytes, use twice for\n"
+          "                    hex words\n"
+          "    --ignore|-i     ignore identifier names, output id value "
+          "instead\n"
+          "    --raw|-r        output response in binary to stdout\n"
+          "    --reset|-R      reset counters (after read)\n"
+          "    --verbose|-v    increase verbosity\n"
+          "    --version|-V    print version string then exit\n\n"
+          "Sends an ATA READ LOG EXT command via a SAT pass through to "
+          "fetch\nlog page 11h which contains SATA phy event counters\n");
+}
+
+struct phy_event_t {
+    int id;
+    const char * desc;
+};
+
+static struct phy_event_t phy_event_arr[] = {
+    {0x1, "Command failed and ICRC error bit set in Error register"},
+    {0x2, "R_ERR(p) response for data FIS"},
+    {0x3, "R_ERR(p) response for device-to-host data FIS"},
+    {0x4, "R_ERR(p) response for host-to-device data FIS"},
+    {0x5, "R_ERR(p) response for non-data FIS"},
+    {0x6, "R_ERR(p) response for device-to-host non-data FIS"},
+    {0x7, "R_ERR(p) response for host-to-device non-data FIS"},
+    {0x8, "Device-to-host non-data FIS retries"},
+    {0x9, "Transition from drive PHYRDY to drive PHYRDYn"},
+    {0xa, "Signature device-to-host register FISes due to COMRESET"},
+    {0xb, "CRC errors within host-to-device FIS"},
+    {0xd, "non CRC errors within host-to-device FIS"},
+    {0xf, "R_ERR(p) response for host-to-device data FIS, CRC"},
+    {0x10, "R_ERR(p) response for host-to-device data FIS, non-CRC"},
+    {0x12, "R_ERR(p) response for host-to-device non-data FIS, CRC"},
+    {0x13, "R_ERR(p) response for host-to-device non-data FIS, non-CRC"},
+    {0xc00, "PM: host-to-device non-data FIS, R_ERR(p) due to collision"},
+    {0xc01, "PM: signature register - device-to-host FISes"},
+    {0xc02, "PM: corrupts CRC propagation of device-to-host FISes"},
+    {0x0, NULL},
+};
+
+static const char * find_phy_desc(int id)
+{
+    const struct phy_event_t * pep;
+
+    for (pep = phy_event_arr; pep->desc; ++pep) {
+        if ((id & 0xfff) == pep->id)
+            return pep->desc;
+    }
+    return NULL;
+}
+
+static void dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, c, k, j, ok, res, id, len, vendor;
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    sg_io_hdr_t io_hdr;
+    char * device_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char inBuff[READ_LOG_EXT_RESPONSE_LEN];
+    unsigned char sense_buffer[64];
+    int hex = 0;
+    int ignore = 0;
+    int raw = 0;
+    int reset = 0;
+    int verbose = 0;
+    int extend = 0;
+    int chk_cond = 0;   /* set to 1 to read register(s) back */
+    int protocol = 4;   /* PIO data-in */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+    int t_length = 2;   /* 0 -> no data transferred, 2 -> sector count */
+    const unsigned char * cucp;
+    int ret = 0;
+    uint64_t ull;
+    const char * cp;
+
+    memset(inBuff, 0, sizeof(inBuff));
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hHirRvV",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+            usage();
+            exit(0);
+        case 'H':
+            ++hex;
+            break;
+        case 'i':
+            ++ignore;
+            break;
+        case 'r':
+            ++raw;
+            break;
+        case 'R':
+            ++reset;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            fprintf(stderr, "version: %s\n", version_str);
+            exit(0);
+        default:
+            fprintf(stderr, "unrecognised option code %c [0x%x]\n", c, c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                fprintf(stderr, "Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (0 == device_name) {
+        fprintf(stderr, "no DEVICE name detected\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if ((sg_fd = open(device_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_sat_phy_event: error opening file: %s", device_name);
+        perror(ebuff);
+        return SG_LIB_FILE_ERROR;
+    }
+
+    /* Prepare SCSI ATA PASS-THROUGH COMMAND (16) command */
+    if (reset > 0)
+        aptCmdBlk[4] = 1;                       /* features (7:0) */
+    aptCmdBlk[6] = 1;                           /* sector count */
+    aptCmdBlk[8] = SATA_PHY_EVENT_LPAGE;        /* lba_low (7:0) */
+    aptCmdBlk[14] = ATA_READ_LOG_EXT;           /* command */
+    aptCmdBlk[1] = (protocol << 1) | extend;
+    aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
+                   (byte_block << 2) | t_length;
+    if (verbose) {
+        fprintf(stderr, "    ata pass through(16) cdb: ");
+        for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
+            fprintf(stderr, "%02x ", aptCmdBlk[k]);
+        fprintf(stderr, "\n");
+    }
+
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(aptCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = READ_LOG_EXT_RESPONSE_LEN;
+    io_hdr.dxferp = inBuff;
+    io_hdr.cmdp = aptCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_sat_phy_event: SG_IO ioctl error");
+        close(sg_fd);
+        return SG_LIB_CAT_OTHER;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    ret = sg_err_category3(&io_hdr);
+    switch (ret) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        if (verbose)
+            sg_chk_n_print3(">>> ATA_16 command", &io_hdr, 1);
+        /* check for ATA Return Descriptor */
+        cucp = sg_scsi_sense_desc_find(io_hdr.sbp, io_hdr.sb_len_wr,
+                                       SAT_ATA_RETURN_DESC);
+        if (cucp && (cucp[3])) {
+            if (cucp[3] & 0x4) {
+                fprintf(stderr, "error in returned FIS: aborted command\n");
+                break;
+            }
+        }
+        ret = 0;
+        ok = 1;         /* not sure what is happening so output response */
+        if (0 == verbose) {
+            fprintf(stderr, ">>> Recovered error on ATA_16, may have "
+                    "failed\n");
+            fprintf(stderr, "    Add '-v' for more information\n");
+        }
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok) { /* output result if it is available */
+        if (raw > 0)
+            dStrRaw((const char *)inBuff, 512);
+        else {
+            if (verbose && hex)
+                fprintf(stderr, "Response to READ LOG EXT (page=11h):\n");
+            if (1 == hex)
+                dStrHex((const char *)inBuff, 512, 0);
+            else if (hex > 1)
+                dWordHex((const unsigned short *)inBuff, 256, 0,
+                         sg_is_big_endian());
+            else {
+                printf("SATA phy event counters:\n");
+                for (k = 4; k < 512; k += (len + 2)) {
+                    id = (inBuff[k + 1] << 8) + inBuff[k];
+                    if (0 == id)
+                        break;
+                    len = ((id >> 12) & 0x7) * 2;
+                    vendor = !!(id & 0x8000);
+                    id = id & 0xfff;
+                    ull = 0;
+                    for (j = len - 1; j >= 0; --j) {
+                        if (j < (len - 1))
+                            ull <<= 8;
+                        ull |= inBuff[k + 2 + j];
+                    }
+                    cp = NULL;
+                    if ((0 == vendor) && (0 == ignore))
+                        cp = find_phy_desc(id);
+                    if (cp)
+                        printf("  %s: %" PRIu64 "\n", cp, ull);
+                    else
+                        printf("  id=0x%x, vendor=%d, data_len=%d, "
+                               "val=%" PRIu64 "\n", id, vendor, len, ull);
+                }
+            }
+        }
+    }
+    res = close(sg_fd);
+    if (res < 0) {
+        fprintf(stderr, "close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/examples/sg__sat_set_features.c b/sg3_utils/examples/sg__sat_set_features.c
new file mode 100644
index 0000000..97bb930
--- /dev/null
+++ b/sg3_utils/examples/sg__sat_set_features.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2006-2007 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program performs a ATA PASS-THROUGH (16) SCSI command in order
+   to perform an ATA SET FEATURES command. See http://www.t10.org
+   SAT draft at time of writing: sat-r09.pdf
+
+   Invocation:
+        sg_sat_set_features [-c <n>] [-f <n>] [-h] [-L <n>] [-v] [-V] <device>
+
+*/
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
+
+#define ATA_SET_FEATURES 0xef
+
+#define EBUFF_SZ 256
+
+static char * version_str = "1.03 20070719";
+
+static struct option long_options[] = {
+        {"count", required_argument, 0, 'c'},
+        {"chk_cond", no_argument, 0, 'C'},
+        {"feature", required_argument, 0, 'f'},
+        {"help", no_argument, 0, 'h'},
+        {"lba", required_argument, 0, 'L'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+void usage()
+{
+    fprintf(stderr, "Usage: "
+          "sg_sat_set_features [--count=C] [--chk_cond] [--feature=F] "
+          "[--help]\n"
+          "                           [-lba=LBA] [--verbose] [--version] "
+          "DEVICE\n"
+          "  where:\n"
+          "    --count=C|-c C       count field contents (def: 0)\n"
+          "    --chk_cond|-C        set chk_cond field in pass-through "
+          "(def: 0)\n"
+          "    --feature=F|-f F     feature field contents (def: 0)\n"
+          "    --help|-h            output this usage message\n"
+          "    --lba=LBA| -L LBA    LBA field contents (def: 0)\n"
+          "    --verbose|-v         increase verbosity\n"
+          "    --version|-V         print version string and exit\n\n"
+          "Sends an ATA SET FEATURES command via a SAT pass through.\n"
+          "Primary feature code is placed in '--feature=F' with '--count=C' "
+          "and\n"
+          "'--lba=LBA' being auxiliaries for some features.  The arguments C, "
+          "F and LBA\n"
+          "are decimal unless prefixed by '0x' or have a trailing 'h'.\n"
+          "Example enabling write cache: 'sg_sat_set_feature --feature=2 "
+          "/dev/sdc'\n");
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, c, k;
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    sg_io_hdr_t io_hdr;
+    char device_name[256];
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[64];
+    int count = 0;
+    int feature = 0;
+    int lba = 0;
+    int verbose = 0;
+    int extend = 0;
+    int chk_cond = 0;   /* set to 1 to read register(s) back */
+    int protocol = 3;   /* non-data data-in */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+    int t_length = 0;   /* 0 -> no data transferred, 2 -> sector count */
+    const unsigned char * ucp = NULL;
+
+    memset(device_name, 0, sizeof(device_name));
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "c:Cf:hL:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            count = sg_get_num(optarg);
+            if ((count < 0) || (count > 255)) {
+                fprintf(stderr, "bad argument for '--count'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'C':
+            chk_cond = 1;
+            break;
+        case 'f':
+            feature = sg_get_num(optarg);
+            if ((feature < 0) || (feature > 255)) {
+                fprintf(stderr, "bad argument for '--feature'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'L':
+            lba = sg_get_num(optarg);
+            if ((lba < 0) || (lba > 255)) {
+                fprintf(stderr, "bad argument for '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            fprintf(stderr, "version: %s\n", version_str);
+            return 0;
+        default:
+            fprintf(stderr, "unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if ('\0' == device_name[0]) {
+            strncpy(device_name, argv[optind], sizeof(device_name) - 1);
+            device_name[sizeof(device_name) - 1] = '\0';
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                fprintf(stderr, "Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if ('\0' == device_name[0]) {
+        fprintf(stderr, "missing device name!\n");
+        usage();
+        return 1;
+    }
+
+    if ((sg_fd = open(device_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_sat_set_features: error opening file: %s", device_name);
+        perror(ebuff);
+        return 1;
+    }
+
+    /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+    aptCmdBlk[14] = ATA_SET_FEATURES;
+    aptCmdBlk[1] = (protocol << 1) | extend;
+    aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
+                   (byte_block << 2) | t_length;
+    aptCmdBlk[4] = feature;
+    aptCmdBlk[6] = count;
+    aptCmdBlk[8] = lba & 0xff;
+    aptCmdBlk[10] = (lba >> 8) & 0xff;
+    aptCmdBlk[12] = (lba >> 16) & 0xff;
+    if (verbose) {
+        fprintf(stderr, "    ata pass through(16) cdb: ");
+        for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
+            fprintf(stderr, "%02x ", aptCmdBlk[k]);
+        fprintf(stderr, "\n");
+    }
+
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(aptCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_NONE;
+    io_hdr.dxfer_len = 0;
+    io_hdr.dxferp = NULL;
+    io_hdr.cmdp = aptCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_sat_set_features: SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* error processing: N.B. expect check condition, no sense ... !! */
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        break;
+    case SG_LIB_CAT_RECOVERED:  /* sat-r09 uses this sk */
+    case SG_LIB_CAT_NO_SENSE:   /* earlier SAT drafts used this */
+        ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
+                                      SAT_ATA_RETURN_DESC);
+        if (NULL == ucp) {
+            if (verbose > 1)
+                printf("ATA Return Descriptor expected in sense but not "
+                       "found\n");
+            sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+        } else if (verbose)
+            sg_chk_n_print3("ATA Return Descriptor", &io_hdr, 1);
+        if (ucp && ucp[3]) {
+            if (ucp[3] & 0x4)
+                printf("error in returned FIS: aborted command\n");
+            else
+                printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
+        }
+        break;
+    default:
+        fprintf(stderr, "unexpected SCSI sense category\n");
+        ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
+                                      SAT_ATA_RETURN_DESC);
+        if (NULL == ucp)
+            sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+        else if (verbose)
+            sg_chk_n_print3("ATA Return Descriptor, as expected",
+                             &io_hdr, 1);
+        if (ucp && ucp[3]) {
+            if (ucp[3] & 0x4)
+                printf("error in returned FIS: aborted command\n");
+            else
+                printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
+        }
+        break;
+    }
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_compare_and_write.txt b/sg3_utils/examples/sg_compare_and_write.txt
new file mode 100644
index 0000000..05d72b9
--- /dev/null
+++ b/sg3_utils/examples/sg_compare_and_write.txt
@@ -0,0 +1,67 @@
+#		sg_compare_and_write.txt
+# This file provides a usage example of sg_compare_and_write.
+# sg_compare_and_write accepts a buffer containing 2 logical instances:
+# - the verify instance: used to match the current content of the LBA range
+# - the write instance: used to write to the LBA if the verify succeeds
+#
+# In case of failure to verify the data, the command will return with check
+# condition with the sense code set to MISCOMPARE DURING VERIFY OPERATION.
+#
+# The following example shows initialization, successful and unsuccessful
+# compare and write using sg3_utils. I am using caw_buf_zero2one and
+# caw_buf_one2zero as shown bellow.
+
+$ hexdump /tmp/caw_buf_zero2one
+0000000 0000 0000 0000 0000 0000 0000 0000 0000
+*
+0000200 1111 1111 1111 1111 1111 1111 1111 1111
+*
+0000400
+
+$ hexdump /tmp/caw_buf_one2zero
+0000000 1111 1111 1111 1111 1111 1111 1111 1111
+*
+0000200 0000 0000 0000 0000 0000 0000 0000 0000
+*
+0000400
+
+$ sg_map -i -x
+/dev/sg0  0 0 0 0  0  /dev/sda  ATA       ST3320613AS       CC2H
+/dev/sg1  3 0 0 0  5  /dev/scd0  HL-DT-ST  DVD-RAM GH22NS30  1.01
+/dev/sg2  5 0 0 0  0  /dev/sdb  KMNRIO    K2                0000
+/dev/sg3  5 0 0 1  0  /dev/sdc  KMNRIO    K2                0000
+
+# First I zero out the volume to make sure that the first compare and write
+# will succeed
+$ sg_write_same --16 -i /dev/zero -n 0x200000  -x 512 /dev/sdc
+
+$ dd if=/dev/sdc bs=512 count=1 skip=100 2>/dev/null | hexdump
+0000000 0000 0000 0000 0000 0000 0000 0000 0000
+*
+0000200
+
+$ ./sg_compare_and_write --in=/tmp/caw_buf_zero2one --lba=100 --xferlen=1024 /dev/sdc
+
+# contents of LBA 100 are a block of ones
+$ dd if=/dev/sdc bs=512 count=1 skip=100 2>/dev/null | hexdump
+0000000 1111 1111 1111 1111 1111 1111 1111 1111
+*
+0000200
+
+# We repeat the same compare and write command (zero2one input buffer).
+# compare and write fails since the verify failed (compared the zero block to
+# the actual 1 block in LBA 100
+$ ./sg_compare_and_write --in=/tmp/caw_buf_zero2one --lba=100 --xferlen=1024 /dev/sdc
+COMPARE AND WRITE:  Fixed format, current;  Sense key: Miscompare
+ Additional sense: Miscompare during verify operation
+sg_compare_and_write: SCSI COMPARE AND WRITE failed
+
+# Now we use the second buffer (one2zero)
+$ ./sg_compare_and_write --in=/tmp/caw_buf_one2zero --lba=100 --xferlen=1024 /dev/sdc
+
+# operation succeeded, contents of LBA 100 are back to zero
+$ dd if=/dev/sdc bs=512 count=1 skip=100 2>/dev/null | hexdump
+0000000 0000 0000 0000 0000 0000 0000 0000 0000
+*
+0000200
+
diff --git a/sg3_utils/examples/sg_excl.c b/sg3_utils/examples/sg_excl.c
new file mode 100644
index 0000000..6f9aa3a
--- /dev/null
+++ b/sg3_utils/examples/sg_excl.c
@@ -0,0 +1,198 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This is a simple program that tests the O_EXCL flag in sg while
+   executing a SCSI INQUIRY command and a
+   TEST UNIT READY command using the SCSI generic (sg) driver
+
+*  Copyright (C) 2003-2013 D. Gilbert
+*  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.
+
+   Invocation: sg_excl [-x] <sg_device>
+
+   Version 3.58 (20130731)
+
+6 byte INQUIRY command:
+[0x12][   |lu][pg cde][res   ][al len][cntrl ]
+
+6 byte TEST UNIT READY command:
+[0x00][   |lu][res   ][res   ][res   ][res   ]
+
+*/
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+#define TUR_CMD_LEN 6
+
+#define EBUFF_SZ 256
+
+#define ME "sg_excl: "
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok /*, sg_fd2 */;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char turCmdBlk [TUR_CMD_LEN] =
+                                {0x00, 0, 0, 0, 0, 0};
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[32];
+    int do_extra = 0;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-x", argv[k], 2))
+            do_extra = 1;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_excl [-x] <sg_device>'\n");
+        return 1;
+    }
+
+    /* N.B. An access mode of O_RDWR is required for some SCSI commands */
+    if ((sg_fd = open(file_name, O_RDWR | O_EXCL | O_NONBLOCK)) < 0) {
+        snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+    /* Just to be safe, check we have a new sg device by trying an ioctl */
+    if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+        printf(ME "%s doesn't seem to be an new sg device\n",
+               file_name);
+        close(sg_fd);
+        return 1;
+    }
+#if 0
+    if ((sg_fd2 = open(file_name, O_RDWR | O_EXCL)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 ME "error opening file: %s a second time", file_name);
+        perror(ebuff);
+        return 1;
+    } else {
+        printf(ME "second open of %s in violation of O_EXCL\n", file_name);
+        close(sg_fd2);
+    }
+#endif
+
+    /* Prepare INQUIRY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(inqCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = INQ_REPLY_LEN;
+    io_hdr.dxferp = inqBuff;
+    io_hdr.cmdp = inqCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror(ME "Inquiry SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on INQUIRY, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("INQUIRY command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok) { /* output result if it is available */
+        char * p = (char *)inqBuff;
+        int f = (int)*(p + 7);
+        printf("Some of the INQUIRY command's results:\n");
+        printf("    %.8s  %.16s  %.4s  ", p + 8, p + 16, p + 32);
+        printf("[wide=%d sync=%d cmdque=%d sftre=%d]\n",
+               !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1));
+        /* Extra info, not necessary to look at */
+        if (do_extra)
+            printf("INQUIRY duration=%u millisecs, resid=%d, msg_status=%d\n",
+                   io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
+    }
+
+
+    /* Prepare TEST UNIT READY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(turCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_NONE;
+    io_hdr.cmdp = turCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror(ME "Test Unit Ready SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on Test Unit Ready, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("Test Unit Ready command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok)
+        printf("Test Unit Ready successful so unit is ready!\n");
+    else
+        printf("Test Unit Ready failed so unit may _not_ be ready!\n");
+
+    if (do_extra)
+        printf("TEST UNIT READY duration=%u millisecs, resid=%d, "
+               "msg_status=%d\n", io_hdr.duration, io_hdr.resid,
+                (int)io_hdr.msg_status);
+
+    sleep(60);
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_iovec_tst.c b/sg3_utils/examples/sg_iovec_tst.c
new file mode 100644
index 0000000..6a0848d
--- /dev/null
+++ b/sg3_utils/examples/sg_iovec_tst.c
@@ -0,0 +1,266 @@
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+
+/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
+   device driver.
+*  Copyright (C) 2003-2015 D. Gilbert
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This program will read a certain number of blocks of a given block size
+   from a given sg device node and write what is retrieved out to a
+   normal file. The purpose is to test the sg_iovec mechanism within the
+   sg_io_hdr structure.
+
+   Version 0.14 (20151209)
+*/
+
+
+#define ME "sg_iovec_tst: "
+
+#define A_PRIME 509
+#define IOVEC_ELEMS 2048
+
+#define SENSE_BUFF_LEN 32
+#define DEF_TIMEOUT 40000       /* 40,000 milliseconds */
+
+struct sg_iovec iovec[IOVEC_ELEMS];
+
+
+static void
+usage(void)
+{
+    printf("Usage: sg_iovec_tst [-a] [-b=bs] -c=num [-e=es] [-h]\n"
+           "                    <generic_device> <output_filename>\n");
+    printf("  where: -a       async sg use (def: use ioctl(SGIO) )\n");
+    printf("         -b=bs    block size (default 512 Bytes)\n");
+    printf("         -c=num   count of blocks to transfer\n");
+    printf("         -e=es    iovec element size (def: 509)\n");
+    printf("         -h       this usage message\n");
+    printf(" reads from <generic_device> and sends to "
+           "<output_filename>\nUses iovec (a scatter list) in linear "
+           "mode\n");
+}
+
+/* Returns 0 if everything ok */
+static int sg_read(int sg_fd, unsigned char * buff, int num_blocks,
+                   int from_block, int bs, int elem_size, int async)
+{
+    unsigned char rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char senseBuff[SENSE_BUFF_LEN];
+    struct sg_io_hdr io_hdr;
+    struct pollfd a_poll;
+    int dxfer_len = bs * num_blocks;
+    int k, pos, rem, res;
+
+    sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
+    sg_put_unaligned_be16((uint16_t)from_block, rdCmd + 7);
+
+    for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
+        iovec[k].iov_base = buff + pos;
+        iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
+        if (rem <= elem_size)
+            break;
+        pos += elem_size;
+        rem -= elem_size;
+    }
+    if (k >= IOVEC_ELEMS) {
+        fprintf(stderr, "Can't fit dxfer_len=%d bytes in iovec\n", dxfer_len);
+        return -1;
+    }
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(rdCmd);
+    io_hdr.cmdp = rdCmd;
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = dxfer_len;
+    io_hdr.iovec_count = k + 1;
+    io_hdr.dxferp = iovec;
+    io_hdr.mx_sb_len = SENSE_BUFF_LEN;
+    io_hdr.sbp = senseBuff;
+    io_hdr.timeout = DEF_TIMEOUT;
+    io_hdr.pack_id = from_block;
+
+    if (async) {
+        res = write(sg_fd, &io_hdr, sizeof(io_hdr));
+        if (res < 0) {
+            perror("write(<sg_device>), error");
+            return -1;
+        } else if (res < (int)sizeof(io_hdr)) {
+            fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n",
+                    res, (int)sizeof(io_hdr));
+            return -1;
+        }
+        a_poll.fd = sg_fd;
+        a_poll.events = POLLIN;
+        a_poll.revents = 0;
+        res = poll(&a_poll, 1, 2000 /* millisecs */ );
+        if (res < 0) {
+            perror("poll error on <sg_device>");
+            return -1;
+        }
+        if (0 == (POLLIN & a_poll.revents)) {
+            fprintf(stderr, "strange, poll() completed without data to "
+                    "read\n");
+            return -1;
+        }
+        res = read(sg_fd, &io_hdr, sizeof(io_hdr));
+        if (res < 0) {
+            perror("read(<sg_device>), error");
+            return -1;
+        } else if (res < (int)sizeof(io_hdr)) {
+            fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n",
+                    res, (int)sizeof(io_hdr));
+            return -1;
+        }
+    } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
+        perror("reading (SG_IO) on sg device, error");
+        return -1;
+    }
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
+               from_block, num_blocks);
+        break;
+    case SG_LIB_CAT_UNIT_ATTENTION:
+        fprintf(stderr, "Unit attention\n");
+        return -1;
+    default:
+        sg_chk_n_print3("reading", &io_hdr, 1);
+        return -1;
+    }
+    return 0;
+}
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, fd, res, j, m, dxfer_len;
+    unsigned int k, num;
+    int do_async = 0;
+    int do_help = 0;
+    int blk_size = 512;
+    int elem_size = A_PRIME;
+    int count = 0;
+    char * sg_file_name = 0;
+    char * out_file_name = 0;
+    unsigned char * buffp;
+
+    for (j = 1; j < argc; ++j) {
+        if (0 == strcmp("-a", argv[j]))
+            do_async = 1;
+        else if (0 == strncmp("-b=", argv[j], 3)) {
+            m = 3;
+            num = sscanf(argv[j] + m, "%d", &blk_size);
+            if ((1 != num) || (blk_size <= 0)) {
+                printf("Couldn't decode number after '-b' switch\n");
+                sg_file_name = 0;
+                break;
+            }
+        } else if (0 == strncmp("-c=", argv[j], 3)) {
+            m = 3;
+            num = sscanf(argv[j] + m, "%d", &count);
+            if (1 != num) {
+                printf("Couldn't decode number after '-c' switch\n");
+                sg_file_name = 0;
+                break;
+            }
+        } else if (0 == strncmp("-e=", argv[j], 3)) {
+            m = 3;
+            num = sscanf(argv[j] + m, "%d", &elem_size);
+            if (1 != num) {
+                printf("Couldn't decode number after '-e' switch\n");
+                sg_file_name = 0;
+                break;
+            }
+        } else if (0 == strcmp("-h", argv[j]))
+            do_help = 1;
+        else if (*argv[j] == '-') {
+            printf("Unrecognized switch: %s\n", argv[j]);
+            sg_file_name = 0;
+            break;
+        } else if (NULL == sg_file_name)
+            sg_file_name = argv[j];
+        else
+            out_file_name = argv[j];
+    }
+    if (do_help) {
+        usage();
+        return 0;
+    }
+    if (NULL == sg_file_name) {
+        printf(">>> need sg node name (e.g. /dev/sg3)\n\n");
+        usage();
+        return 1;
+    }
+    if (NULL == out_file_name) {
+        printf(">>> need out filename (to place what is fetched by READ\n\n");
+        usage();
+        return 1;
+    }
+    if (0 == count) {
+        printf(">>> need count of blocks to READ\n\n");
+        usage();
+        return 1;
+    }
+
+    if (do_async)
+        sg_fd = open(sg_file_name, O_RDWR);
+    else
+        sg_fd = open(sg_file_name, O_RDONLY);
+    if (sg_fd < 0) {
+        perror(ME "sg device node open error");
+        return 1;
+    }
+    /* Don't worry, being very careful not to write to a none-sg file ... */
+    res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
+    if ((res < 0) || (k < 30000)) {
+        printf(ME "not a sg device, or driver prior to 3.x\n");
+        return 1;
+    }
+    fd = open(out_file_name, O_WRONLY | O_CREAT, 0666);
+    if (fd < 0) {
+        perror(ME "output file open error");
+        return 1;
+    }
+    dxfer_len = count * blk_size;
+    buffp = (unsigned char *)malloc(dxfer_len);
+    if (buffp) {
+        if (0 == sg_read(sg_fd, buffp, count, 0, blk_size, elem_size,
+                         do_async)) {
+            if (write(fd, buffp, dxfer_len) < 0)
+                perror(ME "output write failed");
+        }
+        free(buffp);
+    } else
+        fprintf(stderr, "user space malloc for %d bytes failed\n",
+                dxfer_len);
+    res = close(fd);
+    if (res < 0) {
+        perror(ME "output file close error");
+        close(sg_fd);
+        return 1;
+    }
+    res = close(sg_fd);
+    if (res < 0) {
+        perror(ME "sg device close error");
+        return 1;
+    }
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_persist_tst.sh b/sg3_utils/examples/sg_persist_tst.sh
new file mode 100755
index 0000000..b6b926b
--- /dev/null
+++ b/sg3_utils/examples/sg_persist_tst.sh
@@ -0,0 +1,130 @@
+#!/bin/sh
+# This script is meant as an example of using the sg_persist utility
+# in the sg3_utils package. This script works as expected on the
+# author's Fujitsu MAM3184, Seagate ST373455 and ST9146803SS disks.
+#
+#  Version 1.9 20140612
+
+# N.B. make sure the device name is correct for your environment.
+
+key="123abc"
+key2="333aaa"
+kk=${key}
+rtype="1"
+verbose=""
+
+usage()
+{
+  echo "Usage: sg_persist_tst.sh [-e] [-h] [-s] [-v] <device>"
+  echo "  where:"
+  echo -n "    -e, --exclusive      exclusive access (def: write "
+  echo "exclusive)"
+  echo "    -h, --help           print usage message"
+  echo "    -s, --second         use second key"
+  echo "    -v, --verbose        more verbose output"
+  echo "    -vv                  even more verbose output"
+  echo "    -vvv                 even more verbose output"
+  echo ""
+  echo "Test SCSI Persistent Reservations with sg_persist utility."
+  echo "Default key is ${key} and alternate, second key is ${key2} ."
+  echo "Should be harmless (unless one of those keys is already in use)."
+  echo "The APTPL bit is not set in the PR register so a power cycle"
+  echo "on the device will clear the reservation if this script stops"
+  echo "(or is stopped) before clearing it. Tape drives only seem to "
+  echo "support 'exclusive access' type (so use '-e')."
+}
+
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    e|-exclusive) rtype="3" ;;
+    h|-help) usage ; exit 0 ;;
+    s|-second) kk=${key2} ;;
+    vvv) verbose="-vvv" ;;
+    vv) verbose="-vv" ;;
+    v|-verbose) verbose="-v" ;;
+    *) echo "Unknown option: -$opt " ; exit 1 ;;
+  esac
+  shift
+  opt="$1"
+done
+
+if [ $# -lt 1 ]
+  then
+    usage
+    exit 1
+fi
+
+echo ">>> try to report capabilities:"
+sg_persist -c ${verbose} $1
+res=$?
+case "$res" in
+    0) ;;
+    1) echo "  syntax error" ;;
+    2) echo "  not ready" ;;
+    3) echo "  medium error" ;;
+    5) echo "  illegal request, report capabilities not supported?" ;;
+    6) echo "  unit attention" ;;
+    9) echo "  illegal request, Persistent Reserve (In) not supported" ;;
+    11) echo "  aborted command" ;;
+    15) echo "  file error with $1 " ;;
+    20) echo "  no sense" ;;
+    21) echo "  recovered error" ;;
+    33) echo "  timeout" ;;
+    97) echo "  response fails sanity" ;;
+    98) echo "  other SCSI error" ;;
+    99) echo "  other error" ;;
+    *) echo "  unknown exit status for sg_persist: $res" ;;
+esac
+echo ""
+sleep 1
+
+echo ">>> check if any keys are registered:"
+sg_persist --no-inquiry --read-keys ${verbose} $1
+sleep 1
+
+echo
+echo ">>> register a key:"
+sg_persist -n --out --register --param-sark=${kk} ${verbose} $1
+sleep 1
+
+echo
+echo ">>> now key ${kk} should be registered:"
+sg_persist -n --read-keys ${verbose} $1
+sleep 1
+
+echo
+echo ">>> reserve the device (based on key ${kk}):"
+sg_persist -n --out --reserve --param-rk=${kk} --prout-type=${rtype} ${verbose} $1
+sleep 1
+
+echo
+echo ">>> check if the device is reserved (it should be now):"
+sg_persist -n --read-reservation ${verbose} $1
+sleep 1
+
+echo
+echo ">>> try to 'read full status' (may not be supported):"
+sg_persist -n --read-full-status ${verbose} $1
+sleep 1
+
+echo
+echo ">>> now release reservation:"
+sg_persist -n --out --release --param-rk=${kk} --prout-type=${rtype} ${verbose} $1
+sleep 1
+
+echo
+echo ">>> check if the device is reserved (it should _not_ be now):"
+sg_persist -n --read-reservation ${verbose} $1
+sleep 1
+
+echo
+echo ">>> unregister key ${kk}:"
+sg_persist -n --out --register --param-rk=${kk} ${verbose} $1
+sleep 1
+
+echo
+echo ">>> now key ${kk} should not be registered:"
+sg_persist -n -k ${verbose} $1
+sleep 1
diff --git a/sg3_utils/examples/sg_queue_tst.c b/sg3_utils/examples/sg_queue_tst.c
new file mode 100644
index 0000000..b8bc549
--- /dev/null
+++ b/sg3_utils/examples/sg_queue_tst.c
@@ -0,0 +1,165 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_linux_inc.h"
+
+/* This program was used to test SCSI mid level queue ordering.
+   The default behaviour is to "queue at head" which is useful for
+   error processing but not for streaming READ and WRITE commands.
+
+*  Copyright (C) 2010 D. Gilbert
+*  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.
+
+   Invocation: sg_queue_tst [-t] <sg_device>
+        -t      queue at tail
+
+   Version 0.90 (20100324)
+
+*/
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+#define SDIAG_CMD_LEN 6
+#define SENSE_BUFFER_LEN 96
+
+#define EBUFF_SZ 256
+
+#ifndef SG_FLAG_Q_AT_TAIL
+#define SG_FLAG_Q_AT_TAIL 0x10
+#endif
+
+#ifndef SG_FLAG_Q_AT_HEAD
+#define SG_FLAG_Q_AT_HEAD 0x20
+#endif
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok;
+    unsigned char inqCmdBlk[INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char sdiagCmdBlk[SDIAG_CMD_LEN] =
+                                {0x1d, 0, 0, 0, 0, 0};
+    unsigned char inqBuff[16][INQ_REPLY_LEN];
+    sg_io_hdr_t io_hdr[16];
+    sg_io_hdr_t rio_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[16][SENSE_BUFFER_LEN];
+    int q_at_tail = 0;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-t", argv[k], 2))
+            ++q_at_tail;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_queue_tst [-t] <sg_device>'\n"
+               "where:\n      -t   queue_at_tail (def: q_at_head)\n");
+        return 1;
+    }
+
+    /* An access mode of O_RDWR is required for write()/read() interface */
+    if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_queue_tst: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+
+    for (k = 0; k < 16; ++k) {
+        /* Prepare INQUIRY command */
+        memset(&io_hdr[k], 0, sizeof(sg_io_hdr_t));
+        io_hdr[k].interface_id = 'S';
+        /* io_hdr[k].iovec_count = 0; */  /* memset takes care of this */
+        io_hdr[k].mx_sb_len = (unsigned char)sizeof(sense_buffer);
+        if (0 == (k % 3)) {
+            io_hdr[k].cmd_len = sizeof(sdiagCmdBlk);
+            io_hdr[k].cmdp = sdiagCmdBlk;
+            io_hdr[k].dxfer_direction = SG_DXFER_NONE;
+        } else {
+            io_hdr[k].cmd_len = sizeof(inqCmdBlk);
+            io_hdr[k].cmdp = inqCmdBlk;
+            io_hdr[k].dxfer_direction = SG_DXFER_FROM_DEV;
+            io_hdr[k].dxfer_len = INQ_REPLY_LEN;
+            io_hdr[k].dxferp = inqBuff[k];
+        }
+        io_hdr[k].sbp = sense_buffer[k];
+        io_hdr[k].mx_sb_len = SENSE_BUFFER_LEN;
+        io_hdr[k].timeout = 20000;     /* 20000 millisecs == 20 seconds */
+        io_hdr[k].pack_id = k;
+        /* default is to queue at head (in SCSI mid level) */
+        if (q_at_tail)
+            io_hdr[k].flags |= SG_FLAG_Q_AT_TAIL;
+        else
+            io_hdr[k].flags |= SG_FLAG_Q_AT_HEAD;
+        /* io_hdr[k].usr_ptr = NULL; */
+
+        if (write(sg_fd, &io_hdr[k], sizeof(sg_io_hdr_t)) < 0) {
+            perror("sg_queue_tst: sg write error");
+            close(sg_fd);
+            return 1;
+        }
+    }
+    /* sleep(3); */
+    for (k = 0; k < 16; ++k) {
+        memset(&rio_hdr, 0, sizeof(sg_io_hdr_t));
+        rio_hdr.interface_id = 'S';
+        if (read(sg_fd, &rio_hdr, sizeof(sg_io_hdr_t)) < 0) {
+            perror("sg_queue_tst: sg read error");
+            close(sg_fd);
+            return 1;
+        }
+        /* now for the error processing */
+        ok = 0;
+        switch (sg_err_category3(&rio_hdr)) {
+        case SG_LIB_CAT_CLEAN:
+            ok = 1;
+            break;
+        case SG_LIB_CAT_RECOVERED:
+            printf("Recovered error, continuing\n");
+            ok = 1;
+            break;
+        default: /* won't bother decoding other categories */
+            sg_chk_n_print3("command error", &rio_hdr, 1);
+            break;
+        }
+
+        if (ok) { /* output result if it is available */
+            /* if (0 == rio_hdr.pack_id) */
+            if (0 == (rio_hdr.pack_id % 3))
+                printf("SEND DIAGNOSTIC %d duration=%u\n", rio_hdr.pack_id,
+                       rio_hdr.duration);
+            else
+                printf("INQUIRY %d duration=%u\n", rio_hdr.pack_id,
+                       rio_hdr.duration);
+        }
+    }
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_sat_chk_power.c b/sg3_utils/examples/sg_sat_chk_power.c
new file mode 100644
index 0000000..78bd84d
--- /dev/null
+++ b/sg3_utils/examples/sg_sat_chk_power.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2006-2012 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program performs a ATA PASS-THROUGH (16) SCSI command in order
+   to perform an ATA CHECK POWER MODE command. See http://www.t10.org
+   SAT draft at time of writing: sat-r09.pdf
+
+   Invocation: sg_sat_chk_power [-v] [-V] <device>
+
+*/
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
+#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
+
+#define ATA_CHECK_POWER_MODE 0xe5
+
+#define EBUFF_SZ 256
+
+static const char * version_str = "1.04 20120319";
+
+
+#if 0
+/* Returns length of decoded fixed format sense for SAT ATA pass-through
+ * command, else returns 0. If returns 0 (expected sense data not found)
+ * then '\0' placed in first byte of bp. */
+static int
+sg_sat_decode_fixed_sense(const unsigned char * sp, int slen, char * bp,
+                          int max_blen, int verbose)
+{
+    int n;
+
+    if ((NULL == bp) || (NULL == sp) || (max_blen < 1) || (slen < 14))
+        return 0;
+    bp[0] = '\0';
+    if ((0x70 != (0x7f & sp[0])) ||
+        (SPC_SK_RECOVERED_ERROR != (0xf & sp[2])) ||
+        (0 != sp[12]) || (ASCQ_ATA_PT_INFO_AVAILABLE != sp[13]))
+        return 0;
+    n = snprintf(bp, max_blen, "error=0x%x, status=0x%x, device=0x%x, "
+                 "sector_count(7:0)=0x%x%c\n", sp[3], sp[4], sp[5], sp[6],
+                 ((0x40 & sp[8]) ? '+' : ' '));
+    if (n >= max_blen)
+        return max_blen - 1;
+    n += snprintf(bp + n, max_blen - n, "extend=%d, log_index=0x%x, "
+                  "lba_high,mid,low(7:0)=0x%x,0x%x,0x%x%c\n",
+                  (!!(0x80 & sp[8])), (0xf & sp[8]), sp[9], sp[10], sp[11],
+                  ((0x20 & sp[8]) ? '+' : ' '));
+    if (n >= max_blen)
+        return max_blen - 1;
+    if (verbose)
+        n += snprintf(bp + n, max_blen - n, "  sector_count_upper_nonzero="
+                      "%d, lba_upper_nonzero=%d\n", !!(0x40 & sp[8]),
+                      !!(0x20 & sp[8]));
+    return (n >= max_blen) ? max_blen - 1 : n;
+}
+#endif
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k;
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[64];
+    int verbose = 0;
+    int extend = 0;
+    int chk_cond = 1;   /* set to 1 to read register(s) back */
+    int protocol = 3;   /* non-dat data-in */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+    int t_length = 0;   /* 0 -> no data transferred, 2 -> sector count */
+    const unsigned char * ucp = NULL;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == strcmp(argv[k], "-v"))
+            ++verbose;
+        else if (0 == strcmp(argv[k], "-vv"))
+            verbose += 2;
+        else if (0 == strcmp(argv[k], "-vvv"))
+            verbose += 3;
+        else if (0 == strcmp(argv[k], "-V")) {
+            fprintf(stderr, "version: %s\n", version_str);
+            exit(0);
+        } else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_sat_chk_power [-v] [-V] <device>'\n");
+        return 1;
+    }
+
+    if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_sat_chk_power: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+
+    /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+    aptCmdBlk[14] = ATA_CHECK_POWER_MODE;
+    aptCmdBlk[1] = (protocol << 1) | extend;
+    aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
+                   (byte_block << 2) | t_length;
+    if (verbose) {
+        fprintf(stderr, "    ata pass through(16) cdb: ");
+        for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
+            fprintf(stderr, "%02x ", aptCmdBlk[k]);
+        fprintf(stderr, "\n");
+    }
+
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(aptCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_NONE;
+    io_hdr.dxfer_len = 0;
+    io_hdr.dxferp = NULL;
+    io_hdr.cmdp = aptCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_sat_chk_power: SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* error processing: N.B. expect check condition, no sense ... !! */
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        break;
+    case SG_LIB_CAT_RECOVERED:  /* sat-r09 (latest) uses this sk */
+    case SG_LIB_CAT_NO_SENSE:   /* earlier SAT drafts used this */
+        /* XXX: Until the spec decides which one to go with. 20060607 */
+        ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
+                                      SAT_ATA_RETURN_DESC);
+        if (NULL == ucp) {
+            if (verbose > 1)
+                printf("ATA Return Descriptor expected in sense but not "
+                       "found\n");
+            sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+        } else if (verbose)
+            sg_chk_n_print3("ATA Return Descriptor, as expected",
+                             &io_hdr, 1);
+        if (ucp && ucp[3]) {
+            if (ucp[3] & 0x4)
+                printf("error in returned FIS: aborted command\n");
+            else
+                printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
+        }
+        break;
+    default:
+        fprintf(stderr, "unexpected SCSI sense category\n");
+        ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
+                                      SAT_ATA_RETURN_DESC);
+        if (NULL == ucp)
+            sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+        else if (verbose)
+            sg_chk_n_print3("ATA Return Descriptor, as expected",
+                             &io_hdr, 1);
+        if (ucp && ucp[3]) {
+            if (ucp[3] & 0x4)
+                printf("error in returned FIS: aborted command\n");
+            else
+                printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
+        }
+        break;
+    }
+
+    if (ucp) {
+        switch (ucp[5]) {       /* sector_count (7:0) */
+        case 0xff:
+            printf("In active mode or idle mode\n");
+            break;
+        case 0x80:
+            printf("In idle mode\n");
+            break;
+        case 0x41:
+            printf("In NV power mode and spindle is spun or spinning up\n");
+            break;
+        case 0x40:
+            printf("In NV power mode and spindle is spun or spinning down\n");
+            break;
+        case 0x0:
+            printf("In standby mode\n");
+            break;
+        default:
+            printf("unknown power mode (sector count) value=0x%x\n", ucp[5]);
+            break;
+        }
+    } else
+        fprintf(stderr, "Expecting a ATA Return Descriptor in sense and "
+                "didn't receive it\n");
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_sat_smart_rd_data.c b/sg3_utils/examples/sg_sat_smart_rd_data.c
new file mode 100644
index 0000000..dcf4b41
--- /dev/null
+++ b/sg3_utils/examples/sg_sat_smart_rd_data.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2006-2007 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program performs a ATA PASS-THROUGH (16) SCSI command in order
+   to perform an ATA SMART/READ DATA command. See http://www.t10.org
+   SAT draft at time of writing: sat-r08.pdf
+
+   Invocation: sg_sat_smart_rd_data [-v] [-V] <device>
+
+*/
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
+
+#define ATA_SMART 0xb0
+#define ATA_SMART_READ_DATA 0xd0
+#define SMART_READ_DATA_RESPONSE_LEN 512
+
+#define EBUFF_SZ 256
+
+static char * version_str = "1.02 20070130";
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok;
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char inBuff[SMART_READ_DATA_RESPONSE_LEN];
+    unsigned char sense_buffer[32];
+    int verbose = 0;
+    int extend = 0;
+    int chk_cond = 0;   /* set to 1 to read register(s) back */
+    int protocol = 4;   /* PIO data-in */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+    int t_length = 2;   /* 0 -> no data transferred, 2 -> sector count */
+    const unsigned char * ucp = NULL;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == strcmp(argv[k], "-v"))
+            ++verbose;
+        else if (0 == strcmp(argv[k], "-vv"))
+            verbose += 2;
+        else if (0 == strcmp(argv[k], "-vvv"))
+            verbose += 3;
+        else if (0 == strcmp(argv[k], "-V")) {
+            fprintf(stderr, "version: %s\n", version_str);
+            exit(0);
+        } else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_sat_smart_rd_data [-v] [-V] <device>'\n");
+        return 1;
+    }
+
+    if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_sat_smart_rd_data: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+
+    /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+    aptCmdBlk[4] = ATA_SMART_READ_DATA;   /* feature (7:0) */
+    aptCmdBlk[6] = 1;   /* number of block (sector count) */
+    aptCmdBlk[10] = 0x4f;    /* lba_mid (7:0) */
+    aptCmdBlk[12] = 0xc2;    /* lba_high (7:0) */
+    aptCmdBlk[14] = ATA_SMART;
+    aptCmdBlk[1] = (protocol << 1) | extend;
+    aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
+                   (byte_block << 2) | t_length;
+    if (verbose) {
+        fprintf(stderr, "    ata pass through(16) cdb: ");
+        for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
+            fprintf(stderr, "%02x ", aptCmdBlk[k]);
+        fprintf(stderr, "\n");
+    }
+
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(aptCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = SMART_READ_DATA_RESPONSE_LEN;
+    io_hdr.dxferp = inBuff;
+    io_hdr.cmdp = aptCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_sat_smart_rd_data: SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
+                                      SAT_ATA_RETURN_DESC);
+        if (NULL == ucp) {
+            if (verbose > 1)
+                printf("ATA Return Descriptor expected in sense but not "
+                       "found\n");
+            sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+        } else if (verbose)
+            sg_chk_n_print3("ATA Return Descriptor", &io_hdr, 1);
+        if (ucp && ucp[3])
+            printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
+        else
+            ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok) { /* output result if it is available */
+        printf("Response:\n");
+        dWordHex((const unsigned short *)inBuff, 256, 0,
+                 sg_is_big_endian());
+    }
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_sense_test.c b/sg3_utils/examples/sg_sense_test.c
new file mode 100644
index 0000000..4ada185
--- /dev/null
+++ b/sg3_utils/examples/sg_sense_test.c
@@ -0,0 +1,181 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+
+/* This is a simple program that tests the sense data descriptor format
+   printout function in sg_lib.c
+
+*  Copyright (C) 2004-2016 D. Gilbert
+*  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.
+
+*/
+
+#define EBUFF_SZ 256
+
+#define ME "sg_sense_test: "
+
+static char * version_str = "2.00 20160128";
+
+static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"leadin",  required_argument, 0, 'l'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},   /* sentinel */
+};
+
+
+static void
+usage()
+{
+    fprintf(stderr,
+            "Usage: %s [--help] [--leadin=STR] [--verbose] [--version]\n"
+            "  where: --help|-h          print out usage message\n"
+            "         --leadin=STR|-l STR    every line output by --sense "
+            "should\n"
+            "                                be prefixed by STR\n"
+            "         --verbose|-v       increase verbosity\n"
+            "         --version|-V       print version string and exit\n\n"
+            "Test sense data handling of sg_lib. Overlaps somewhat with "
+            "utils/tst_sg_lib\n", ME
+           );
+
+}
+
+int main(int argc, char * argv[])
+{
+    unsigned char err1[] = {0x72, 0x5, 0x24, 0x0, 0, 0, 0, 32,
+                            0x2, 0x6, 0, 0, 0xc8, 0x0, 0x3, 0,
+                            0, 0xa, 0x80, 0, 1, 2, 3, 4,
+                            0xaa, 0xbb, 0xcc, 0xdd,
+                            1, 0xa, 0, 0, 1, 2, 3, 4,
+                            0xaa, 0xbb, 0xee, 0xff};
+    unsigned char err2[] = {0x72, SPC_SK_MEDIUM_ERROR, 0x11, 0xb, 0x80, 0, 0,
+                            32,
+                            0x2, 0x6, 0, 0, 0xc8, 0x0, 0x3, 0,
+                            0, 0xa, 0x80, 0, 1, 2, 3, 4,
+                            0xaa, 0xbb, 0xcc, 0xdd,
+                            1, 0xa, 0, 0, 1, 2, 3, 4,
+                            0xaa, 0xbb, 0xee, 0xff};
+                           /* Set SDAT_OVFL */
+    unsigned char err3[] = {0x72, SPC_SK_NO_SENSE, 0x4, 0x4, 0, 0, 0, 8,
+                            0x2, 0x6, 0, 0, 0xc8, 0x12, 0x34, 0};
+    unsigned char err4[] = {0x73, SPC_SK_COPY_ABORTED, 0x8, 0x4, 0, 0, 0, 22,
+                            0x2, 0x6, 0, 0, 0xc8, 0x0, 0x3, 0,
+                            0x3, 0x2, 0, 0x55,
+                            0x5, 0x2, 0, 0x20,
+                            0x85, 0x4, 0, 0x20, 0x33, 0x44};
+                           /* Set Filemark, EOM, ILI and SDAT_OVFL */
+    unsigned char err5[] = {0xf1, 0, (0xf0 | SPC_SK_ILLEGAL_REQUEST), 0x11,
+                            0x22, 0x33, 0x44, 0xa,
+                            0x0, 0x0, 0, 0, 0x4, 0x1, 0, 0xcf, 0, 5,};
+    unsigned char err6[] = {0x72, SPC_SK_NO_SENSE, 0x4, 0x1, 0, 0, 0, 14,
+                            0x9, 0xc, 1, 0, 0x11, 0x22, 0x66, 0x33,
+                            0x77, 0x44, 0x88, 0x55, 0x1, 0x2};
+    unsigned char err7[] = {0xf1, 0, 0xe5, 0x11, 0x22, 0x33, 0x44, 0xa,
+                            0x0, 0x0, 0x0, 0x0, 0x24, 0x1, 0xbb,
+                            0xc9, 0x0, 0x2};
+    const char * leadin = NULL;
+    char b[2048];
+    int c, k, prev_len;
+    int verbose = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hl:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'l':
+            leadin = optarg;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            fprintf(stderr, "version: %s\n", version_str);
+            return 0;
+        default:
+            fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
+            usage();
+            return 1;
+        }
+    }
+    if (optind < argc) {
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                fprintf(stderr, "Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return 1;
+        }
+    }
+
+    printf("err1 test:\n");
+    sg_print_sense(leadin, err1, sizeof(err1), verbose /* raw_info */);
+    printf("\n");
+    printf("err2 test:\n");
+    sg_print_sense(leadin, err2, sizeof(err2), verbose);
+    printf("\n");
+    printf("err3 test:\n");
+    sg_print_sense(leadin, err3, sizeof(err3), verbose);
+    printf("\n");
+    printf("err4 test:\n");
+    sg_print_sense(leadin, err4, sizeof(err4), verbose);
+    printf("\n");
+    printf("err5 test: Set Filemark, EOM, ILI and SDAT_OVFL\n");
+    sg_print_sense(leadin, err5, sizeof(err5), verbose);
+    printf("\n");
+    printf("err6 test:\n");
+    sg_print_sense(leadin, err6, sizeof(err6), verbose);
+    printf("\n");
+    printf("err7 test:\n");
+    sg_print_sense(leadin, err7, sizeof(err7), verbose);
+
+    if (verbose > 1) {
+        printf("\n\nTry different output string sizes with "
+               "sg_get_sense_str(err2):\n");
+        for (k = 1, prev_len = -1; k < 512; ++k) {
+            /* snprintf(leadin, sizeof(leadin), "blen=%d", k); */
+            sg_get_sense_str(NULL, err2, sizeof(err2), 0, k, b);
+            printf("%s\n", b);
+            if (prev_len == (int)strlen(b))
+                break;
+            else
+                prev_len = strlen(b);
+        }
+    }
+
+    if (verbose > 2) {
+        printf("\n\nTry different output string sizes with "
+               "sg_get_sense_str(err4):\n");
+        for (k = 1, prev_len = -1; k < 512; ++k) {
+            /* snprintf(leadin, sizeof(leadin), "blen=%d", k); */
+            sg_get_sense_str(NULL, err4, sizeof(err4), 0, k, b);
+            printf("%s\n", b);
+            if (prev_len == (int)strlen(b))
+                break;
+            else
+                prev_len = strlen(b);
+        }
+    }
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_simple1.c b/sg3_utils/examples/sg_simple1.c
new file mode 100644
index 0000000..73ba76e
--- /dev/null
+++ b/sg3_utils/examples/sg_simple1.c
@@ -0,0 +1,189 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This is a simple program executing a SCSI INQUIRY command and a
+   TEST UNIT READY command using the SCSI generic (sg) driver
+   There is another variant of this program called "sg_simple2"
+   which does not include the sg_lib.h header and logic and so has
+   simpler but more primitive error processing.
+   In the lk 2.6 series devices nodes such as /dev/sda also support
+   the SG_IO ioctl.
+
+*  Copyright (C) 1999-2007 D. Gilbert
+*  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.
+
+   Invocation: sg_simple1 [-x] <scsi_device>
+
+   Version 03.58 (20070312)
+
+6 byte INQUIRY command:
+[0x12][   |lu][pg cde][res   ][al len][cntrl ]
+
+6 byte TEST UNIT READY command:
+[0x00][   |lu][res   ][res   ][res   ][res   ]
+
+*/
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+#define TUR_CMD_LEN 6
+
+#define EBUFF_SZ 256
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char turCmdBlk [TUR_CMD_LEN] =
+                                {0x00, 0, 0, 0, 0, 0};
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[32];
+    int do_extra = 0;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-x", argv[k], 2))
+            do_extra = 1;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_simple1 [-x] <sg_device>'\n");
+        return 1;
+    }
+
+    /* N.B. An access mode of O_RDWR is required for some SCSI commands */
+    if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_simple1: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+    /* Just to be safe, check we have a new sg device by trying an ioctl */
+    if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+        printf("sg_simple1: %s doesn't seem to be an new sg device\n",
+               file_name);
+        close(sg_fd);
+        return 1;
+    }
+
+    /* Prepare INQUIRY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(inqCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = INQ_REPLY_LEN;
+    io_hdr.dxferp = inqBuff;
+    io_hdr.cmdp = inqCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple1: Inquiry SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on INQUIRY, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("INQUIRY command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok) { /* output result if it is available */
+        char * p = (char *)inqBuff;
+        int f = (int)*(p + 7);
+        printf("Some of the INQUIRY command's results:\n");
+        printf("    %.8s  %.16s  %.4s  ", p + 8, p + 16, p + 32);
+        printf("[wide=%d sync=%d cmdque=%d sftre=%d]\n",
+               !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1));
+        /* Extra info, not necessary to look at */
+        if (do_extra)
+            printf("INQUIRY duration=%u millisecs, resid=%d, msg_status=%d\n",
+                   io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
+    }
+
+
+    /* Prepare TEST UNIT READY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(turCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_NONE;
+    io_hdr.cmdp = turCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple1: Test Unit Ready SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on Test Unit Ready, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("Test Unit Ready command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok)
+        printf("Test Unit Ready successful so unit is ready!\n");
+    else
+        printf("Test Unit Ready failed so unit may _not_ be ready!\n");
+
+    if (do_extra)
+        printf("TEST UNIT READY duration=%u millisecs, resid=%d, "
+               "msg_status=%d\n", io_hdr.duration, io_hdr.resid,
+               (int)io_hdr.msg_status);
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_simple16.c b/sg3_utils/examples/sg_simple16.c
new file mode 100644
index 0000000..9f8a48c
--- /dev/null
+++ b/sg3_utils/examples/sg_simple16.c
@@ -0,0 +1,121 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This program performs a READ_16 command as scsi mid-level support
+   16 byte commands from lk 2.4.15
+
+*  Copyright (C) 2001 D. Gilbert
+*  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.
+
+   Invocation: sg_simple16 <scsi_device>
+
+   Version 1.02 (20020206)
+
+*/
+
+#define READ16_REPLY_LEN 512
+#define READ16_CMD_LEN 16
+
+#define EBUFF_SZ 256
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok;
+    unsigned char r16CmdBlk [READ16_CMD_LEN] =
+                {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char inBuff[READ16_REPLY_LEN];
+    unsigned char sense_buffer[32];
+
+    for (k = 1; k < argc; ++k) {
+        if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_simple16 <sg_device>'\n");
+        return 1;
+    }
+
+    if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_simple16: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+    /* Just to be safe, check we have a new sg device by trying an ioctl */
+    if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+        printf("sg_simple16: %s doesn't seem to be an new sg device\n",
+               file_name);
+        close(sg_fd);
+        return 1;
+    }
+
+    /* Prepare READ_16 command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(r16CmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = READ16_REPLY_LEN;
+    io_hdr.dxferp = inBuff;
+    io_hdr.cmdp = r16CmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple16: Inquiry SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on READ_16, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("READ_16 command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok) { /* output result if it is available */
+        printf("READ_16 duration=%u millisecs, resid=%d, msg_status=%d\n",
+               io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
+    }
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_simple2.c b/sg3_utils/examples/sg_simple2.c
new file mode 100644
index 0000000..a4763b5
--- /dev/null
+++ b/sg3_utils/examples/sg_simple2.c
@@ -0,0 +1,197 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_linux_inc.h"
+
+/* This is a simple program executing a SCSI INQUIRY command and a
+   TEST UNIT READY command using the SCSI generic (sg) driver.
+   There is another variant of this program called "sg_simple1"
+   which includes the sg_lib.h header and logic and so has more
+   advanced error processing.
+   This version demonstrates the "sg3" interface.
+   In the lk 2.6 series devices nodes such as /dev/sda also support
+   the SG_IO ioctl.
+
+*  Copyright (C) 1999-2007 D. Gilbert
+*  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.
+
+   Invocation: sg_simple2 [-x] <scsi_device>
+
+   Version 03.58 (20070312)
+
+6 byte INQUIRY command:
+[0x12][   |lu][pg cde][res   ][al len][cntrl ]
+
+6 byte TEST UNIT READY command:
+[0x00][   |lu][res   ][res   ][res   ][res   ]
+
+*/
+
+#define INQ_REPLY_LEN 96        /* logic assumes >= sizeof(inqCmdBlk) */
+#define INQ_CMD_LEN 6
+#define TUR_CMD_LEN 6
+
+#define EBUFF_SZ 256
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char turCmdBlk [TUR_CMD_LEN] =
+                                {0x00, 0, 0, 0, 0, 0};
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[32];
+    int do_extra = 0;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-x", argv[k], 2))
+            do_extra = 1;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_simple2 [-x] <sg_device>'\n");
+        return 1;
+    }
+
+    /* N.B. An access mode of O_RDWR is required for some SCSI commands */
+    if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_simple2: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+    /* Just to be safe, check we have a new sg device by trying an ioctl */
+    if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+        printf("sg_simple2: %s doesn't seem to be an new sg device\n",
+               file_name);
+        close(sg_fd);
+        return 1;
+    }
+
+    /* Prepare INQUIRY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(inqCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = INQ_REPLY_LEN;
+    io_hdr.dxferp = inqBuff;
+    io_hdr.cmdp = inqCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple2: Inquiry SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
+        if (io_hdr.sb_len_wr > 0) {
+            printf("INQUIRY sense data: ");
+            for (k = 0; k < io_hdr.sb_len_wr; ++k) {
+                if ((k > 0) && (0 == (k % 10)))
+                    printf("\n  ");
+                printf("0x%02x ", sense_buffer[k]);
+            }
+            printf("\n");
+        }
+        if (io_hdr.masked_status)
+            printf("INQUIRY SCSI status=0x%x\n", io_hdr.status);
+        if (io_hdr.host_status)
+            printf("INQUIRY host_status=0x%x\n", io_hdr.host_status);
+        if (io_hdr.driver_status)
+            printf("INQUIRY driver_status=0x%x\n", io_hdr.driver_status);
+    }
+    else {  /* output result if it is available */
+        char * p = (char *)inqBuff;
+        int f = (int)*(p + 7);
+        printf("Some of the INQUIRY command's results:\n");
+        printf("    %.8s  %.16s  %.4s  ", p + 8, p + 16, p + 32);
+        printf("[wide=%d sync=%d cmdque=%d sftre=%d]\n",
+               !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1));
+    }
+    /* Extra info, not necessary to look at */
+    if (do_extra)
+        printf("INQUIRY duration=%u millisecs, resid=%d, msg_status=%d\n",
+               io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
+
+    /* Prepare TEST UNIT READY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(turCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_NONE;
+    io_hdr.cmdp = turCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple2: Test Unit Ready SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
+        if (io_hdr.sb_len_wr > 0) {
+            printf("TEST UNIT READY sense data: ");
+            for (k = 0; k < io_hdr.sb_len_wr; ++k) {
+                if ((k > 0) && (0 == (k % 10)))
+                    printf("\n  ");
+                printf("0x%02x ", sense_buffer[k]);
+            }
+            printf("\n");
+        }
+        else if (io_hdr.masked_status)
+            printf("TEST UNIT READY SCSI status=0x%x\n", io_hdr.status);
+        else if (io_hdr.host_status)
+            printf("TEST UNIT READY host_status=0x%x\n", io_hdr.host_status);
+        else if (io_hdr.driver_status)
+            printf("TEST UNIT READY driver_status=0x%x\n",
+                   io_hdr.driver_status);
+        else
+            printf("TEST UNIT READY unexpected error\n");
+        printf("Test Unit Ready failed so unit may _not_ be ready!\n");
+    }
+    else
+        printf("Test Unit Ready successful so unit is ready!\n");
+    /* Extra info, not necessary to look at */
+    if (do_extra)
+        printf("TEST UNIT READY duration=%u millisecs, resid=%d, "
+               "msg_status=%d\n", io_hdr.duration, io_hdr.resid,
+               (int)io_hdr.msg_status);
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_simple3.c b/sg3_utils/examples/sg_simple3.c
new file mode 100644
index 0000000..93f2971
--- /dev/null
+++ b/sg3_utils/examples/sg_simple3.c
@@ -0,0 +1,205 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This is a simple program executing a SCSI INQUIRY command and a
+   TEST UNIT READY command using the SCSI generic (sg) driver.
+   There is another variant of this program called "sg_simple1".
+   This variant demonstrates using the scatter gather facility in
+   the sg_io_hdr interface to break an INQUIRY response into its
+   component parts.
+
+*  Copyright (C) 1999 D. Gilbert
+*  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.
+
+   Invocation: sg_simple3 [-x] <sg_device>
+
+   Version 03.58 (20020226)
+
+6 byte INQUIRY command:
+[0x12][   |lu][pg cde][res   ][al len][cntrl ]
+
+6 byte TEST UNIT READY command:
+[0x00][   |lu][res   ][res   ][res   ][res   ]
+
+*/
+
+#define INQ_REPLY_BASE_LEN 8
+#define INQ_REPLY_VID_LEN 8
+#define INQ_REPLY_PID_LEN 16
+#define INQ_REPLY_PREV_LEN 4
+#define INQ_REPLY_IOVEC_COUNT 4
+#define INQ_CMD_LEN 6
+#define TUR_CMD_LEN 6
+
+#define EBUFF_SZ 256
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] = {0x12, 0, 0, 0,
+                    INQ_REPLY_BASE_LEN + INQ_REPLY_VID_LEN +
+                    INQ_REPLY_PID_LEN + INQ_REPLY_PREV_LEN, 0};
+    unsigned char turCmdBlk [TUR_CMD_LEN] = {0x00, 0, 0, 0, 0, 0};
+    sg_iovec_t iovec[INQ_REPLY_IOVEC_COUNT];
+    unsigned char inqBaseBuff[INQ_REPLY_BASE_LEN];
+    char inqVidBuff[INQ_REPLY_VID_LEN];
+    char inqPidBuff[INQ_REPLY_PID_LEN];
+    char inqPRevBuff[INQ_REPLY_PREV_LEN];
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[32];
+    int do_extra = 0;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-x", argv[k], 2))
+            do_extra = 1;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_simple3 [-x] <sg_device>'\n");
+        return 1;
+    }
+
+    /* N.B. An access mode of O_RDWR is required for some SCSI commands */
+    if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_simple3: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+    /* Just to be safe, check we have a new sg device by trying an ioctl */
+    if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
+        printf("sg_simple3: %s doesn't seem to be an new sg device\n",
+               file_name);
+        close(sg_fd);
+        return 1;
+    }
+
+    /* Prepare INQUIRY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(inqCmdBlk);
+    io_hdr.iovec_count = INQ_REPLY_IOVEC_COUNT;
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = INQ_REPLY_BASE_LEN + INQ_REPLY_VID_LEN +
+                       INQ_REPLY_PID_LEN + INQ_REPLY_PREV_LEN;
+    iovec[0].iov_base = inqBaseBuff;
+    iovec[0].iov_len = INQ_REPLY_BASE_LEN;
+    iovec[1].iov_base = inqVidBuff;
+    iovec[1].iov_len = INQ_REPLY_VID_LEN;
+    iovec[2].iov_base = inqPidBuff;
+    iovec[2].iov_len = INQ_REPLY_PID_LEN;
+    iovec[3].iov_base = inqPRevBuff;
+    iovec[3].iov_len = INQ_REPLY_PREV_LEN;
+    io_hdr.dxferp = iovec;
+    io_hdr.cmdp = inqCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple3: Inquiry SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on INQUIRY, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("INQUIRY command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok) { /* output result if it is available */
+        char * p = (char *)inqBaseBuff;
+        int f = (int)*(p + 7);
+        printf("Some of the INQUIRY command's results:\n");
+        printf("  %.8s  %.16s  %.4s  ", inqVidBuff, inqPidBuff, inqPRevBuff);
+        printf("[wide=%d sync=%d cmdque=%d sftre=%d]\n",
+               !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1));
+        /* Extra info, not necessary to look at */
+        if (do_extra)
+            printf("INQUIRY duration=%u millisecs, resid=%d, msg_status=%d\n",
+                   io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
+    }
+
+
+    /* Prepare TEST UNIT READY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(turCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_NONE;
+    io_hdr.cmdp = turCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple3: Test Unit Ready SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on Test Unit Ready, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("Test Unit Ready command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok)
+        printf("Test Unit Ready successful so unit is ready!\n");
+    else
+        printf("Test Unit Ready failed so unit may _not_ be ready!\n");
+
+    if (do_extra)
+        printf("TEST UNIT READY duration=%u millisecs, resid=%d, "
+                "msg_status=%d\n", io_hdr.duration, io_hdr.resid,
+                (int)io_hdr.msg_status);
+
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_simple4.c b/sg3_utils/examples/sg_simple4.c
new file mode 100644
index 0000000..8b633ab
--- /dev/null
+++ b/sg3_utils/examples/sg_simple4.c
@@ -0,0 +1,238 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+/* This is a simple program executing a SCSI INQUIRY command and a
+   TEST UNIT READY command using the SCSI generic (sg) driver
+   This variant shows mmap-ed IO being used to read the data returned
+   by the INQUIRY command.
+
+*  Copyright (C) 2001 D. Gilbert
+*  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.
+
+   Invocation: sg_simple4 [-x] <sg_device>
+
+   Version 1.01 (20020113)
+
+6 byte INQUIRY command:
+[0x12][   |lu][pg cde][res   ][al len][cntrl ]
+
+6 byte TEST UNIT READY command:
+[0x00][   |lu][res   ][res   ][res   ][res   ]
+
+*/
+
+#ifndef SG_FLAG_MMAP_IO
+#define SG_FLAG_MMAP_IO 4
+#endif  /* since /usr/include/scsi/sg.h doesn't know about this yet */
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+#define TUR_CMD_LEN 6
+
+#define EBUFF_SZ 256
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char turCmdBlk [TUR_CMD_LEN] =
+                                {0x00, 0, 0, 0, 0, 0};
+    unsigned char * inqBuff;
+    unsigned char * inqBuff2;
+    sg_io_hdr_t io_hdr;
+    char * file_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char sense_buffer[32];
+    int do_extra = 0;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-x", argv[k], 2))
+            do_extra = 1;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_simple4 [-x] <sg_device>'\n");
+        return 1;
+    }
+
+    /* N.B. An access mode of O_RDWR is required for some SCSI commands */
+    if ((sg_fd = open(file_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_simple4: error opening file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+    /* Just to be safe, check we have a new sg device by trying an ioctl */
+    if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30122)) {
+        printf("sg_simple4: %s needs sg driver version >= 3.1.22\n",
+               file_name);
+        close(sg_fd);
+        return 1;
+    }
+
+    /* since I know this program will only read from inqBuff then I use
+       PROT_READ rather than PROT_READ | PROT_WRITE */
+    inqBuff = (unsigned char *)mmap(NULL, 8000, PROT_READ | PROT_WRITE,
+                                    MAP_SHARED, sg_fd, 0);
+    if (MAP_FAILED == inqBuff) {
+        snprintf(ebuff, EBUFF_SZ, "sg_simple4: error using mmap() on "
+                 "file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+    if (inqBuff[0])
+        printf("non-null char at inqBuff[0]\n");
+    if (inqBuff[5000])
+        printf("non-null char at inqBuff[5000]\n");
+
+    /* Prepare INQUIRY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(inqCmdBlk);
+    /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = INQ_REPLY_LEN;
+    /* io_hdr.dxferp = inqBuff; // ignored in mmap-ed IO */
+    io_hdr.cmdp = inqCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    io_hdr.flags = SG_FLAG_MMAP_IO;
+    /* io_hdr.pack_id = 0; */
+    /* io_hdr.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple4: Inquiry SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on INQUIRY, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("INQUIRY command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok) { /* output result if it is available */
+        char * p = (char *)inqBuff;
+        int f = (int)*(p + 7);
+        printf("Some of the INQUIRY command's results:\n");
+        printf("    %.8s  %.16s  %.4s  ", p + 8, p + 16, p + 32);
+        printf("[wide=%d sync=%d cmdque=%d sftre=%d]\n",
+               !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1));
+        /* Extra info, not necessary to look at */
+        if (do_extra)
+            printf("INQUIRY duration=%u millisecs, resid=%d, msg_status=%d\n",
+                   io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
+    }
+
+
+    /* Prepare TEST UNIT READY command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(turCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_NONE;
+    io_hdr.cmdp = turCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("sg_simple4: Test Unit Ready SG_IO ioctl error");
+        close(sg_fd);
+        return 1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        printf("Recovered error on Test Unit Ready, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("Test Unit Ready command error", &io_hdr, 1);
+        break;
+    }
+
+    if (ok)
+        printf("Test Unit Ready successful so unit is ready!\n");
+    else
+        printf("Test Unit Ready failed so unit may _not_ be ready!\n");
+
+    if (do_extra)
+        printf("TEST UNIT READY duration=%u millisecs, resid=%d, "
+               "msg_status=%d\n",
+               io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
+
+    /* munmap(inqBuff, 8000); */
+    /* could call munmap(inqBuff, INQ_REPLY_LEN) here but following close()
+       causes this too happen anyway */
+#if 1
+    inqBuff2 = (unsigned char *)mmap(NULL, 8000, PROT_READ | PROT_WRITE,
+                                     MAP_SHARED, sg_fd, 0);
+    if (MAP_FAILED == inqBuff2) {
+        snprintf(ebuff, EBUFF_SZ, "sg_simple4: error using mmap() 2 on "
+                 "file: %s", file_name);
+        perror(ebuff);
+        return 1;
+    }
+    if (inqBuff2[0])
+        printf("non-null char at inqBuff2[0]\n");
+    if (inqBuff2[5000])
+        printf("non-null char at inqBuff2[5000]\n");
+    {
+        pid_t pid;
+        pid = fork();
+        if (pid) {
+            inqBuff2[5000] = 33;
+            munmap(inqBuff, 8000);
+            sleep(3);
+        }
+        else {
+            inqBuff[5000] = 0xaa;
+            munmap(inqBuff, 8000);
+            sleep(1);
+        }
+    }
+#endif
+    close(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_simple5.c b/sg3_utils/examples/sg_simple5.c
new file mode 100644
index 0000000..3a09d8f
--- /dev/null
+++ b/sg3_utils/examples/sg_simple5.c
@@ -0,0 +1,236 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sg_lib.h"
+#include "sg_pt.h"
+
+/* This is a simple program executing a SCSI INQUIRY command and a
+   TEST UNIT READY command using the SCSI generic pass through
+   interface. This allows this example program to be ported to
+   OSes other than linux.
+
+*  Copyright (C) 2006-2007 D. Gilbert
+*  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.
+
+   Invocation: sg_simple5 [-x] <scsi_device>
+
+   Version 1.01 (20070331)
+
+*/
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+#define TUR_CMD_LEN 6
+
+#define CMD_TIMEOUT_SECS 60
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, ok, dsize, res, duration, resid, cat, got, slen;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char turCmdBlk [TUR_CMD_LEN] =
+                                {0x00, 0, 0, 0, 0, 0};
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    char * file_name = 0;
+    char b[512];
+    unsigned char sense_b[32];
+    int verbose = 0;
+    struct sg_pt_base * ptvp;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == strcmp("-v", argv[k]))
+            verbose = 1;
+        else if (0 == strcmp("-vv", argv[k]))
+            verbose = 2;
+        else if (0 == strcmp("-vvv", argv[k]))
+            verbose = 3;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            file_name = 0;
+            break;
+        }
+        else if (0 == file_name)
+            file_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            file_name = 0;
+            break;
+        }
+    }
+    if (0 == file_name) {
+        printf("Usage: 'sg_simple5 [-v|-vv|-vvv] <device>'\n");
+        return 1;
+    }
+
+    sg_fd = scsi_pt_open_device(file_name, 1 /* ro */, 0);
+    /* N.B. An access mode of O_RDWR is required for some SCSI commands */
+    if (sg_fd < 0) {
+        fprintf(stderr, "error opening file: %s: %s\n",
+                file_name, safe_strerror(-sg_fd));
+        return 1;
+    }
+
+    dsize = sizeof(inqBuff);
+    ok = 0;
+
+    ptvp = construct_scsi_pt_obj();     /* one object per command */
+    if (NULL == ptvp) {
+        fprintf(stderr, "sg_simple5: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, inqCmdBlk, sizeof(inqCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, inqBuff, dsize);
+    res = do_scsi_pt(ptvp, sg_fd, CMD_TIMEOUT_SECS, verbose);
+    if (res < 0) {
+        fprintf(stderr, "  pass through os error: %s\n",
+                safe_strerror(-res));
+        goto finish_inq;
+    } else if (SCSI_PT_DO_BAD_PARAMS == res) {
+        fprintf(stderr, "  bad pass through setup\n");
+        goto finish_inq;
+    } else if (SCSI_PT_DO_TIMEOUT == res) {
+        fprintf(stderr, "  pass through timeout\n");
+        goto finish_inq;
+    }
+    if ((verbose > 1) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
+        fprintf(stderr, "      duration=%d ms\n", duration);
+    resid = get_scsi_pt_resid(ptvp);
+    switch ((cat = get_scsi_pt_result_category(ptvp))) {
+    case SCSI_PT_RESULT_GOOD:
+        got = dsize - resid;
+        if (verbose && (resid > 0))
+            fprintf(stderr, "    requested %d bytes but "
+                    "got %d bytes)\n", dsize, got);
+        break;
+    case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
+        if (verbose) {
+            sg_get_scsi_status_str(get_scsi_pt_status_response(ptvp),
+                                   sizeof(b), b);
+            fprintf(stderr, "  scsi status: %s\n", b);
+        }
+        goto finish_inq;
+    case SCSI_PT_RESULT_SENSE:
+        slen = get_scsi_pt_sense_len(ptvp);
+        if (verbose) {
+            sg_get_sense_str("", sense_b, slen, (verbose > 1),
+                             sizeof(b), b);
+            fprintf(stderr, "%s", b);
+        }
+        if (verbose && (resid > 0)) {
+            got = dsize - resid;
+            if ((verbose) || (got > 0))
+                fprintf(stderr, "    requested %d bytes but "
+                        "got %d bytes\n", dsize, got);
+        }
+        goto finish_inq;
+    case SCSI_PT_RESULT_TRANSPORT_ERR:
+        if (verbose) {
+            get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
+            fprintf(stderr, "  transport: %s", b);
+        }
+        goto finish_inq;
+    case SCSI_PT_RESULT_OS_ERR:
+        if (verbose) {
+            get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
+            fprintf(stderr, "  os: %s", b);
+        }
+        goto finish_inq;
+    default:
+        fprintf(stderr, "  unknown pass through result "
+                "category (%d)\n", cat);
+        goto finish_inq;
+    }
+
+    ok = 1;
+finish_inq:
+    destruct_scsi_pt_obj(ptvp);
+
+    if (ok) { /* output result if it is available */
+        char * p = (char *)inqBuff;
+
+        printf("Some of the INQUIRY command's results:\n");
+        printf("    %.8s  %.16s  %.4s\n", p + 8, p + 16, p + 32);
+    }
+    ok = 0;
+
+
+    /* Now prepare TEST UNIT READY command */
+    ptvp = construct_scsi_pt_obj();     /* one object per command */
+    if (NULL == ptvp) {
+        fprintf(stderr, "sg_simple5: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, turCmdBlk, sizeof(turCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    /* no data in or out */
+    res = do_scsi_pt(ptvp, sg_fd, CMD_TIMEOUT_SECS, verbose);
+    if (res < 0) {
+        fprintf(stderr, "  pass through os error: %s\n",
+                safe_strerror(-res));
+        goto finish_inq;
+    } else if (SCSI_PT_DO_BAD_PARAMS == res) {
+        fprintf(stderr, "  bad pass through setup\n");
+        goto finish_inq;
+    } else if (SCSI_PT_DO_TIMEOUT == res) {
+        fprintf(stderr, "  pass through timeout\n");
+        goto finish_inq;
+    }
+    if ((verbose > 1) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
+        fprintf(stderr, "      duration=%d ms\n", duration);
+    resid = get_scsi_pt_resid(ptvp);
+    switch ((cat = get_scsi_pt_result_category(ptvp))) {
+    case SCSI_PT_RESULT_GOOD:
+        break;
+    case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
+        if (verbose) {
+            sg_get_scsi_status_str(get_scsi_pt_status_response(ptvp),
+                                   sizeof(b), b);
+            fprintf(stderr, "  scsi status: %s\n", b);
+        }
+        goto finish_tur;
+    case SCSI_PT_RESULT_SENSE:
+        slen = get_scsi_pt_sense_len(ptvp);
+        if (verbose) {
+            sg_get_sense_str("", sense_b, slen, (verbose > 1),
+                             sizeof(b), b);
+            fprintf(stderr, "%s", b);
+        }
+        goto finish_tur;
+    case SCSI_PT_RESULT_TRANSPORT_ERR:
+        if (verbose) {
+            get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
+            fprintf(stderr, "  transport: %s", b);
+        }
+        goto finish_tur;
+    case SCSI_PT_RESULT_OS_ERR:
+        if (verbose) {
+            get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
+            fprintf(stderr, "  os: %s", b);
+        }
+        goto finish_tur;
+    default:
+        fprintf(stderr, "  unknown pass through result "
+                "category (%d)\n", cat);
+        goto finish_tur;
+    }
+
+    ok = 1;
+finish_tur:
+    destruct_scsi_pt_obj(ptvp);
+
+    if (ok)
+        printf("Test Unit Ready successful so unit is ready!\n");
+    else
+        printf("Test Unit Ready failed so unit may _not_ be ready!\n");
+
+    scsi_pt_close_device(sg_fd);
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_tst_async.cpp b/sg3_utils/examples/sg_tst_async.cpp
new file mode 100644
index 0000000..67f247c
--- /dev/null
+++ b/sg3_utils/examples/sg_tst_async.cpp
@@ -0,0 +1,1215 @@
+/*
+ * Copyright (c) 2014-2015 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <vector>
+#include <map>
+#include <list>
+#include <system_error>
+#include <thread>
+#include <mutex>
+#include <chrono>
+#include <atomic>
+#include <random>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <poll.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <limits.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+
+static const char * version_str = "1.11 20150227";
+static const char * util_name = "sg_tst_async";
+
+/* This is a test program for checking the async usage of the Linux sg
+ * driver. Each thread opens 1 file descriptor to the next sg device (1
+ * or more can be given on the command line) and then starts up to 16
+ * commands while checking with the poll command (or
+ * ioctl(SG_GET_NUM_WAITING) ) for the completion of those commands. Each
+ * command has a unique "pack_id" which is a sequence starting at 1.
+ * Either TEST UNIT UNIT, READ(16) or WRITE(16) commands are issued.
+ *
+ * This is C++ code with some things from C++11 (e.g. threads) and was
+ * only just able to compile (when some things were reverted) with gcc/g++
+ * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support
+ * was not available until g++ version 4.8.1 . It should build okay on
+ * recent distributions.
+ *
+ * The build uses various object files from the <sg3_utils>/lib directory
+ * which is assumed to be a sibling of this examples directory. Those
+ * object files in the lib directory can be built with:
+ *   cd <sg3_utils_package_root> ; ./configure ; cd lib; make
+ *   cd ../examples
+ * Then to build sg_tst_async concatenate the next 3 lines:
+ *   g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o
+ *     ../lib/sg_lib_data.o ../lib/sg_io_linux.o -o sg_tst_async
+ *     sg_tst_async.cpp
+ * or use the C++ Makefile in that directory:
+ *   make -f Makefile.cplus sg_tst_async
+ *
+ * Currently this utility is Linux only and uses the sg driver. The bsg
+ * driver is known to be broken (it doesn't match responses to the
+ * correct file descriptor that requested them) so this utility won't
+ * be extended to bsg until that is fixed.
+ *
+ * BEWARE: >>> This utility will modify a logical block (default LBA 1000)
+ * on the given device when the '-W' option is given.
+ *
+ */
+
+using namespace std;
+using namespace std::chrono;
+
+#define DEF_NUM_PER_THREAD 1000
+#define DEF_NUM_THREADS 4
+#define DEF_WAIT_MS 10          /* 0: yield or no wait */
+#define DEF_TIMEOUT_MS 20000    /* 20 seconds */
+#define DEF_LB_SZ 512
+#define DEF_BLOCKING 0
+#define DEF_DIRECT 0            /* 1: direct_io [future maybe 2: mmap IO] */
+#define DEF_NO_XFER 0
+#define DEF_LBA 1000
+
+#define MAX_Q_PER_FD 16     /* sg driver per file descriptor limit */
+#define MAX_CONSEC_NOMEMS 16
+#define URANDOM_DEV "/dev/urandom"
+
+#ifndef SG_FLAG_Q_AT_TAIL
+#define SG_FLAG_Q_AT_TAIL 0x10
+#endif
+#ifndef SG_FLAG_Q_AT_HEAD
+#define SG_FLAG_Q_AT_HEAD 0x20
+#endif
+
+
+
+#define EBUFF_SZ 256
+
+static mutex console_mutex;
+static mutex rand_lba_mutex;
+static atomic<int> async_starts(0);
+static atomic<int> async_finishes(0);
+static atomic<int> ebusy_count(0);
+static atomic<int> start_eagain_count(0);
+static atomic<int> fin_eagain_count(0);
+static atomic<int> uniq_pack_id(1);
+
+static int page_size = 4096;   /* rough guess, will ask sysconf() */
+
+enum command2execute {SCSI_TUR, SCSI_READ16, SCSI_WRITE16};
+/* Linux Block layer queue disciplines: */
+enum blkLQDiscipline {BLQ_DEFAULT, BLQ_AT_HEAD, BLQ_AT_TAIL};
+/* Queue disciplines of this utility. When both completions and
+ * queuing a new command are both possible: */
+enum myQDiscipline {MYQD_LOW,   /* favour completions over new cmds */
+                    MYQD_MEDIUM,
+                    MYQD_HIGH}; /* favour new cmds over completions */
+
+struct opts_t {
+    vector<const char *> dev_names;
+    int direct;
+    int maxq_per_thread;
+    int num_per_thread;
+    bool block;
+    uint64_t lba;
+    unsigned int hi_lba;        /* last one, inclusive range */
+    vector<unsigned int> hi_lbas; /* only used when hi_lba=-1 */
+    int lb_sz;
+    bool no_xfer;
+    int stats;
+    int verbose;
+    int wait_ms;
+    command2execute c2e;
+    blkLQDiscipline blqd;
+    myQDiscipline myqd;
+};
+
+#if 0
+class Rand_uint {
+public:
+    Rand_uint(unsigned int lo, unsigned int hi) : p{lo, hi} {}
+    unsigned int operator()() const { return r(); }
+private:
+    uniform_int_distribution<unsigned int>::param_type p;
+    auto r = bind(uniform_int_distribution<unsigned int>{p},
+                  default_random_engine());
+    /* compiler thinks auto should be a static, bs again? */
+};
+#endif
+
+#if 0
+class Rand_uint {
+public:
+    Rand_uint(unsigned int lo, unsigned int hi, unsigned int my_seed)
+        : r(bind(uniform_int_distribution<unsigned int>{lo, hi},
+                 default_random_engine())) { r.seed(myseed); }
+    unsigned int operator()() const { return r(); }
+private:
+    function<unsigned int()> r;
+};
+#endif
+
+/* Use this class to wrap C++11 <random> features to produce uniform random
+ * unsigned ints in the range [lo, hi] (inclusive) given a_seed */
+class Rand_uint {
+public:
+    Rand_uint(unsigned int lo, unsigned int hi, unsigned int a_seed)
+        : uid(lo, hi), dre(a_seed) { }
+    /* uid ctor takes inclusive range when integral type */
+
+    unsigned int get() { return uid(dre); }
+
+private:
+    uniform_int_distribution<unsigned int> uid;
+    default_random_engine dre;
+};
+
+static struct option long_options[] = {
+        {"direct", no_argument, 0, 'd'},
+        {"force", no_argument, 0, 'f'},
+        {"help", no_argument, 0, 'h'},
+        {"lba", required_argument, 0, 'l'},
+        {"maxqpt", required_argument, 0, 'M'},
+        {"numpt", required_argument, 0, 'n'},
+        {"noxfer", no_argument, 0, 'N'},
+        {"qat", required_argument, 0, 'q'},
+        {"qfav", required_argument, 0, 'Q'},
+        {"read", no_argument, 0, 'R'},
+        {"szlb", required_argument, 0, 's'},
+        {"stats", no_argument, 0, 'S'},
+        {"tnum", required_argument, 0, 't'},
+        {"tur", no_argument, 0, 'T'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {"wait", required_argument, 0, 'w'},
+        {"write", no_argument, 0, 'W'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage(void)
+{
+    printf("Usage: %s [--direct] [--force] [--help] [--lba=LBA+] "
+           "[--maxqpt=QPT]\n"
+           "                    [--numpt=NPT] [--noxfer] [--qat=AT] "
+           "[-qfav=FAV]\n"
+           "                    [--read] [--szlb=LB] [--stats] [--tnum=NT] "
+           "[--tur]\n"
+           "                    [--verbose] [--version] [--wait=MS] "
+           "[--write]\n"
+           "                    <sg_disk_device>*\n",
+           util_name);
+    printf("  where\n");
+    printf("    --direct|-d     do direct_io (def: indirect)\n");
+    printf("    --force|-f      force: any sg device (def: only scsi_debug "
+           "owned)\n");
+    printf("                    WARNING: <lba> written to if '-W' given\n");
+    printf("    --help|-h       print this usage message then exit\n");
+    printf("    --lba=LBA|-l LBA    logical block to access (def: %u)\n",
+           DEF_LBA);
+    printf("    --lba=LBA,HI_LBA|-l LBA,HI_LBA    logical block range "
+           "(inclusive)\n"
+           "                          if hi_lba=-1 assume last block on "
+           "device\n");
+    printf("    --maxqpt=QPT|-M QPT    maximum commands queued per thread "
+           "(def:%d)\n", MAX_Q_PER_FD);
+    printf("    --numpt=NPT|-n NPT    number of commands per thread "
+           "(def: %d)\n", DEF_NUM_PER_THREAD);
+    printf("    --noxfer|-N             no data xfer (def: xfer on READ and "
+           "WRITE)\n");
+    printf("    --qat=AT|-q AT       AT=0: q_at_head; AT=1: q_at_tail\n");
+    printf("    --qfav=FAV|-Q FAV    FAV=0: favour completions (smaller q),\n"
+           "                         FAV=1: medium,\n"
+           "                         FAV=2: favour submissions (larger q, "
+           "default)\n");
+    printf("    --read|-R       do READs (def: TUR)\n");
+    printf("    --szlb=LB|-s LB    logical block size (def: 512)\n");
+    printf("    --stats|-S      show more statistics on completion\n");
+    printf("    --tnum=NT|-t NT    number of threads (def: %d)\n",
+           DEF_NUM_THREADS);
+    printf("    --tur|-T        do TEST UNIT READYs (default is TURs)\n");
+    printf("    --verbose|-v    increase verbosity\n");
+    printf("    --version|-V    print version number then exit\n");
+    printf("    --wait=MS|-w MS    >0: poll(<wait_ms>); =0: poll(0); (def: "
+           "%d)\n", DEF_WAIT_MS);
+    printf("    --write|-W      do WRITEs (def: TUR)\n\n");
+    printf("Multiple threads send READ(16), WRITE(16) or TEST UNIT READY "
+           "(TUR) SCSI\ncommands. There can be 1 or more <sg_disk_device>s "
+           "and each thread takes\nthe next in a round robin fashion. "
+           "Each thread queues up to 16 commands.\nOne block is transferred "
+           "by each READ and WRITE; zeros are written. If a\nlogical block "
+           "range is given, a uniform distribution generates a pseudo\n"
+           "random sequence of LBAs.\n");
+}
+
+#ifdef __GNUC__
+static int pr2serr_lk(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+static void pr_errno_lk(int e_no, const char * fmt, ...)
+        __attribute__ ((format (printf, 2, 3)));
+#else
+static int pr2serr_lk(const char * fmt, ...);
+static void pr_errno_lk(int e_no, const char * fmt, ...);
+#endif
+
+
+static int
+pr2serr_lk(const char * fmt, ...)
+{
+    int n;
+    va_list args;
+    lock_guard<mutex> lg(console_mutex);
+
+    va_start(args, fmt);
+    n = vfprintf(stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+static void
+pr_errno_lk(int e_no, const char * fmt, ...)
+{
+    char b[160];
+    va_list args;
+    lock_guard<mutex> lg(console_mutex);
+
+    va_start(args, fmt);
+    vsnprintf(b, sizeof(b), fmt, args);
+    fprintf(stderr, "%s: %s\n", b, strerror(e_no));
+    va_end(args);
+}
+
+static unsigned int
+get_urandom_uint(void)
+{
+    unsigned int res = 0;
+    int n;
+    unsigned char b[sizeof(unsigned int)];
+    lock_guard<mutex> lg(rand_lba_mutex);
+
+    int fd = open(URANDOM_DEV, O_RDONLY);
+    if (fd >= 0) {
+        n = read(fd, b, sizeof(unsigned int));
+        if (sizeof(unsigned int) == n)
+            memcpy(&res, b, sizeof(unsigned int));
+        close(fd);
+    }
+    return res;
+}
+
+#define TUR_CMD_LEN 6
+#define READ16_REPLY_LEN 512
+#define READ16_CMD_LEN 16
+#define WRITE16_REPLY_LEN 512
+#define WRITE16_CMD_LEN 16
+
+/* Returns 0 if command injected okay, else -1 */
+static int
+start_sg3_cmd(int sg_fd, command2execute cmd2exe, int pack_id, uint64_t lba,
+              unsigned char * lbp, int xfer_bytes, int flags,
+              unsigned int & eagains)
+{
+    struct sg_io_hdr pt;
+    unsigned char turCmdBlk[TUR_CMD_LEN] = {0, 0, 0, 0, 0, 0};
+    unsigned char r16CmdBlk[READ16_CMD_LEN] =
+                {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    unsigned char w16CmdBlk[WRITE16_CMD_LEN] =
+                {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    unsigned char sense_buffer[64];
+    const char * np = NULL;
+
+    memset(&pt, 0, sizeof(pt));
+    switch (cmd2exe) {
+    case SCSI_TUR:
+        np = "TEST UNIT READY";
+        pt.cmdp = turCmdBlk;
+        pt.cmd_len = sizeof(turCmdBlk);
+        pt.dxfer_direction = SG_DXFER_NONE;
+        break;
+    case SCSI_READ16:
+        np = "READ(16)";
+        if (lba > 0xffffffff)
+            sg_put_unaligned_be32(lba >> 32, &r16CmdBlk[2]);
+        sg_put_unaligned_be32(lba & 0xffffffff, &r16CmdBlk[6]);
+        pt.cmdp = r16CmdBlk;
+        pt.cmd_len = sizeof(r16CmdBlk);
+        pt.dxfer_direction = SG_DXFER_FROM_DEV;
+        pt.dxferp = lbp;
+        pt.dxfer_len = xfer_bytes;
+        break;
+    case SCSI_WRITE16:
+        np = "WRITE(16)";
+        if (lba > 0xffffffff)
+            sg_put_unaligned_be32(lba >> 32, &w16CmdBlk[2]);
+        sg_put_unaligned_be32(lba & 0xffffffff, &w16CmdBlk[6]);
+        pt.cmdp = w16CmdBlk;
+        pt.cmd_len = sizeof(w16CmdBlk);
+        pt.dxfer_direction = SG_DXFER_TO_DEV;
+        pt.dxferp = lbp;
+        pt.dxfer_len = xfer_bytes;
+        break;
+    }
+    pt.interface_id = 'S';
+    pt.mx_sb_len = sizeof(sense_buffer);
+    pt.sbp = sense_buffer;      /* ignored .... */
+    pt.timeout = DEF_TIMEOUT_MS;
+    pt.pack_id = pack_id;
+    pt.flags = flags;
+
+    for (int k = 0; write(sg_fd, &pt, sizeof(pt)) < 0; ++k) {
+        if ((ENOMEM == errno) && (k < MAX_CONSEC_NOMEMS)) {
+            this_thread::yield();
+            continue;
+        }
+        if (EAGAIN == errno) {
+            ++eagains;
+            this_thread::yield();
+            continue;
+        }
+        pr_errno_lk(errno, "%s: %s, pack_id=%d", __func__, np, pack_id);
+        return -1;
+    }
+    return 0;
+}
+
+static int
+finish_sg3_cmd(int sg_fd, command2execute cmd2exe, int & pack_id, int wait_ms,
+               unsigned int & eagains)
+{
+    int ok, res;
+    struct sg_io_hdr pt;
+    unsigned char sense_buffer[64];
+    const char * np = NULL;
+
+    memset(&pt, 0, sizeof(pt));
+    switch (cmd2exe) {
+    case SCSI_TUR:
+        np = "TEST UNIT READY";
+        break;
+    case SCSI_READ16:
+        np = "READ(16)";
+        break;
+    case SCSI_WRITE16:
+        np = "WRITE(16)";
+        break;
+    }
+    pt.interface_id = 'S';
+    pt.mx_sb_len = sizeof(sense_buffer);
+    pt.sbp = sense_buffer;
+    pt.timeout = DEF_TIMEOUT_MS;
+    pt.pack_id = 0;
+
+    while (((res = read(sg_fd, &pt, sizeof(pt))) < 0) &&
+           (EAGAIN == errno)) {
+        ++eagains;
+        if (wait_ms > 0)
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+    }
+    if (res < 0) {
+        pr_errno_lk(errno, "%s: %s", __func__, np);
+        return -1;
+    }
+    /* now for the error processing */
+    pack_id = pt.pack_id;
+    ok = 0;
+    switch (sg_err_category3(&pt)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        pr2serr_lk("%s: Recovered error on %s, continuing\n", __func__, np);
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        {
+            lock_guard<mutex> lg(console_mutex);
+            sg_chk_n_print3(np, &pt, 1);
+        }
+        break;
+    }
+    return ok ? 0 : -1;
+}
+
+/* Should have page alignment if direct_io chosen */
+static unsigned char *
+get_aligned_heap(int bytes_at_least)
+{
+    int n;
+    void * wp;
+
+    if (bytes_at_least < page_size)
+        n = page_size;
+    else
+        n = bytes_at_least;
+#if 1
+    int err = posix_memalign(&wp, page_size, n);
+    if (err) {
+        pr2serr_lk("posix_memalign: error [%d] out of memory?\n", err);
+        return NULL;
+    }
+    memset(wp, 0, n);
+    return (unsigned char *)wp;
+#else
+    /* hack if posix_memalign() is not available */
+    if (n == page_size) {
+        wp = calloc(page_size, 1);
+        memset(wp, 0, n);
+        return (unsigned char *)wp;
+    } else {
+        pr2serr_lk("get_aligned_heap: too fiddly to align, choose smaller "
+                   "lb_sz\n");
+        return NULL;
+    }
+#endif
+}
+
+static void
+work_thread(int id, struct opts_t * op)
+{
+    int thr_async_starts = 0;
+    int thr_async_finishes = 0;
+    unsigned int thr_start_eagain_count = 0;
+    unsigned int thr_fin_eagain_count = 0;
+    unsigned int seed = 0;
+    unsigned int hi_lba;
+    int k, n, res, sg_fd, num_outstanding, do_inc, npt, pack_id, sg_flags;
+    int num_waiting_read, num_to_read;
+    int open_flags = O_RDWR;
+    bool is_rw = (SCSI_TUR != op->c2e);
+    char ebuff[EBUFF_SZ];
+    uint64_t lba;
+    unsigned char * lbp;
+    const char * dev_name;
+    const char * err = NULL;
+    Rand_uint * ruip = NULL;
+    struct pollfd  pfd[1];
+    list<unsigned char *> free_lst;         /* of aligned lb buffers */
+    map<int, unsigned char *> pi_2_buff;    /* pack_id -> lb buffer */
+    map<int, uint64_t> pi_2_lba;            /* pack_id -> LBA */
+
+    /* device name and hi_lba may depend on id */
+    n = op->dev_names.size();
+    dev_name = op->dev_names[id % n];
+    if ((UINT_MAX == op->hi_lba) && (n == (int)op->hi_lbas.size()))
+        hi_lba = op->hi_lbas[id % n];
+    else
+        hi_lba = op->hi_lba;
+
+    if (op->verbose) {
+        if ((op->verbose > 1) && hi_lba)
+            pr2serr_lk("Enter work_thread id=%d using %s\n"
+                       "    LBA range: 0x%x to 0x%x (inclusive)\n",
+                       id, dev_name, (unsigned int)op->lba, hi_lba);
+        else
+            pr2serr_lk("Enter work_thread id=%d using %s\n", id, dev_name);
+    }
+    if (! op->block)
+        open_flags |= O_NONBLOCK;
+
+    sg_fd = open(dev_name, open_flags);
+    if (sg_fd < 0) {
+        pr_errno_lk(errno, "%s: id=%d, error opening file: %s", __func__, id,
+                    dev_name);
+        return;
+    }
+    pfd[0].fd = sg_fd;
+    pfd[0].events = POLLIN;
+    if (is_rw && hi_lba) {
+        seed = get_urandom_uint();
+        if (op->verbose > 1)
+            pr2serr_lk("  id=%d, /dev/urandom seed=0x%x\n", id, seed);
+        ruip = new Rand_uint((unsigned int)op->lba, hi_lba, seed);
+    }
+
+    sg_flags = 0;
+    if (BLQ_AT_TAIL == op->blqd)
+        sg_flags |= SG_FLAG_Q_AT_TAIL;
+    else if (BLQ_AT_HEAD == op->blqd)
+        sg_flags |= SG_FLAG_Q_AT_HEAD;
+    if (op->direct)
+        sg_flags |= SG_FLAG_DIRECT_IO;
+    if (op->no_xfer)
+        sg_flags |= SG_FLAG_NO_DXFER;
+    if (op->verbose > 1)
+        pr2serr_lk("  id=%d, sg_flags=0x%x, %s cmds\n", id, sg_flags,
+                   ((SCSI_TUR == op->c2e) ? "TUR":
+                    ((SCSI_READ16 == op->c2e) ? "READ" : "WRITE")));
+
+    npt = op->num_per_thread;
+    /* main loop, continues until num_per_thread exhausted and there are
+     * no more outstanding responses */
+    for (k = 0, num_outstanding = 0; (k < npt) || num_outstanding;
+         k = do_inc ? k + 1 : k) {
+        do_inc = 0;
+        if ((num_outstanding < op->maxq_per_thread) && (k < npt)) {
+            do_inc = 1;
+            pack_id = uniq_pack_id.fetch_add(1);
+            if (is_rw) {    /* get new lb buffer or one from free list */
+                if (free_lst.empty()) {
+                    lbp = get_aligned_heap(op->lb_sz);
+                    if (NULL == lbp) {
+                        err = "out of memory";
+                        break;
+                    }
+                } else {
+                    lbp = free_lst.back();
+                    free_lst.pop_back();
+                }
+            } else
+                lbp = NULL;
+            if (is_rw) {
+                if (ruip) {
+                    lba = ruip->get();  /* fetch a random LBA */
+                    if (op->verbose > 3)
+                        pr2serr_lk("  id=%d: start IO at lba=0x%" PRIx64 "\n",
+                                   id, lba);
+                } else
+                    lba = op->lba;
+            } else
+                lba = 0;
+            if (start_sg3_cmd(sg_fd, op->c2e, pack_id, lba, lbp, op->lb_sz,
+                              sg_flags, thr_start_eagain_count)) {
+                err = "start_sg3_cmd()";
+                break;
+            }
+            ++thr_async_starts;
+            ++num_outstanding;
+            pi_2_buff[pack_id] = lbp;
+            if (ruip)
+                pi_2_lba[pack_id] = lba;
+        }
+        num_to_read = 0;
+        if ((num_outstanding >= op->maxq_per_thread) || (k >= npt)) {
+            /* full queue or finished injecting */
+            num_waiting_read = 0;
+            if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting_read) < 0) {
+                err = "ioctl(SG_GET_NUM_WAITING) failed";
+                break;
+            }
+            if (1 == num_waiting_read)
+                num_to_read = num_waiting_read;
+            else if (num_waiting_read > 0) {
+                if (k >= npt)
+                    num_to_read = num_waiting_read;
+                else {
+                    switch (op->myqd) {
+                    case MYQD_LOW:
+                        num_to_read = num_waiting_read;
+                        break;
+                    case MYQD_MEDIUM:
+                        num_to_read = num_waiting_read / 2;
+                        break;
+                    case MYQD_HIGH:
+                    default:
+                        num_to_read = 1;
+                        break;
+                    }
+                }
+            } else {    /* nothing waiting to be read */
+                n = (op->wait_ms > 0) ? op->wait_ms : 0;
+                while (0 == (res = poll(pfd, 1, n))) {
+                    if (res < 0) {
+                        err = "poll(wait_ms) failed";
+                        break;
+                    }
+                }
+                if (err)
+                    break;
+            }
+        } else {        /* not full, not finished injecting */
+            if (MYQD_HIGH == op->myqd)
+                num_to_read = 0;
+            else {
+                num_waiting_read = 0;
+                if (ioctl(sg_fd, SG_GET_NUM_WAITING, &num_waiting_read) < 0) {
+                    err = "ioctl(SG_GET_NUM_WAITING) failed";
+                    break;
+                }
+                if (num_waiting_read > 0)
+                    num_to_read = num_waiting_read /
+                                  ((MYQD_LOW == op->myqd) ? 1 : 2);
+                else
+                    num_to_read = 0;
+            }
+        }
+
+        while (num_to_read-- > 0) {
+            if (finish_sg3_cmd(sg_fd, op->c2e, pack_id, op->wait_ms,
+                               thr_fin_eagain_count)) {
+                err = "finish_sg3_cmd()";
+                if (ruip && (pack_id > 0)) {
+                    auto q = pi_2_lba.find(pack_id);
+
+                    if (q != pi_2_lba.end()) {
+                        snprintf(ebuff, sizeof(ebuff), "%s: lba=0x%" PRIx64 ,
+                                 err, q->second);
+                        err = ebuff;
+                    }
+                }
+                break;
+            }
+            ++thr_async_finishes;
+            --num_outstanding;
+            auto p = pi_2_buff.find(pack_id);
+
+            if (p == pi_2_buff.end()) {
+                snprintf(ebuff, sizeof(ebuff), "pack_id=%d from "
+                         "finish_sg3_cmd() not found\n", pack_id);
+                if (! err)
+                    err = ebuff;
+            } else {
+                lbp = p->second;
+                pi_2_buff.erase(p);
+                if (lbp)
+                    free_lst.push_front(lbp);
+            }
+            if (ruip && (pack_id > 0)) {
+                auto q = pi_2_lba.find(pack_id);
+
+                if (q != pi_2_lba.end()) {
+                    if (op->verbose > 3)
+                        pr2serr_lk("    id=%d: finish IO at lba=0x%" PRIx64
+                                   "\n", id, q->second);
+                    pi_2_lba.erase(q);
+                }
+            }
+            if (err)
+                break;
+        }
+        if (err)
+            break;
+    }
+    close(sg_fd);       // sg driver will handle any commands "in flight"
+    if (ruip)
+        delete ruip;
+
+    if (err || (k < npt)) {
+        if (k < npt)
+            pr2serr_lk("thread id=%d FAILed at iteration %d%s%s\n", id, k,
+                       (err ? ", Reason: " : ""), (err ? err : ""));
+        else
+            pr2serr_lk("thread id=%d FAILed on last%s%s\n", id,
+                       (err ? ", Reason: " : ""), (err ? err : ""));
+    }
+    n = pi_2_buff.size();
+    if (n > 0)
+        pr2serr_lk("thread id=%d Still %d elements in pi_2_buff map on "
+                   "exit\n", id, n);
+    for (k = 0; ! free_lst.empty(); ++k) {
+        lbp = free_lst.back();
+        free_lst.pop_back();
+        if (lbp)
+            free(lbp);
+    }
+    if ((op->verbose > 2) && (k > 0))
+        pr2serr_lk("thread id=%d Maximum number of READ/WRITEs queued: %d\n",
+                   id, k);
+    async_starts += thr_async_starts;
+    async_finishes += thr_async_finishes;
+    start_eagain_count += thr_start_eagain_count;
+    fin_eagain_count += thr_fin_eagain_count;
+}
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+
+/* Send INQUIRY and fetches response. If okay puts PRODUCT ID field
+ * in b (up to m_blen bytes). Does not use O_EXCL flag. Returns 0 on success,
+ * else -1 . */
+static int
+do_inquiry_prod_id(const char * dev_name, int block, char * b, int b_mlen)
+{
+    int sg_fd, ok, ret;
+    struct sg_io_hdr pt;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    unsigned char sense_buffer[64];
+    int open_flags = O_RDWR;    /* O_EXCL | O_RDONLY fails with EPERM */
+
+    if (! block)
+        open_flags |= O_NONBLOCK;
+    sg_fd = open(dev_name, open_flags);
+    if (sg_fd < 0) {
+        pr_errno_lk(errno, "%s: error opening file: %s", __func__, dev_name);
+        return -1;
+    }
+    /* Prepare INQUIRY command */
+    memset(&pt, 0, sizeof(pt));
+    pt.interface_id = 'S';
+    pt.cmd_len = sizeof(inqCmdBlk);
+    /* pt.iovec_count = 0; */  /* memset takes care of this */
+    pt.mx_sb_len = sizeof(sense_buffer);
+    pt.dxfer_direction = SG_DXFER_FROM_DEV;
+    pt.dxfer_len = INQ_REPLY_LEN;
+    pt.dxferp = inqBuff;
+    pt.cmdp = inqCmdBlk;
+    pt.sbp = sense_buffer;
+    pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* pt.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* pt.pack_id = 0; */
+    /* pt.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &pt) < 0) {
+        pr_errno_lk(errno, "%s: Inquiry SG_IO ioctl error", __func__);
+        close(sg_fd);
+        return -1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&pt)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        pr2serr_lk("Recovered error on INQUIRY, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        {
+            lock_guard<mutex> lg(console_mutex);
+            sg_chk_n_print3("INQUIRY command error", &pt, 1);
+        }
+        break;
+    }
+    if (ok) {
+        /* Good, so fetch Product ID from response, copy to 'b' */
+        if (b_mlen > 0) {
+            if (b_mlen > 16) {
+                memcpy(b, inqBuff + 16, 16);
+                b[16] = '\0';
+            } else {
+                memcpy(b, inqBuff + 16, b_mlen - 1);
+                b[b_mlen - 1] = '\0';
+            }
+        }
+        ret = 0;
+    } else
+        ret = -1;
+    close(sg_fd);
+    return ret;
+}
+
+/* Only allow ranges up to 2**32-1 upper limit, so READ CAPACITY(10)
+ * sufficient. Return of 0 -> success, -1 -> failure, 2 -> try again */
+static int
+do_read_capacity(const char * dev_name, int block, unsigned int * last_lba,
+                 unsigned int * blk_sz)
+{
+    int res, sg_fd;
+    unsigned char rcCmdBlk [10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char rcBuff[64];
+    unsigned char sense_b[64];
+    sg_io_hdr_t io_hdr;
+    int open_flags = O_RDWR;    /* O_EXCL | O_RDONLY fails with EPERM */
+
+    if (! block)
+        open_flags |= O_NONBLOCK;
+    sg_fd = open(dev_name, open_flags);
+    if (sg_fd < 0) {
+        pr_errno_lk(errno, "%s: error opening file: %s", __func__, dev_name);
+        return -1;
+    }
+    /* Prepare READ CAPACITY(10) command */
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(rcCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_b);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = sizeof(rcBuff);
+    io_hdr.dxferp = rcBuff;
+    io_hdr.cmdp = rcCmdBlk;
+    io_hdr.sbp = sense_b;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */;
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        pr_errno_lk(errno, "%s (SG_IO) error", __func__);
+        close(sg_fd);
+        return -1;
+    }
+    res = sg_err_category3(&io_hdr);
+    if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+        lock_guard<mutex> lg(console_mutex);
+        sg_chk_n_print3("read capacity", &io_hdr, 1);
+        close(sg_fd);
+        return 2; /* probably have another go ... */
+    } else if (SG_LIB_CAT_CLEAN != res) {
+        lock_guard<mutex> lg(console_mutex);
+        sg_chk_n_print3("read capacity", &io_hdr, 1);
+        close(sg_fd);
+        return -1;
+    }
+    *last_lba = sg_get_unaligned_be32(&rcBuff[0]);
+    *blk_sz = sg_get_unaligned_be32(&rcBuff[4]);
+    close(sg_fd);
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int k, n, c, res;
+    int force = 0;
+    int64_t ll;
+    int num_threads = DEF_NUM_THREADS;
+    char b[128];
+    struct timespec start_tm, end_tm;
+    struct opts_t opts;
+    struct opts_t * op;
+    const char * cp;
+    const char * dev_name;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->direct = DEF_DIRECT;
+    op->lba = DEF_LBA;
+    op->hi_lba = 0;
+    op->lb_sz = DEF_LB_SZ;
+    op->maxq_per_thread = MAX_Q_PER_FD;
+    op->num_per_thread = DEF_NUM_PER_THREAD;
+    op->no_xfer = !! DEF_NO_XFER;
+    op->verbose = 0;
+    op->wait_ms = DEF_WAIT_MS;
+    op->c2e = SCSI_TUR;
+    op->blqd = BLQ_DEFAULT;
+    op->block = !! DEF_BLOCKING;
+    op->myqd = MYQD_HIGH;
+    page_size = sysconf(_SC_PAGESIZE);
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "dfhl:M:n:Nq:Q:Rs:St:TvVw:W",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'd':
+            op->direct = 1;
+            break;
+        case 'f':
+            force = true;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'l':
+            if (isdigit(*optarg)) {
+                ll = sg_get_llnum(optarg);
+                if (-1 == ll) {
+                    pr2serr_lk("could not decode lba\n");
+                    return 1;
+                } else
+                    op->lba = (uint64_t)ll;
+                cp = strchr(optarg, ',');
+                if (cp) {
+                    if (0 == strcmp("-1", cp + 1))
+                        op->hi_lba = UINT_MAX;
+                    else {
+                        ll = sg_get_llnum(cp + 1);
+                        if ((-1 == ll) || (ll > UINT_MAX)) {
+                            pr2serr_lk("could not decode hi_lba, or > "
+                                       "UINT_MAX\n");
+                            return 1;
+                        } else
+                            op->hi_lba = (unsigned int)ll;
+                    }
+                }
+            } else {
+                pr2serr_lk("--lba= expects a number\n");
+                return 1;
+            }
+            break;
+        case 'M':
+            if (isdigit(*optarg)) {
+                n = atoi(optarg);
+                if ((n < 1) || (n > MAX_Q_PER_FD)) {
+                    pr2serr_lk("-M expects a value from 1 to %d\n",
+                               MAX_Q_PER_FD);
+                    return 1;
+                }
+                op->maxq_per_thread = n;
+            } else {
+                pr2serr_lk("--maxqpt= expects a number\n");
+                return 1;
+            }
+            break;
+        case 'n':
+            if (isdigit(*optarg))
+                op->num_per_thread = sg_get_num(optarg);
+            else {
+                pr2serr_lk("--numpt= expects a number\n");
+                return 1;
+            }
+            break;
+        case 'N':
+            op->no_xfer = true;
+            break;
+        case 'q':
+            if (isdigit(*optarg)) {
+                n = atoi(optarg);
+                if (0 == n)
+                    op->blqd = BLQ_AT_HEAD;
+                else if (1 == n)
+                    op->blqd = BLQ_AT_TAIL;
+            } else {
+                pr2serr_lk("--qat= expects a number: 0 or 1\n");
+                return 1;
+            }
+            break;
+        case 'Q':
+            if (isdigit(*optarg)) {
+                n = atoi(optarg);
+                if (0 == n)
+                    op->myqd = MYQD_LOW;
+                else if (1 == n)
+                    op->myqd = MYQD_MEDIUM;
+                else if (2 == n)
+                    op->myqd = MYQD_HIGH;
+            } else {
+                pr2serr_lk("--qfav= expects a number: 0, 1 or 2\n");
+                return 1;
+            }
+            break;
+        case 'R':
+            op->c2e = SCSI_READ16;
+            break;
+        case 's':
+            if (isdigit(*optarg)) {
+                op->lb_sz = atoi(optarg);
+                if (op->lb_sz < 256) {
+                    cerr << "Strange lb_sz, using 256" << endl;
+                    op->lb_sz = 256;
+                }
+            } else {
+                pr2serr_lk("--szlb= expects a number\n");
+                return 1;
+            }
+            break;
+        case 'S':
+            ++op->stats;
+            break;
+        case 't':
+            if (isdigit(*optarg))
+                num_threads = atoi(optarg);
+            else {
+                pr2serr_lk("--tnum= expects a number\n");
+                return 1;
+            }
+            break;
+        case 'T':
+            op->c2e = SCSI_TUR;
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            pr2serr_lk("version: %s\n", version_str);
+            return 0;
+        case 'w':
+            if ((isdigit(*optarg) || ('-' == *optarg))) {
+                if ('-' == *optarg)
+                    op->wait_ms = - atoi(optarg + 1);
+                else
+                    op->wait_ms = atoi(optarg);
+            } else {
+                pr2serr_lk("--wait= expects a number\n");
+                return 1;
+            }
+            break;
+        case 'W':
+            op->c2e = SCSI_WRITE16;
+            break;
+        default:
+            pr2serr_lk("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return 1;
+        }
+    }
+    if (optind < argc) {
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                op->dev_names.push_back(argv[optind]);
+        }
+    }
+
+    if (0 == op->dev_names.size()) {
+        usage();
+        return 1;
+    }
+    if (op->hi_lba && (op->lba > op->hi_lba)) {
+        cerr << "lba,hi_lba range is illegal" << endl;
+        return 1;
+    }
+
+    try {
+        struct stat a_stat;
+
+        for (k = 0; k < (int)op->dev_names.size(); ++k) {
+            dev_name = op->dev_names[k];
+            if (stat(dev_name, &a_stat) < 0) {
+                snprintf(b, sizeof(b), "could not stat() %s", dev_name);
+                perror(b);
+                return 1;
+            }
+            if (! S_ISCHR(a_stat.st_mode)) {
+                pr2serr_lk("%s should be a sg device which is a char "
+                           "device. %s\n", dev_name, dev_name);
+                pr2serr_lk("is not a char device and damage could be done "
+                           "if it is a BLOCK\ndevice, exiting ...\n");
+                return 1;
+            }
+            if (! force) {
+                res = do_inquiry_prod_id(dev_name, op->block, b, sizeof(b));
+                if (res) {
+                    pr2serr_lk("INQUIRY failed on %s\n", dev_name);
+                    return 1;
+                }
+                // For safety, since <lba> written to, only permit scsi_debug
+                // devices. Bypass this with '-f' option.
+                if (0 != memcmp("scsi_debug", b, 10)) {
+                    pr2serr_lk("Since this utility may write to LBAs, "
+                               "only devices with the\n"
+                               "product ID 'scsi_debug' accepted. Use '-f' "
+                               "to override.\n");
+                    return 2;
+                }
+            }
+            if (UINT_MAX == op->hi_lba) {
+                unsigned int last_lba;
+                unsigned int blk_sz;
+
+                res = do_read_capacity(dev_name, op->block, &last_lba,
+                                       &blk_sz);
+                if (2 == res)
+                    res = do_read_capacity(dev_name, op->block, &last_lba,
+                                           &blk_sz);
+                if (res) {
+                    pr2serr_lk("READ CAPACITY(10) failed on %s\n", dev_name);
+                    return 1;
+                }
+                op->hi_lbas.push_back(last_lba);
+                if (blk_sz != (unsigned int)op->lb_sz)
+                    pr2serr_lk(">>> warning: Logical block size (%d) of %s\n"
+                               "    differs from command line option (or "
+                               "default)\n", blk_sz, dev_name);
+            }
+        }
+
+        start_tm.tv_sec = 0;
+        start_tm.tv_nsec = 0;
+        if (clock_gettime(CLOCK_MONOTONIC, &start_tm) < 0)
+            perror("clock_gettime failed");
+
+        vector<thread *> vt;
+
+        /* start multi-threaded section */
+        for (k = 0; k < num_threads; ++k) {
+            thread * tp = new thread {work_thread, k, op};
+            vt.push_back(tp);
+        }
+
+        // g++ 4.7.3 didn't like range-for loop here
+        for (k = 0; k < (int)vt.size(); ++k)
+            vt[k]->join();
+        /* end multi-threaded section, just this main thread left */
+
+        for (k = 0; k < (int)vt.size(); ++k)
+            delete vt[k];
+
+        n = uniq_pack_id.load() - 1;
+        if ((n > 0) && (0 == clock_gettime(CLOCK_MONOTONIC, &end_tm))) {
+            struct timespec res_tm;
+            double a, b;
+
+            res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+            res_tm.tv_nsec = end_tm.tv_nsec - start_tm.tv_nsec;
+            if (res_tm.tv_nsec < 0) {
+                --res_tm.tv_sec;
+                res_tm.tv_nsec += 1000000000;
+            }
+            a = res_tm.tv_sec;
+            a += (0.000001 * (res_tm.tv_nsec / 1000));
+            b = (double)n;
+            if (a > 0.000001) {
+                printf("Time to complete %d commands was %d.%06d seconds\n",
+                       n, (int)res_tm.tv_sec, (int)(res_tm.tv_nsec / 1000));
+                printf("Implies %.0f IOPS\n", (b / a));
+            }
+        }
+
+        if (op->verbose || op->stats) {
+            cout << "Number of async_starts: " << async_starts.load() << endl;
+            cout << "Number of async_finishes: " << async_finishes.load() <<
+                    endl;
+            cout << "Last pack_id: " << n << endl;
+            cout << "Number of EBUSYs: " << ebusy_count.load() << endl;
+            cout << "Number of start EAGAINs: " << start_eagain_count.load()
+                 << endl;
+            cout << "Number of finish EAGAINs: " << fin_eagain_count.load()
+                 << endl;
+        }
+    }
+    catch(system_error& e)  {
+        cerr << "got a system_error exception: " << e.what() << '\n';
+        auto ec = e.code();
+        cerr << "category: " << ec.category().name() << '\n';
+        cerr << "value: " << ec.value() << '\n';
+        cerr << "message: " << ec.message() << '\n';
+        cerr << "\nNote: if g++ may need '-pthread' or similar in "
+                "compile/link line" << '\n';
+    }
+    catch(...) {
+        cerr << "got another exception: " << '\n';
+    }
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_tst_context.cpp b/sg3_utils/examples/sg_tst_context.cpp
new file mode 100644
index 0000000..6ac9935
--- /dev/null
+++ b/sg3_utils/examples/sg_tst_context.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2013 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <vector>
+#include <system_error>
+#include <thread>
+#include <mutex>
+#include <chrono>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_pt.h"
+
+static const char * version_str = "1.02 20140828";
+static const char * util_name = "sg_tst_context";
+
+/* This is a test program for checking that file handles keep their
+ * context properly when sent (synchronous) SCSI pass-through commands.
+ * A disk device is assumed and even-numbered threads send TEST UNIT
+ * READY commands while odd-numbered threads send alternating START STOP
+ * UNIT commands (i.e. start then stop then start, etc). The point is to
+ * check the results to make sure that they don't get the other command's
+ * response. For example a START STOP UNIT command should not see a "not
+ * ready" sense key.
+ *
+ * This is C++ code with some things from C++11 (e.g. threads) and was
+ * only just able to compile (when some things were reverted) with gcc/g++
+ * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support
+ * was not available until g++ version 4.8.1 and that is found in Fedora
+ * 19 and Ubuntu 13.10 .
+ *
+ * The build uses various object files from the <sg3_utils>/lib directory
+ * which is assumed to be a sibling of this examples directory. Those
+ * object files in the lib directory can be built with:
+ *   cd <sg3_utils> ; ./configure ; cd lib; make
+ * Then to build sg_tst_context concatenate the next 3 lines:
+ *   g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o
+ *     ../lib/sg_lib_data.o ../lib/sg_pt_linux.o -o sg_tst_context
+ *     sg_tst_context.cpp
+ * Alternatively use 'make -f Makefile.cplus sg_tst_context'
+ *
+ */
+
+using namespace std;
+using namespace std::chrono;
+
+#define DEF_NUM_PER_THREAD 200
+#define DEF_NUM_THREADS 2
+
+#define EBUFF_SZ 256
+
+
+static mutex count_mutex;
+static mutex console_mutex;
+static unsigned int even_notreadys;
+static unsigned int odd_notreadys;
+static unsigned int ebusy_count;
+
+
+static void
+usage(void)
+{
+    printf("Usage: %s [-e] [-h] [-n <n_per_thr>] [-N] [-R] [-s]\n"
+           "                      [-t <num_thrs>] [-V] <disk_device>\n",
+           util_name);
+    printf("  where\n");
+    printf("    -e                use O_EXCL on open (def: don't)\n");
+    printf("    -h                print this usage message then exit\n");
+    printf("    -n <n_per_thr>    number of loops per thread "
+           "(def: %d)\n", DEF_NUM_PER_THREAD);
+    printf("    -N                use O_NONBLOCK on open (def: don't)\n");
+    printf("    -R                make sure device in ready (started) "
+           "state after\n"
+           "                      test (do extra iteration if "
+           "necessary)\n");
+    printf("    -s                share an open file handle (def: one "
+           "per thread)\n");
+    printf("    -t <num_thrs>     number of threads (def: %d)\n",
+           DEF_NUM_THREADS);
+    printf("    -V                print version number then exit\n\n");
+    printf("Test if file handles keep context through to their responses. "
+           "Sends\nTEST UNIT READY commands on even threads (origin 0) and "
+           "START STOP\nUNIT commands on odd threads. Expect NOT READY "
+           "sense keys only\nfrom the even threads (i.e from TUR)\n");
+}
+
+static int
+pt_err(int res)
+{
+    if (res < 0)
+        fprintf(stderr, "  pass through OS error: %s\n", safe_strerror(-res));
+    else if (SCSI_PT_DO_BAD_PARAMS == res)
+        fprintf(stderr, "  bad pass through setup\n");
+    else if (SCSI_PT_DO_TIMEOUT == res)
+        fprintf(stderr, "  pass through timeout\n");
+    else
+        fprintf(stderr, "  do_scsi_pt error=%d\n", res);
+    return (res < 0) ? res : -EPERM /* -1 */;
+}
+
+static int
+pt_cat_no_good(int cat, struct sg_pt_base * ptp, const unsigned char * sbp)
+{
+    int slen;
+    char b[256];
+    const int bl = (int)sizeof(b);
+    const char * cp = NULL;
+
+    b[0] = '\0';
+    switch (cat) {
+    case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
+        sg_get_scsi_status_str(get_scsi_pt_status_response(ptp), bl, b);
+        cp = "  scsi status: %s\n";
+        break;
+    case SCSI_PT_RESULT_SENSE:
+        slen = get_scsi_pt_sense_len(ptp);
+        sg_get_sense_str("", sbp, slen, 1, bl, b);
+        cp = "%s\n";
+        break;
+    case SCSI_PT_RESULT_TRANSPORT_ERR:
+        get_scsi_pt_transport_err_str(ptp, bl, b);
+        cp = "  transport: %s\n";
+        break;
+    case SCSI_PT_RESULT_OS_ERR:
+        get_scsi_pt_os_err_str(ptp, bl, b);
+        cp = "  os: %s\n";
+        break;
+    default:
+        cp = "  unknown pt result category (%d)\n";
+        break;
+    }
+    if (cp) {
+        lock_guard<mutex> lg(console_mutex);
+
+        fprintf(stderr, cp, b);
+    }
+    return -EIO /* -5 */;
+}
+
+#define TUR_CMD_LEN 6
+#define SSU_CMD_LEN 6
+#define NOT_READY SG_LIB_CAT_NOT_READY
+
+/* Returns 0 for good, 1024 for a sense key of NOT_READY, or a negative
+ * errno */
+static int
+do_tur(int pt_fd, int id)
+{
+    int slen, res, cat;
+    struct sg_pt_base * ptp = NULL;
+    unsigned char turCmdBlk [TUR_CMD_LEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+    unsigned char sense_buffer[64];
+
+    ptp = construct_scsi_pt_obj();
+    set_scsi_pt_cdb(ptp, turCmdBlk, sizeof(turCmdBlk));
+    set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+    res = do_scsi_pt(ptp, pt_fd, 20 /* secs timeout */, 1);
+    if (res) {
+        {
+            lock_guard<mutex> lg(console_mutex);
+
+            fprintf(stderr, "TEST UNIT READY do_scsi_pt() submission error, "
+                    "id=%d\n", id);
+        }
+        res = pt_err(res);
+        goto err;
+    }
+    cat = get_scsi_pt_result_category(ptp);
+    if (SCSI_PT_RESULT_GOOD != cat) {
+        slen = get_scsi_pt_sense_len(ptp);
+        if ((SCSI_PT_RESULT_SENSE == cat) &&
+            (NOT_READY == sg_err_category_sense(sense_buffer, slen))) {
+            res = 1024;
+            goto err;
+        }
+        {
+            lock_guard<mutex> lg(console_mutex);
+
+            fprintf(stderr, "TEST UNIT READY do_scsi_pt() category problem, "
+                    "id=%d\n", id);
+        }
+        res = pt_cat_no_good(cat, ptp, sense_buffer);
+        goto err;
+    }
+    res = 0;
+err:
+    if (ptp)
+        destruct_scsi_pt_obj(ptp);
+    return res;
+}
+
+/* Returns 0 for good, 1024 for a sense key of NOT_READY, or a negative
+ * errno */
+static int
+do_ssu(int pt_fd, int id, bool start)
+{
+    int slen, res, cat;
+    struct sg_pt_base * ptp = NULL;
+    unsigned char ssuCmdBlk [SSU_CMD_LEN] = {0x1b, 0x0, 0x0, 0x0, 0x0, 0x0};
+    unsigned char sense_buffer[64];
+
+    if (start)
+        ssuCmdBlk[4] |= 0x1;
+    ptp = construct_scsi_pt_obj();
+    set_scsi_pt_cdb(ptp, ssuCmdBlk, sizeof(ssuCmdBlk));
+    set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+    res = do_scsi_pt(ptp, pt_fd, 40 /* secs timeout */, 1);
+    if (res) {
+        {
+            lock_guard<mutex> lg(console_mutex);
+
+            fprintf(stderr, "START STOP UNIT do_scsi_pt() submission error, "
+                    "id=%d\n", id);
+        }
+        res = pt_err(res);
+        goto err;
+    }
+    cat = get_scsi_pt_result_category(ptp);
+    if (SCSI_PT_RESULT_GOOD != cat) {
+        slen = get_scsi_pt_sense_len(ptp);
+        if ((SCSI_PT_RESULT_SENSE == cat) &&
+            (NOT_READY == sg_err_category_sense(sense_buffer, slen))) {
+            res = 1024;
+            goto err;
+        }
+        {
+            lock_guard<mutex> lg(console_mutex);
+
+            fprintf(stderr, "START STOP UNIT do_scsi_pt() category problem, "
+                    "id=%d\n", id);
+        }
+        res = pt_cat_no_good(cat, ptp, sense_buffer);
+        goto err;
+    }
+    res = 0;
+err:
+    if (ptp)
+        destruct_scsi_pt_obj(ptp);
+    return res;
+}
+
+static void
+work_thread(const char * dev_name, int id, int num, bool share,
+            int pt_fd, int nonblock, int oexcl, bool ready_after)
+{
+    unsigned int thr_even_notreadys = 0;
+    unsigned int thr_odd_notreadys = 0;
+    unsigned int thr_ebusy_count = 0;
+    int k;
+    int res = 0;
+    bool started = true;
+    char ebuff[EBUFF_SZ];
+
+    {
+        lock_guard<mutex> lg(console_mutex);
+
+        cerr << "Enter work_thread id=" << id << " num=" << num << " share="
+             << share << endl;
+    }
+    if (! share) {
+        int open_flags = O_RDWR;
+
+        if (nonblock)
+            open_flags |= O_NONBLOCK;
+        if (oexcl)
+            open_flags |= O_EXCL;
+        while (((pt_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0)
+               && (-EBUSY == pt_fd)) {
+            ++thr_ebusy_count;
+            this_thread::yield();       // give other threads a chance
+        }
+        if (pt_fd < 0) {
+            snprintf(ebuff, EBUFF_SZ, "work_thread id=%d: error opening: %s",
+                     id, dev_name);
+            perror(ebuff);
+            return;
+        }
+        if (thr_ebusy_count) {
+            lock_guard<mutex> lg(count_mutex);
+
+            ebusy_count += thr_ebusy_count;
+        }
+    }
+    for (k = 0; k < num; ++k) {
+        if (0 == (id % 2)) {
+            res = do_tur(pt_fd, id);
+            if (1024 == res) {
+                ++thr_even_notreadys;
+                res = 0;
+            }
+        } else {
+            started = (0 == (k % 2));
+            res = do_ssu(pt_fd, id, started);
+            if (1024 == res) {
+                ++thr_odd_notreadys;
+                res = 0;
+            }
+        }
+        if (res)
+            break;
+        if (ready_after && (! started))
+            do_ssu(pt_fd, id, true);
+    }
+    if (! share)
+        scsi_pt_close_device(pt_fd);
+
+    {
+        lock_guard<mutex> lg(count_mutex);
+
+        even_notreadys += thr_even_notreadys;
+        odd_notreadys += thr_odd_notreadys;
+    }
+
+    {
+        lock_guard<mutex> lg(console_mutex);
+
+        if (k < num)
+            cerr << "thread id=" << id << " FAILed at iteration: " << k
+                 << "  [negated errno: " << res << "]\n";
+        else
+            cerr << "thread id=" << id << " normal exit" << '\n';
+    }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int k;
+    int pt_fd = -1;
+    int oexcl = 0;
+    int nonblock = 0;
+    int num_per_thread = DEF_NUM_PER_THREAD;
+    bool ready_after = false;
+    bool share = false;
+    int num_threads = DEF_NUM_THREADS;
+    char * dev_name = NULL;
+    char ebuff[EBUFF_SZ];
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-e", argv[k], 2))
+            ++oexcl;
+        else if (0 == memcmp("-h", argv[k], 2)) {
+            usage();
+            return 0;
+        } else if (0 == memcmp("-n", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                num_per_thread = atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-N", argv[k], 2))
+            ++nonblock;
+        else if (0 == memcmp("-R", argv[k], 2))
+            ready_after = true;
+        else if (0 == memcmp("-s", argv[k], 2))
+            share = true;
+        else if (0 == memcmp("-t", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                num_threads = atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-V", argv[k], 2)) {
+            printf("%s version: %s\n", util_name, version_str);
+            return 0;
+        } else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            dev_name = NULL;
+            break;
+        }
+        else if (! dev_name)
+            dev_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            dev_name = 0;
+            break;
+        }
+    }
+    if (0 == dev_name) {
+        usage();
+        return 1;
+    }
+    try {
+        if (share) {
+            int open_flags = O_RDWR;
+
+            if (nonblock)
+                open_flags |= O_NONBLOCK;
+            if (oexcl)
+                open_flags |= O_EXCL;
+            while (((pt_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0)
+                   && (-EBUSY == pt_fd)) {
+                ++ebusy_count;
+                sleep(0);                   // process yield ??
+            }
+            if (pt_fd < 0) {
+                snprintf(ebuff, EBUFF_SZ, "main: error opening: %s",
+                         dev_name);
+                perror(ebuff);
+                return 1;
+            }
+        }
+
+        vector<thread *> vt;
+
+        for (k = 0; k < num_threads; ++k) {
+            thread * tp = new thread {work_thread, dev_name, k, num_per_thread,
+                                      share, pt_fd, nonblock, oexcl,
+                                      ready_after};
+            vt.push_back(tp);
+        }
+
+        for (k = 0; k < (int)vt.size(); ++k)
+            vt[k]->join();
+
+        for (k = 0; k < (int)vt.size(); ++k)
+            delete vt[k];
+
+        if (share)
+            scsi_pt_close_device(pt_fd);
+
+        cout << "Expected not_readys on TEST UNIT READY: " << even_notreadys
+             << endl;
+        cout << "UNEXPECTED not_readys on START STOP UNIT: "
+             << odd_notreadys << endl;
+        if (ebusy_count)
+            cout << "Number of EBUSYs (on open): " << ebusy_count << endl;
+
+    }
+    catch(system_error& e)  {
+        cerr << "got a system_error exception: " << e.what() << '\n';
+        auto ec = e.code();
+        cerr << "category: " << ec.category().name() << '\n';
+        cerr << "value: " << ec.value() << '\n';
+        cerr << "message: " << ec.message() << '\n';
+        cerr << "\nNote: if g++ may need '-pthread' or similar in "
+                "compile/link line" << '\n';
+    }
+    catch(...) {
+        cerr << "got another exception: " << '\n';
+    }
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_tst_excl.cpp b/sg3_utils/examples/sg_tst_excl.cpp
new file mode 100644
index 0000000..5b4e695
--- /dev/null
+++ b/sg3_utils/examples/sg_tst_excl.cpp
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 2013-2014 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <vector>
+#include <system_error>
+#include <thread>
+#include <mutex>
+#include <chrono>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+
+static const char * version_str = "1.09 20140828";
+static const char * util_name = "sg_tst_excl";
+
+/* This is a test program for checking O_EXCL on open() works. It uses
+ * multiple threads and can be run as multiple processes and attempts
+ * to "break" O_EXCL. The strategy is to open a device O_EXCL|O_NONBLOCK
+ * and do a double increment on a LB then close it. Prior to the first
+ * increment, the value is checked for even or odd. Assuming the count
+ * starts as an even (typically 0) then it should remain even. Odd instances
+ * are counted and reported at the end of the program, after all threads
+ * have completed.
+ *
+ * This is C++ code with some things from C++11 (e.g. threads) and was
+ * only just able to compile (when some things were reverted) with gcc/g++
+ * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support
+ * was not available until g++ version 4.8.1 and that is only currently
+ * found in Fedora 19 .
+ *
+ * The build uses various object files from the <sg3_utils>/lib directory
+ * which is assumed to be a sibling of this examples directory. Those
+ * object files in the lib directory can be built with:
+ *   cd <sg3_utils> ; ./configure ; cd lib; make
+ * Then to build sg_tst_excl concatenate the next 3 lines:
+ *   g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o
+ *     ../lib/sg_lib_data.o ../lib/sg_io_linux.o -o sg_tst_excl
+ *     sg_tst_excl.cpp
+ * or use the C++ Makefile in that directory:
+ *   make -f Makefile.cplus sg_tst_excl
+ *
+ * Currently this utility is Linux only and assumes the SG_IO v3 interface
+ * which is supported by sg and block devices (but not bsg devices which
+ * require the SG_IO v4 interface). This restriction is relaxed in the
+ * sg_tst_excl2 variant of this utility.
+ *
+ * BEWARE: this utility modifies a logical block (default LBA 1000) on the
+ * given device.
+ *
+ */
+
+using namespace std;
+using namespace std::chrono;
+
+#define DEF_NUM_PER_THREAD 200
+#define DEF_NUM_THREADS 4
+#define DEF_WAIT_MS 0          /* 0: yield; -1: don't wait; -2: sleep(0) */
+
+
+#define DEF_LBA 1000
+
+#define EBUFF_SZ 256
+
+static mutex odd_count_mutex;
+static mutex console_mutex;
+static unsigned int odd_count;
+static unsigned int ebusy_count;
+static unsigned int eagain_count;
+
+
+static void
+usage(void)
+{
+    printf("Usage: %s [-b] [-f] [-h] [-l <lba>] [-n <n_per_thr>] "
+           "[-t <num_thrs>]\n"
+           "                   [-V] [-w <wait_ms>] [-x] [-xx] "
+           "<sg_disk_device>\n", util_name);
+    printf("  where\n");
+    printf("    -b                block on open (def: O_NONBLOCK)\n");
+    printf("    -f                force: any SCSI disk (def: only "
+           "scsi_debug)\n");
+    printf("                      WARNING: <lba> written to\n");
+    printf("    -h                print this usage message then exit\n");
+    printf("    -l <lba>          logical block to increment (def: %u)\n",
+           DEF_LBA);
+    printf("    -n <n_per_thr>    number of loops per thread "
+           "(def: %d)\n", DEF_NUM_PER_THREAD);
+    printf("    -t <num_thrs>     number of threads (def: %d)\n",
+           DEF_NUM_THREADS);
+    printf("    -V                print version number then exit\n");
+    printf("    -w <wait_ms>      >0: sleep_for(<wait_ms>); =0: "
+           "yield(); -1: no\n"
+           "                      wait; -2: sleep(0)  (def: %d)\n",
+           DEF_WAIT_MS);
+    printf("    -x                don't use O_EXCL on first thread "
+           "(def: use\n"
+           "                      O_EXCL on all threads)\n"
+           "    -xx               don't use O_EXCL on any thread\n\n");
+    printf("Test O_EXCL open flag with Linux sg driver. Each open/close "
+           "cycle with the\nO_EXCL flag does a double increment on "
+           "lba (using its first 4 bytes).\nEach increment uses a READ_16, "
+           "READ_16, increment, WRITE_16 cycle. The two\nREAD_16s are "
+           "launched asynchronously. Note that '-xx' will run test\n"
+           "without any O_EXCL flags.\n");
+}
+
+
+#define READ16_REPLY_LEN 512
+#define READ16_CMD_LEN 16
+#define WRITE16_REPLY_LEN 512
+#define WRITE16_CMD_LEN 16
+
+/* Opens dev_name and spins if busy (i.e. gets EBUSY), sleeping for
+ * wait_ms milliseconds if wait_ms is positive.
+ * Reads lba (twice) and treats the first 4 bytes as an int (SCSI endian),
+ * increments it and writes it back. Repeats so that happens twice. Then
+ * closes dev_name. If an error occurs returns -1 else returns 0 if
+ * first int read from lba is even otherwise returns 1. */
+static int
+do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
+                   int excl, int wait_ms, int id, unsigned int & ebusy,
+                   unsigned int & eagains)
+{
+    int k, sg_fd, ok, res;
+    int odd = 0;
+    unsigned int u = 0;
+    struct sg_io_hdr pt, pt2;
+    unsigned char r16CmdBlk [READ16_CMD_LEN] =
+                {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    unsigned char w16CmdBlk [WRITE16_CMD_LEN] =
+                {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    unsigned char sense_buffer[64];
+    unsigned char lb[READ16_REPLY_LEN];
+    char ebuff[EBUFF_SZ];
+    int open_flags = O_RDWR;
+
+    r16CmdBlk[6] = w16CmdBlk[6] = (lba >> 24) & 0xff;
+    r16CmdBlk[7] = w16CmdBlk[7] = (lba >> 16) & 0xff;
+    r16CmdBlk[8] = w16CmdBlk[8] = (lba >> 8) & 0xff;
+    r16CmdBlk[9] = w16CmdBlk[9] = lba & 0xff;
+    if (! block)
+        open_flags |= O_NONBLOCK;
+    if (excl)
+        open_flags |= O_EXCL;
+
+    while (((sg_fd = open(dev_name, open_flags)) < 0) &&
+           (EBUSY == errno)) {
+        ++ebusy;
+        if (wait_ms > 0)
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+    }
+    if (sg_fd < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "do_rd_inc_wr_twice: error opening file: %s", dev_name);
+        perror(ebuff);
+        return -1;
+    }
+
+    for (k = 0; k < 2; ++k) {
+        /* Prepare READ_16 command */
+        memset(&pt, 0, sizeof(pt));
+        pt.interface_id = 'S';
+        pt.cmd_len = sizeof(r16CmdBlk);
+        pt.mx_sb_len = sizeof(sense_buffer);
+        pt.dxfer_direction = SG_DXFER_FROM_DEV;
+        pt.dxfer_len = READ16_REPLY_LEN;
+        pt.dxferp = lb;
+        pt.cmdp = r16CmdBlk;
+        pt.sbp = sense_buffer;
+        pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+        pt.pack_id = id;
+
+        // queue up two READ_16s to same LBA
+        if (write(sg_fd, &pt, sizeof(pt)) < 0) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                perror("do_rd_inc_wr_twice: write(sg, READ_16)");
+            }
+            close(sg_fd);
+            return -1;
+        }
+        pt2 = pt;
+        if (write(sg_fd, &pt2, sizeof(pt2)) < 0) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                perror("do_rd_inc_wr_twice: write(sg, READ_16) 2");
+            }
+            close(sg_fd);
+            return -1;
+        }
+
+        while (((res = read(sg_fd, &pt, sizeof(pt))) < 0) &&
+               (EAGAIN == errno)) {
+            ++eagains;
+            if (wait_ms > 0)
+                this_thread::sleep_for(milliseconds{wait_ms});
+            else if (0 == wait_ms)
+                this_thread::yield();
+            else if (-2 == wait_ms)
+                sleep(0);                   // process yield ??
+        }
+        if (res < 0) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                perror("do_rd_inc_wr_twice: read(sg, READ_16)");
+            }
+            close(sg_fd);
+            return -1;
+        }
+        /* now for the error processing */
+        ok = 0;
+        switch (sg_err_category3(&pt)) {
+        case SG_LIB_CAT_CLEAN:
+            ok = 1;
+            break;
+        case SG_LIB_CAT_RECOVERED:
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "Recovered error on READ_16, continuing\n");
+            }
+            ok = 1;
+            break;
+        default: /* won't bother decoding other categories */
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                sg_chk_n_print3("READ_16 command error", &pt, 1);
+            }
+            break;
+        }
+        if (ok) {
+            while (((res = read(sg_fd, &pt2, sizeof(pt2))) < 0) &&
+                   (EAGAIN == errno)) {
+                ++eagains;
+                if (wait_ms > 0)
+                    this_thread::sleep_for(milliseconds{wait_ms});
+                else if (0 == wait_ms)
+                    this_thread::yield();
+                else if (-2 == wait_ms)
+                    sleep(0);                   // process yield ??
+            }
+            if (res < 0) {
+                {
+                    lock_guard<mutex> lg(console_mutex);
+
+                    perror("do_rd_inc_wr_twice: read(sg, READ_16) 2");
+                }
+                close(sg_fd);
+                return -1;
+            }
+            pt = pt2;
+            /* now for the error processing */
+            ok = 0;
+            switch (sg_err_category3(&pt)) {
+            case SG_LIB_CAT_CLEAN:
+                ok = 1;
+                break;
+            case SG_LIB_CAT_RECOVERED:
+                {
+                    lock_guard<mutex> lg(console_mutex);
+
+                    fprintf(stderr, "Recovered error on READ_16, continuing "
+                            "2\n");
+                }
+                ok = 1;
+                break;
+            default: /* won't bother decoding other categories */
+                {
+                    lock_guard<mutex> lg(console_mutex);
+
+                    sg_chk_n_print3("READ_16 command error 2", &pt, 1);
+                }
+                break;
+            }
+        }
+        if (! ok) {
+            close(sg_fd);
+            return -1;
+        }
+
+        u = (lb[0] << 24) + (lb[1] << 16) + (lb[2] << 8) + lb[3];
+        if (0 == k)
+            odd = (1 == (u % 2));
+        ++u;
+        lb[0] = (u >> 24) & 0xff;
+        lb[1] = (u >> 16) & 0xff;
+        lb[2] = (u >> 8) & 0xff;
+        lb[3] = u & 0xff;
+
+        if (wait_ms > 0)       /* allow daylight for bad things ... */
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+
+        /* Prepare WRITE_16 command */
+        memset(&pt, 0, sizeof(pt));
+        pt.interface_id = 'S';
+        pt.cmd_len = sizeof(w16CmdBlk);
+        pt.mx_sb_len = sizeof(sense_buffer);
+        pt.dxfer_direction = SG_DXFER_TO_DEV;
+        pt.dxfer_len = WRITE16_REPLY_LEN;
+        pt.dxferp = lb;
+        pt.cmdp = w16CmdBlk;
+        pt.sbp = sense_buffer;
+        pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+        pt.pack_id = id;
+
+        if (ioctl(sg_fd, SG_IO, &pt) < 0) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                perror("do_rd_inc_wr_twice: WRITE_16 SG_IO ioctl error");
+            }
+            close(sg_fd);
+            return -1;
+        }
+        /* now for the error processing */
+        ok = 0;
+        switch (sg_err_category3(&pt)) {
+        case SG_LIB_CAT_CLEAN:
+            ok = 1;
+            break;
+        case SG_LIB_CAT_RECOVERED:
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "Recovered error on WRITE_16, continuing\n");
+            }
+            ok = 1;
+            break;
+        default: /* won't bother decoding other categories */
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                sg_chk_n_print3("WRITE_16 command error", &pt, 1);
+            }
+            break;
+        }
+        if (! ok) {
+            close(sg_fd);
+            return -1;
+        }
+    }
+    close(sg_fd);
+    return odd;
+}
+
+
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+
+/* Send INQUIRY and fetches response. If okay puts PRODUCT ID field
+ * in b (up to m_blen bytes). Does not use O_EXCL flag. Returns 0 on success,
+ * else -1 . */
+static int
+do_inquiry_prod_id(const char * dev_name, int block, int wait_ms,
+                   unsigned int & ebusys, char * b, int b_mlen)
+{
+    int sg_fd, ok, ret;
+    struct sg_io_hdr pt;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    unsigned char sense_buffer[64];
+    char ebuff[EBUFF_SZ];
+    int open_flags = O_RDWR;    /* O_EXCL | O_RDONLY fails with EPERM */
+
+    if (! block)
+        open_flags |= O_NONBLOCK;
+    while (((sg_fd = open(dev_name, open_flags)) < 0) &&
+           (EBUSY == errno)) {
+        ++ebusys;
+        if (wait_ms > 0)
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+    }
+    if (sg_fd < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "do_inquiry_prod_id: error opening file: %s", dev_name);
+        perror(ebuff);
+        return -1;
+    }
+    /* Prepare INQUIRY command */
+    memset(&pt, 0, sizeof(pt));
+    pt.interface_id = 'S';
+    pt.cmd_len = sizeof(inqCmdBlk);
+    /* pt.iovec_count = 0; */  /* memset takes care of this */
+    pt.mx_sb_len = sizeof(sense_buffer);
+    pt.dxfer_direction = SG_DXFER_FROM_DEV;
+    pt.dxfer_len = INQ_REPLY_LEN;
+    pt.dxferp = inqBuff;
+    pt.cmdp = inqCmdBlk;
+    pt.sbp = sense_buffer;
+    pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+    /* pt.flags = 0; */     /* take defaults: indirect IO, etc */
+    /* pt.pack_id = 0; */
+    /* pt.usr_ptr = NULL; */
+
+    if (ioctl(sg_fd, SG_IO, &pt) < 0) {
+        perror("do_inquiry_prod_id: Inquiry SG_IO ioctl error");
+        close(sg_fd);
+        return -1;
+    }
+
+    /* now for the error processing */
+    ok = 0;
+    switch (sg_err_category3(&pt)) {
+    case SG_LIB_CAT_CLEAN:
+        ok = 1;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        fprintf(stderr, "Recovered error on INQUIRY, continuing\n");
+        ok = 1;
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("INQUIRY command error", &pt, 1);
+        break;
+    }
+    if (ok) {
+        /* Good, so fetch Product ID from response, copy to 'b' */
+        if (b_mlen > 0) {
+            if (b_mlen > 16) {
+                memcpy(b, inqBuff + 16, 16);
+                b[16] = '\0';
+            } else {
+                memcpy(b, inqBuff + 16, b_mlen - 1);
+                b[b_mlen - 1] = '\0';
+            }
+        }
+        ret = 0;
+    } else
+        ret = -1;
+    close(sg_fd);
+    return ret;
+}
+
+static void
+work_thread(const char * dev_name, unsigned int lba, int id, int block,
+            int excl, int num, int wait_ms)
+{
+    unsigned int thr_odd_count = 0;
+    unsigned int thr_ebusy_count = 0;
+    unsigned int thr_eagain_count = 0;
+    int k, res;
+
+    {
+        lock_guard<mutex> lg(console_mutex);
+
+        cerr << "Enter work_thread id=" << id << " excl=" << excl << " block="
+             << block << endl;
+    }
+    for (k = 0; k < num; ++k) {
+        res = do_rd_inc_wr_twice(dev_name, lba, block, excl, wait_ms, k,
+                                 thr_ebusy_count, thr_eagain_count);
+        if (res < 0)
+            break;
+        if (res)
+            ++thr_odd_count;
+    }
+    {
+        lock_guard<mutex> lg(console_mutex);
+
+        if (k < num)
+            cerr << "thread id=" << id << " FAILed at iteration: " << k <<
+                    '\n';
+        else
+            cerr << "thread id=" << id << " normal exit" << '\n';
+    }
+    {
+        lock_guard<mutex> lg(odd_count_mutex);
+
+        odd_count += thr_odd_count;
+        ebusy_count += thr_ebusy_count;
+        eagain_count += thr_eagain_count;
+    }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int k, res;
+    int block = 0;
+    int force = 0;
+    unsigned int lba = DEF_LBA;
+    int num_per_thread = DEF_NUM_PER_THREAD;
+    int num_threads = DEF_NUM_THREADS;
+    int wait_ms = DEF_WAIT_MS;
+    int no_o_excl = 0;
+    char * dev_name = NULL;
+    char b[64];
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-b", argv[k], 2))
+            ++block;
+        else if (0 == memcmp("-f", argv[k], 2))
+            ++force;
+        else if (0 == memcmp("-h", argv[k], 2)) {
+            usage();
+            return 0;
+        } else if (0 == memcmp("-l", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                lba = (unsigned int)atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-n", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                num_per_thread = atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-t", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                num_threads = atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-V", argv[k], 2)) {
+            printf("%s version: %s\n", util_name, version_str);
+            return 0;
+        } else if (0 == memcmp("-w", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && (isdigit(*argv[k]) || ('-' == *argv[k]))) {
+                if ('-' == *argv[k])
+                    wait_ms = - atoi(argv[k] + 1);
+                else
+                    wait_ms = atoi(argv[k]);
+            } else
+                break;
+        } else if (0 == memcmp("-xxx", argv[k], 4))
+            no_o_excl += 3;
+        else if (0 == memcmp("-xx", argv[k], 3))
+            no_o_excl += 2;
+        else if (0 == memcmp("-x", argv[k], 2))
+            ++no_o_excl;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            dev_name = NULL;
+            break;
+        }
+        else if (! dev_name)
+            dev_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            dev_name = 0;
+            break;
+        }
+    }
+    if (0 == dev_name) {
+        usage();
+        return 1;
+    }
+    try {
+        struct stat a_stat;
+
+        if (stat(dev_name, &a_stat) < 0) {
+            perror("stat() on dev_name failed");
+            return 1;
+        }
+        if (! S_ISCHR(a_stat.st_mode)) {
+            fprintf(stderr, "%s should be a sg device which is a char "
+                    "device. %s\n", dev_name, dev_name);
+            fprintf(stderr, "is not a char device and damage could be done "
+                    "if it is a BLOCK\ndevice, exiting ...\n");
+            return 1;
+        }
+        if (! force) {
+            res = do_inquiry_prod_id(dev_name, block, wait_ms, ebusy_count,
+                                     b, sizeof(b));
+            if (res) {
+                fprintf(stderr, "INQUIRY failed on %s\n", dev_name);
+                return 1;
+            }
+            // For safety, since <lba> written to, only permit scsi_debug
+            // devices. Bypass this with '-f' option.
+            if (0 != memcmp("scsi_debug", b, 10)) {
+                fprintf(stderr, "Since this utility writes to LBA %d, only "
+                        "devices with scsi_debug\nproduct ID accepted.\n",
+                        lba);
+                return 2;
+            }
+        }
+
+        vector<thread *> vt;
+
+        for (k = 0; k < num_threads; ++k) {
+            int excl = 1;
+
+            if (no_o_excl > 1)
+                excl = 0;
+            else if ((0 == k) && (1 == no_o_excl))
+                excl = 0;
+
+            thread * tp = new thread {work_thread, dev_name, lba, k, block,
+                                      excl, num_per_thread, wait_ms};
+            vt.push_back(tp);
+        }
+
+        // g++ 4.7.3 didn't like range-for loop here
+        for (k = 0; k < (int)vt.size(); ++k)
+            vt[k]->join();
+
+        for (k = 0; k < (int)vt.size(); ++k)
+            delete vt[k];
+
+        if (no_o_excl)
+            cout << "Odd count: " << odd_count << endl;
+        else
+            cout << "Expecting odd count of 0, got " << odd_count << endl;
+        cout << "Number of EBUSYs: " << ebusy_count << endl;
+        cout << "Number of EAGAINs: " << eagain_count << endl;
+
+    }
+    catch(system_error& e)  {
+        cerr << "got a system_error exception: " << e.what() << '\n';
+        auto ec = e.code();
+        cerr << "category: " << ec.category().name() << '\n';
+        cerr << "value: " << ec.value() << '\n';
+        cerr << "message: " << ec.message() << '\n';
+        cerr << "\nNote: if g++ may need '-pthread' or similar in "
+                "compile/link line" << '\n';
+    }
+    catch(...) {
+        cerr << "got another exception: " << '\n';
+    }
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_tst_excl2.cpp b/sg3_utils/examples/sg_tst_excl2.cpp
new file mode 100644
index 0000000..24d3677
--- /dev/null
+++ b/sg3_utils/examples/sg_tst_excl2.cpp
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2013-2014 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <vector>
+#include <system_error>
+#include <thread>
+#include <mutex>
+#include <chrono>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_pt.h"
+
+static const char * version_str = "1.07 20140828";
+static const char * util_name = "sg_tst_excl2";
+
+/* This is a test program for checking O_EXCL on open() works. It uses
+ * multiple threads and can be run as multiple processes and attempts
+ * to "break" O_EXCL. The strategy is to open a device O_EXCL|O_NONBLOCK
+ * and do a double increment on a LB then close it. Prior to the first
+ * increment, the value is checked for even or odd. Assuming the count
+ * starts as an even (typically 0) then it should remain even. Odd instances
+ * are counted and reported at the end of the program, after all threads
+ * have completed.
+ *
+ * This is C++ code with some things from C++11 (e.g. threads) and was
+ * only just able to compile (when some things were reverted) with gcc/g++
+ * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support
+ * was not available until g++ version 4.8.1 and that is only currently
+ * found in Fedora 19 .
+ *
+ * The build uses various object files from the <sg3_utils>/lib directory
+ * which is assumed to be a sibling of this examples directory. Those
+ * object files in the lib directory can be built with:
+ *   cd <sg3_utils> ; ./configure ; cd lib; make
+ * Then to build sg_tst_excl2 concatenate the next 3 lines:
+ *   g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o
+ *     ../lib/sg_lib_data.o ../lib/sg_pt_linux.o -o sg_tst_excl2
+ *     sg_tst_excl2.cpp
+ * Alternatively use 'make -f Makefile.cplus sg_tst_excl2'
+ *
+ * BEWARE: this utility modifies a logical block (default LBA 1000) on the
+ * given device.
+ *
+ * Test breaks sg driver in lk 3.10.4 but works with proposed fix so should
+ * work soon thereafter. Works on standard block driver (e.g. /dev/sdc) in
+ * lk 3.10.4 . Fails on bsg driver in lk 3.10.4 because it ignores the
+ * O_EXCL flag (and that is unlikely to change).
+ *
+ */
+
+using namespace std;
+using namespace std::chrono;
+
+#define DEF_NUM_PER_THREAD 200
+#define DEF_NUM_THREADS 4
+#define DEF_WAIT_MS 0          /* 0: yield; -1: don't wait; -2: sleep(0) */
+
+#define DEF_LBA 1000
+
+#define EBUFF_SZ 256
+
+
+static mutex odd_count_mutex;
+static mutex console_mutex;
+static unsigned int odd_count;
+static unsigned int ebusy_count;
+
+
+static void
+usage(void)
+{
+    printf("Usage: %s [-b] [-f] [-h] [-l <lba>] [-n <n_per_thr>] "
+           "[-t <num_thrs>]\n"
+           "                    [-V] [-w <wait_ms>] [-x] "
+           "<disk_device>\n", util_name);
+    printf("  where\n");
+    printf("    -b                block on open (def: O_NONBLOCK)\n");
+    printf("    -f                force: any SCSI disk (def: only "
+           "scsi_debug)\n");
+    printf("                      WARNING: <lba> written to\n");
+    printf("    -h                print this usage message then exit\n");
+    printf("    -l <lba>          logical block to increment (def: %u)\n",
+           DEF_LBA);
+    printf("    -n <n_per_thr>    number of loops per thread "
+           "(def: %d)\n", DEF_NUM_PER_THREAD);
+    printf("    -t <num_thrs>     number of threads (def: %d)\n",
+           DEF_NUM_THREADS);
+    printf("    -V                print version number then exit\n");
+    printf("    -w <wait_ms>      >0: sleep_for(<wait_ms>); =0: "
+           "yield(); -1: no\n"
+           "                      wait; -2: sleep(0)  (def: %d)\n",
+           DEF_WAIT_MS);
+    printf("    -x                don't use O_EXCL on first thread "
+           "(def: use\n"
+           "                      O_EXCL on all threads)\n\n");
+    printf("Test O_EXCL open flag with pass-through drivers. Each "
+           "open/close cycle with\nthe O_EXCL flag does a double increment "
+           "on lba (using its first 4 bytes).\n");
+}
+
+/* Assumed a lock (mutex) held when pt_err() is called */
+static int
+pt_err(int res)
+{
+    if (res < 0)
+        fprintf(stderr, "  pass through os error: %s\n", safe_strerror(-res));
+    else if (SCSI_PT_DO_BAD_PARAMS == res)
+        fprintf(stderr, "  bad pass through setup\n");
+    else if (SCSI_PT_DO_TIMEOUT == res)
+        fprintf(stderr, "  pass through timeout\n");
+    else
+        fprintf(stderr, "  do_scsi_pt error=%d\n", res);
+    return -1;
+}
+
+/* Assumed a lock (mutex) held when pt_cat_no_good() is called */
+static int
+pt_cat_no_good(int cat, struct sg_pt_base * ptp, const unsigned char * sbp)
+{
+    int slen;
+    char b[256];
+    const int bl = (int)sizeof(b);
+
+    switch (cat) {
+    case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
+        sg_get_scsi_status_str(get_scsi_pt_status_response(ptp), bl, b);
+        fprintf(stderr, "  scsi status: %s\n", b);
+        break;
+    case SCSI_PT_RESULT_SENSE:
+        slen = get_scsi_pt_sense_len(ptp);
+        sg_get_sense_str("", sbp, slen, 1, bl, b);
+        fprintf(stderr, "%s", b);
+        break;
+    case SCSI_PT_RESULT_TRANSPORT_ERR:
+        get_scsi_pt_transport_err_str(ptp, bl, b);
+        fprintf(stderr, "  transport: %s", b);
+        break;
+    case SCSI_PT_RESULT_OS_ERR:
+        get_scsi_pt_os_err_str(ptp, bl, b);
+        fprintf(stderr, "  os: %s", b);
+        break;
+    default:
+        fprintf(stderr, "  unknown pt result category (%d)\n", cat);
+        break;
+    }
+    return -1;
+}
+
+#define READ16_REPLY_LEN 512
+#define READ16_CMD_LEN 16
+#define WRITE16_REPLY_LEN 512
+#define WRITE16_CMD_LEN 16
+
+/* Opens dev_name and spins if busy (i.e. gets EBUSY), sleeping for
+ * wait_ms milliseconds if wait_ms is positive. Reads lba and treats the
+ * first 4 bytes as an int (SCSI endian), increments it and writes it back.
+ * Repeats so that happens twice. Then closes dev_name. If an error occurs
+ * returns -1 else returns 0 if first int read is even otherwise returns 1. */
+static int
+do_rd_inc_wr_twice(const char * dev_name, unsigned int lba, int block,
+                   int excl, int wait_ms, unsigned int & ebusys)
+{
+    int k, sg_fd, res, cat;
+    int odd = 0;
+    unsigned int u = 0;
+    struct sg_pt_base * ptp = NULL;
+    unsigned char r16CmdBlk [READ16_CMD_LEN] =
+                {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    unsigned char w16CmdBlk [WRITE16_CMD_LEN] =
+                {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    unsigned char sense_buffer[64];
+    unsigned char lb[READ16_REPLY_LEN];
+    char ebuff[EBUFF_SZ];
+    int open_flags = O_RDWR;
+
+    r16CmdBlk[6] = w16CmdBlk[6] = (lba >> 24) & 0xff;
+    r16CmdBlk[7] = w16CmdBlk[7] = (lba >> 16) & 0xff;
+    r16CmdBlk[8] = w16CmdBlk[8] = (lba >> 8) & 0xff;
+    r16CmdBlk[9] = w16CmdBlk[9] = lba & 0xff;
+    if (! block)
+        open_flags |= O_NONBLOCK;
+    if (excl)
+        open_flags |= O_EXCL;
+
+    while (((sg_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
+           (-EBUSY == sg_fd)) {
+        ++ebusys;
+        if (wait_ms > 0)
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();       // thread yield
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+    }
+    if (sg_fd < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "do_rd_inc_wr_twice: error opening file: %s", dev_name);
+        {
+            lock_guard<mutex> lg(console_mutex);
+
+            perror(ebuff);
+        }
+        return -1;
+    }
+
+    ptp = construct_scsi_pt_obj();
+    for (k = 0; k < 2; ++k) {
+        /* Prepare READ_16 command */
+        clear_scsi_pt_obj(ptp);
+        set_scsi_pt_cdb(ptp, r16CmdBlk, sizeof(r16CmdBlk));
+        set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+        set_scsi_pt_data_in(ptp, lb, READ16_REPLY_LEN);
+        res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+        if (res) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "READ_16 do_scsi_pt() submission error\n");
+                res = pt_err(res);
+            }
+            goto err;
+        }
+        cat = get_scsi_pt_result_category(ptp);
+        if (SCSI_PT_RESULT_GOOD != cat) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "READ_16 do_scsi_pt() category problem\n");
+                res = pt_cat_no_good(cat, ptp, sense_buffer);
+            }
+            goto err;
+        }
+
+        u = (lb[0] << 24) + (lb[1] << 16) + (lb[2] << 8) + lb[3];
+        // Assuming u starts test as even (probably 0), expect it to stay even
+        if (0 == k)
+            odd = (1 == (u % 2));
+        ++u;
+        lb[0] = (u >> 24) & 0xff;
+        lb[1] = (u >> 16) & 0xff;
+        lb[2] = (u >> 8) & 0xff;
+        lb[3] = u & 0xff;
+
+        if (wait_ms > 0)       /* allow daylight for bad things ... */
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();       // thread yield
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+
+        /* Prepare WRITE_16 command */
+        clear_scsi_pt_obj(ptp);
+        set_scsi_pt_cdb(ptp, w16CmdBlk, sizeof(w16CmdBlk));
+        set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+        set_scsi_pt_data_out(ptp, lb, WRITE16_REPLY_LEN);
+        res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+        if (res) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "WRITE_16 do_scsi_pt() submission error\n");
+                res = pt_err(res);
+            }
+            goto err;
+        }
+        cat = get_scsi_pt_result_category(ptp);
+        if (SCSI_PT_RESULT_GOOD != cat) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n");
+                res = pt_cat_no_good(cat, ptp, sense_buffer);
+            }
+            goto err;
+        }
+    }
+err:
+    if (ptp)
+        destruct_scsi_pt_obj(ptp);
+    scsi_pt_close_device(sg_fd);
+    return odd;
+}
+
+
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+
+/* Send INQUIRY and fetches response. If okay puts PRODUCT ID field
+ * in b (up to m_blen bytes). Does not use O_EXCL flag. Returns 0 on success,
+ * else -1 . */
+static int
+do_inquiry_prod_id(const char * dev_name, int block, int wait_ms,
+                   unsigned int & ebusys, char * b, int b_mlen)
+{
+    int sg_fd, res, cat;
+    struct sg_pt_base * ptp = NULL;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    unsigned char sense_buffer[64];
+    char ebuff[EBUFF_SZ];
+    int open_flags = O_RDWR;    /* since O_EXCL | O_RDONLY gives EPERM */
+
+    if (! block)
+        open_flags |= O_NONBLOCK;
+    while (((sg_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
+           (-EBUSY == sg_fd)) {
+        ++ebusys;
+        if (wait_ms > 0)
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();       // thread yield
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+    }
+    if (sg_fd < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "do_inquiry_prod_id: error opening file: %s", dev_name);
+        perror(ebuff);
+        return -1;
+    }
+    /* Prepare INQUIRY command */
+    ptp = construct_scsi_pt_obj();
+    clear_scsi_pt_obj(ptp);
+    set_scsi_pt_cdb(ptp, inqCmdBlk, sizeof(inqCmdBlk));
+    set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+    set_scsi_pt_data_in(ptp, inqBuff, INQ_REPLY_LEN);
+    res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+    if (res) {
+        {
+            lock_guard<mutex> lg(console_mutex);
+
+            fprintf(stderr, "INQUIRY do_scsi_pt() submission error\n");
+            res = pt_err(res);
+        }
+        goto err;
+    }
+    cat = get_scsi_pt_result_category(ptp);
+    if (SCSI_PT_RESULT_GOOD != cat) {
+        {
+            lock_guard<mutex> lg(console_mutex);
+
+            fprintf(stderr, "INQUIRY do_scsi_pt() category problem\n");
+            res = pt_cat_no_good(cat, ptp, sense_buffer);
+        }
+        goto err;
+    }
+
+    /* Good, so fetch Product ID from response, copy to 'b' */
+    if (b_mlen > 0) {
+        if (b_mlen > 16) {
+            memcpy(b, inqBuff + 16, 16);
+            b[16] = '\0';
+        } else {
+            memcpy(b, inqBuff + 16, b_mlen - 1);
+            b[b_mlen - 1] = '\0';
+        }
+    }
+err:
+    if (ptp)
+        destruct_scsi_pt_obj(ptp);
+    close(sg_fd);
+    return 0;
+}
+
+static void
+work_thread(const char * dev_name, unsigned int lba, int id, int block,
+            int excl, int num, int wait_ms)
+{
+    unsigned int thr_odd_count = 0;
+    unsigned int thr_ebusy_count = 0;
+    int k, res;
+
+    {
+        lock_guard<mutex> lg(console_mutex);
+
+        cerr << "Enter work_thread id=" << id << " excl=" << excl << " block="
+             << block << endl;
+    }
+    for (k = 0; k < num; ++k) {
+        res = do_rd_inc_wr_twice(dev_name, lba, block, excl, wait_ms,
+                                 thr_ebusy_count);
+        if (res < 0)
+            break;
+        if (res)
+            ++thr_odd_count;
+    }
+    {
+        lock_guard<mutex> lg(console_mutex);
+
+        if (k < num)
+            cerr << "thread id=" << id << " FAILed at iteration: " << k <<
+                    '\n';
+        else
+            cerr << "thread id=" << id << " normal exit" << '\n';
+    }
+
+    {
+        lock_guard<mutex> lg(odd_count_mutex);
+
+        odd_count += thr_odd_count;
+        ebusy_count += thr_ebusy_count;
+    }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int k, res;
+    int block = 0;
+    int force = 0;
+    unsigned int lba = DEF_LBA;
+    int num_per_thread = DEF_NUM_PER_THREAD;
+    int num_threads = DEF_NUM_THREADS;
+    int wait_ms = DEF_WAIT_MS;
+    int exclude_o_excl = 0;
+    char * dev_name = NULL;
+    char b[64];
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-b", argv[k], 2))
+            ++block;
+        else if (0 == memcmp("-f", argv[k], 2))
+            ++force;
+        else if (0 == memcmp("-h", argv[k], 2)) {
+            usage();
+            return 0;
+        } else if (0 == memcmp("-l", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                lba = (unsigned int)atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-n", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                num_per_thread = atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-t", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                num_threads = atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-V", argv[k], 2)) {
+            printf("%s version: %s\n", util_name, version_str);
+            return 0;
+        } else if (0 == memcmp("-w", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && (isdigit(*argv[k]) || ('-' == *argv[k]))) {
+                if ('-' == *argv[k])
+                    wait_ms = - atoi(argv[k] + 1);
+                else
+                    wait_ms = atoi(argv[k]);
+            } else
+                break;
+        } else if (0 == memcmp("-x", argv[k], 2))
+            ++exclude_o_excl;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            dev_name = NULL;
+            break;
+        }
+        else if (! dev_name)
+            dev_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            dev_name = 0;
+            break;
+        }
+    }
+    if (0 == dev_name) {
+        usage();
+        return 1;
+    }
+    try {
+
+        if (! force) {
+            res = do_inquiry_prod_id(dev_name, block, wait_ms, ebusy_count,
+                                     b, sizeof(b));
+            if (res) {
+                fprintf(stderr, "INQUIRY failed on %s\n", dev_name);
+                return 1;
+            }
+            // For safety, since <lba> written to, only permit scsi_debug
+            // devices. Bypass this with '-f' option.
+            if (0 != memcmp("scsi_debug", b, 10)) {
+                fprintf(stderr, "Since this utility writes to LBA %d, only "
+                        "devices with scsi_debug\nproduct ID accepted.\n",
+                        lba);
+                return 2;
+            }
+        }
+
+        vector<thread *> vt;
+
+        for (k = 0; k < num_threads; ++k) {
+            int excl = ((0 == k) && exclude_o_excl) ? 0 : 1;
+
+            thread * tp = new thread {work_thread, dev_name, lba, k, block,
+                                      excl, num_per_thread, wait_ms};
+            vt.push_back(tp);
+        }
+
+        for (k = 0; k < (int)vt.size(); ++k)
+            vt[k]->join();
+
+        for (k = 0; k < (int)vt.size(); ++k)
+            delete vt[k];
+
+        cout << "Expecting odd count of 0, got " << odd_count << endl;
+        cout << "Number of EBUSYs: " << ebusy_count << endl;
+
+    }
+    catch(system_error& e)  {
+        cerr << "got a system_error exception: " << e.what() << '\n';
+        auto ec = e.code();
+        cerr << "category: " << ec.category().name() << '\n';
+        cerr << "value: " << ec.value() << '\n';
+        cerr << "message: " << ec.message() << '\n';
+        cerr << "\nNote: if g++ may need '-pthread' or similar in "
+                "compile/link line" << '\n';
+    }
+    catch(...) {
+        cerr << "got another exception: " << '\n';
+    }
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_tst_excl3.cpp b/sg3_utils/examples/sg_tst_excl3.cpp
new file mode 100644
index 0000000..6b6b880
--- /dev/null
+++ b/sg3_utils/examples/sg_tst_excl3.cpp
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2013-2014 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <vector>
+#include <system_error>
+#include <thread>
+#include <mutex>
+#include <chrono>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sg_lib.h"
+#include "sg_pt.h"
+
+static const char * version_str = "1.05 20140828";
+static const char * util_name = "sg_tst_excl3";
+
+/* This is a test program for checking O_EXCL on open() works. It uses
+ * multiple threads and can be run as multiple processes and attempts
+ * to "break" O_EXCL. The strategy is to open a device O_EXCL|O_NONBLOCK
+ * and do a double increment on a LB then close it from a single thread.
+ * the remaining threads open that device O_NONBLOCK and do a read and
+ * note of the number is odd. Assuming the count starts as an even
+ * (typically 0) then it should remain even. Odd instances
+ * are counted and reported at the end of the program, after all threads
+ * have completed.
+ *
+ * This is C++ code with some things from C++11 (e.g. threads) and was
+ * only just able to compile (when some things were reverted) with gcc/g++
+ * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support
+ * was not available until g++ version 4.8.1 and that is found in Fedora
+ * 19 and Ubuntu 13.10 .
+ *
+ * The build uses various object files from the <sg3_utils>/lib directory
+ * which is assumed to be a sibling of this examples directory. Those
+ * object files in the lib directory can be built with:
+ *   cd <sg3_utils> ; ./configure ; cd lib; make
+ * Then to build sg_tst_excl3 concatenate the next 3 lines:
+ *   g++ -Wall -std=c++11 -pthread -I ../include ../lib/sg_lib.o
+ *     ../lib/sg_lib_data.o ../lib/sg_pt_linux.o -o sg_tst_excl3
+ *     sg_tst_excl3.cpp
+ * Alternatively use 'make -f Makefile.cplus sg_tst_excl3'
+ *
+ * BEWARE: this utility modifies a logical block (default LBA 1000) on the
+ * given device.
+ *
+ * Test breaks sg driver in lk 3.10.4 but works with proposed fix so should
+ * work soon thereafter. It works on standard block driver (e.g. /dev/sdc)
+ * in lk 3.10.4 (most of the time). It fails on bsg driver in lk 3.10.4
+ * because it ignores the O_EXCL flag (and that is unlikely to change in
+ * the short term).
+ *
+ */
+
+using namespace std;
+using namespace std::chrono;
+
+#define DEF_NUM_PER_THREAD 200
+#define DEF_NUM_THREADS 4
+#define DEF_WAIT_MS 0          /* 0: yield; -1: don't wait; -2: sleep(0) */
+
+#define DEF_LBA 1000
+
+#define EBUFF_SZ 256
+
+
+static mutex odd_count_mutex;
+static mutex console_mutex;
+static unsigned int odd_count;
+static unsigned int ebusy_count;
+
+
+static void
+usage(void)
+{
+    printf("Usage: %s [-b] [-f] [-h] [-l <lba>] [-n <n_per_thr>]\n"
+           "                    [-R] [-t <num_thrs>] [-V] [-w <wait_ms>] "
+           "[-x]\n"
+           "                    <disk_device>\n", util_name);
+    printf("  where\n");
+    printf("    -b                block on open (def: O_NONBLOCK)\n");
+    printf("    -f                force: any SCSI disk (def: only "
+           "scsi_debug)\n");
+    printf("                      WARNING: <lba> written to\n");
+    printf("    -h                print this usage message then exit\n");
+    printf("    -l <lba>          logical block to increment (def: %u)\n",
+           DEF_LBA);
+    printf("    -n <n_per_thr>    number of loops per thread "
+           "(def: %d)\n", DEF_NUM_PER_THREAD);
+    printf("    -R                all readers; so first thread (id=0) "
+           "just reads\n");
+    printf("    -t <num_thrs>     number of threads (def: %d)\n",
+           DEF_NUM_THREADS);
+    printf("    -V                print version number then exit\n");
+    printf("    -w <wait_ms>      >0: sleep_for(<wait_ms>); =0: "
+           "yield(); -1: no\n"
+           "                      wait; -2: sleep(0)  (def: %d)\n",
+           DEF_WAIT_MS);
+    printf("    -x                don't use O_EXCL on first thread "
+           "(def: use\n"
+           "                      O_EXCL on first thread)\n\n");
+    printf("Test O_EXCL open flag with pass-through drivers. First thread "
+           "(id=0) does\nopen/close cycle with the O_EXCL flag then does a "
+           "double increment on\nlba (using its first 4 bytes). Remaining "
+           "theads read (without\nO_EXCL flag on open) and check the "
+           "value is even.\n");
+}
+
+/* Assumed a lock (mutex) held when pt_err() is called */
+static int
+pt_err(int res)
+{
+    if (res < 0)
+        fprintf(stderr, "  pass through os error: %s\n", safe_strerror(-res));
+    else if (SCSI_PT_DO_BAD_PARAMS == res)
+        fprintf(stderr, "  bad pass through setup\n");
+    else if (SCSI_PT_DO_TIMEOUT == res)
+        fprintf(stderr, "  pass through timeout\n");
+    else
+        fprintf(stderr, "  do_scsi_pt error=%d\n", res);
+    return -1;
+}
+
+/* Assumed a lock (mutex) held when pt_cat_no_good() is called */
+static int
+pt_cat_no_good(int cat, struct sg_pt_base * ptp, const unsigned char * sbp)
+{
+    int slen;
+    char b[256];
+    const int bl = (int)sizeof(b);
+
+    switch (cat) {
+    case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
+        sg_get_scsi_status_str(get_scsi_pt_status_response(ptp), bl, b);
+        fprintf(stderr, "  scsi status: %s\n", b);
+        break;
+    case SCSI_PT_RESULT_SENSE:
+        slen = get_scsi_pt_sense_len(ptp);
+        sg_get_sense_str("", sbp, slen, 1, bl, b);
+        fprintf(stderr, "%s", b);
+        break;
+    case SCSI_PT_RESULT_TRANSPORT_ERR:
+        get_scsi_pt_transport_err_str(ptp, bl, b);
+        fprintf(stderr, "  transport: %s", b);
+        break;
+    case SCSI_PT_RESULT_OS_ERR:
+        get_scsi_pt_os_err_str(ptp, bl, b);
+        fprintf(stderr, "  os: %s", b);
+        break;
+    default:
+        fprintf(stderr, "  unknown pt result category (%d)\n", cat);
+        break;
+    }
+    return -1;
+}
+
+#define READ16_REPLY_LEN 512
+#define READ16_CMD_LEN 16
+#define WRITE16_REPLY_LEN 512
+#define WRITE16_CMD_LEN 16
+
+/* Opens dev_name and spins if busy (i.e. gets EBUSY), sleeping for
+ * wait_ms milliseconds if wait_ms is positive. Reads lba and treats the
+ * first 4 bytes as an int (SCSI endian), increments it and writes it back.
+ * Repeats so that happens twice. Then closes dev_name. If an error occurs
+ * returns -1 else returns 0 if first int read is even otherwise returns 1. */
+static int
+do_rd_inc_wr_twice(const char * dev_name, int read_only, unsigned int lba,
+                   int block, int excl, int wait_ms, unsigned int & ebusys)
+{
+    int k, sg_fd, res, cat;
+    int odd = 0;
+    unsigned int u = 0;
+    struct sg_pt_base * ptp = NULL;
+    unsigned char r16CmdBlk [READ16_CMD_LEN] =
+                {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    unsigned char w16CmdBlk [WRITE16_CMD_LEN] =
+                {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
+    unsigned char sense_buffer[64];
+    unsigned char lb[READ16_REPLY_LEN];
+    char ebuff[EBUFF_SZ];
+    int open_flags = O_RDWR;
+
+    r16CmdBlk[6] = w16CmdBlk[6] = (lba >> 24) & 0xff;
+    r16CmdBlk[7] = w16CmdBlk[7] = (lba >> 16) & 0xff;
+    r16CmdBlk[8] = w16CmdBlk[8] = (lba >> 8) & 0xff;
+    r16CmdBlk[9] = w16CmdBlk[9] = lba & 0xff;
+    if (! block)
+        open_flags |= O_NONBLOCK;
+    if (excl)
+        open_flags |= O_EXCL;
+
+    while (((sg_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
+           (-EBUSY == sg_fd)) {
+        ++ebusys;
+        if (wait_ms > 0)
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();       // thread yield
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+    }
+    if (sg_fd < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "do_rd_inc_wr_twice: error opening file: %s", dev_name);
+        {
+            lock_guard<mutex> lg(console_mutex);
+
+            perror(ebuff);
+        }
+        return -1;
+    }
+
+    ptp = construct_scsi_pt_obj();
+    for (k = 0; k < 2; ++k) {
+        /* Prepare READ_16 command */
+        clear_scsi_pt_obj(ptp);
+        set_scsi_pt_cdb(ptp, r16CmdBlk, sizeof(r16CmdBlk));
+        set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+        set_scsi_pt_data_in(ptp, lb, READ16_REPLY_LEN);
+        res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+        if (res) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "READ_16 do_scsi_pt() submission error\n");
+                res = pt_err(res);
+            }
+            goto err;
+        }
+        cat = get_scsi_pt_result_category(ptp);
+        if (SCSI_PT_RESULT_GOOD != cat) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "READ_16 do_scsi_pt() category problem\n");
+                res = pt_cat_no_good(cat, ptp, sense_buffer);
+            }
+            goto err;
+        }
+
+        u = (lb[0] << 24) + (lb[1] << 16) + (lb[2] << 8) + lb[3];
+        // Assuming u starts test as even (probably 0), expect it to stay even
+        if (0 == k)
+            odd = (1 == (u % 2));
+
+        if (wait_ms > 0)       /* allow daylight for bad things ... */
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();       // thread yield
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+
+        if (read_only)
+            break;
+        ++u;
+        lb[0] = (u >> 24) & 0xff;
+        lb[1] = (u >> 16) & 0xff;
+        lb[2] = (u >> 8) & 0xff;
+        lb[3] = u & 0xff;
+
+        /* Prepare WRITE_16 command */
+        clear_scsi_pt_obj(ptp);
+        set_scsi_pt_cdb(ptp, w16CmdBlk, sizeof(w16CmdBlk));
+        set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+        set_scsi_pt_data_out(ptp, lb, WRITE16_REPLY_LEN);
+        res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+        if (res) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "WRITE_16 do_scsi_pt() submission error\n");
+                res = pt_err(res);
+            }
+            goto err;
+        }
+        cat = get_scsi_pt_result_category(ptp);
+        if (SCSI_PT_RESULT_GOOD != cat) {
+            {
+                lock_guard<mutex> lg(console_mutex);
+
+                fprintf(stderr, "WRITE_16 do_scsi_pt() category problem\n");
+                res = pt_cat_no_good(cat, ptp, sense_buffer);
+            }
+            goto err;
+        }
+    }
+err:
+    if (ptp)
+        destruct_scsi_pt_obj(ptp);
+    scsi_pt_close_device(sg_fd);
+    return odd;
+}
+
+
+#define INQ_REPLY_LEN 96
+#define INQ_CMD_LEN 6
+
+/* Send INQUIRY and fetches response. If okay puts PRODUCT ID field
+ * in b (up to m_blen bytes). Does not use O_EXCL flag. Returns 0 on success,
+ * else -1 . */
+static int
+do_inquiry_prod_id(const char * dev_name, int block, int wait_ms,
+                   unsigned int & ebusys, char * b, int b_mlen)
+{
+    int sg_fd, res, cat;
+    struct sg_pt_base * ptp = NULL;
+    unsigned char inqCmdBlk [INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    unsigned char sense_buffer[64];
+    char ebuff[EBUFF_SZ];
+    int open_flags = O_RDWR;    /* since O_EXCL | O_RDONLY gives EPERM */
+
+    if (! block)
+        open_flags |= O_NONBLOCK;
+    while (((sg_fd = scsi_pt_open_flags(dev_name, open_flags, 0)) < 0) &&
+           (-EBUSY == sg_fd)) {
+        ++ebusys;
+        if (wait_ms > 0)
+            this_thread::sleep_for(milliseconds{wait_ms});
+        else if (0 == wait_ms)
+            this_thread::yield();       // thread yield
+        else if (-2 == wait_ms)
+            sleep(0);                   // process yield ??
+    }
+    if (sg_fd < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "do_inquiry_prod_id: error opening file: %s", dev_name);
+        perror(ebuff);
+        return -1;
+    }
+    /* Prepare INQUIRY command */
+    ptp = construct_scsi_pt_obj();
+    clear_scsi_pt_obj(ptp);
+    set_scsi_pt_cdb(ptp, inqCmdBlk, sizeof(inqCmdBlk));
+    set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
+    set_scsi_pt_data_in(ptp, inqBuff, INQ_REPLY_LEN);
+    res = do_scsi_pt(ptp, sg_fd, 20 /* secs timeout */, 1);
+    if (res) {
+        fprintf(stderr, "INQUIRY do_scsi_pt() submission error\n");
+        res = pt_err(res);
+        goto err;
+    }
+    cat = get_scsi_pt_result_category(ptp);
+    if (SCSI_PT_RESULT_GOOD != cat) {
+        fprintf(stderr, "INQUIRY do_scsi_pt() category problem\n");
+        res = pt_cat_no_good(cat, ptp, sense_buffer);
+        goto err;
+    }
+
+    /* Good, so fetch Product ID from response, copy to 'b' */
+    if (b_mlen > 0) {
+        if (b_mlen > 16) {
+            memcpy(b, inqBuff + 16, 16);
+            b[16] = '\0';
+        } else {
+            memcpy(b, inqBuff + 16, b_mlen - 1);
+            b[b_mlen - 1] = '\0';
+        }
+    }
+err:
+    if (ptp)
+        destruct_scsi_pt_obj(ptp);
+    close(sg_fd);
+    return res;
+}
+
+static void
+work_thread(const char * dev_name, unsigned int lba, int id, int block,
+            int excl, bool all_readers, int num, int wait_ms)
+{
+    unsigned int thr_odd_count = 0;
+    unsigned int thr_ebusy_count = 0;
+    int k, res;
+    int reader = ((id > 0) || (all_readers));
+
+    {
+        lock_guard<mutex> lg(console_mutex);
+
+        cerr << "Enter work_thread id=" << id << " excl=" << excl << " block="
+             << block << " reader=" << reader << endl;
+    }
+    for (k = 0; k < num; ++k) {
+        res = do_rd_inc_wr_twice(dev_name, reader, lba, block, excl,
+                                 wait_ms, thr_ebusy_count);
+        if (res < 0)
+            break;
+        if (res)
+            ++thr_odd_count;
+    }
+    {
+        lock_guard<mutex> lg(console_mutex);
+
+        if (k < num)
+            cerr << "thread id=" << id << " FAILed at iteration: " << k
+                 << '\n';
+        else
+            cerr << "thread id=" << id << " normal exit" << '\n';
+    }
+
+    {
+        lock_guard<mutex> lg(odd_count_mutex);
+
+        odd_count += thr_odd_count;
+        ebusy_count += thr_ebusy_count;
+    }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int k, res;
+    int block = 0;
+    int force = 0;
+    unsigned int lba = DEF_LBA;
+    int num_per_thread = DEF_NUM_PER_THREAD;
+    bool all_readers = false;
+    int num_threads = DEF_NUM_THREADS;
+    int wait_ms = DEF_WAIT_MS;
+    int exclude_o_excl = 0;
+    char * dev_name = NULL;
+    char b[64];
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == memcmp("-b", argv[k], 2))
+            ++block;
+        else if (0 == memcmp("-f", argv[k], 2))
+            ++force;
+        else if (0 == memcmp("-h", argv[k], 2)) {
+            usage();
+            return 0;
+        } else if (0 == memcmp("-l", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                lba = (unsigned int)atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-n", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                num_per_thread = atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-t", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && isdigit(*argv[k]))
+                num_threads = atoi(argv[k]);
+            else
+                break;
+        } else if (0 == memcmp("-R", argv[k], 2))
+            all_readers = true;
+        else if (0 == memcmp("-V", argv[k], 2)) {
+            printf("%s version: %s\n", util_name, version_str);
+            return 0;
+        } else if (0 == memcmp("-w", argv[k], 2)) {
+            ++k;
+            if ((k < argc) && (isdigit(*argv[k]) || ('-' == *argv[k]))) {
+                if ('-' == *argv[k])
+                    wait_ms = - atoi(argv[k] + 1);
+                else
+                    wait_ms = atoi(argv[k]);
+            } else
+                break;
+        } else if (0 == memcmp("-x", argv[k], 2))
+            ++exclude_o_excl;
+        else if (*argv[k] == '-') {
+            printf("Unrecognized switch: %s\n", argv[k]);
+            dev_name = NULL;
+            break;
+        }
+        else if (! dev_name)
+            dev_name = argv[k];
+        else {
+            printf("too many arguments\n");
+            dev_name = 0;
+            break;
+        }
+    }
+    if (0 == dev_name) {
+        usage();
+        return 1;
+    }
+    try {
+
+        if (! force) {
+            res = do_inquiry_prod_id(dev_name, block, wait_ms, ebusy_count,
+                                     b, sizeof(b));
+            if (res) {
+                fprintf(stderr, "INQUIRY failed on %s\n", dev_name);
+                return 1;
+            }
+            // For safety, since <lba> written to, only permit scsi_debug
+            // devices. Bypass this with '-f' option.
+            if (0 != memcmp("scsi_debug", b, 10)) {
+                fprintf(stderr, "Since this utility writes to LBA %d, only "
+                        "devices with scsi_debug\nproduct ID accepted.\n",
+                        lba);
+                return 2;
+            }
+        }
+
+        vector<thread *> vt;
+
+        for (k = 0; k < num_threads; ++k) {
+            int excl = ((0 == k) && (! exclude_o_excl)) ? 1 : 0;
+
+            thread * tp = new thread {work_thread, dev_name, lba, k, block,
+                                      excl, all_readers, num_per_thread,
+                                      wait_ms};
+            vt.push_back(tp);
+        }
+
+        for (k = 0; k < (int)vt.size(); ++k)
+            vt[k]->join();
+
+        for (k = 0; k < (int)vt.size(); ++k)
+            delete vt[k];
+
+        cout << "Expecting odd count of 0, got " << odd_count << endl;
+        cout << "Number of EBUSYs: " << ebusy_count << endl;
+
+    }
+    catch(system_error& e)  {
+        cerr << "got a system_error exception: " << e.what() << '\n';
+        auto ec = e.code();
+        cerr << "category: " << ec.category().name() << '\n';
+        cerr << "value: " << ec.value() << '\n';
+        cerr << "message: " << ec.message() << '\n';
+        cerr << "\nNote: if g++ may need '-pthread' or similar in "
+                "compile/link line" << '\n';
+    }
+    catch(...) {
+        cerr << "got another exception: " << '\n';
+    }
+    return 0;
+}
diff --git a/sg3_utils/examples/sg_unmap_example.txt b/sg3_utils/examples/sg_unmap_example.txt
new file mode 100644
index 0000000..1a5f44d
--- /dev/null
+++ b/sg3_utils/examples/sg_unmap_example.txt
@@ -0,0 +1,40 @@
+#                sg_unmap_example.txt
+# This is an example of the contents of a file that can be given to sg_unmap
+# For example, assume the /dev/sdc is a scratch disk (e.g. one made by the
+# scsi_debug kernel module) then:
+#    sg_unmap --in=sg_unmap_example.txt /dev/sdc
+
+0x12345677,1	# unmap LBA 0x12345677
+0x12345678 2	# unmap LBA 0x12345678 and 0x12345679
+0x12340000 3333	# unmap 3333 blocks starting at LBA 0x12340000
+
+0X5a5a5a5a5a	0  # unmaps 0 blocks (i.e. does nothing)
+
+    a5a5a5h
+7		# unmap 7 blocks starting at LBA 0xa5a5a5
+
+# Note that there can be leading and trailing whitespace and whitespace
+# (plus comma) can be a separator.
+#
+# Example invocation:
+# $ sg_unmap --in=../examples/sg_unmap_example.txt -vv /dev/sdc
+# open /dev/sg2 with flags=0x802
+#     unmap cdb: 42 00 00 00 00 00 00 00 58 00 
+#     unmap parameter list:
+#         00 56 00 50 00 00 00 00  00 00 00 00 12 34 56 77    
+#         00 00 00 01 00 00 00 00  00 00 00 00 12 34 56 78    
+#         00 00 00 02 00 00 00 00  00 00 00 00 12 34 00 00    
+#         00 00 0d 05 00 00 00 00  00 00 00 5a 5a 5a 5a 5a    
+#         00 00 00 00 00 00 00 00  00 00 00 00 00 a5 a5 a5    
+#         00 00 00 07 00 00 00 00
+# sg_cmds_process_resp: slen=18
+# unmap:  Fixed format, current;  Sense key: Illegal Request
+#  Additional sense: Invalid command operation code
+#  Raw sense data (in hex):
+#         70 00 05 00 00 00 00 0a  00 00 00 00 20 00 00 00    
+#         00 00                                               
+# UNMAP not supported
+#
+# --------------------------------------------------------
+# Notice the 8 byte header then 5 descriptors in the parameter
+# list
diff --git a/sg3_utils/examples/sgq_dd.c b/sg3_utils/examples/sgq_dd.c
new file mode 100644
index 0000000..b3d8273
--- /dev/null
+++ b/sg3_utils/examples/sgq_dd.c
@@ -0,0 +1,1161 @@
+#define _XOPEN_SOURCE 500
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <linux/major.h>
+#include <sys/time.h>
+typedef unsigned char u_char;   /* horrible, for scsi.h */
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+
+/* A utility program for the Linux OS SCSI generic ("sg") device driver.
+*  Copyright (C) 1999-2002 D. Gilbert and P. Allworth
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This program is a specialization of the Unix "dd" command in which
+   one or both of the given files is a scsi generic device or a raw
+   device. A block size ('bs') is assumed to be 512 if not given. This
+   program complains if 'ibs' or 'obs' are given with some other value
+   than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
+   'of' is not given or 'of=-' then stdout assumed.  Multipliers:
+      'c','C'  *1       'b','B' *512      'k' *1024      'K' *1000
+      'm' *(1024^2)     'M' *(1000^2)     'g' *(1024^3)  'G' *(1000^3)
+
+   A non-standard argument "bpt" (blocks per transfer) is added to control
+   the maximum number of blocks in each transfer. The default value is 128.
+   For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16KB
+   in this case) are transferred to or from the sg device in a single SCSI
+   command.
+
+   This version should compile with Linux sg drivers with version numbers
+   >= 30000 . This version uses queuing within the Linux sg driver.
+
+*/
+
+static char * version_str = "0.58 20151209";
+/* resurrected from "0.55 20020509" */
+
+#define DEF_BLOCK_SIZE 512
+#define DEF_BLOCKS_PER_TRANSFER 128
+
+/* #define SG_DEBUG */
+
+#define SENSE_BUFF_LEN 32       /* Arbitrary, could be larger */
+#define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
+#define S_RW_LEN 10             /* Use SCSI READ(10) and WRITE(10) */
+
+#define SGP_READ10 0x28
+#define SGP_WRITE10 0x2a
+#define DEF_NUM_THREADS 4       /* actually degree of concurrency */
+#define MAX_NUM_THREADS 32
+
+#ifndef RAW_MAJOR
+#define RAW_MAJOR 255   /*unlikey value */
+#endif
+
+#define FT_OTHER 0              /* filetype other than sg or raw device */
+#define FT_SG 1                 /* filetype is sg char device */
+#define FT_RAW 2                /* filetype is raw char device */
+
+#define QS_IDLE 0               /* ready to start a copy cycle */
+#define QS_IN_STARTED 1         /* commenced read */
+#define QS_IN_FINISHED 2        /* finished read, ready for write */
+#define QS_OUT_STARTED 3        /* commenced write */
+
+#define QS_IN_POLL 11
+#define QS_OUT_POLL 12
+
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+#define EBUFF_SZ 512
+
+
+struct request_element;
+
+typedef struct request_collection
+{       /* one instance visible to all threads */
+    int infd;
+    int skip;
+    int in_type;
+    int in_scsi_type;
+    int in_blk;                 /* next block address to read */
+    int in_count;               /* blocks remaining for next read */
+    int in_done_count;          /* count of completed in blocks */
+    int in_partial;
+    int outfd;
+    int seek;
+    int out_type;
+    int out_scsi_type;
+    int out_blk;                /* next block address to write */
+    int out_count;              /* blocks remaining for next write */
+    int out_done_count;         /* count of completed out blocks */
+    int out_partial;
+    int bs;
+    int bpt;
+    int dio;
+    int dio_incomplete;
+    int sum_of_resids;
+    int coe;
+    int debug;
+    int num_rq_elems;
+    struct request_element * req_arr;
+} Rq_coll;
+
+typedef struct request_element
+{       /* one instance per worker thread */
+    int qstate;                 /* "QS" state */
+    int infd;
+    int outfd;
+    int wr;
+    int blk;
+    int num_blks;
+    unsigned char * buffp;
+    unsigned char * alloc_bp;
+    sg_io_hdr_t io_hdr;
+    unsigned char cmd[S_RW_LEN];
+    unsigned char sb[SENSE_BUFF_LEN];
+    int bs;
+    int dio;
+    int dio_incomplete;
+    int resid;
+    int in_scsi_type;
+    int out_scsi_type;
+    int debug;
+} Rq_elem;
+
+static Rq_coll rcoll;
+static struct pollfd in_pollfd_arr[MAX_NUM_THREADS];
+static struct pollfd out_pollfd_arr[MAX_NUM_THREADS];
+static int dd_count = -1;
+
+static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
+
+static int sg_finish_io(int wr, Rq_elem * rep);
+
+
+static void
+install_handler (int sig_num, void (*sig_handler) (int sig))
+{
+    struct sigaction sigact;
+    sigaction (sig_num, NULL, &sigact);
+    if (sigact.sa_handler != SIG_IGN)
+    {
+        sigact.sa_handler = sig_handler;
+        sigemptyset (&sigact.sa_mask);
+        sigact.sa_flags = 0;
+        sigaction (sig_num, &sigact, NULL);
+    }
+}
+
+static void
+print_stats()
+{
+    int infull, outfull;
+
+    if (0 != rcoll.out_count)
+        fprintf(stderr, "  remaining block count=%d\n", rcoll.out_count);
+    infull = dd_count - rcoll.in_done_count - rcoll.in_partial;
+    fprintf(stderr, "%d+%d records in\n", infull, rcoll.in_partial);
+    outfull = dd_count - rcoll.out_done_count - rcoll.out_partial;
+    fprintf(stderr, "%d+%d records out\n", outfull, rcoll.out_partial);
+}
+
+static void
+interrupt_handler(int sig)
+{
+    struct sigaction sigact;
+
+    sigact.sa_handler = SIG_DFL;
+    sigemptyset (&sigact.sa_mask);
+    sigact.sa_flags = 0;
+    sigaction (sig, &sigact, NULL);
+    fprintf(stderr, "Interrupted by signal,");
+    print_stats ();
+    kill (getpid (), sig);
+}
+
+static void
+siginfo_handler(int sig)
+{
+    fprintf(stderr, "Progress report, continuing ...\n");
+    print_stats ();
+    if (sig) { }        /* suppress unused warning */
+}
+
+static int
+dd_filetype(const char * filename)
+{
+    struct stat st;
+
+    if (stat(filename, &st) < 0)
+        return FT_OTHER;
+    if (S_ISCHR(st.st_mode)) {
+        if (RAW_MAJOR == major(st.st_rdev))
+            return FT_RAW;
+        else if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
+            return FT_SG;
+    }
+    return FT_OTHER;
+}
+
+static void
+usage()
+{
+    fprintf(stderr, "Usage: "
+           "sgq_dd  [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>] "
+           "[bs=<num>]\n"
+           "            [bpt=<num>] [count=<n>] [dio=0|1] [thr=<n>] "
+           "[coe=0|1] [gen=<n>]\n"
+           "            [time=0|1] [deb=<n>] [--version]\n"
+           "         usually either 'if' or 'of' is a sg or raw device\n"
+           " 'bpt' is blocks_per_transfer (default is 128)\n"
+           " 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
+           " 'thr' is number of queues, must be > 0, default 4, max 32\n");
+    fprintf(stderr, " 'coe' continue on sg error, 0->exit (def), "
+           "1->zero + continue\n"
+           " 'time' 0->no timing(def), 1->time plus calculate throughput\n"
+           " 'gen' 0-> 1 file is special(def), 1-> any files allowed\n"
+           " 'deb' is debug, 0->none (def), > 0->varying degrees of debug\n");
+}
+
+/* Returns -1 for error, 0 for nothing found, QS_IN_POLL or QS_OUT_POLL */
+static int
+do_poll(Rq_coll * clp, int timeout, int * req_indexp)
+{
+    int k, res;
+
+    if (FT_SG == clp->out_type) {
+        while (((res = poll(out_pollfd_arr, clp->num_rq_elems, timeout)) < 0)
+               && (EINTR == errno))
+            ;
+        if (res < 0) {
+            perror("poll error on output fds");
+            return -1;
+        }
+        else if (res > 0) {
+            for (k = 0; k < clp->num_rq_elems; ++k) {
+                if (out_pollfd_arr[k].revents & POLLIN) {
+                    if (req_indexp)
+                        *req_indexp = k;
+                    return QS_OUT_POLL;
+                }
+            }
+        }
+    }
+    if (FT_SG == clp->in_type) {
+        while (((res = poll(in_pollfd_arr, clp->num_rq_elems, timeout)) < 0)
+               && (EINTR == errno))
+            ;
+        if (res < 0) {
+            perror("poll error on input fds");
+            return -1;
+        }
+        else if (res > 0) {
+            for (k = 0; k < clp->num_rq_elems; ++k) {
+                if (in_pollfd_arr[k].revents & POLLIN) {
+                    if (req_indexp)
+                        *req_indexp = k;
+                    return QS_IN_POLL;
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+
+/* Return of 0 -> success, -1 -> failure, 2 -> try again */
+static int
+read_capacity(int sg_fd, int * num_sect, int * sect_sz)
+{
+    int res;
+    unsigned char rcCmdBlk [10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char rcBuff[64];
+    unsigned char sense_b[64];
+    sg_io_hdr_t io_hdr;
+
+    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(rcCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_b);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = sizeof(rcBuff);
+    io_hdr.dxferp = rcBuff;
+    io_hdr.cmdp = rcCmdBlk;
+    io_hdr.sbp = sense_b;
+    io_hdr.timeout = DEF_TIMEOUT;
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("read_capacity (SG_IO) error");
+        return -1;
+    }
+    res = sg_err_category3(&io_hdr);
+    if (SG_LIB_CAT_UNIT_ATTENTION == res)
+        return 2; /* probably have another go ... */
+    else if (SG_LIB_CAT_CLEAN != res) {
+        sg_chk_n_print3("read capacity", &io_hdr, 1);
+        return -1;
+    }
+    *num_sect = 1 + sg_get_unaligned_be32(rcBuff + 0);
+    *sect_sz = sg_get_unaligned_be32(rcBuff + 4);
+#ifdef SG_DEBUG
+    fprintf(stderr, "number of sectors=%d, sector size=%d\n",
+            *num_sect, *sect_sz);
+#endif
+    return 0;
+}
+
+/* 0 -> ok, 1 -> short read, -1 -> error */
+static int
+normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
+{
+    int res;
+    int stop_after_write = 0;
+
+    rep->qstate = QS_IN_STARTED;
+    if (rep->debug > 8)
+        fprintf(stderr, "normal_in_operation: start blk=%d num_blks=%d\n",
+                rep->blk, rep->num_blks);
+    while (((res = read(rep->infd, rep->buffp,
+                        blocks * rep->bs)) < 0) && (EINTR == errno))
+        ;
+    if (res < 0) {
+        fprintf(stderr, "sgq_dd: reading, in_blk=%d, errno=%d\n", rep->blk,
+                errno);
+        return -1;
+    }
+    if (res < blocks * rep->bs) {
+        int o_blocks = blocks;
+        stop_after_write = 1;
+        blocks = res / rep->bs;
+        if ((res % rep->bs) > 0) {
+            blocks++;
+            clp->in_partial++;
+        }
+        /* Reverse out + re-apply blocks on clp */
+        clp->in_blk -= o_blocks;
+        clp->in_count += o_blocks;
+        rep->num_blks = blocks;
+        clp->in_blk += blocks;
+        clp->in_count -= blocks;
+    }
+    clp->in_done_count -= blocks;
+    rep->qstate = QS_IN_FINISHED;
+    return stop_after_write;
+}
+
+/* 0 -> ok, -1 -> error */
+static int
+normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
+{
+    int res;
+
+    rep->qstate = QS_OUT_STARTED;
+    if (rep->debug > 8)
+        fprintf(stderr, "normal_out_operation: start blk=%d num_blks=%d\n",
+                rep->blk, rep->num_blks);
+    while (((res = write(rep->outfd, rep->buffp,
+                 rep->num_blks * rep->bs)) < 0) && (EINTR == errno))
+        ;
+    if (res < 0) {
+        fprintf(stderr, "sgq_dd: output, out_blk=%d, errno=%d\n", rep->blk,
+                errno);
+        return -1;
+    }
+    if (res < blocks * rep->bs) {
+        blocks = res / rep->bs;
+        if ((res % rep->bs) > 0) {
+            blocks++;
+            clp->out_partial++;
+        }
+        rep->num_blks = blocks;
+    }
+    clp->out_done_count -= blocks;
+    rep->qstate = QS_IDLE;
+    return 0;
+}
+
+/* Returns 1 for retryable, 0 for ok, -ve for error */
+static int
+sg_fin_in_operation(Rq_coll * clp, Rq_elem * rep)
+{
+    int res;
+
+    rep->qstate = QS_IN_FINISHED;
+    res = sg_finish_io(rep->wr, rep);
+    if (res < 0) {
+        if (clp->coe) {
+            memset(rep->buffp, 0, rep->num_blks * rep->bs);
+            fprintf(stderr, ">> substituted zeros for in blk=%d for "
+                    "%d bytes\n", rep->blk, rep->num_blks * rep->bs);
+            res = 0;
+        }
+        else {
+            fprintf(stderr, "error finishing sg in command\n");
+            return res;
+        }
+    }
+    if (0 == res) { /* looks good, going to return */
+        if (rep->dio_incomplete || rep->resid) {
+            clp->dio_incomplete += rep->dio_incomplete;
+            clp->sum_of_resids += rep->resid;
+        }
+        clp->in_done_count -= rep->num_blks;
+    }
+    return res;
+}
+
+/* Returns 1 for retryable, 0 for ok, -ve for error */
+static int
+sg_fin_out_operation(Rq_coll * clp, Rq_elem * rep)
+{
+    int res;
+
+    rep->qstate = QS_IDLE;
+    res = sg_finish_io(rep->wr, rep);
+    if (res < 0) {
+        if (clp->coe) {
+            fprintf(stderr, ">> ignored error for out blk=%d for "
+                    "%d bytes\n", rep->blk, rep->num_blks * rep->bs);
+            res = 0;
+        }
+        else {
+            fprintf(stderr, "error finishing sg out command\n");
+            return res;
+        }
+    }
+    if (0 == res) {
+        if (rep->dio_incomplete || rep->resid) {
+            clp->dio_incomplete += rep->dio_incomplete;
+            clp->sum_of_resids += rep->resid;
+        }
+        clp->out_done_count -= rep->num_blks;
+    }
+    return res;
+}
+
+static int
+sg_start_io(Rq_elem * rep)
+{
+    sg_io_hdr_t * hp = &rep->io_hdr;
+    int res;
+
+    rep->qstate = rep->wr ? QS_OUT_STARTED : QS_IN_STARTED;
+    memset(rep->cmd, 0, sizeof(rep->cmd));
+    rep->cmd[0] = rep->wr ? SGP_WRITE10 : SGP_READ10;
+    sg_put_unaligned_be32((uint32_t)rep->blk, rep->cmd + 2);
+    sg_put_unaligned_be16((uint16_t)rep->num_blks, rep->cmd + 7);
+    memset(hp, 0, sizeof(sg_io_hdr_t));
+    hp->interface_id = 'S';
+    hp->cmd_len = sizeof(rep->cmd);
+    hp->cmdp = rep->cmd;
+    hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
+    hp->dxfer_len = rep->bs * rep->num_blks;
+    hp->dxferp = rep->buffp;
+    hp->mx_sb_len = sizeof(rep->sb);
+    hp->sbp = rep->sb;
+    hp->timeout = DEF_TIMEOUT;
+    hp->usr_ptr = rep;
+    hp->pack_id = rep->blk;
+    if (rep->dio)
+        hp->flags |= SG_FLAG_DIRECT_IO;
+    if (rep->debug > 8) {
+        fprintf(stderr, "sg_start_io: SCSI %s, blk=%d num_blks=%d\n",
+               rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
+        sg_print_command(hp->cmdp);
+        fprintf(stderr, " len=%d, dxfrp=%p, cmd_len=%d\n",
+                hp->dxfer_len, hp->dxferp, hp->cmd_len);
+    }
+
+    while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
+                         sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
+        ;
+    if (res < 0) {
+        if (ENOMEM == errno)
+            return 1;
+        return res;
+    }
+    return 0;
+}
+
+/* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */
+static int
+sg_finish_io(int wr, Rq_elem * rep)
+{
+    int res;
+    sg_io_hdr_t io_hdr;
+    sg_io_hdr_t * hp;
+#if 0
+    static int testing = 0;     /* thread dubious! */
+#endif
+
+    memset(&io_hdr, 0 , sizeof(sg_io_hdr_t));
+    /* FORCE_PACK_ID active set only read packet with matching pack_id */
+    io_hdr.interface_id = 'S';
+    io_hdr.dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
+    io_hdr.pack_id = rep->blk;
+
+    while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr,
+                        sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
+        ;
+    if (res < 0) {
+        perror("finishing io on sg device, error");
+        return -1;
+    }
+    if (rep != (Rq_elem *)io_hdr.usr_ptr) {
+        fprintf(stderr,
+                "sg_finish_io: bad usr_ptr, request-response mismatch\n");
+        exit(1);
+    }
+    memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t));
+    hp = &rep->io_hdr;
+
+    switch (sg_err_category3(hp)) {
+        case SG_LIB_CAT_CLEAN:
+            break;
+        case SG_LIB_CAT_RECOVERED:
+            fprintf(stderr, "Recovered error on block=%d, num=%d\n",
+                    rep->blk, rep->num_blks);
+            break;
+        case SG_LIB_CAT_UNIT_ATTENTION:
+            return 1;
+        default:
+            {
+                char ebuff[EBUFF_SZ];
+                snprintf(ebuff, EBUFF_SZ, "%s blk=%d",
+                         rep->wr ? "writing": "reading", rep->blk);
+                sg_chk_n_print3(ebuff, hp, 1);
+                return -1;
+            }
+    }
+#if 0
+    if (0 == (++testing % 100)) return -1;
+#endif
+    if (rep->dio &&
+        ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+        rep->dio_incomplete = 1; /* count dios done as indirect IO */
+    else
+        rep->dio_incomplete = 0;
+    rep->resid = hp->resid;
+    if (rep->debug > 8)
+        fprintf(stderr, "sg_finish_io: completed %s, blk=%d\n",
+                wr ? "WRITE" : "READ", rep->blk);
+    return 0;
+}
+
+/* Returns scsi_type or -1 for error */
+static int
+sg_prepare(int fd, int sz)
+{
+    int res, t;
+    struct sg_scsi_id info;
+
+    res = ioctl(fd, SG_GET_VERSION_NUM, &t);
+    if ((res < 0) || (t < 30000)) {
+        fprintf(stderr, "sgq_dd: sg driver prior to 3.x.y\n");
+        return -1;
+    }
+    res = ioctl(fd, SG_SET_RESERVED_SIZE, &sz);
+    if (res < 0)
+        perror("sgq_dd: SG_SET_RESERVED_SIZE error");
+#if 0
+    t = 1;
+    res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
+    if (res < 0)
+        perror("sgq_dd: SG_SET_FORCE_PACK_ID error");
+#endif
+    res = ioctl(fd, SG_GET_SCSI_ID, &info);
+    if (res < 0) {
+        perror("sgq_dd: SG_SET_SCSI_ID error");
+        return -1;
+    }
+    else
+        return info.scsi_type;
+}
+
+/* Return 0 for ok, anything else for errors */
+static int
+prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf)
+{
+    int k;
+    Rq_elem * rep;
+    size_t psz;
+    char ebuff[EBUFF_SZ];
+    int sz = clp->bpt * clp->bs;
+    int scsi_type;
+
+    clp->req_arr = malloc(sizeof(Rq_elem) * clp->num_rq_elems);
+    if (NULL == clp->req_arr)
+        return 1;
+    for (k = 0; k < clp->num_rq_elems; ++k) {
+        rep = &clp->req_arr[k];
+        memset(rep, 0, sizeof(Rq_elem));
+        psz = getpagesize();
+        if (NULL == (rep->alloc_bp = malloc(sz + psz)))
+            return 1;
+        rep->buffp = (unsigned char *)
+                (((unsigned long)rep->alloc_bp + psz - 1) & (~(psz - 1)));
+        rep->qstate = QS_IDLE;
+        rep->bs = clp->bs;
+        rep->dio = clp->dio;
+        rep->debug = clp->debug;
+        rep->out_scsi_type = clp->out_scsi_type;
+        if (FT_SG == clp->in_type) {
+            if (0 == k)
+                rep->infd = clp->infd;
+            else {
+                if ((rep->infd = open(inf, O_RDWR)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                             "sgq_dd: could not open %s for sg reading", inf);
+                    perror(ebuff);
+                    return 1;
+                }
+            }
+            in_pollfd_arr[k].fd = rep->infd;
+            in_pollfd_arr[k].events = POLLIN;
+            if ((scsi_type = sg_prepare(rep->infd, sz)) < 0)
+                return 1;
+            if (0 == k)
+                clp->in_scsi_type = scsi_type;
+            rep->in_scsi_type = clp->in_scsi_type;
+        }
+        else
+            rep->infd = clp->infd;
+
+        if (FT_SG == clp->out_type) {
+            if (0 == k)
+                rep->outfd = clp->outfd;
+            else {
+                if ((rep->outfd = open(outf, O_RDWR)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                             "sgq_dd: could not open %s for sg writing", outf);
+                    perror(ebuff);
+                    return 1;
+                }
+            }
+            out_pollfd_arr[k].fd = rep->outfd;
+            out_pollfd_arr[k].events = POLLIN;
+            if ((scsi_type = sg_prepare(rep->outfd, sz)) < 0)
+                return 1;
+            if (0 == k)
+                clp->out_scsi_type = scsi_type;
+            rep->out_scsi_type = clp->out_scsi_type;
+        }
+        else
+            rep->outfd = clp->outfd;
+    }
+    return 0;
+}
+
+/* Returns a "QS" code and req index, or QS_IDLE and position of first idle
+   (-1 if no idle position). Returns -1 on poll error. */
+static int
+decider(Rq_coll * clp, int first_xfer, int * req_indexp)
+{
+    int k, res;
+    Rq_elem * rep;
+    int first_idle_index = -1;
+    int lowest_blk_index = -1;
+    int times;
+    int try_poll = 0;
+    int lowest_blk = INT_MAX;
+
+    times = first_xfer ? 1 : clp->num_rq_elems;
+    for (k = 0; k < times; ++k) {
+        rep = &clp->req_arr[k];
+        if ((QS_IN_STARTED == rep->qstate) ||
+            (QS_OUT_STARTED == rep->qstate))
+            try_poll = 1;
+        else if ((QS_IN_FINISHED == rep->qstate) && (rep->blk < lowest_blk)) {
+            lowest_blk = rep->blk;
+            lowest_blk_index = k;
+        }
+        else if ((QS_IDLE == rep->qstate) && (first_idle_index < 0))
+            first_idle_index = k;
+    }
+    if (try_poll) {
+        res = do_poll(clp, 0, req_indexp);
+        if (0 != res)
+            return res;
+    }
+
+    if (lowest_blk_index >= 0) {
+        if (req_indexp)
+            *req_indexp = lowest_blk_index;
+        return QS_IN_FINISHED;
+    }
+#if 0
+    if (try_poll) {
+        res = do_poll(clp, 2, req_indexp);
+        if (0 != res)
+            return res;
+    }
+#endif
+    if (req_indexp)
+        *req_indexp = first_idle_index;
+    return QS_IDLE;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int skip = 0;
+    int seek = 0;
+    int ibs = 0;
+    int obs = 0;
+    char str[STR_SZ];
+    char * key;
+    char * buf;
+    char inf[INOUTF_SZ];
+    char outf[INOUTF_SZ];
+    int res, k;
+    int in_num_sect = 0;
+    int out_num_sect = 0;
+    int num_threads = DEF_NUM_THREADS;
+    int gen = 0;
+    int do_time = 0;
+    int in_sect_sz, out_sect_sz, first_xfer, qstate, req_index, seek_skip;
+    int blocks, stop_after_write, terminate;
+    char ebuff[EBUFF_SZ];
+    Rq_elem * rep;
+    struct timeval start_tm, end_tm;
+
+    memset(&rcoll, 0, sizeof(Rq_coll));
+    rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
+    rcoll.in_type = FT_OTHER;
+    rcoll.out_type = FT_OTHER;
+    inf[0] = '\0';
+    outf[0] = '\0';
+    if (argc < 2) {
+        usage();
+        return 1;
+    }
+
+    for(k = 1; k < argc; k++) {
+        if (argv[k])
+            strncpy(str, argv[k], STR_SZ);
+        else
+            continue;
+        for(key = str, buf = key; *buf && *buf != '=';)
+            buf++;
+        if (*buf)
+            *buf++ = '\0';
+        if (strcmp(key,"if") == 0)
+            strncpy(inf, buf, INOUTF_SZ);
+        else if (strcmp(key,"of") == 0)
+            strncpy(outf, buf, INOUTF_SZ);
+        else if (0 == strcmp(key,"ibs"))
+            ibs = sg_get_num(buf);
+        else if (0 == strcmp(key,"obs"))
+            obs = sg_get_num(buf);
+        else if (0 == strcmp(key,"bs"))
+            rcoll.bs = sg_get_num(buf);
+        else if (0 == strcmp(key,"bpt"))
+            rcoll.bpt = sg_get_num(buf);
+        else if (0 == strcmp(key,"skip"))
+            skip = sg_get_num(buf);
+        else if (0 == strcmp(key,"seek"))
+            seek = sg_get_num(buf);
+        else if (0 == strcmp(key,"count"))
+            dd_count = sg_get_num(buf);
+        else if (0 == strcmp(key,"dio"))
+            rcoll.dio = sg_get_num(buf);
+        else if (0 == strcmp(key,"thr"))
+            num_threads = sg_get_num(buf);
+        else if (0 == strcmp(key,"coe"))
+            rcoll.coe = sg_get_num(buf);
+        else if (0 == strcmp(key,"gen"))
+            gen = sg_get_num(buf);
+        else if (0 == strncmp(key,"deb", 3))
+            rcoll.debug = sg_get_num(buf);
+        else if (0 == strcmp(key,"time"))
+            do_time = sg_get_num(buf);
+        else if (0 == strncmp(key, "--vers", 6)) {
+            fprintf(stderr, "sgq_dd for sg version 3 driver: %s\n",
+                    version_str);
+            return 0;
+        }
+        else {
+            fprintf(stderr, "Unrecognized argument '%s'\n", key);
+            usage();
+            return 1;
+        }
+    }
+    if (rcoll.bs <= 0) {
+        rcoll.bs = DEF_BLOCK_SIZE;
+        fprintf(stderr, "Assume default 'bs' (block size) of %d bytes\n",
+                rcoll.bs);
+    }
+    if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) {
+        fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
+        usage();
+        return 1;
+    }
+    if ((skip < 0) || (seek < 0)) {
+        fprintf(stderr, "skip and seek cannot be negative\n");
+        return 1;
+    }
+    if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) {
+        fprintf(stderr, "too few or too many threads requested\n");
+        usage();
+        return 1;
+    }
+    if (rcoll.debug)
+        fprintf(stderr, "sgq_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
+               inf, skip, outf, seek, dd_count);
+    install_handler (SIGINT, interrupt_handler);
+    install_handler (SIGQUIT, interrupt_handler);
+    install_handler (SIGPIPE, interrupt_handler);
+    install_handler (SIGUSR1, siginfo_handler);
+
+    rcoll.infd = STDIN_FILENO;
+    rcoll.outfd = STDOUT_FILENO;
+    if (inf[0] && ('-' != inf[0])) {
+        rcoll.in_type = dd_filetype(inf);
+
+        if (FT_SG == rcoll.in_type) {
+            if ((rcoll.infd = open(inf, O_RDWR)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         "sgq_dd: could not open %s for sg reading", inf);
+                perror(ebuff);
+                return 1;
+            }
+        }
+        if (FT_SG != rcoll.in_type) {
+            if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         "sgq_dd: could not open %s for reading", inf);
+                perror(ebuff);
+                return 1;
+            }
+            else if (skip > 0) {
+                loff_t offset = skip;
+
+                offset *= rcoll.bs;       /* could exceed 32 here! */
+                if (lseek(rcoll.infd, offset, SEEK_SET) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                "sgq_dd: couldn't skip to required position on %s", inf);
+                    perror(ebuff);
+                    return 1;
+                }
+            }
+        }
+    }
+    if (outf[0] && ('-' != outf[0])) {
+        rcoll.out_type = dd_filetype(outf);
+
+        if (FT_SG == rcoll.out_type) {
+            if ((rcoll.outfd = open(outf, O_RDWR)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                        "sgq_dd: could not open %s for sg writing", outf);
+                perror(ebuff);
+                return 1;
+            }
+        }
+        else {
+            if (FT_OTHER == rcoll.out_type) {
+                if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                            "sgq_dd: could not open %s for writing", outf);
+                    perror(ebuff);
+                    return 1;
+                }
+            }
+            else {
+                if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                            "sgq_dd: could not open %s for raw writing", outf);
+                    perror(ebuff);
+                    return 1;
+                }
+            }
+            if (seek > 0) {
+                loff_t offset = seek;
+
+                offset *= rcoll.bs;       /* could exceed 32 bits here! */
+                if (lseek(rcoll.outfd, offset, SEEK_SET) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                "sgq_dd: couldn't seek to required position on %s", outf);
+                    perror(ebuff);
+                    return 1;
+                }
+            }
+        }
+    }
+    if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) {
+        fprintf(stderr, "Disallow both if and of to be stdin and stdout");
+        return 1;
+    }
+    if ((FT_OTHER == rcoll.in_type) && (FT_OTHER == rcoll.out_type) && !gen) {
+        fprintf(stderr, "Either 'if' or 'of' must be a sg or raw device\n");
+        return 1;
+    }
+    if (0 == dd_count)
+        return 0;
+    else if (dd_count < 0) {
+        if (FT_SG == rcoll.in_type) {
+            res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
+            if (2 == res) {
+                fprintf(stderr, "Unit attention, media changed(in), repeat\n");
+                res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
+            }
+            if (0 != res) {
+                fprintf(stderr, "Unable to read capacity on %s\n", inf);
+                in_num_sect = -1;
+            }
+            else {
+                if (in_num_sect > skip)
+                    in_num_sect -= skip;
+            }
+        }
+        if (FT_SG == rcoll.out_type) {
+            res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
+            if (2 == res) {
+                fprintf(stderr, "Unit attention, media changed(out), "
+                        "repeat\n");
+                res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
+            }
+            if (0 != res) {
+                fprintf(stderr, "Unable to read capacity on %s\n", outf);
+                out_num_sect = -1;
+            }
+            else {
+                if (out_num_sect > seek)
+                    out_num_sect -= seek;
+            }
+        }
+        if (in_num_sect > 0) {
+            if (out_num_sect > 0)
+                dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
+                                                       in_num_sect;
+            else
+                dd_count = in_num_sect;
+        }
+        else
+            dd_count = out_num_sect;
+    }
+    if (rcoll.debug > 1)
+        fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, "
+                "out_num_sect=%d\n", dd_count, in_num_sect, out_num_sect);
+    if (dd_count <= 0) {
+        fprintf(stderr, "Couldn't calculate count, please give one\n");
+        return 1;
+    }
+
+    rcoll.in_count = dd_count;
+    rcoll.in_done_count = dd_count;
+    rcoll.skip = skip;
+    rcoll.in_blk = skip;
+    rcoll.out_count = dd_count;
+    rcoll.out_done_count = dd_count;
+    rcoll.seek = seek;
+    rcoll.out_blk = seek;
+
+    if ((FT_SG == rcoll.in_type) || (FT_SG == rcoll.out_type))
+        rcoll.num_rq_elems = num_threads;
+    else
+        rcoll.num_rq_elems = 1;
+    if (prepare_rq_elems(&rcoll, inf, outf)) {
+        fprintf(stderr, "Setup failure, perhaps no memory\n");
+        return 1;
+    }
+
+    first_xfer = 1;
+    stop_after_write = 0;
+    terminate = 0;
+    seek_skip =  rcoll.seek - rcoll.skip;
+    if (do_time) {
+        start_tm.tv_sec = 0;
+        start_tm.tv_usec = 0;
+        gettimeofday(&start_tm, NULL);
+    }
+    while (rcoll.out_done_count > 0) { /* >>>>>>>>> main loop */
+        req_index = -1;
+        qstate = decider(&rcoll, first_xfer, &req_index);
+        rep = (req_index < 0) ? NULL : (rcoll.req_arr + req_index);
+        switch (qstate) {
+        case QS_IDLE:
+            if ((NULL == rep) || (rcoll.in_count <= 0)) {
+                /* usleep(1000); */
+                /* do_poll(&rcoll, 10, NULL); */
+                /* do_poll(&rcoll, 0, NULL); */
+                break;
+            }
+            if (rcoll.debug > 8)
+                fprintf(stderr, "    sgq_dd: non-sleeping QS_IDLE state, "
+                                "req_index=%d\n", req_index);
+            if (first_xfer >= 2)
+                first_xfer = 0;
+            else if (1 == first_xfer)
+                ++first_xfer;
+            if (stop_after_write) {
+                terminate = 1;
+                break;
+            }
+            blocks = (rcoll.in_count > rcoll.bpt) ? rcoll.bpt : rcoll.in_count;
+            rep->wr = 0;
+            rep->blk = rcoll.in_blk;
+            rep->num_blks = blocks;
+            rcoll.in_blk += blocks;
+            rcoll.in_count -= blocks;
+
+            if (FT_SG == rcoll.in_type) {
+                res = sg_start_io(rep);
+                if (0 != res) {
+                    if (1 == res)
+                        fprintf(stderr, "Out of memory starting sg io\n");
+                    terminate = 1;
+                }
+            }
+            else {
+                res = normal_in_operation(&rcoll, rep, blocks);
+                if (res < 0)
+                    terminate = 1;
+                else if (res > 0)
+                    stop_after_write = 1;
+            }
+            break;
+        case QS_IN_FINISHED:
+            if (rcoll.debug > 8)
+                fprintf(stderr, "    sgq_dd: state is QS_IN_FINISHED, "
+                                "req_index=%d\n", req_index);
+            if ((rep->blk + seek_skip) != rcoll.out_blk) {
+                /* if write would be out of sequence then wait */
+                if (rcoll.debug > 4)
+                    fprintf(stderr, "    sgq_dd: QS_IN_FINISHED, "
+                            "out of sequence\n");
+                usleep(200);
+                break;
+            }
+            rep->wr = 1;
+            rep->blk = rcoll.out_blk;
+            blocks = rep->num_blks;
+            rcoll.out_blk += blocks;
+            rcoll.out_count -= blocks;
+
+            if (FT_SG == rcoll.out_type) {
+                res = sg_start_io(rep);
+                if (0 != res) {
+                    if (1 == res)
+                        fprintf(stderr, "Out of memory starting sg io\n");
+                    terminate = 1;
+                }
+            }
+            else {
+                if (normal_out_operation(&rcoll, rep, blocks) < 0)
+                    terminate = 1;
+            }
+            break;
+        case QS_IN_POLL:
+            if (rcoll.debug > 8)
+                fprintf(stderr, "    sgq_dd: state is QS_IN_POLL, "
+                                "req_index=%d\n", req_index);
+            res = sg_fin_in_operation(&rcoll, rep);
+            if (res < 0)
+                terminate = 1;
+            else if (res > 1) {
+                if (first_xfer) {
+                    /* only retry on first xfer */
+                    if (0 != sg_start_io(rep))
+                        terminate = 1;
+                }
+                else
+                    terminate = 1;
+            }
+            break;
+        case QS_OUT_POLL:
+            if (rcoll.debug > 8)
+                fprintf(stderr, "    sgq_dd: state is QS_OUT_POLL, "
+                                "req_index=%d\n", req_index);
+            res = sg_fin_out_operation(&rcoll, rep);
+            if (res < 0)
+                terminate = 1;
+            else if (res > 1) {
+                if (first_xfer) {
+                    /* only retry on first xfer */
+                    if (0 != sg_start_io(rep))
+                        terminate = 1;
+                }
+                else
+                    terminate = 1;
+            }
+            break;
+        default:
+            if (rcoll.debug > 8)
+                fprintf(stderr, "    sgq_dd: state is ?????\n");
+            terminate = 1;
+            break;
+        }
+        if (terminate)
+            break;
+    } /* >>>>>>>>>>>>> end of main loop */
+
+    if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
+        struct timeval res_tm;
+        double a, b;
+
+        gettimeofday(&end_tm, NULL);
+        res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+        res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+        if (res_tm.tv_usec < 0) {
+            --res_tm.tv_sec;
+            res_tm.tv_usec += 1000000;
+        }
+        a = res_tm.tv_sec;
+        a += (0.000001 * res_tm.tv_usec);
+        b = (double)rcoll.bs * (dd_count - rcoll.out_done_count);
+        printf("time to transfer data was %d.%06d secs",
+               (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+        if ((a > 0.00001) && (b > 511))
+            printf(", %.2f MB/sec\n", b / (a * 1000000.0));
+        else
+            printf("\n");
+    }
+
+    if (STDIN_FILENO != rcoll.infd)
+        close(rcoll.infd);
+    if (STDOUT_FILENO != rcoll.outfd)
+        close(rcoll.outfd);
+    res = 0;
+    if (0 != rcoll.out_count) {
+        fprintf(stderr, ">>>> Some error occurred,\n");
+        res = 2;
+    }
+    print_stats();
+    if (rcoll.dio_incomplete) {
+        int fd;
+        char c;
+
+        fprintf(stderr, ">> Direct IO requested but incomplete %d times\n",
+                rcoll.dio_incomplete);
+        if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
+            if (1 == read(fd, &c, 1)) {
+                if ('0' == c)
+                    fprintf(stderr, ">>> %s set to '0' but should be set "
+                            "to '1' for direct IO\n", proc_allow_dio);
+            }
+            close(fd);
+        }
+    }
+    if (rcoll.sum_of_resids)
+        fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
+               rcoll.sum_of_resids);
+    return res;
+}
diff --git a/sg3_utils/examples/transport_ids.txt b/sg3_utils/examples/transport_ids.txt
new file mode 100644
index 0000000..0c60456
--- /dev/null
+++ b/sg3_utils/examples/transport_ids.txt
@@ -0,0 +1,31 @@
+# This file is an example for the sg_persist utility.
+# It discusses using "TransportID"s which are defined (most recently)
+# in SPC-4 revison 20 section 7.5.4 titled: "TransportID identifiers".
+#
+# The sg_persist utility can take one or more "transportID"s from stdin when
+# either the '--transport-id=-" or "-X -" option is given on the command
+# line.
+
+# To see transport IDs decoded after they have been read in (e.g. to check
+# they are well formed) use the verbose flag 3 times (i.e. "... -vvv ...").
+
+# Here is a simple example (for SPI) of a comma separted hex list:
+1,0,0,7,0,0,0,1         # SPI, initiator address=7, relative_port_num=1
+
+# and here is the transport specific format for the same thing:
+# spi,1,7
+
+# An example for SAS follows, first as a hex string, then in transport
+# specific format (incremented by 1)
+6,0,0,0,50,6,5,b0,0,6,f2,60
+sas,500605b00006f261
+
+# For iSCSI the hex list form is awkward, better to use the transport
+# specific format. [The leading spaces are ignored.]
+    iqn.5886.com.acme.diskarrays-sn-a8675309
+
+
+        # Leading spaces and tabs before a '#' are ok.
+             
+
+# dpg 20090824
diff --git a/sg3_utils/getopt_long/getopt.h b/sg3_utils/getopt_long/getopt.h
new file mode 100644
index 0000000..4d6543b
--- /dev/null
+++ b/sg3_utils/getopt_long/getopt.h
@@ -0,0 +1,86 @@
+/*	$NetBSD: getopt.h,v 1.7 2005/02/03 04:39:32 perry Exp $	*/
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * modified May 12, 2005 by Jim Basney <jbasney@ncsa.uiuc.edu>
+ *
+ * removed #include of non-POSIX <sys/cdefs.h> and <sys/featuretest.h>
+ * removed references to _NETBSD_SOURCE and HAVE_NBTOOL_CONFIG_H
+ * added #if !HAVE_GETOPT_LONG
+ * removed __BEGIN_DECLS and __END_DECLS
+ */
+
+#ifndef _MYPROXY_GETOPT_H_
+#define _MYPROXY_GETOPT_H_
+
+#if !HAVE_GETOPT_LONG
+
+#include <unistd.h>
+
+/*
+ * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions
+ */
+#define no_argument        0
+#define required_argument  1
+#define optional_argument  2
+
+extern char *optarg;
+extern int optind;
+extern int optopt;
+extern int opterr;
+
+struct option {
+	/* name of long option */
+	const char *name;
+	/*
+	 * one of no_argument, required_argument, and optional_argument:
+	 * whether option takes an argument
+	 */
+	int has_arg;
+	/* if not NULL, set *flag to val when option found */
+	int *flag;
+	/* if flag not NULL, value to set *flag to; else return value */
+	int val;
+};
+
+int getopt_long(int, char * const *, const char *,
+    const struct option *, int *);
+ 
+#endif /* !HAVE_GETOPT_LONG */
+
+#endif /* !_MYPROXY_GETOPT_H_ */
diff --git a/sg3_utils/getopt_long/getopt_long.c b/sg3_utils/getopt_long/getopt_long.c
new file mode 100644
index 0000000..f94dcae
--- /dev/null
+++ b/sg3_utils/getopt_long/getopt_long.c
@@ -0,0 +1,434 @@
+/*	$NetBSD: getopt_long.c,v 1.17 2004/06/20 22:20:15 jmc Exp $	*/
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * modified May 12, 2005 by Jim Basney <jbasney@ncsa.uiuc.edu>
+ *
+ * removed #include of non-POSIX <sys/cdefs.h> <err.h>
+ * removed #include of "namespace.h"
+ * use local "port_getopt.h" instead of <getopt.h>
+ * removed REPLACE_GETOPT and HAVE_NBTOOL_CONFIG_H sections
+ * removed __P() from function declarations
+ * use ANSI C function parameter lists
+ * removed optreset support
+ * replace _DIAGASSERT() with assert()
+ * replace non-POSIX warnx(...) with fprintf(stderr, ...)
+ * added extern declarations for optarg, optind, opterr, and optopt
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: getopt_long.c,v 1.17 2004/06/20 22:20:15 jmc Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include <assert.h>
+#include <errno.h>
+#include "getopt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __weak_alias
+__weak_alias(getopt_long,_getopt_long)
+#endif
+
+#if !HAVE_GETOPT_LONG
+#define IGNORE_FIRST	(*options == '-' || *options == '+')
+#define PRINT_ERROR	((opterr) && ((*options != ':') \
+				      || (IGNORE_FIRST && options[1] != ':')))
+#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
+#define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
+/* XXX: GNU ignores PC if *options == '-' */
+#define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
+
+/* return values */
+#define	BADCH	(int)'?'
+#define	BADARG		((IGNORE_FIRST && options[1] == ':') \
+			 || (*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define	EMSG	""
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static int getopt_internal (int, char * const *, const char *);
+static int gcd (int, int);
+static void permute_args (int, int, int, char * const *);
+
+static char *place = EMSG; /* option letter processing */
+
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1;   /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptchar[] = "unknown option -- %c";
+static const char illoptstring[] = "unknown option -- %s";
+
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b)
+{
+	int c;
+
+	c = a % b;
+	while (c != 0) {
+		a = b;
+		b = c;
+		c = a % b;
+	}
+	   
+	return b;
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+	     char * const *nargv)
+{
+	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+	char *swap;
+
+	assert(nargv != NULL);
+
+	/*
+	 * compute lengths of blocks and number and size of cycles
+	 */
+	nnonopts = panonopt_end - panonopt_start;
+	nopts = opt_end - panonopt_end;
+	ncycle = gcd(nnonopts, nopts);
+	cyclelen = (opt_end - panonopt_start) / ncycle;
+
+	for (i = 0; i < ncycle; i++) {
+		cstart = panonopt_end+i;
+		pos = cstart;
+		for (j = 0; j < cyclelen; j++) {
+			if (pos >= panonopt_end)
+				pos -= nnonopts;
+			else
+				pos += nopts;
+			swap = nargv[pos];
+			/* LINTED const cast */
+			((char **) nargv)[pos] = nargv[cstart];
+			/* LINTED const cast */
+			((char **)nargv)[cstart] = swap;
+		}
+	}
+}
+
+/*
+ * getopt_internal --
+ *	Parse argc/argv argument vector.  Called by user level routines.
+ *  Returns -2 if -- is found (can be long option or end of options marker).
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options)
+{
+	char *oli;				/* option letter list index */
+	int optchar;
+
+	assert(nargv != NULL);
+	assert(options != NULL);
+
+	optarg = NULL;
+
+	/*
+	 * XXX Some programs (like rsyncd) expect to be able to
+	 * XXX re-initialize optind to 0 and have getopt_long(3)
+	 * XXX properly function again.  Work around this braindamage.
+	 */
+	if (optind == 0)
+		optind = 1;
+
+start:
+	if (!*place) {		                /* update scanning pointer */
+		if (optind >= nargc) {          /* end of argument vector */
+			place = EMSG;
+			if (nonopt_end != -1) {
+				/* do permutation, if we have to */
+				permute_args(nonopt_start, nonopt_end,
+				    optind, nargv);
+				optind -= nonopt_end - nonopt_start;
+			}
+			else if (nonopt_start != -1) {
+				/*
+				 * If we skipped non-options, set optind
+				 * to the first of them.
+				 */
+				optind = nonopt_start;
+			}
+			nonopt_start = nonopt_end = -1;
+			return -1;
+		}
+		if ((*(place = nargv[optind]) != '-')
+		    || (place[1] == '\0')) {    /* found non-option */
+			place = EMSG;
+			if (IN_ORDER) {
+				/*
+				 * GNU extension: 
+				 * return non-option as argument to option 1
+				 */
+				optarg = nargv[optind++];
+				return INORDER;
+			}
+			if (!PERMUTE) {
+				/*
+				 * if no permutation wanted, stop parsing
+				 * at first non-option
+				 */
+				return -1;
+			}
+			/* do permutation */
+			if (nonopt_start == -1)
+				nonopt_start = optind;
+			else if (nonopt_end != -1) {
+				permute_args(nonopt_start, nonopt_end,
+				    optind, nargv);
+				nonopt_start = optind -
+				    (nonopt_end - nonopt_start);
+				nonopt_end = -1;
+			}
+			optind++;
+			/* process next argument */
+			goto start;
+		}
+		if (nonopt_start != -1 && nonopt_end == -1)
+			nonopt_end = optind;
+		if (place[1] && *++place == '-') {	/* found "--" */
+			place++;
+			return -2;
+		}
+	}
+	if ((optchar = (int)*place++) == (int)':' ||
+	    (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
+		/* option letter unknown or ':' */
+		if (!*place)
+			++optind;
+		if (PRINT_ERROR)
+			fprintf(stderr, illoptchar, optchar);
+		optopt = optchar;
+		return BADCH;
+	}
+	if (optchar == 'W' && oli[1] == ';') {		/* -W long-option */
+		/* XXX: what if no long options provided (called by getopt)? */
+		if (*place) 
+			return -2;
+
+		if (++optind >= nargc) {	/* no arg */
+			place = EMSG;
+			if (PRINT_ERROR)
+				fprintf(stderr, recargchar, optchar);
+			optopt = optchar;
+			return BADARG;
+		} else				/* white space */
+			place = nargv[optind];
+		/*
+		 * Handle -W arg the same as --arg (which causes getopt to
+		 * stop parsing).
+		 */
+		return -2;
+	}
+	if (*++oli != ':') {			/* doesn't take argument */
+		if (!*place)
+			++optind;
+	} else {				/* takes (optional) argument */
+		optarg = NULL;
+		if (*place)			/* no white space */
+			optarg = place;
+		/* XXX: disable test for :: if PC? (GNU doesn't) */
+		else if (oli[1] != ':') {	/* arg not optional */
+			if (++optind >= nargc) {	/* no arg */
+				place = EMSG;
+				if (PRINT_ERROR)
+					fprintf(stderr, recargchar, optchar);
+				optopt = optchar;
+				return BADARG;
+			} else
+				optarg = nargv[optind];
+		}
+		place = EMSG;
+		++optind;
+	}
+	/* dump back option letter */
+	return optchar;
+}
+
+/*
+ * getopt_long --
+ *	Parse argc/argv argument vector.
+ */
+int
+getopt_long(int nargc, char * const *nargv, const char *options,
+	    const struct option *long_options, int *idx)
+{
+	int retval;
+
+	assert(nargv != NULL);
+	assert(options != NULL);
+	assert(long_options != NULL);
+	/* idx may be NULL */
+
+	if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
+		char *current_argv, *has_equal;
+		size_t current_argv_len;
+		int i, match;
+
+		current_argv = place;
+		match = -1;
+
+		optind++;
+		place = EMSG;
+
+		if (*current_argv == '\0') {		/* found "--" */
+			/*
+			 * We found an option (--), so if we skipped
+			 * non-options, we have to permute.
+			 */
+			if (nonopt_end != -1) {
+				permute_args(nonopt_start, nonopt_end,
+				    optind, nargv);
+				optind -= nonopt_end - nonopt_start;
+			}
+			nonopt_start = nonopt_end = -1;
+			return -1;
+		}
+		if ((has_equal = strchr(current_argv, '=')) != NULL) {
+			/* argument found (--option=arg) */
+			current_argv_len = has_equal - current_argv;
+			has_equal++;
+		} else
+			current_argv_len = strlen(current_argv);
+	    
+		for (i = 0; long_options[i].name; i++) {
+			/* find matching long option */
+			if (strncmp(current_argv, long_options[i].name,
+			    current_argv_len))
+				continue;
+
+			if (strlen(long_options[i].name) ==
+			    (unsigned)current_argv_len) {
+				/* exact match */
+				match = i;
+				break;
+			}
+			if (match == -1)		/* partial match */
+				match = i;
+			else {
+				/* ambiguous abbreviation */
+				if (PRINT_ERROR)
+					fprintf(stderr, ambig, (int)current_argv_len,
+					     current_argv);
+				optopt = 0;
+				return BADCH;
+			}
+		}
+		if (match != -1) {			/* option found */
+		        if (long_options[match].has_arg == no_argument
+			    && has_equal) {
+				if (PRINT_ERROR)
+					fprintf(stderr, noarg, (int)current_argv_len,
+					     current_argv);
+				/*
+				 * XXX: GNU sets optopt to val regardless of
+				 * flag
+				 */
+				if (long_options[match].flag == NULL)
+					optopt = long_options[match].val;
+				else
+					optopt = 0;
+				return BADARG;
+			}
+			if (long_options[match].has_arg == required_argument ||
+			    long_options[match].has_arg == optional_argument) {
+				if (has_equal)
+					optarg = has_equal;
+				else if (long_options[match].has_arg ==
+				    required_argument) {
+					/*
+					 * optional argument doesn't use
+					 * next nargv
+					 */
+					optarg = nargv[optind++];
+				}
+			}
+			if ((long_options[match].has_arg == required_argument)
+			    && (optarg == NULL)) {
+				/*
+				 * Missing argument; leading ':'
+				 * indicates no error should be generated
+				 */
+				if (PRINT_ERROR)
+					fprintf(stderr, recargstring, current_argv);
+				/*
+				 * XXX: GNU sets optopt to val regardless
+				 * of flag
+				 */
+				if (long_options[match].flag == NULL)
+					optopt = long_options[match].val;
+				else
+					optopt = 0;
+				--optind;
+				return BADARG;
+			}
+		} else {			/* unknown option */
+			if (PRINT_ERROR)
+				fprintf(stderr, illoptstring, current_argv);
+			optopt = 0;
+			return BADCH;
+		}
+		if (long_options[match].flag) {
+			*long_options[match].flag = long_options[match].val;
+			retval = 0;
+		} else 
+			retval = long_options[match].val;
+		if (idx)
+			*idx = match;
+	}
+	return retval;
+}
+#endif /* !GETOPT_LONG */
diff --git a/sg3_utils/include/Makefile.am b/sg3_utils/include/Makefile.am
new file mode 100644
index 0000000..ba92185
--- /dev/null
+++ b/sg3_utils/include/Makefile.am
@@ -0,0 +1,58 @@
+
+scsiincludedir      = $(includedir)/scsi
+
+scsiinclude_HEADERS = \
+	sg_lib.h \
+	sg_lib_data.h \
+	sg_cmds.h \
+	sg_cmds_basic.h \
+	sg_cmds_extra.h \
+	sg_cmds_mmc.h \
+	sg_pt.h
+
+if OS_LINUX
+scsiinclude_HEADERS += \
+	sg_linux_inc.h \
+	sg_io_linux.h
+	
+noinst_HEADERS = \
+	sg_pt_win32.h
+endif
+
+if OS_WIN32_MINGW
+scsiinclude_HEADERS += sg_pt_win32.h
+	
+noinst_HEADERS = \
+	sg_linux_inc.h \
+	sg_io_linux.h
+endif
+
+if OS_WIN32_CYGWIN
+scsiinclude_HEADERS += sg_pt_win32.h
+	
+noinst_HEADERS = \
+	sg_linux_inc.h \
+	sg_io_linux.h
+endif
+
+if OS_FREEBSD
+noinst_HEADERS = \
+	sg_linux_inc.h \
+	sg_io_linux.h \
+	sg_pt_win32.h
+endif
+
+if OS_SOLARIS
+noinst_HEADERS = \
+	sg_linux_inc.h \
+	sg_io_linux.h \
+	sg_pt_win32.h
+endif
+
+if OS_OSF
+noinst_HEADERS = \
+	sg_linux_inc.h \
+	sg_io_linux.h \
+	sg_pt_win32.h
+endif
+
diff --git a/sg3_utils/include/Makefile.in b/sg3_utils/include/Makefile.in
new file mode 100644
index 0000000..5f96e7f
--- /dev/null
+++ b/sg3_utils/include/Makefile.in
@@ -0,0 +1,596 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@OS_LINUX_TRUE@am__append_1 = \
+@OS_LINUX_TRUE@	sg_linux_inc.h \
+@OS_LINUX_TRUE@	sg_io_linux.h
+
+@OS_WIN32_MINGW_TRUE@am__append_2 = sg_pt_win32.h
+@OS_WIN32_CYGWIN_TRUE@am__append_3 = sg_pt_win32.h
+subdir = include
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__noinst_HEADERS_DIST) \
+	$(am__scsiinclude_HEADERS_DIST) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__noinst_HEADERS_DIST = sg_linux_inc.h sg_io_linux.h sg_pt_win32.h
+am__scsiinclude_HEADERS_DIST = sg_lib.h sg_lib_data.h sg_cmds.h \
+	sg_cmds_basic.h sg_cmds_extra.h sg_cmds_mmc.h sg_pt.h \
+	sg_linux_inc.h sg_io_linux.h sg_pr2serr.h sg_pt_win32.h \
+	sg_unaligned.h
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(scsiincludedir)"
+HEADERS = $(noinst_HEADERS) $(scsiinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETOPT_O_FILES = @GETOPT_O_FILES@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+scsiincludedir = $(includedir)/scsi
+scsiinclude_HEADERS = sg_lib.h sg_lib_data.h sg_cmds.h sg_cmds_basic.h \
+	sg_cmds_extra.h sg_cmds_mmc.h sg_pt.h $(am__append_1) \
+	$(am__append_2) $(am__append_3)
+@OS_FREEBSD_TRUE@noinst_HEADERS = \
+@OS_FREEBSD_TRUE@	sg_linux_inc.h \
+@OS_FREEBSD_TRUE@	sg_io_linux.h \
+@OS_FREEBSD_TRUE@	sg_pt_win32.h
+
+@OS_LINUX_TRUE@noinst_HEADERS = \
+@OS_LINUX_TRUE@	sg_pt_win32.h
+
+@OS_OSF_TRUE@noinst_HEADERS = \
+@OS_OSF_TRUE@	sg_linux_inc.h \
+@OS_OSF_TRUE@	sg_io_linux.h \
+@OS_OSF_TRUE@	sg_pt_win32.h
+
+@OS_SOLARIS_TRUE@noinst_HEADERS = \
+@OS_SOLARIS_TRUE@	sg_linux_inc.h \
+@OS_SOLARIS_TRUE@	sg_io_linux.h \
+@OS_SOLARIS_TRUE@	sg_pt_win32.h
+
+@OS_WIN32_CYGWIN_TRUE@noinst_HEADERS = \
+@OS_WIN32_CYGWIN_TRUE@	sg_linux_inc.h \
+@OS_WIN32_CYGWIN_TRUE@	sg_io_linux.h
+
+@OS_WIN32_MINGW_TRUE@noinst_HEADERS = \
+@OS_WIN32_MINGW_TRUE@	sg_linux_inc.h \
+@OS_WIN32_MINGW_TRUE@	sg_io_linux.h
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu include/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+install-scsiincludeHEADERS: $(scsiinclude_HEADERS)
+	@$(NORMAL_INSTALL)
+	@list='$(scsiinclude_HEADERS)'; test -n "$(scsiincludedir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(scsiincludedir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(scsiincludedir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(scsiincludedir)'"; \
+	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(scsiincludedir)" || exit $$?; \
+	done
+
+uninstall-scsiincludeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(scsiinclude_HEADERS)'; test -n "$(scsiincludedir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	dir='$(DESTDIR)$(scsiincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS)
+installdirs:
+	for dir in "$(DESTDIR)$(scsiincludedir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-scsiincludeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-scsiincludeHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool cscopelist-am ctags ctags-am distclean \
+	distclean-generic distclean-libtool distclean-tags distdir dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-info install-info-am install-man install-pdf \
+	install-pdf-am install-ps install-ps-am \
+	install-scsiincludeHEADERS install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+	uninstall-am uninstall-scsiincludeHEADERS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/sg3_utils/include/sg_cmds.h b/sg3_utils/include/sg_cmds.h
new file mode 100644
index 0000000..690f53a
--- /dev/null
+++ b/sg3_utils/include/sg_cmds.h
@@ -0,0 +1,21 @@
+#ifndef SG_CMDS_H
+#define SG_CMDS_H
+
+/********************************************************************
+ * This header did contain wrapper declarations for many SCSI commands
+ * up until sg3_utils version 1.22 . In that version, the command
+ * wrappers were broken into two groups, the 'basic' ones found in the
+ * "sg_cmds_basic.h" header and the 'extra' ones found in the
+ * "sg_cmds_extra.h" header. This header now simply includes those two
+ * headers.
+ * In sg3_utils version 1.26 the sg_cmds_mmc.h header was added and
+ * contains some MMC specific commands.
+ * The corresponding function definitions are found in the sg_cmds_basic.c,
+ * sg_cmds_extra.c and sg_cmds_mmc.c files.
+ ********************************************************************/
+
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_cmds_mmc.h"
+
+#endif
diff --git a/sg3_utils/include/sg_cmds_basic.h b/sg3_utils/include/sg_cmds_basic.h
new file mode 100644
index 0000000..39964f5
--- /dev/null
+++ b/sg3_utils/include/sg_cmds_basic.h
@@ -0,0 +1,259 @@
+#ifndef SG_CMDS_BASIC_H
+#define SG_CMDS_BASIC_H
+
+/*
+ * Copyright (c) 2004-2014 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.
+ */
+
+/*
+ * Error, warning and verbose output is sent to the file pointed to by
+ * sg_warnings_strm which is declared in sg_lib.h and can be set with
+ * the sg_set_warnings_strm() function. If not given sg_warnings_strm
+ * defaults to stderr.
+ * If 'noisy' and 'verbose' are both zero then following functions should
+ * not output anything to sg_warnings_strm. If 'noisy' is non-zero and
+ * 'verbose' is zero then Unit Attention, Recovered, Medium and Hardware
+ * errors (sense keys) send output to sg_warnings_strm. Increasing values
+ * of 'verbose' send increasing amounts of (debug) output to
+ * sg_warnings_strm.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Invokes a SCSI INQUIRY command and yields the response
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ABORTED_COMMAND, -1 -> other errors */
+int sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op, void * resp,
+                  int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI LOG SELECT command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Log Select not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_ABORTED_COMMAND, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * -1 -> other failure */
+int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc, int pg_code,
+                     int subpg_code, unsigned char * paramp, int param_len,
+                     int noisy, int verbose);
+
+/* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Log Sense not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
+                    int subpg_code, int paramp, unsigned char * resp,
+                    int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI MODE SELECT (6) command.  Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp,
+                        int param_len, int noisy, int verbose);
+
+/* Invokes a SCSI MODE SELECT (10) command.  Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp,
+                        int param_len, int noisy, int verbose);
+
+/* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code,
+                      int sub_pg_code, void * resp, int mx_resp_len,
+                      int noisy, int verbose);
+
+/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ ->
+ * bad field in cdb, * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code,
+                       int sub_pg_code, void * resp, int mx_resp_len,
+                       int noisy, int verbose);
+
+/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command (SPC-3)
+ * prevent==0 allows removal, prevent==1 prevents removal ...
+ * Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> command not supported
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_prevent_allow(int sg_fd, int prevent, int noisy, int verbose);
+
+/* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION
+ * -> perhaps media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba, void * resp,
+                     int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success,
+ * SG_LIB_CAT_UNIT_ATTENTION -> media changed??, SG_LIB_CAT_INVALID_OP
+ *  -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_readcap_16(int sg_fd, int pmi, uint64_t llba, void * resp,
+                     int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Report Luns not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */
+int sg_ll_report_luns(int sg_fd, int select_report, void * resp,
+                      int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI REQUEST SENSE command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Request Sense not supported??,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_request_sense(int sg_fd, int desc, void * resp, int mx_resp_len,
+                        int noisy, int verbose);
+
+/* Invokes a SCSI START STOP UNIT command (SBC + MMC).
+ * Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Start stop unit not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure
+ * SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and
+ * format_layer_number(mmc) fields. They also overlap on the noflush(sbc)
+ * and fl(mmc) one bit field. This is the cause of the awkardly named
+ * pc_mod__fl_num and noflush__fl arguments to this function.  */
+int sg_ll_start_stop_unit(int sg_fd, int immed, int pc_mod__fl_num,
+                          int power_cond, int noflush__fl, int loej,
+                          int start, int noisy, int verbose);
+
+/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_INVALID_OP -> cdb not supported,
+ * SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
+int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group,
+                        unsigned int lba, unsigned int count, int noisy,
+                        int verbose);
+
+/* Invokes a SCSI TEST UNIT READY command.
+ * 'pack_id' is just for diagnostics, safe to set to 0.
+ * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_ABORTED_COMMAND, -1 -> other failure */
+int sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy, int verbose);
+
+/* Invokes a SCSI TEST UNIT READY command.
+ * 'pack_id' is just for diagnostics, safe to set to 0.
+ * Looks for progress indicator if 'progress' non-NULL;
+ * if found writes value [0..65535] else write -1.
+ * Return of 0 -> success, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_ABORTED_COMMAND, SG_LIB_CAT_NOT_READY ->
+ * device not ready, -1 -> other failure */
+int sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
+                                   int noisy, int verbose);
+
+
+struct sg_simple_inquiry_resp {
+    unsigned char peripheral_qualifier;
+    unsigned char peripheral_type;
+    unsigned char byte_1;       /* was 'rmb' prior to version 1.39 */
+                                /* now rmb == !!(0x80 & byte_1) */
+    unsigned char version;      /* as per recent drafts: whole of byte 2 */
+    unsigned char byte_3;
+    unsigned char byte_5;
+    unsigned char byte_6;
+    unsigned char byte_7;
+    char vendor[9];             /* T10 field is 8 bytes, NUL char appended */
+    char product[17];
+    char revision[5];
+};
+
+/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP -> not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other errors */
+int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
+                      int noisy, int verbose);
+
+/* MODE SENSE commands yield a response that has block descriptors followed
+ * by mode pages. In most cases users are interested in the first mode page.
+ * This function returns the (byte) offset of the start of the first mode
+ * page. Set mode_sense_6 to 1 for MODE SENSE (6) and 0 for MODE SENSE (10).
+ * Returns >= 0 is successful or -1 if failure. If there is a failure
+ * a message is written to err_buff. */
+int sg_mode_page_offset(const unsigned char * resp, int resp_len,
+                        int mode_sense_6, char * err_buff, int err_buff_len);
+
+/* Fetches current, changeable, default and/or saveable modes pages as
+ * indicated by pcontrol_arr for given pg_code and sub_pg_code. If
+ * mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If
+ * flexible set and mode data length seems wrong then try and
+ * fix (compensating hack for bad device or driver). pcontrol_arr
+ * should have 4 elements for output of current, changeable, default
+ * and saved values respectively. Each element should be NULL or
+ * at least mx_mpage_len bytes long.
+ * Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready,
+ * SG_LIB_CAT_MALFORMED -> bad response, -1 -> other failure.
+ * If success_mask pointer is not NULL then first zeros it. Then set bits
+ * 0, 1, 2 and/or 3 if the current, changeable, default and saved values
+ * respectively have been fetched. If error on current page
+ * then stops and returns that error; otherwise continues if an error is
+ * detected but returns the first error encountered.  */
+int sg_get_mode_page_controls(int sg_fd, int mode6, int pg_code,
+                              int sub_pg_code, int dbd, int flexible,
+                              int mx_mpage_len, int * success_mask,
+                              void * pcontrol_arr[], int * reported_len,
+                              int verbose);
+
+/* Returns file descriptor >= 0 if successful. If error in Unix returns
+   negated errno. Implementation calls scsi_pt_open_device(). */
+int sg_cmds_open_device(const char * device_name, int read_only, int verbose);
+
+/* Returns file descriptor >= 0 if successful. If error in Unix returns
+   negated errno. Implementation calls scsi_pt_open_flags(). */
+int sg_cmds_open_flags(const char * device_name, int flags, int verbose);
+
+/* Returns 0 if successful. If error in Unix returns negated errno.
+   Implementation calls scsi_pt_close_device(). */
+int sg_cmds_close_device(int device_fd);
+
+const char * sg_cmds_version();
+
+
+struct sg_pt_base;
+
+/* This is a helper function used by sg_cmds_* implementations after the
+ * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
+ * sense data is found it is decoded and output to sg_warnings_strm (def:
+ * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
+ * sense data (may not be fatal), -1 for failed, 0, or a positive number. If
+ * 'mx_di_len > 0' then asks pass-through for resid and returns
+ * (mx_di_len - resid); otherwise returns 0. So for data-in it should return
+ * the actual number of bytes received. For data-out (to device) or no data
+ * call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
+ * output via 'o_sense_cat' pointer (if not NULL). Note that several sense
+ * categories also have data in bytes received; -2 is still returned. */
+int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
+                         int pt_res, int mx_di_len,
+                         const unsigned char * sense_b, int noisy,
+                         int verbose, int * o_sense_cat);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sg3_utils/include/sg_cmds_extra.h b/sg3_utils/include/sg_cmds_extra.h
new file mode 100644
index 0000000..534fe38
--- /dev/null
+++ b/sg3_utils/include/sg_cmds_extra.h
@@ -0,0 +1,314 @@
+#ifndef SG_CMDS_EXTRA_H
+#define SG_CMDS_EXTRA_H
+
+/*
+ * Copyright (c) 2004-2016 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.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Note: all functions that have an 'int timeout_secs' argument will use
+ * that value if it is > 0. Otherwise they will set an internal default
+ * which is currently 60 seconds. This timeout is typically applied in the
+ * SCSI stack above the initiator. If it goes off then the SCSI command is
+ * aborted and there can be other unwelcome side effects. Note that some
+ * commands (e.g. FORMAT UNIT and the Third Party copy commands) can take
+ * a lot longer than the default timeout. */
+
+
+/* Invokes a ATA PASS-THROUGH (12 or 16) SCSI command (SAT). If cdb_len is
+ * 12 then a ATA PASS-THROUGH (12) command is called. If cdb_len is 16 then
+ * a ATA PASS-THROUGH (16) command is called. If cdb_len is any other value
+ * -1 is returned. After copying from cdbp to an internal buffer, the first
+ * byte (i.e. offset 0) is set to 0xa1 if cdb_len is 12; or is set to 0x85
+ * if cdb_len is 16. The last byte (offset 11 or offset 15) is set to 0x0 in
+ * the internal buffer. For data in or out transfers set dinp or doutp, and
+ * dlen to the number of bytes to transfer. If dlen is zero then no data
+ * transfer is assumed. If sense buffer obtained then it is written to
+ * sensep, else sensep[0] is set to 0x0. If ATA return descriptor is obtained
+ * then written to ata_return_dp, else ata_return_dp[0] is set to 0x0. Either
+ * sensep or ata_return_dp (or both) may be NULL pointers. Returns SCSI
+ * status value (>= 0) or -1 if other error. Users are expected to check the
+ * sense buffer themselves. If available the data in resid is written to
+ * residp. Note in SAT-2 and later, fixed format sense data may be placed in
+ * *sensep in which case sensep[0]==0x70 .
+ */
+int sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len,
+                 int timeout_secs,  void * dinp, void * doutp, int dlen,
+                 unsigned char * sensep, int max_sense_len,
+                 unsigned char * ata_return_dp, int max_ata_return_len,
+                 int * residp, int verbose);
+
+/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Format unit not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_format_unit(int sg_fd, int fmtpinfo, int longlist, int fmtdata,
+                      int cmplist, int dlist_format, int timeout_secs,
+                      void * paramp, int param_len, int noisy, int verbose);
+int sg_ll_format_unit2(int sg_fd, int fmtpinfo, int longlist, int fmtdata,
+                       int cmplist, int dlist_format, int ffmt,
+                       int timeout_secs, void * paramp, int param_len,
+                       int noisy, int verbose);
+
+/* Invokes a SCSI GET LBA STATUS command (SBC). Returns 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> GET LBA STATUS not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
+int sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp,
+                         int alloc_len, int noisy, int verbose);
+
+/* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0
+ * when successful, SG_LIB_CAT_INVALID_OP if command not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
+int sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp,
+                                int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0
+ * when successful, SG_LIB_CAT_INVALID_OP if command not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
+int sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope,
+                                 unsigned int rq_type, void * paramp,
+                                 int param_len, int noisy, int verbose);
+
+/* Invokes a SCSI READ BLOCK LIMITS command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> READ BLOCK LIMITS not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_NOT_READY (shouldn't happen), -1 -> other failure */
+int sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len,
+                            int noisy, int verbose);
+
+/* Invokes a SCSI READ BUFFER command (SPC). Return of 0 ->
+ * success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
+                      void * resp, int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 ->
+ * success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist,
+                        int dl_format, void * resp, int mx_resp_len,
+                        int noisy, int verbose);
+
+/* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> READ LONG(10) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
+ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_read_long10(int sg_fd, int pblock, int correct, unsigned int lba,
+                      void * resp, int xfer_len, int * offsetp, int noisy,
+                      int verbose);
+
+/* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> READ LONG(16) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
+ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ *  -1 -> other failure */
+int sg_ll_read_long16(int sg_fd, int pblock, int correct, uint64_t llba,
+                      void * resp, int xfer_len, int * offsetp, int noisy,
+                      int verbose);
+
+/* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Read media serial number not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len,
+                                int noisy, int verbose);
+
+/* Invokes a SCSI REASSIGN BLOCKS command.  Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_NOT_READY -> device not ready, -1 -> other failure */
+int sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist, void * paramp,
+                          int param_len, int noisy, int verbose);
+
+/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Receive diagnostic results not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp,
+                       int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI REPORT IDENTIFYING INFORMATION command. This command was
+ * called REPORT DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Report identifying information not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len,
+                         int noisy, int verbose);
+
+/* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
+int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len,
+                             int noisy, int verbose);
+int sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len,
+                              int extended, int noisy, int verbose);
+
+/* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
+int sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, int noisy,
+                          int verbose);
+
+/* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Report Referrals not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
+int sg_ll_report_referrals(int sg_fd, uint64_t start_llba, int one_seg,
+                           void * resp, int mx_resp_len, int noisy,
+                           int verbose);
+
+/* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can
+ * take a long time, if so set long_duration flag in which case the timout
+ * is set to 7200 seconds; if the value of long_duration is > 7200 then that
+ * value is taken as the timeout value in seconds. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
+                    int devofl_bit, int unitofl_bit, int long_duration,
+                    void * paramp, int param_len, int noisy, int verbose);
+
+/* Invokes a SCSI SET IDENTIFYING INFORMATION command. This command was
+ * called SET DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Set identifying information not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len,
+                      int noisy, int verbose);
+
+/* Invokes a SCSI UNMAP (SBC-3) command. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> command not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_UNIT_ATTENTION, -1 -> other failure */
+int sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp,
+                int param_len, int noisy, int verbose);
+/* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field
+ * (sbc3r22). Otherwise same as sg_ll_unmap() . */
+int sg_ll_unmap_v2(int sg_fd, int anchor, int group_num, int timeout_secs,
+                   void * paramp, int param_len, int noisy, int verbose);
+
+/* Invokes a SCSI VERIFY (10) command (SBC and MMC).
+ * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
+ * Returns of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Verify(10) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
+ * SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_MISCOMPARE, -1 -> other failure */
+int sg_ll_verify10(int sg_fd, int vrprotect, int dpo, int bytechk,
+                   unsigned int lba, int veri_len, void * data_out,
+                   int data_out_len, unsigned int * infop, int noisy,
+                   int verbose);
+
+/* Invokes a SCSI VERIFY (16) command (SBC).
+ * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
+ * Returns of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Verify(16) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_MEDIUM_HARD -> medium or hardware error, no valid info,
+ * SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> as previous, with valid info,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_MISCOMPARE, -1 -> other failure */
+int sg_ll_verify16(int sg_fd, int vrprotect, int dpo, int bytechk,
+                   uint64_t llba, int veri_len, int group_num,
+                   void * data_out, int data_out_len, uint64_t * infop,
+                   int noisy, int verbose);
+
+/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 ->
+ * success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
+                       void * paramp, int param_len, int noisy, int verbose);
+/* Need a sg_ll_write_buffer_v2() function because SPC-4 rev32 has added
+ * a "mode specific" field. Wait for next rev change of this library */
+
+/* Invokes a SCSI WRITE LONG (10) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> WRITE LONG(10) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
+ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_write_long10(int sg_fd, int cor_dis, int wr_uncor, int pblock,
+                       unsigned int lba, void * data_out, int xfer_len,
+                       int * offsetp, int noisy, int verbose);
+
+/* Invokes a SCSI WRITE LONG (16) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> WRITE LONG(16) not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb,
+ * SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO -> bad field in cdb, with info
+ * field written to 'offsetp', SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_write_long16(int sg_fd, int cor_dis, int wr_uncor, int pblock,
+                       uint64_t llba, void * data_out, int xfer_len,
+                       int * offsetp, int noisy, int verbose);
+
+/* Invokes a SPC-3 SCSI RECEIVE COPY RESULTS command. In SPC-4 this function
+ * supports all service action variants of the THIRD-PARTY COPY IN opcode.
+ * SG_LIB_CAT_INVALID_OP -> Receive copy results not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
+                               int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI EXTENDED COPY(LID1) command. For EXTENDED COPY(LID4)
+ * including POPULATE TOKEN and WRITE USING TOKEN use
+ * sg_ll_3party_copy_out().  Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Extended copy not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, int noisy,
+                        int verbose);
+
+/* Handles various service actions associated with opcode 0x83 which is
+ * called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID4),
+ * POPULATE TOKEN and WRITE USING TOKEN commands. Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> opcode 0x83 not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id,
+                          int group_num, int timeout_secs, void * paramp,
+                          int param_len, int noisy, int verbose);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sg3_utils/include/sg_cmds_mmc.h b/sg3_utils/include/sg_cmds_mmc.h
new file mode 100644
index 0000000..e0b76a7
--- /dev/null
+++ b/sg3_utils/include/sg_cmds_mmc.h
@@ -0,0 +1,52 @@
+#ifndef SG_CMDS_MMC_H
+#define SG_CMDS_MMC_H
+
+/*
+ * Copyright (c) 2008-2013 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.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Invokes a SCSI GET CONFIGURATION command (MMC-3...6).
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
+ * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
+int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
+                     int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
+ * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
+int sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
+                          int max_num_desc, int type, void * resp,
+                          int mx_resp_len, int noisy, int verbose);
+
+/* Invokes a SCSI SET CD SPEED command (MMC).
+ * Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
+                       int drv_write_speed, int noisy, int verbose);
+
+/* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
+ * -1 -> other failure */
+int sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
+                        int noisy, int verbose);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sg3_utils/include/sg_io_linux.h b/sg3_utils/include/sg_io_linux.h
new file mode 100644
index 0000000..1b8b974
--- /dev/null
+++ b/sg3_utils/include/sg_io_linux.h
@@ -0,0 +1,185 @@
+#ifndef SG_IO_LINUX_H
+#define SG_IO_LINUX_H
+
+/*
+ * Copyright (c) 2004-2015 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.
+ */
+
+/*
+ * Version 1.04 [20151217]
+ */
+
+/*
+ * This header file contains linux specific information related to the SCSI
+ * command pass through in the SCSI generic (sg) driver and the linux
+ * block layer.
+ */
+
+#include "sg_lib.h"
+#include "sg_linux_inc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following are 'host_status' codes */
+#ifndef DID_OK
+#define DID_OK 0x00
+#endif
+#ifndef DID_NO_CONNECT
+#define DID_NO_CONNECT 0x01     /* Unable to connect before timeout */
+#define DID_BUS_BUSY 0x02       /* Bus remain busy until timeout */
+#define DID_TIME_OUT 0x03       /* Timed out for some other reason */
+#define DID_BAD_TARGET 0x04     /* Bad target (id?) */
+#define DID_ABORT 0x05          /* Told to abort for some other reason */
+#define DID_PARITY 0x06         /* Parity error (on SCSI bus) */
+#define DID_ERROR 0x07          /* Internal error */
+#define DID_RESET 0x08          /* Reset by somebody */
+#define DID_BAD_INTR 0x09       /* Received an unexpected interrupt */
+#define DID_PASSTHROUGH 0x0a    /* Force command past mid-level */
+#define DID_SOFT_ERROR 0x0b     /* The low-level driver wants a retry */
+#endif
+#ifndef DID_IMM_RETRY
+#define DID_IMM_RETRY 0x0c      /* Retry without decrementing retry count  */
+#endif
+#ifndef DID_REQUEUE
+#define DID_REQUEUE 0x0d        /* Requeue command (no immediate retry) also
+                                 * without decrementing the retry count    */
+#endif
+#ifndef DID_TRANSPORT_DISRUPTED
+#define DID_TRANSPORT_DISRUPTED 0xe
+#endif
+#ifndef DID_TRANSPORT_FAILFAST
+#define DID_TRANSPORT_FAILFAST 0xf
+#endif
+#ifndef DID_TARGET_FAILURE
+#define DID_TARGET_FAILURE 0x10
+#endif
+#ifndef DID_NEXUS_FAILURE
+#define DID_NEXUS_FAILURE 0x11
+#endif
+
+/* These defines are to isolate applications from kernel define changes */
+#define SG_LIB_DID_OK           DID_OK
+#define SG_LIB_DID_NO_CONNECT   DID_NO_CONNECT
+#define SG_LIB_DID_BUS_BUSY     DID_BUS_BUSY
+#define SG_LIB_DID_TIME_OUT     DID_TIME_OUT
+#define SG_LIB_DID_BAD_TARGET   DID_BAD_TARGET
+#define SG_LIB_DID_ABORT        DID_ABORT
+#define SG_LIB_DID_PARITY       DID_PARITY
+#define SG_LIB_DID_ERROR        DID_ERROR
+#define SG_LIB_DID_RESET        DID_RESET
+#define SG_LIB_DID_BAD_INTR     DID_BAD_INTR
+#define SG_LIB_DID_PASSTHROUGH  DID_PASSTHROUGH
+#define SG_LIB_DID_SOFT_ERROR   DID_SOFT_ERROR
+#define SG_LIB_DID_IMM_RETRY    DID_IMM_RETRY
+#define SG_LIB_DID_REQUEUE      DID_REQUEUE
+#define SG_LIB_TRANSPORT_DISRUPTED      DID_TRANSPORT_DISRUPTED
+#define SG_LIB_DID_TRANSPORT_FAILFAST   DID_TRANSPORT_FAILFAST
+#define SG_LIB_DID_TARGET_FAILURE       DID_TARGET_FAILURE
+#define SG_LIB_DID_NEXUS_FAILURE        DID_NEXUS_FAILURE
+
+/* The following are 'driver_status' codes */
+#ifndef DRIVER_OK
+#define DRIVER_OK 0x00
+#endif
+#ifndef DRIVER_BUSY
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+#define DRIVER_SENSE 0x08       /* Sense_buffer has been set */
+
+/* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept
+ * to stop compilation breakages.
+ * Following "suggests" are "or-ed" with one of previous 8 entries */
+#define SUGGEST_RETRY 0x10
+#define SUGGEST_ABORT 0x20
+#define SUGGEST_REMAP 0x30
+#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
+#define SUGGEST_IS_OK 0xff
+#endif
+
+#ifndef DRIVER_MASK
+#define DRIVER_MASK 0x0f
+#endif
+#ifndef SUGGEST_MASK
+#define SUGGEST_MASK 0xf0
+#endif
+
+/* These defines are to isolate applications from kernel define changes */
+#define SG_LIB_DRIVER_OK        DRIVER_OK
+#define SG_LIB_DRIVER_BUSY      DRIVER_BUSY
+#define SG_LIB_DRIVER_SOFT      DRIVER_SOFT
+#define SG_LIB_DRIVER_MEDIA     DRIVER_MEDIA
+#define SG_LIB_DRIVER_ERROR     DRIVER_ERROR
+#define SG_LIB_DRIVER_INVALID   DRIVER_INVALID
+#define SG_LIB_DRIVER_TIMEOUT   DRIVER_TIMEOUT
+#define SG_LIB_DRIVER_HARD      DRIVER_HARD
+#define SG_LIB_DRIVER_SENSE     DRIVER_SENSE
+
+
+/* N.B. the SUGGEST_* codes are no longer used in Linux and are only kept
+ * to stop compilation breakages. */
+#define SG_LIB_SUGGEST_RETRY    SUGGEST_RETRY
+#define SG_LIB_SUGGEST_ABORT    SUGGEST_ABORT
+#define SG_LIB_SUGGEST_REMAP    SUGGEST_REMAP
+#define SG_LIB_SUGGEST_DIE      SUGGEST_DIE
+#define SG_LIB_SUGGEST_SENSE    SUGGEST_SENSE
+#define SG_LIB_SUGGEST_IS_OK    SUGGEST_IS_OK
+#define SG_LIB_DRIVER_MASK      DRIVER_MASK
+#define SG_LIB_SUGGEST_MASK     SUGGEST_MASK
+
+void sg_print_masked_status(int masked_status);
+void sg_print_host_status(int host_status);
+void sg_print_driver_status(int driver_status);
+
+/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings
+   else it prints errors/warnings (prefixed by 'leadin') to
+   'sg_warnings_fd' and returns 0. raw_sinfo indicates whether the
+   raw sense buffer (in ASCII hex) should be printed. */
+int sg_chk_n_print(const char * leadin, int masked_status, int host_status,
+                   int driver_status, const unsigned char * sense_buffer,
+                   int sb_len, int raw_sinfo);
+
+/* The following function declaration is for the sg version 3 driver. */
+struct sg_io_hdr;
+/* sg_chk_n_print3() returns 1 quietly if there are no errors/warnings;
+   else it prints errors/warnings (prefixed by 'leadin') to
+   'sg_warnings_fd' and returns 0. */
+int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp,
+                    int raw_sinfo);
+
+/* Calls sg_scsi_normalize_sense() after obtaining the sense buffer and
+   its length from the struct sg_io_hdr pointer. If these cannot be
+   obtained, 0 is returned. */
+int sg_normalize_sense(const struct sg_io_hdr * hp,
+                       struct sg_scsi_sense_hdr * sshp);
+
+int sg_err_category(int masked_status, int host_status, int driver_status,
+                    const unsigned char * sense_buffer, int sb_len);
+
+int sg_err_category_new(int scsi_status, int host_status, int driver_status,
+                        const unsigned char * sense_buffer, int sb_len);
+
+/* The following function declaration is for the sg version 3 driver. */
+int sg_err_category3(struct sg_io_hdr * hp);
+
+
+/* Note about SCSI status codes found in older versions of Linux.
+   Linux has traditionally used a 1 bit right shifted and masked
+   version of SCSI standard status codes. Now CHECK_CONDITION
+   and friends (in <scsi/scsi.h>) are deprecated. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sg3_utils/include/sg_lib.h b/sg3_utils/include/sg_lib.h
new file mode 100644
index 0000000..b3e741b
--- /dev/null
+++ b/sg3_utils/include/sg_lib.h
@@ -0,0 +1,462 @@
+#ifndef SG_LIB_H
+#define SG_LIB_H
+
+/*
+ * Copyright (c) 2004-2016 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.
+ */
+
+/*
+ *
+ * On 5th October 2004 a FreeBSD license was added to this file.
+ * The intention is to keep this file and the related sg_lib.c file
+ * as open source and encourage their unencumbered use.
+ *
+ * Current version number is in the sg_lib.c file and can be accessed
+ * with the sg_lib_version() function.
+ */
+
+
+/*
+ * This header file contains defines and function declarations that may
+ * be useful to applications that communicate with devices that use a
+ * SCSI command set. These command sets have names like SPC-4, SBC-3,
+ * SSC-3, SES-2 and draft standards defining them can be found at
+ * http://www.t10.org . Virtually all devices in the Linux SCSI subsystem
+ * utilize SCSI command sets. Many devices in other Linux device subsystems
+ * utilize SCSI command sets either natively or via emulation (e.g. a
+ * parallel ATA disk in a USB enclosure).
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* SCSI Peripheral Device Types (PDT) [5 bit field] */
+#define PDT_DISK 0x0    /* direct access block device (disk) */
+#define PDT_TAPE 0x1    /* sequential access device (magnetic tape) */
+#define PDT_PRINTER 0x2 /* printer device (see SSC-1) */
+#define PDT_PROCESSOR 0x3       /* processor device (e.g. SAFTE device) */
+#define PDT_WO 0x4      /* write once device (some optical disks) */
+#define PDT_MMC 0x5     /* CD/DVD/BD (multi-media) */
+#define PDT_SCANNER 0x6 /* obsolete */
+#define PDT_OPTICAL 0x7 /* optical memory device (some optical disks) */
+#define PDT_MCHANGER 0x8        /* media changer device (e.g. tape robot) */
+#define PDT_COMMS 0x9   /* communications device (obsolete) */
+#define PDT_SAC 0xc     /* storage array controller device */
+#define PDT_SES 0xd     /* SCSI Enclosure Services (SES) device */
+#define PDT_RBC 0xe     /* Reduced Block Commands (simplified PDT_DISK) */
+#define PDT_OCRW 0xf    /* optical card read/write device */
+#define PDT_BCC 0x10    /* bridge controller commands */
+#define PDT_OSD 0x11    /* Object Storage Device (OSD) */
+#define PDT_ADC 0x12    /* Automation/drive commands (ADC) */
+#define PDT_SMD 0x13    /* Security Manager Device (SMD) */
+#define PDT_ZBC 0x14    /* Zoned Block Commands (ZBC) */
+#define PDT_WLUN 0x1e   /* Well known logical unit (WLUN) */
+#define PDT_UNKNOWN 0x1f        /* Unknown or no device type */
+
+#ifndef SAM_STAT_GOOD
+/* The SCSI status codes as found in SAM-4 at www.t10.org */
+#define SAM_STAT_GOOD 0x0
+#define SAM_STAT_CHECK_CONDITION 0x2
+#define SAM_STAT_CONDITION_MET 0x4
+#define SAM_STAT_BUSY 0x8
+#define SAM_STAT_INTERMEDIATE 0x10              /* obsolete in SAM-4 */
+#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14  /* obsolete in SAM-4 */
+#define SAM_STAT_RESERVATION_CONFLICT 0x18
+#define SAM_STAT_COMMAND_TERMINATED 0x22        /* obsolete in SAM-3 */
+#define SAM_STAT_TASK_SET_FULL 0x28
+#define SAM_STAT_ACA_ACTIVE 0x30
+#define SAM_STAT_TASK_ABORTED 0x40
+#endif
+
+/* The SCSI sense key codes as found in SPC-4 at www.t10.org */
+#define SPC_SK_NO_SENSE 0x0
+#define SPC_SK_RECOVERED_ERROR 0x1
+#define SPC_SK_NOT_READY 0x2
+#define SPC_SK_MEDIUM_ERROR 0x3
+#define SPC_SK_HARDWARE_ERROR 0x4
+#define SPC_SK_ILLEGAL_REQUEST 0x5
+#define SPC_SK_UNIT_ATTENTION 0x6
+#define SPC_SK_DATA_PROTECT 0x7
+#define SPC_SK_BLANK_CHECK 0x8
+#define SPC_SK_VENDOR_SPECIFIC 0x9
+#define SPC_SK_COPY_ABORTED 0xa
+#define SPC_SK_ABORTED_COMMAND 0xb
+#define SPC_SK_RESERVED 0xc
+#define SPC_SK_VOLUME_OVERFLOW 0xd
+#define SPC_SK_MISCOMPARE 0xe
+#define SPC_SK_COMPLETED 0xf
+
+/* Transport protocol identifiers or just Protocol identifiers */
+#define TPROTO_FCP 0
+#define TPROTO_SPI 1
+#define TPROTO_SSA 2
+#define TPROTO_1394 3
+#define TPROTO_SRP 4
+#define TPROTO_ISCSI 5
+#define TPROTO_SAS 6
+#define TPROTO_ADT 7
+#define TPROTO_ATA 8
+#define TPROTO_UAS 9            /* USB attached SCSI */
+#define TPROTO_SOP 0xa          /* SCSI over PCIe */
+#define TPROTO_PCIE 0xb         /* includes NVMe */
+#define TPROTO_NONE 0xf
+
+
+/* The format of the version string is like this: "1.87 20130731" */
+const char * sg_lib_version();
+
+/* Returns length of SCSI command given the opcode (first byte).
+ * Yields the wrong answer for variable length commands (opcode=0x7f)
+ * and potentially some vendor specific commands. */
+int sg_get_command_size(unsigned char cdb_byte0);
+
+/* Command name given pointer to the cdb. Certain command names
+ * depend on peripheral type (give 0 if unknown). Places command
+ * name into buff and will write no more than buff_len bytes. */
+void sg_get_command_name(const unsigned char * cdbp, int peri_type,
+                         int buff_len, char * buff);
+
+/* Command name given only the first byte (byte 0) of a cdb and
+ * peripheral type. */
+void sg_get_opcode_name(unsigned char cdb_byte0, int peri_type, int buff_len,
+                        char * buff);
+
+/* Command name given opcode (byte 0), service action and peripheral type.
+ * If no service action give 0, if unknown peripheral type give 0. */
+void sg_get_opcode_sa_name(unsigned char cdb_byte0, int service_action,
+                           int peri_type, int buff_len, char * buff);
+
+/* Fetch scsi status string. */
+void sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff);
+
+/* This is a slightly stretched SCSI sense "descriptor" format header.
+ * The addition is to allow the 0x70 and 0x71 response codes. The idea
+ * is to place the salient data of both "fixed" and "descriptor" sense
+ * format into one structure to ease application processing.
+ * The original sense buffer should be kept around for those cases
+ * in which more information is required (e.g. the LBA of a MEDIUM ERROR). */
+struct sg_scsi_sense_hdr {
+    unsigned char response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
+    unsigned char sense_key;
+    unsigned char asc;
+    unsigned char ascq;
+    unsigned char byte4;
+    unsigned char byte5;
+    unsigned char byte6;
+    unsigned char additional_length;
+};
+
+/* Maps the salient data from a sense buffer which is in either fixed or
+ * descriptor format into a structure mimicking a descriptor format
+ * header (i.e. the first 8 bytes of sense descriptor format).
+ * If zero response code returns 0. Otherwise returns 1 and if 'sshp' is
+ * non-NULL then zero all fields and then set the appropriate fields in
+ * that structure. sshp::additional_length is always 0 for response
+ * codes 0x70 and 0x71 (fixed format). */
+int sg_scsi_normalize_sense(const unsigned char * sensep, int sense_len,
+                            struct sg_scsi_sense_hdr * sshp);
+
+/* Attempt to find the first SCSI sense data descriptor that matches the
+ * given 'desc_type'. If found return pointer to start of sense data
+ * descriptor; otherwise (including fixed format sense data) returns NULL. */
+const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
+                                              int sense_len, int desc_type);
+
+/* Get sense key from sense buffer. If successful returns a sense key value
+ * between 0 and 15. If sense buffer cannot be decode, returns -1 . */
+int sg_get_sense_key(const unsigned char * sensep, int sense_len);
+
+/* Yield string associated with sense_key value. Returns 'buff'. */
+char * sg_get_sense_key_str(int sense_key, int buff_len, char * buff);
+
+/* Yield string associated with ASC/ASCQ values. Returns 'buff'. */
+char * sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff);
+
+/* Returns 1 if valid bit set, 0 if valid bit clear. Irrespective the
+ * information field is written out via 'info_outp' (except when it is
+ * NULL). Handles both fixed and descriptor sense formats. */
+int sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
+                          uint64_t * info_outp);
+
+/* Returns 1 if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
+ * In descriptor format if the stream commands descriptor not found
+ * then returns 0. Writes 1 or 0 corresponding to these bits to the
+ * last three arguments if they are non-NULL. */
+int sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len,
+                                  int * filemark_p, int * eom_p, int * ili_p);
+
+/* Returns 1 if SKSV is set and sense key is NO_SENSE or NOT_READY. Also
+ * returns 1 if progress indication sense data descriptor found. Places
+ * progress field from sense data where progress_outp points. If progress
+ * field is not available returns 0. Handles both fixed and descriptor
+ * sense formats. N.B. App should multiply by 100 and divide by 65536
+ * to get percentage completion from given value. */
+int sg_get_sense_progress_fld(const unsigned char * sensep, int sb_len,
+                              int * progress_outp);
+
+/* Closely related to sg_print_sense(). Puts decoded sense data in 'buff'.
+ * Usually multiline with multiple '\n' including one trailing. If
+ * 'raw_sinfo' set appends sense buffer in hex. 'leadin' is string prepended
+ * to each line written to 'buff', NULL treated as "". Returns the number of
+ * bytes written to 'buff' excluding the trailing '\0'.
+ * N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the first
+ * line output. Also this function returned type void. */
+int sg_get_sense_str(const char * leadin, const unsigned char * sense_buffer,
+                     int sb_len, int raw_sinfo, int buff_len, char * buff);
+
+/* Decode descriptor format sense descriptors (assumes sense buffer is
+ * in descriptor format). 'leadin' is string prepended to each line written
+ * to 'b', NULL treated as "". Returns the number of bytes written to 'b'
+ * excluding the trailing '\0'. */
+int sg_get_sense_descriptors_str(const char * leadin,
+                                 const unsigned char * sense_buffer,
+                                 int sb_len, int blen, char * b);
+
+/* Decodes a designation descriptor (e.g. as found in the Device
+ * Identification VPD page (0x83)) into string 'b' whose maximum length is
+ * blen. 'leadin' is string prepended to each line written to 'b', NULL
+ * treated as "". Returns the number of bytes written to 'b' excluding the
+ * trailing '\0'. */
+int sg_get_designation_descriptor_str(const char * leadin,
+                                      const unsigned char * ddp, int dd_len,
+                                      int print_assoc, int do_long, int blen,
+                                      char * b);
+
+/* Yield string associated with peripheral device type (pdt). Returns
+ * 'buff'. If 'pdt' out of range yields "bad pdt" string. */
+char * sg_get_pdt_str(int pdt, int buff_len, char * buff);
+
+/* Some lesser used PDTs share a lot in common with a more used PDT.
+ * Examples are PDT_ADC decaying to PDT_TAPE and PDT_ZBC to PDT_DISK.
+ * If such a lesser used 'pdt' is given to this function, then it will
+ * return the more used PDT (i.e. "decays to"); otherwise 'pdt' is returned.
+ * Valid for 'pdt' 0 to 31, for other values returns 'pdt'. */
+int sg_lib_pdt_decay(int pdt);
+
+/* Yield string associated with transport protocol identifier (tpi). Returns
+ *    'buff'. If 'tpi' out of range yields "bad tpi" string. */
+char * sg_get_trans_proto_str(int tpi, int buff_len, char * buff);
+
+/* Returns a designator's type string given 'val' (0 to 15 inclusive),
+ * otherwise returns NULL. */
+const char * sg_get_desig_type_str(int val);
+
+/* Returns a designator's code_set string given 'val' (0 to 15 inclusive),
+ * otherwise returns NULL. */
+const char * sg_get_desig_code_set_str(int val);
+
+/* Returns a designator's association string given 'val' (0 to 3 inclusive),
+ * otherwise returns NULL. */
+const char * sg_get_desig_assoc_str(int val);
+
+extern FILE * sg_warnings_strm;
+
+void sg_set_warnings_strm(FILE * warnings_strm);
+
+/* The following "print" functions send ACSII to 'sg_warnings_strm' file
+ * descriptor (default value is stderr). 'leadin' is string prepended to
+ * each line printed out, NULL treated as "". */
+void sg_print_command(const unsigned char * command);
+void sg_print_scsi_status(int scsi_status);
+
+/* 'leadin' is string prepended to each line printed out, NULL treated as
+ * "". N.B. prior to sg3_utils v 1.42 'leadin' was only prepended to the
+ * first line printed. */
+void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
+                    int sb_len, int raw_info);
+
+/* Utilities can use these exit status values for syntax errors and
+ * file (device node) problems (e.g. not found or permissions). */
+#define SG_LIB_SYNTAX_ERROR 1   /* command line syntax problem */
+#define SG_LIB_FILE_ERROR 15    /* device or other file problem */
+
+/* The sg_err_category_sense() function returns one of the following.
+ * These may be used as exit status values (from a process). Notice that
+ * some of the lower values correspond to SCSI sense key values. */
+#define SG_LIB_CAT_CLEAN 0      /* No errors or other information */
+/* Value 1 left unused for utilities to use SG_LIB_SYNTAX_ERROR */
+#define SG_LIB_CAT_NOT_READY 2  /* sense key, unit stopped? */
+                                /*       [sk,asc,ascq: 0x2,*,*] */
+#define SG_LIB_CAT_MEDIUM_HARD 3 /* medium or hardware error, blank check */
+                                /*       [sk,asc,ascq: 0x3/0x4/0x8,*,*] */
+#define SG_LIB_CAT_ILLEGAL_REQ 5 /* Illegal request (other than invalid */
+                                /* opcode):   [sk,asc,ascq: 0x5,*,*] */
+#define SG_LIB_CAT_UNIT_ATTENTION 6 /* sense key, device state changed */
+                                /*       [sk,asc,ascq: 0x6,*,*] */
+        /* was SG_LIB_CAT_MEDIA_CHANGED earlier [sk,asc,ascq: 0x6,0x28,*] */
+#define SG_LIB_CAT_DATA_PROTECT 7 /* sense key, media write protected? */
+                                /*       [sk,asc,ascq: 0x7,*,*] */
+#define SG_LIB_CAT_INVALID_OP 9 /* (Illegal request,) Invalid opcode: */
+                                /*       [sk,asc,ascq: 0x5,0x20,0x0] */
+#define SG_LIB_CAT_COPY_ABORTED 10 /* sense key, some data transferred */
+                                /*       [sk,asc,ascq: 0xa,*,*] */
+#define SG_LIB_CAT_ABORTED_COMMAND 11 /* interpreted from sense buffer */
+                                /*       [sk,asc,ascq: 0xb,! 0x10,*] */
+#define SG_LIB_CAT_MISCOMPARE 14 /* sense key, probably verify */
+                                /*       [sk,asc,ascq: 0xe,*,*] */
+#define SG_LIB_CAT_NO_SENSE 20  /* sense data with key of "no sense" */
+                                /*       [sk,asc,ascq: 0x0,*,*] */
+#define SG_LIB_CAT_RECOVERED 21 /* Successful command after recovered err */
+                                /*       [sk,asc,ascq: 0x1,*,*] */
+#define SG_LIB_CAT_RES_CONFLICT SAM_STAT_RESERVATION_CONFLICT
+                                /* 24: this is a SCSI status, not sense. */
+                                /* It indicates reservation by another */
+                                /* machine blocks this command */
+#define SG_LIB_CAT_CONDITION_MET 25 /* SCSI status, not sense key. */
+                                    /* Only from PRE-FETCH (SBC-4) */
+#define SG_LIB_CAT_BUSY       26 /* SCSI status, not sense. Invites retry */
+#define SG_LIB_CAT_TS_FULL    27 /* SCSI status, not sense. Wait then retry */
+#define SG_LIB_CAT_ACA_ACTIVE 28 /* SCSI status; ACA seldom used */
+#define SG_LIB_CAT_TASK_ABORTED 29 /* SCSI status, this command aborted by? */
+#define SG_LIB_CAT_PROTECTION 40 /* subset of aborted command (for PI, DIF) */
+                                /*       [sk,asc,ascq: 0xb,0x10,*] */
+#define SG_LIB_CAT_MALFORMED 97 /* Response to SCSI command malformed */
+#define SG_LIB_CAT_SENSE 98     /* Something else is in the sense buffer */
+#define SG_LIB_CAT_OTHER 99     /* Some other error/warning has occurred */
+                                /* (e.g. a transport or driver error) */
+
+/* Returns a SG_LIB_CAT_* value. If cannot decode sense_buffer or a less
+ * common sense key then return SG_LIB_CAT_SENSE .*/
+int sg_err_category_sense(const unsigned char * sense_buffer, int sb_len);
+
+/* Here are some additional sense data categories that are not returned
+ * by sg_err_category_sense() but are returned by some related functions. */
+#define SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO 17 /* Illegal request (other than */
+                                /* invalid opcode) plus 'info' field: */
+                                /*  [sk,asc,ascq: 0x5,*,*] */
+#define SG_LIB_CAT_MEDIUM_HARD_WITH_INFO 18 /* medium or hardware error */
+                                /* sense key plus 'info' field: */
+                                /*       [sk,asc,ascq: 0x3/0x4,*,*] */
+#define SG_LIB_CAT_PROTECTION_WITH_INFO 41 /* aborted command sense key, */
+                                /* protection plus 'info' field: */
+                                /*  [sk,asc,ascq: 0xb,0x10,*] */
+#define SG_LIB_CAT_TIMEOUT 33
+
+/* Yield string associated with sense category. Returns 'buff' (or pointer
+ * to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then
+ * yield "Sense category: <sense_cat>" string. */
+const char * sg_get_category_sense_str(int sense_cat, int buff_len,
+                                       char * buff, int verbose);
+
+
+/* Iterates to next designation descriptor in the device identification
+ * VPD page. The 'initial_desig_desc' should point to start of first
+ * descriptor with 'page_len' being the number of valid bytes in that
+ * and following descriptors. To start, 'off' should point to a negative
+ * value, thereafter it should point to the value yielded by the previous
+ * call. If 0 returned then 'initial_desig_desc + *off' should be a valid
+ * descriptor; returns -1 if normal end condition and -2 for an abnormal
+ * termination. Matches association, designator_type and/or code_set when
+ * any of those values are greater than or equal to zero. */
+int sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
+                       int * off, int m_assoc, int m_desig_type,
+                       int m_code_set);
+
+
+/* <<< General purpose (i.e. not SCSI specific) utility functions >>> */
+
+/* Always returns valid string even if errnum is wild (or library problem).
+ * If errnum is negative, flip its sign. */
+char * safe_strerror(int errnum);
+
+
+/* Print (to stdout) 'str' of bytes in hex, 16 bytes per line optionally
+ * followed at the right hand side of the line with an ASCII interpretation.
+ * Each line is prefixed with an address, starting at 0 for str[0]..str[15].
+ * All output numbers are in hex. 'no_ascii' allows for 3 output types:
+ *     > 0     each line has address then up to 16 ASCII-hex bytes
+ *     = 0     in addition, the bytes are listed in ASCII to the right
+ *     < 0     only the ASCII-hex bytes are listed (i.e. without address)
+*/
+void dStrHex(const char* str, int len, int no_ascii);
+
+/* Print (to sg_warnings_strm (stderr)) 'str' of bytes in hex, 16 bytes per
+ * line optionally followed at right by its ASCII interpretation. Same
+ * logic as dStrHex() with different output stream (i.e. stderr). */
+void dStrHexErr(const char* str, int len, int no_ascii);
+
+/* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space
+ * separated) to 'b' not to exceed 'b_len' characters. Each line
+ * starts with 'leadin' (NULL for no leadin) and there are 16 bytes
+ * per line with an extra space between the 8th and 9th bytes. 'format'
+ * is unused, set to 0 . Returns number of bytes written to 'b' excluding
+ * the trailing '\0'.*/
+int dStrHexStr(const char* str, int len, const char * leadin, int format,
+               int b_len, char * b);
+
+/* Returns 1 when executed on big endian machine; else returns 0.
+ * Useful for displaying ATA identify words (which need swapping on a
+ * big endian machine).
+*/
+int sg_is_big_endian();
+
+/* Extract character sequence from ATA words as in the model string
+ * in a IDENTIFY DEVICE response. Returns number of characters
+ * written to 'ochars' before 0 character is found or 'num' words
+ * are processed. */
+int sg_ata_get_chars(const unsigned short * word_arr, int start_word,
+                     int num_words, int is_big_endian, char * ochars);
+
+/* Print (to stdout) 16 bit 'words' in hex, 8 words per line optionally
+ * followed at the right hand side of the line with an ASCII interpretation
+ * (pairs of ASCII characters in big endian order (upper first)).
+ * Each line is prefixed with an address, starting at 0.
+ * All output numbers are in hex. 'no_ascii' allows for 3 output types:
+ *     > 0     each line has address then up to 8 ASCII-hex words
+ *     = 0     in addition, the words are listed in ASCII pairs to the right
+ *     = -1    only the ASCII-hex words are listed (i.e. without address)
+ *     = -2    only the ASCII-hex words, formatted for "hdparm --Istdin"
+ *     < -2    same as -1
+ * If 'swapb' non-zero then bytes in each word swapped. Needs to be set
+ * for ATA IDENTIFY DEVICE response on big-endian machines.
+*/
+void dWordHex(const unsigned short* words, int num, int no_ascii, int swapb);
+
+/* If the number in 'buf' can not be decoded or the multiplier is unknown
+ * then -1 is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H')
+ * suffix. Otherwise a decimal multiplier suffix may be given. Recognised
+ * multipliers: c C  *1;  w W  *2; b  B *512;  k K KiB  *1,024;
+ * KB  *1,000;  m M MiB  *1,048,576; MB *1,000,000; g G GiB *1,073,741,824;
+ * GB *1,000,000,000 and <n>x<m> which multiplies <n> by <m> . Ignore leading
+ * spaces and tabs; accept comma, space, tab and hash as terminator. */
+int sg_get_num(const char * buf);
+
+/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
+ * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
+ * assumed. Does not accept multipliers. Accept a comma (","), a whitespace
+ * or newline as terminator.  */
+int sg_get_num_nomult(const char * buf);
+
+/* If the number in 'buf' can not be decoded or the multiplier is unknown
+ * then -1LL is returned. Accepts a hex prefix (0x or 0X) or a 'h' (or 'H')
+ * suffix. Otherwise a decimal multiplier suffix may be given. In addition
+ * to supporting the multipliers of sg_get_num(), this function supports:
+ * t T TiB  *(2**40); TB *(10**12); p P PiB  *(2**50); PB  *(10**15) .
+ * Ignore leading spaces and tabs; accept comma, space, tab and hash as
+ * terminator. */
+int64_t sg_get_llnum(const char * buf);
+
+
+/* <<< Architectural support functions [is there a better place?] >>> */
+
+/* Non Unix OSes distinguish between text and binary files.
+ * Set text mode on fd. Does nothing in Unix. Returns negative number on
+ * failure. */
+int sg_set_text_mode(int fd);
+
+/* Set binary mode on fd. Does nothing in Unix. Returns negative number on
+ * failure. */
+int sg_set_binary_mode(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sg3_utils/include/sg_lib_data.h b/sg3_utils/include/sg_lib_data.h
new file mode 100644
index 0000000..3b2f86f
--- /dev/null
+++ b/sg3_utils/include/sg_lib_data.h
@@ -0,0 +1,104 @@
+#ifndef SG_LIB_DATA_H
+#define SG_LIB_DATA_H
+
+/*
+ * Copyright (c) 2007-2016 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.
+ */
+
+/*
+ * This header file contains some structure declarations and array name
+ * declarations which are defined in the sg_lib_data.c .
+ * Typically this header does not need to be exposed to users of the
+ * sg_lib interface declared in sg_libs.h .
+ */
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Operation codes with associated service actions that change or qualify
+ * the command name */
+#define SG_EXTENDED_COPY 0x83 /* since spc4r34 became next entry */
+#define SG_3PARTY_COPY_OUT 0x83 /* new in spc4r34: Third party copy out */
+#define SG_RECEIVE_COPY 0x84  /* since spc4r34 became next entry */
+#define SG_3PARTY_COPY_IN 0x84 /* new in spc4r34: Third party copy in */
+#define SG_MAINTENANCE_IN 0xa3
+#define SG_MAINTENANCE_OUT 0xa4
+#define SG_PERSISTENT_RESERVE_IN 0x5e
+#define SG_PERSISTENT_RESERVE_OUT 0x5f
+#define SG_READ_ATTRIBUTE 0x8c
+#define SG_READ_BUFFER 0x3c
+#define SG_READ_POSITION 0x34   /* SSC command with service actions */
+#define SG_SANITIZE 0x48
+#define SG_SERVICE_ACTION_BIDI 0x9d
+#define SG_SERVICE_ACTION_IN_12 0xab
+#define SG_SERVICE_ACTION_IN_16 0x9e
+#define SG_SERVICE_ACTION_OUT_12 0xa9
+#define SG_SERVICE_ACTION_OUT_16 0x9f
+#define SG_VARIABLE_LENGTH_CMD 0x7f
+#define SG_WRITE_BUFFER 0x3b
+#define SG_ZONING_OUT 0x94
+#define SG_ZONING_IN 0x95
+
+
+
+struct sg_lib_value_name_t {
+    int value;
+    int peri_dev_type; /* 0 -> SPC and/or PDT_DISK, >0 -> PDT */
+    const char * name;
+};
+
+struct sg_lib_asc_ascq_t {
+    unsigned char asc;          /* additional sense code */
+    unsigned char ascq;         /* additional sense code qualifier */
+    const char * text;
+};
+
+struct sg_lib_asc_ascq_range_t {
+    unsigned char asc;  /* additional sense code (ASC) */
+    unsigned char ascq_min;     /* ASCQ minimum in range */
+    unsigned char ascq_max;     /* ASCQ maximum in range */
+    const char * text;
+};
+
+
+extern const char * sg_lib_version_str;
+
+extern struct sg_lib_value_name_t sg_lib_normal_opcodes[];
+extern struct sg_lib_value_name_t sg_lib_read_buff_arr[];
+extern struct sg_lib_value_name_t sg_lib_write_buff_arr[];
+extern struct sg_lib_value_name_t sg_lib_maint_in_arr[];
+extern struct sg_lib_value_name_t sg_lib_maint_out_arr[];
+extern struct sg_lib_value_name_t sg_lib_pr_in_arr[];
+extern struct sg_lib_value_name_t sg_lib_pr_out_arr[];
+extern struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[];
+extern struct sg_lib_value_name_t sg_lib_serv_in12_arr[];
+extern struct sg_lib_value_name_t sg_lib_serv_out12_arr[];
+extern struct sg_lib_value_name_t sg_lib_serv_in16_arr[];
+extern struct sg_lib_value_name_t sg_lib_serv_out16_arr[];
+extern struct sg_lib_value_name_t sg_lib_serv_bidi_arr[];
+extern struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[];
+extern struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[];
+extern struct sg_lib_value_name_t sg_lib_variable_length_arr[];
+extern struct sg_lib_value_name_t sg_lib_zoning_out_arr[];
+extern struct sg_lib_value_name_t sg_lib_zoning_in_arr[];
+extern struct sg_lib_value_name_t sg_lib_read_attr_arr[];
+extern struct sg_lib_value_name_t sg_lib_read_pos_arr[];
+extern struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[];
+extern struct sg_lib_asc_ascq_t sg_lib_asc_ascq[];
+extern const char * sg_lib_sense_key_desc[];
+extern const char * sg_lib_pdt_strs[];
+extern const char * sg_lib_transport_proto_strs[];
+extern int sg_lib_pdt_decay_arr[];
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sg3_utils/include/sg_linux_inc.h b/sg3_utils/include/sg_linux_inc.h
new file mode 100644
index 0000000..b587c6c
--- /dev/null
+++ b/sg3_utils/include/sg_linux_inc.h
@@ -0,0 +1,56 @@
+#ifndef SG_LINUX_INC_H
+#define SG_LINUX_INC_H
+
+#ifdef SG_KERNEL_INCLUDES
+  #define __user
+  typedef unsigned char u8;
+  #include "/usr/src/linux/include/scsi/sg.h"
+  #include "/usr/src/linux/include/scsi/scsi.h"
+#else
+  #ifdef SG_TRICK_GNU_INCLUDES
+    #include <linux/../scsi/sg.h>
+    #include <linux/../scsi/scsi.h>
+  #else
+    #include <scsi/sg.h>
+    #include <scsi/scsi.h>
+  #endif
+#endif
+
+#ifdef BLKGETSIZE64
+  #ifndef u64
+    #include <stdint.h>   /* C99 header for exact integer types */
+    typedef uint64_t u64; /* problems with BLKGETSIZE64 ioctl in lk 2.4 */
+  #endif
+#endif
+
+/*
+  Getting the correct include files for the sg interface can be an ordeal.
+  In a perfect world, one would just write:
+    #include <scsi/sg.h>
+    #include <scsi/scsi.h>
+  This would include the files found in the /usr/include/scsi directory.
+  Those files are maintained with the GNU library which may or may not
+  agree with the kernel and version of sg driver that is running. Any
+  many cases this will not matter. However in some it might, for example
+  glibc 2.1's include files match the sg driver found in the lk 2.2
+  series. Hence if glibc 2.1 is used with lk 2.4 then the additional
+  sg v3 interface will not be visible.
+  If this is a problem then defining SG_KERNEL_INCLUDES will access the
+  kernel supplied header files (assuming they are in the normal place).
+  The GNU library maintainers and various kernel people don't like
+  this approach (but it does work).
+  The technique selected by defining SG_TRICK_GNU_INCLUDES worked (and
+  was used) prior to glibc 2.2 . Prior to that version /usr/include/linux
+  was a symbolic link to /usr/src/linux/include/linux .
+
+  There are other approaches if this include "mixup" causes pain. These
+  would involve include files being copied or symbolic links being
+  introduced.
+
+  Sorry about the inconvenience. Typically neither SG_KERNEL_INCLUDES
+  nor SG_TRICK_GNU_INCLUDES is defined.
+
+  dpg 20010415, 20030522
+*/
+
+#endif
diff --git a/sg3_utils/include/sg_pr2serr.h b/sg3_utils/include/sg_pr2serr.h
new file mode 100644
index 0000000..558f9cf
--- /dev/null
+++ b/sg3_utils/include/sg_pr2serr.h
@@ -0,0 +1,20 @@
+#ifndef SG_PR2SERR_H
+#define SG_PR2SERR_H
+
+/*
+ * Copyright (c) 2004-2015 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 <stdio.h>
+
+#ifdef __GNUC__
+int pr2serr(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+int pr2serr(const char * fmt, ...);
+#endif
+
+#endif
diff --git a/sg3_utils/include/sg_pt.h b/sg3_utils/include/sg_pt.h
new file mode 100644
index 0000000..f0869d5
--- /dev/null
+++ b/sg3_utils/include/sg_pt.h
@@ -0,0 +1,147 @@
+#ifndef SG_PT_H
+#define SG_PT_H
+
+/*
+ * Copyright (c) 2005-2014 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 <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This declaration hides the fact that each implementation has its own
+ * structure "derived" (using a C++ term) from this one. It compiles
+ * because 'struct sg_pt_base' is only referenced (by pointer: 'objp')
+ * in this interface. An instance of this structure represents the
+ * context of one SCSI command. */
+struct sg_pt_base;
+
+
+/* The format of the version string is like this: "2.01 20090201".
+ * The leading digit will be incremented if this interface changes
+ * in a way that may impact backward compatibility. */
+const char * scsi_pt_version();
+
+
+/* Returns >= 0 if successful. If error in Unix returns negated errno. */
+int scsi_pt_open_device(const char * device_name, int read_only, int verbose);
+
+/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
+ * together. Returns valid file descriptor( >= 0 ) if successful, otherwise
+ * returns -1 or a negated errno.
+ * In Win32 O_EXCL translated to equivalent. */
+int scsi_pt_open_flags(const char * device_name, int flags, int verbose);
+
+/* Returns 0 if successful. If error in Unix returns negated errno. */
+int scsi_pt_close_device(int device_fd);
+
+
+/* Creates an object that can be used to issue one or more SCSI commands
+ * (or task management functions). Returns NULL if problem.
+ * Once this object has been created it should be destroyed with
+ * destruct_scsi_pt_obj() when it is no longer needed. */
+struct sg_pt_base * construct_scsi_pt_obj(void);
+
+/* Clear state information held in *objp . This allows this object to be
+ * used to issue more than one SCSI command. */
+void clear_scsi_pt_obj(struct sg_pt_base * objp);
+
+/* Set the CDB (command descriptor block) */
+void set_scsi_pt_cdb(struct sg_pt_base * objp, const unsigned char * cdb,
+                     int cdb_len);
+/* Set the sense buffer and the maximum length that it can handle */
+void set_scsi_pt_sense(struct sg_pt_base * objp, unsigned char * sense,
+                       int max_sense_len);
+/* Set a pointer and length to be used for data transferred from device */
+void set_scsi_pt_data_in(struct sg_pt_base * objp,   /* from device */
+                         unsigned char * dxferp, int dxfer_len);
+/* Set a pointer and length to be used for data transferred to device */
+void set_scsi_pt_data_out(struct sg_pt_base * objp,    /* to device */
+                          const unsigned char * dxferp, int dxfer_len);
+/* The following "set_"s implementations may be dummies */
+void set_scsi_pt_packet_id(struct sg_pt_base * objp, int pack_id);
+void set_scsi_pt_tag(struct sg_pt_base * objp, uint64_t tag);
+void set_scsi_pt_task_management(struct sg_pt_base * objp, int tmf_code);
+void set_scsi_pt_task_attr(struct sg_pt_base * objp, int attribute,
+                           int priority);
+
+/* Following is a guard which is defined when set_scsi_pt_flags() is
+ * present. Older versions of this library may not have this function. */
+#define SCSI_PT_FLAGS_FUNCTION 1
+/* If neither QUEUE_AT_HEAD nor QUEUE_AT_TAIL are given, or both
+ * are given, use the pass-through default. */
+#define SCSI_PT_FLAGS_QUEUE_AT_TAIL 0x10
+#define SCSI_PT_FLAGS_QUEUE_AT_HEAD 0x20
+/* Set (potentially OS dependant) flags for pass-through mechanism.
+ * Apart from contradictions, flags can be OR-ed together. */
+void set_scsi_pt_flags(struct sg_pt_base * objp, int flags);
+
+#define SCSI_PT_DO_START_OK 0
+#define SCSI_PT_DO_BAD_PARAMS 1
+#define SCSI_PT_DO_TIMEOUT 2
+/* If OS error prior to or during command submission then returns negated
+ * error value (e.g. Unix '-errno'). This includes interrupted system calls
+ * (e.g. by a signal) in which case -EINTR would be returned. Note that
+ * system call errors also can be fetched with get_scsi_pt_os_err().
+ * Return 0 if okay (i.e. at the very least: command sent). Positive
+ * return values are errors (see SCSI_PT_DO_* defines). */
+int do_scsi_pt(struct sg_pt_base * objp, int fd, int timeout_secs,
+               int verbose);
+
+#define SCSI_PT_RESULT_GOOD 0
+#define SCSI_PT_RESULT_STATUS 1 /* other than GOOD and CHECK CONDITION */
+#define SCSI_PT_RESULT_SENSE 2
+#define SCSI_PT_RESULT_TRANSPORT_ERR 3
+#define SCSI_PT_RESULT_OS_ERR 4
+/* highest numbered applicable category returned */
+int get_scsi_pt_result_category(const struct sg_pt_base * objp);
+
+/* If not available return 0 */
+int get_scsi_pt_resid(const struct sg_pt_base * objp);
+/* Returns SCSI status value (from device that received the
+   command). */
+int get_scsi_pt_status_response(const struct sg_pt_base * objp);
+/* Actual sense length returned. If sense data is present but
+   actual sense length is not known, return 'max_sense_len' */
+int get_scsi_pt_sense_len(const struct sg_pt_base * objp);
+/* If not available return 0 */
+int get_scsi_pt_os_err(const struct sg_pt_base * objp);
+char * get_scsi_pt_os_err_str(const struct sg_pt_base * objp, int max_b_len,
+                              char * b);
+/* If not available return 0 */
+int get_scsi_pt_transport_err(const struct sg_pt_base * objp);
+char * get_scsi_pt_transport_err_str(const struct sg_pt_base * objp,
+                                     int max_b_len, char * b);
+
+/* If not available return -1 */
+int get_scsi_pt_duration_ms(const struct sg_pt_base * objp);
+
+
+/* Should be invoked once per objp after other processing is complete in
+ * order to clean up resources. For ever successful construct_scsi_pt_obj()
+ * call there should be one destruct_scsi_pt_obj().  */
+void destruct_scsi_pt_obj(struct sg_pt_base * objp);
+
+#ifdef SG_LIB_WIN32
+#define SG_LIB_WIN32_DIRECT 1
+
+/* Request SPT direct interface when state_direct is 1, state_direct set
+ * to 0 for the SPT indirect interface. Default setting selected by build
+ * (i.e. library compile time) and is usually indirect. */
+void scsi_pt_win32_direct(int state_direct);
+
+/* Returns current SPT interface state, 1 for direct, 0 for indirect */
+int scsi_pt_win32_spt_state(void);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sg3_utils/include/sg_pt_win32.h b/sg3_utils/include/sg_pt_win32.h
new file mode 100644
index 0000000..53366ec
--- /dev/null
+++ b/sg3_utils/include/sg_pt_win32.h
@@ -0,0 +1,154 @@
+#ifndef SG_PT_WIN32_H
+#define SG_PT_WIN32_H
+/*
+ * The information in this file was obtained from scsi-wnt.h by
+ * Richard Stemmer, rs@epost.de . He in turn gives credit to
+ * Jay A. Key (for scsipt.c).
+ * The plscsi program (by Pat LaVarre <p.lavarre@ieee.org>) has
+ * also been used as a reference.
+ * Much of the information in this header can also be obtained
+ * from msdn.microsoft.com .
+ * Updated for cygwin version 1.7.17 changes 20121026
+ */
+
+#include <windows.h>
+
+#define SCSI_MAX_SENSE_LEN 64
+#define SCSI_MAX_CDB_LEN 16
+#define SCSI_MAX_INDIRECT_DATA 16384
+
+typedef struct {
+        USHORT          Length;
+        UCHAR           ScsiStatus;
+        UCHAR           PathId;
+        UCHAR           TargetId;
+        UCHAR           Lun;
+        UCHAR           CdbLength;
+        UCHAR           SenseInfoLength;
+        UCHAR           DataIn;
+        ULONG           DataTransferLength;
+        ULONG           TimeOutValue;
+        ULONG_PTR       DataBufferOffset;  /* was ULONG; problem in 64 bit */
+        ULONG           SenseInfoOffset;
+        UCHAR           Cdb[SCSI_MAX_CDB_LEN];
+} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
+
+
+typedef struct {
+        USHORT          Length;
+        UCHAR           ScsiStatus;
+        UCHAR           PathId;
+        UCHAR           TargetId;
+        UCHAR           Lun;
+        UCHAR           CdbLength;
+        UCHAR           SenseInfoLength;
+        UCHAR           DataIn;
+        ULONG           DataTransferLength;
+        ULONG           TimeOutValue;
+        PVOID           DataBuffer;
+        ULONG           SenseInfoOffset;
+        UCHAR           Cdb[SCSI_MAX_CDB_LEN];
+} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;
+
+
+typedef struct {
+        SCSI_PASS_THROUGH spt;
+        /* plscsi shows a follow on 16 bytes allowing 32 byte cdb */
+        ULONG           Filler;
+        UCHAR           ucSenseBuf[SCSI_MAX_SENSE_LEN];
+        UCHAR           ucDataBuf[SCSI_MAX_INDIRECT_DATA];
+} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS;
+
+
+typedef struct {
+        SCSI_PASS_THROUGH_DIRECT spt;
+        ULONG           Filler;
+        UCHAR           ucSenseBuf[SCSI_MAX_SENSE_LEN];
+} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
+
+
+
+typedef struct {
+        UCHAR           NumberOfLogicalUnits;
+        UCHAR           InitiatorBusId;
+        ULONG           InquiryDataOffset;
+} SCSI_BUS_DATA, *PSCSI_BUS_DATA;
+
+
+typedef struct {
+        UCHAR           NumberOfBusses;
+        SCSI_BUS_DATA   BusData[1];
+} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO;
+
+
+typedef struct {
+        UCHAR           PathId;
+        UCHAR           TargetId;
+        UCHAR           Lun;
+        BOOLEAN         DeviceClaimed;
+        ULONG           InquiryDataLength;
+        ULONG           NextInquiryDataOffset;
+        UCHAR           InquiryData[1];
+} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA;
+
+
+typedef struct {
+        ULONG           Length;
+        UCHAR           PortNumber;
+        UCHAR           PathId;
+        UCHAR           TargetId;
+        UCHAR           Lun;
+} SCSI_ADDRESS, *PSCSI_ADDRESS;
+
+
+/*
+ * method codes
+ */
+#define METHOD_BUFFERED         0
+#define METHOD_IN_DIRECT        1
+#define METHOD_OUT_DIRECT       2
+#define METHOD_NEITHER          3
+
+/*
+ * file access values
+ */
+#define FILE_ANY_ACCESS         0
+#ifndef FILE_READ_ACCESS
+#define FILE_READ_ACCESS        0x0001
+#endif
+#ifndef FILE_WRITE_ACCESS
+#define FILE_WRITE_ACCESS       0x0002
+#endif
+
+
+#define IOCTL_SCSI_BASE    0x00000004
+
+/*
+ * constants for DataIn member of SCSI_PASS_THROUGH* structures
+ */
+#define SCSI_IOCTL_DATA_OUT             0
+#define SCSI_IOCTL_DATA_IN              1
+#define SCSI_IOCTL_DATA_UNSPECIFIED     2
+
+/*
+ * Standard IOCTL define
+ */
+#ifndef CTL_CODE
+#define CTL_CODE(DevType, Function, Method, Access)             \
+        (((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
+#endif
+
+#define IOCTL_SCSI_PASS_THROUGH         CTL_CODE(IOCTL_SCSI_BASE, 0x0401, \
+	METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define IOCTL_SCSI_MINIPORT             CTL_CODE(IOCTL_SCSI_BASE, 0x0402, \
+	METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define IOCTL_SCSI_GET_INQUIRY_DATA     CTL_CODE(IOCTL_SCSI_BASE, 0x0403, \
+	METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SCSI_GET_CAPABILITIES     CTL_CODE(IOCTL_SCSI_BASE, 0x0404, \
+	METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SCSI_PASS_THROUGH_DIRECT  CTL_CODE(IOCTL_SCSI_BASE, 0x0405, \
+	METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define IOCTL_SCSI_GET_ADDRESS          CTL_CODE(IOCTL_SCSI_BASE, 0x0406, \
+	METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#endif
diff --git a/sg3_utils/include/sg_unaligned.h b/sg3_utils/include/sg_unaligned.h
new file mode 100644
index 0000000..e40e359
--- /dev/null
+++ b/sg3_utils/include/sg_unaligned.h
@@ -0,0 +1,324 @@
+#ifndef SG_UNALIGNED_H
+#define SG_UNALIGNED_H
+
+/*
+ * Copyright (c) 2014-2016 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 <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Borrowed from the Linux kernel, via mhvtl */
+
+/* In the first section below, functions that copy unsigned integers in a
+ * computer's native format, to and from an unaligned big endian sequence of
+ * bytes. 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. */
+
+static inline uint16_t __get_unaligned_be16(const uint8_t *p)
+{
+        return p[0] << 8 | p[1];
+}
+
+static inline uint32_t __get_unaligned_be32(const uint8_t *p)
+{
+        return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline uint64_t __get_unaligned_be48(const uint8_t *p)
+{
+        return (uint64_t)__get_unaligned_be16(p) << 32 |
+               __get_unaligned_be32(p + 2);
+}
+
+static inline uint64_t __get_unaligned_be64(const uint8_t *p)
+{
+        return (uint64_t)__get_unaligned_be32(p) << 32 |
+               __get_unaligned_be32(p + 4);
+}
+
+static inline void __put_unaligned_be16(uint16_t val, uint8_t *p)
+{
+        *p++ = val >> 8;
+        *p++ = val;
+}
+
+static inline void __put_unaligned_be32(uint32_t val, uint8_t *p)
+{
+        __put_unaligned_be16(val >> 16, p);
+        __put_unaligned_be16(val, p + 2);
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline void __put_unaligned_be48(uint64_t val, uint8_t *p)
+{
+        __put_unaligned_be16(val >> 32, p);
+        __put_unaligned_be32(val, p + 2);
+}
+
+static inline void __put_unaligned_be64(uint64_t val, uint8_t *p)
+{
+        __put_unaligned_be32(val >> 32, p);
+        __put_unaligned_be32(val, p + 4);
+}
+
+static inline uint16_t sg_get_unaligned_be16(const void *p)
+{
+        return __get_unaligned_be16((const uint8_t *)p);
+}
+
+static inline uint32_t sg_get_unaligned_be24(const uint8_t *p)
+{
+        return p[0] << 16 | p[1] << 8 | p[2];
+}
+
+static inline uint32_t sg_get_unaligned_be32(const void *p)
+{
+        return __get_unaligned_be32((const uint8_t *)p);
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline uint64_t sg_get_unaligned_be48(const void *p)
+{
+        return __get_unaligned_be48((const uint8_t *)p);
+}
+
+static inline uint64_t sg_get_unaligned_be64(const void *p)
+{
+        return __get_unaligned_be64((const uint8_t *)p);
+}
+
+/* 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 bytes 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_be16(uint16_t val, void *p)
+{
+        __put_unaligned_be16(val, (uint8_t *)p);
+}
+
+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;
+}
+
+static inline void sg_put_unaligned_be32(uint32_t val, void *p)
+{
+        __put_unaligned_be32(val, (uint8_t *)p);
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline void sg_put_unaligned_be48(uint64_t val, void *p)
+{
+        __put_unaligned_be48(val, (uint8_t *)p);
+}
+
+static inline void sg_put_unaligned_be64(uint64_t val, void *p)
+{
+        __put_unaligned_be64(val, (uint8_t *)p);
+}
+
+/* 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. */
+static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p)
+{
+        if (val)
+                __put_unaligned_be16(val, (uint8_t *)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)
+                __put_unaligned_be32(val, (uint8_t *)p);
+}
+
+static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p)
+{
+        if (val)
+            __put_unaligned_be64(val, (uint8_t *)p);
+}
+
+
+/* Below are the little endian equivalents of the big endian functions
+ * above. Little endian is used by ATA, networking and PCI.
+ */
+
+static inline uint16_t __get_unaligned_le16(const uint8_t *p)
+{
+        return p[1] << 8 | p[0];
+}
+
+static inline uint32_t __get_unaligned_le32(const uint8_t *p)
+{
+        return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
+}
+
+static inline uint64_t __get_unaligned_le64(const uint8_t *p)
+{
+        return (uint64_t)__get_unaligned_le32(p + 4) << 32 |
+               __get_unaligned_le32(p);
+}
+
+static inline void __put_unaligned_le16(uint16_t val, uint8_t *p)
+{
+        *p++ = val;
+        *p++ = val >> 8;
+}
+
+static inline void __put_unaligned_le32(uint32_t val, uint8_t *p)
+{
+        __put_unaligned_le16(val >> 16, p + 2);
+        __put_unaligned_le16(val, p);
+}
+
+static inline void __put_unaligned_le64(uint64_t val, uint8_t *p)
+{
+        __put_unaligned_le32(val >> 32, p + 4);
+        __put_unaligned_le32(val, p);
+}
+
+static inline uint16_t sg_get_unaligned_le16(const void *p)
+{
+        return __get_unaligned_le16((const uint8_t *)p);
+}
+
+static inline uint32_t sg_get_unaligned_le24(const void *p)
+{
+        return (uint32_t)__get_unaligned_le16((const uint8_t *)p) |
+               ((const uint8_t *)p)[2] << 16;
+}
+
+static inline uint32_t sg_get_unaligned_le32(const void *p)
+{
+        return __get_unaligned_le32((const uint8_t *)p);
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline uint64_t sg_get_unaligned_le48(const void *p)
+{
+        return (uint64_t)__get_unaligned_le16((const uint8_t *)p + 4) << 32 |
+               __get_unaligned_le32((const uint8_t *)p);
+}
+
+static inline uint64_t sg_get_unaligned_le64(const void *p)
+{
+        return __get_unaligned_le64((const uint8_t *)p);
+}
+
+/* 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 bytes 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;
+        }
+}
+
+static inline void sg_put_unaligned_le16(uint16_t val, void *p)
+{
+        __put_unaligned_le16(val, (uint8_t *)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;
+}
+
+static inline void sg_put_unaligned_le32(uint32_t val, void *p)
+{
+        __put_unaligned_le32(val, (uint8_t *)p);
+}
+
+/* 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;
+}
+
+static inline void sg_put_unaligned_le64(uint64_t val, void *p)
+{
+        __put_unaligned_le64(val, (uint8_t *)p);
+}
+
+/* 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. */
+static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p)
+{
+        if (val)
+                __put_unaligned_le16(val, (uint8_t *)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)
+                __put_unaligned_le32(val, (uint8_t *)p);
+}
+
+static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p)
+{
+        if (val)
+            __put_unaligned_le64(val, (uint8_t *)p);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SG_UNALIGNED_H */
diff --git a/sg3_utils/install-sh b/sg3_utils/install-sh
new file mode 100755
index 0000000..0b0fdcb
--- /dev/null
+++ b/sg3_utils/install-sh
@@ -0,0 +1,501 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2013-12-25.23; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab='	'
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve the last data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -s            $stripprog installed files.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
+
+    -o) chowncmd="$chownprog $2"
+        shift;;
+
+    -s) stripcmd=$stripprog;;
+
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
+
+    -T) is_target_a_directory=never;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --) shift
+        break;;
+
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dst=$dstdir/`basename "$src"`
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # Create intermediate dirs using mode 755 as modified by the umask.
+        # This is like FreeBSD 'install' as of 1997-10-28.
+        umask=`umask`
+        case $stripcmd.$umask in
+          # Optimize common cases.
+          *[2367][2367]) mkdir_umask=$umask;;
+          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+          *[0-7])
+            mkdir_umask=`expr $umask + 22 \
+              - $umask % 100 % 40 + $umask % 20 \
+              - $umask % 10 % 4 + $umask % 2
+            `;;
+          *) mkdir_umask=$umask,go-w;;
+        esac
+
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+        case $umask in
+          *[123567][0-7][0-7])
+            # POSIX mkdir -p sets u+wx bits regardless of umask, which
+            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+            ;;
+          *)
+            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+            trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+            if (umask $mkdir_umask &&
+                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+            then
+              if test -z "$dir_arg" || {
+                   # Check for POSIX incompatibilities with -m.
+                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                   # other-writable bit of parent directory when it shouldn't.
+                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                   ls_ld_tmpdir=`ls -ld "$tmpdir"`
+                   case $ls_ld_tmpdir in
+                     d????-?r-*) different_mode=700;;
+                     d????-?--*) different_mode=755;;
+                     *) false;;
+                   esac &&
+                   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+                     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                   }
+                 }
+              then posix_mkdir=:
+              fi
+              rmdir "$tmpdir/d" "$tmpdir"
+            else
+              # Remove any dirs left behind by ancient mkdir implementations.
+              rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+            fi
+            trap '' 0;;
+        esac;;
+    esac
+
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask=$mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd -f "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/sg3_utils/lib/BSD_LICENSE b/sg3_utils/lib/BSD_LICENSE
new file mode 100644
index 0000000..9b5c85e
--- /dev/null
+++ b/sg3_utils/lib/BSD_LICENSE
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 1999-2010 Douglas Gilbert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/sg3_utils/lib/Makefile.am b/sg3_utils/lib/Makefile.am
new file mode 100644
index 0000000..c8ab0cc
--- /dev/null
+++ b/sg3_utils/lib/Makefile.am
@@ -0,0 +1,56 @@
+libsgutils2_la_SOURCES = \
+	sg_lib.c \
+	sg_lib_data.c \
+	sg_cmds_basic.c \
+	sg_cmds_basic2.c \
+	sg_cmds_extra.c \
+	sg_cmds_mmc.c \
+	sg_pt_common.c 
+
+if OS_LINUX
+libsgutils2_la_SOURCES += \
+	sg_pt_linux.c \
+	sg_io_linux.c
+endif
+
+if OS_WIN32_MINGW
+libsgutils2_la_SOURCES += sg_pt_win32.c
+endif
+
+if OS_WIN32_CYGWIN
+libsgutils2_la_SOURCES += sg_pt_win32.c
+endif
+
+if OS_FREEBSD
+libsgutils2_la_SOURCES += sg_pt_freebsd.c
+endif
+
+if OS_SOLARIS
+libsgutils2_la_SOURCES += sg_pt_solaris.c
+endif
+
+if OS_OSF
+libsgutils2_la_SOURCES += sg_pt_osf1.c
+endif
+
+# For C++/clang testing
+## CC = gcc
+## CC = g++
+## CC = clang
+## CC = clang++
+
+# -std=<s> can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same)
+# -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more
+AM_CPPFLAGS = -iquote ${top_srcdir}/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+AM_CFLAGS = -Wall -W
+# AM_CFLAGS = -Wall -W -pedantic -std=c11
+# AM_CFLAGS = -Wall -W -pedantic -std=c++11
+
+lib_LTLIBRARIES = libsgutils2.la
+
+libsgutils2_la_LDFLAGS = -version-info 2:0:0 -no-undefined
+
+libsgutils2_la_LIBADD = @GETOPT_O_FILES@ @os_libs@
+libsgutils2_la_DEPENDENCIES = @GETOPT_O_FILES@
+
+
diff --git a/sg3_utils/lib/Makefile.in b/sg3_utils/lib/Makefile.in
new file mode 100644
index 0000000..942ad8b
--- /dev/null
+++ b/sg3_utils/lib/Makefile.in
@@ -0,0 +1,690 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@OS_LINUX_TRUE@am__append_1 = \
+@OS_LINUX_TRUE@	sg_pt_linux.c \
+@OS_LINUX_TRUE@	sg_io_linux.c
+
+@OS_WIN32_MINGW_TRUE@am__append_2 = sg_pt_win32.c
+@OS_WIN32_CYGWIN_TRUE@am__append_3 = sg_pt_win32.c
+@OS_FREEBSD_TRUE@am__append_4 = sg_pt_freebsd.c
+@OS_SOLARIS_TRUE@am__append_5 = sg_pt_solaris.c
+@OS_OSF_TRUE@am__append_6 = sg_pt_osf1.c
+subdir = lib
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(libdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \
+	sg_cmds_basic.c sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c \
+	sg_pt_common.c sg_pt_linux.c sg_io_linux.c sg_pt_win32.c \
+	sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c
+@OS_LINUX_TRUE@am__objects_1 = sg_pt_linux.lo sg_io_linux.lo
+@OS_WIN32_MINGW_TRUE@am__objects_2 = sg_pt_win32.lo
+@OS_WIN32_CYGWIN_TRUE@am__objects_3 = sg_pt_win32.lo
+@OS_FREEBSD_TRUE@am__objects_4 = sg_pt_freebsd.lo
+@OS_SOLARIS_TRUE@am__objects_5 = sg_pt_solaris.lo
+@OS_OSF_TRUE@am__objects_6 = sg_pt_osf1.lo
+am_libsgutils2_la_OBJECTS = sg_lib.lo sg_lib_data.lo sg_cmds_basic.lo \
+	sg_cmds_basic2.lo sg_cmds_extra.lo sg_cmds_mmc.lo \
+	sg_pt_common.lo $(am__objects_1) $(am__objects_2) \
+	$(am__objects_3) $(am__objects_4) $(am__objects_5) \
+	$(am__objects_6)
+libsgutils2_la_OBJECTS = $(am_libsgutils2_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libsgutils2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(AM_CFLAGS) $(CFLAGS) $(libsgutils2_la_LDFLAGS) $(LDFLAGS) -o \
+	$@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libsgutils2_la_SOURCES)
+DIST_SOURCES = $(am__libsgutils2_la_SOURCES_DIST)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETOPT_O_FILES = @GETOPT_O_FILES@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libsgutils2_la_SOURCES = sg_lib.c sg_lib_data.c sg_cmds_basic.c \
+	sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c sg_pt_common.c \
+	$(am__append_1) $(am__append_2) $(am__append_3) \
+	$(am__append_4) $(am__append_5) $(am__append_6)
+
+# For C++/clang testing
+
+# -std=<s> can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same)
+# -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more
+AM_CPPFLAGS = -iquote ${top_srcdir}/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+AM_CFLAGS = -Wall -W
+# AM_CFLAGS = -Wall -W -pedantic -std=c11
+# AM_CFLAGS = -Wall -W -pedantic -std=c++11
+lib_LTLIBRARIES = libsgutils2.la
+libsgutils2_la_LDFLAGS = -version-info 2:0:0 -no-undefined
+libsgutils2_la_LIBADD = @GETOPT_O_FILES@ @os_libs@
+libsgutils2_la_DEPENDENCIES = @GETOPT_O_FILES@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu lib/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+	}
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+	done
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+	@list='$(lib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libsgutils2.la: $(libsgutils2_la_OBJECTS) $(libsgutils2_la_DEPENDENCIES) $(EXTRA_libsgutils2_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libsgutils2_la_LINK) -rpath $(libdir) $(libsgutils2_la_OBJECTS) $(libsgutils2_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_basic2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_extra.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_mmc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_io_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_common.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_freebsd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_osf1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_solaris.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_win32.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+	for dir in "$(DESTDIR)$(libdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-libLTLIBRARIES install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/sg3_utils/lib/sg_cmds_basic.c b/sg3_utils/lib/sg_cmds_basic.c
new file mode 100644
index 0000000..7a56d51
--- /dev/null
+++ b/sg3_utils/lib/sg_cmds_basic.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 1999-2016 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.
+ */
+
+/*
+ * CONTENTS
+ *    Some SCSI commands are executed in many contexts and hence began
+ *    to appear in several sg3_utils utilities. This files centralizes
+ *    some of the low level command execution code. In most cases the
+ *    interpretation of the command response is left to the each
+ *    utility.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pt.h"
+#include "sg_unaligned.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Needs to be after config.h */
+#ifdef SG_LIB_LINUX
+#include <errno.h>
+#endif
+
+
+static const char * version_str = "1.72 20160126";
+
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define EBUFF_SZ 256
+
+#define DEF_PT_TIMEOUT 60       /* 60 seconds */
+#define START_PT_TIMEOUT 120    /* 120 seconds == 2 minutes */
+#define LONG_PT_TIMEOUT 7200    /* 7,200 seconds == 120 minutes */
+
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+#define REQUEST_SENSE_CMD 0x3
+#define REQUEST_SENSE_CMDLEN 6
+#define REPORT_LUNS_CMD 0xa0
+#define REPORT_LUNS_CMDLEN 12
+#define TUR_CMD  0x0
+#define TUR_CMDLEN  6
+
+#define SAFE_STD_INQ_RESP_LEN 36 /* other lengths lock up some devices */
+
+
+const char *
+sg_cmds_version()
+{
+    return version_str;
+}
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+/* Returns file descriptor >= 0 if successful. If error in Unix returns
+   negated errno. */
+int
+sg_cmds_open_device(const char * device_name, int read_only, int verbose)
+{
+    /* The following 2 lines are temporary. It is to avoid a NULL pointer
+     * crash when an old utility is used with a newer library built after
+     * the sg_warnings_strm cleanup */
+    if (NULL == sg_warnings_strm)
+        sg_warnings_strm = stderr;
+
+    return scsi_pt_open_device(device_name, read_only, verbose);
+}
+
+/* Returns file descriptor >= 0 if successful. If error in Unix returns
+   negated errno. */
+int
+sg_cmds_open_flags(const char * device_name, int flags, int verbose)
+{
+    return scsi_pt_open_flags(device_name, flags, verbose);
+}
+
+/* Returns 0 if successful. If error in Unix returns negated errno. */
+int
+sg_cmds_close_device(int device_fd)
+{
+    return scsi_pt_close_device(device_fd);
+}
+
+static int
+sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid,
+                       const unsigned char * sbp, int slen, int noisy,
+                       int verbose, int * o_sense_cat)
+{
+    int scat, got;
+    int n = 0;
+    int check_data_in = 0;
+    char b[512];
+
+    scat = sg_err_category_sense(sbp, slen);
+    switch (scat) {
+    case SG_LIB_CAT_NOT_READY:
+    case SG_LIB_CAT_INVALID_OP:
+    case SG_LIB_CAT_ILLEGAL_REQ:
+    case SG_LIB_CAT_ABORTED_COMMAND:
+    case SG_LIB_CAT_COPY_ABORTED:
+    case SG_LIB_CAT_DATA_PROTECT:
+    case SG_LIB_CAT_PROTECTION:
+    case SG_LIB_CAT_NO_SENSE:
+    case SG_LIB_CAT_MISCOMPARE:
+        n = 0;
+        break;
+    case SG_LIB_CAT_RECOVERED:
+    case SG_LIB_CAT_MEDIUM_HARD:
+        ++check_data_in;
+        /* drop through */
+    case SG_LIB_CAT_UNIT_ATTENTION:
+    case SG_LIB_CAT_SENSE:
+    default:
+        n = noisy;
+        break;
+    }
+    if (verbose || n) {
+        if (leadin && (strlen(leadin) > 0))
+            pr2ws("%s:\n", leadin);
+        sg_get_sense_str(NULL, sbp, slen, (verbose > 1),
+                         sizeof(b), b);
+        pr2ws("%s", b);
+        if ((mx_di_len > 0) && (resid > 0)) {
+            got = mx_di_len - resid;
+            if ((verbose > 2) || check_data_in || (got > 0))
+                pr2ws("    pass-through requested %d bytes (data-in) but "
+                      "got %d bytes\n", mx_di_len, got);
+        }
+    }
+    if (o_sense_cat)
+        *o_sense_cat = scat;
+    return -2;
+}
+
+/* This is a helper function used by sg_cmds_* implementations after the
+ * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid
+ * sense data is found it is decoded and output to sg_warnings_strm (def:
+ * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for
+ * "sense" category (may not be fatal), -1 for failed, 0, or a positive
+ * number. If 'mx_di_len > 0' then asks pass-through for resid and returns
+ * (mx_di_len - resid); otherwise returns 0. So for data-in it should return
+ * the actual number of bytes received. For data-out (to device) or no data
+ * call with 'mx_di_len' set to 0 or less. If -2 returned then sense category
+ * output via 'o_sense_cat' pointer (if not NULL). Note that several sense
+ * categories also have data in bytes received; -2 is still returned. */
+int
+sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin,
+                     int pt_res, int mx_di_len, const unsigned char * sbp,
+                     int noisy, int verbose, int * o_sense_cat)
+{
+    int got, cat, duration, slen, resid, resp_code, sstat, transport_sense;
+    char b[1024];
+
+    if (NULL == leadin)
+        leadin = "";
+    if (pt_res < 0) {
+#ifdef SG_LIB_LINUX
+        if (verbose)
+            pr2ws("%s: pass through os error: %s\n", leadin,
+                  safe_strerror(-pt_res));
+        if ((-ENXIO == pt_res) && o_sense_cat) {
+            if (verbose > 2)
+                pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n");
+            *o_sense_cat = SG_LIB_CAT_NOT_READY;
+            return -2;
+        } else if (noisy && (0 == verbose))
+            pr2ws("%s: pass through os error: %s\n", leadin,
+                  safe_strerror(-pt_res));
+#else
+        if (noisy || verbose)
+            pr2ws("%s: pass through os error: %s\n", leadin,
+                  safe_strerror(-pt_res));
+#endif
+        return -1;
+    } else if (SCSI_PT_DO_BAD_PARAMS == pt_res) {
+        pr2ws("%s: bad pass through setup\n", leadin);
+        return -1;
+    } else if (SCSI_PT_DO_TIMEOUT == pt_res) {
+        pr2ws("%s: pass through timeout\n", leadin);
+        return -1;
+    }
+    if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
+        pr2ws("      duration=%d ms\n", duration);
+    resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0;
+    slen = get_scsi_pt_sense_len(ptvp);
+    switch ((cat = get_scsi_pt_result_category(ptvp))) {
+    case SCSI_PT_RESULT_GOOD:
+        if (sbp && (slen > 7)) {
+            resp_code = sbp[0] & 0x7f;
+            /* SBC referrals can have status=GOOD and sense_key=COMPLETED */
+            if (resp_code >= 0x70) {
+                if (resp_code < 0x72) {
+                    if (SPC_SK_NO_SENSE != (0xf & sbp[2]))
+                        sg_err_category_sense(sbp, slen);
+                } else if (resp_code < 0x74) {
+                    if (SPC_SK_NO_SENSE != (0xf & sbp[1]))
+                        sg_err_category_sense(sbp, slen);
+                }
+            }
+        }
+        if (mx_di_len > 0) {
+            got = mx_di_len - resid;
+            if ((verbose > 1) && (resid != 0))
+                pr2ws("    %s: pass-through requested %d bytes (data-in) "
+                      "but got %d bytes\n", leadin, mx_di_len, got);
+            if (got >= 0)
+                return got;
+            else {
+                if (verbose)
+                    pr2ws("    %s: pass-through can't get negative bytes, "
+                          "say it got none\n", leadin);
+                return 0;
+            }
+        } else
+            return 0;
+    case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
+        sstat = get_scsi_pt_status_response(ptvp);
+        if (o_sense_cat) {
+            switch (sstat) {
+            case SAM_STAT_RESERVATION_CONFLICT:
+                *o_sense_cat = SG_LIB_CAT_RES_CONFLICT;
+                return -2;
+            case SAM_STAT_CONDITION_MET:
+                *o_sense_cat = SG_LIB_CAT_CONDITION_MET;
+                return -2;
+            case SAM_STAT_BUSY:
+                *o_sense_cat = SG_LIB_CAT_BUSY;
+                return -2;
+            case SAM_STAT_TASK_SET_FULL:
+                *o_sense_cat = SG_LIB_CAT_TS_FULL;
+                return -2;
+            case SAM_STAT_ACA_ACTIVE:
+                *o_sense_cat = SG_LIB_CAT_ACA_ACTIVE;
+                return -2;
+            case SAM_STAT_TASK_ABORTED:
+                *o_sense_cat = SG_LIB_CAT_TASK_ABORTED;
+                return -2;
+            default:
+                break;
+            }
+        }
+        if (verbose || noisy) {
+            sg_get_scsi_status_str(sstat, sizeof(b), b);
+            pr2ws("%s: scsi status: %s\n", leadin, b);
+        }
+        return -1;
+    case SCSI_PT_RESULT_SENSE:
+        return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen,
+                                      noisy, verbose, o_sense_cat);
+    case SCSI_PT_RESULT_TRANSPORT_ERR:
+        if (verbose || noisy) {
+            get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
+            pr2ws("%s: transport: %s\n", leadin, b);
+        }
+#ifdef SG_LIB_LINUX
+        transport_sense = (slen > 0);
+#else
+        transport_sense = ((SAM_STAT_CHECK_CONDITION ==
+                            get_scsi_pt_status_response(ptvp)) && (slen > 0));
+#endif
+        if (transport_sense)
+            return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp,
+                                          slen, noisy, verbose, o_sense_cat);
+        else
+            return -1;
+    case SCSI_PT_RESULT_OS_ERR:
+        if (verbose || noisy) {
+            get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
+            pr2ws("%s: os: %s\n", leadin, b);
+        }
+        return -1;
+    default:
+        pr2ws("%s: unknown pass through result category (%d)\n", leadin, cat);
+        return -1;
+    }
+}
+
+/* Invokes a SCSI INQUIRY command and yields the response. Returns 0 when
+ * successful, various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op, void * resp,
+              int mx_resp_len, int noisy, int verbose)
+{
+    int res, ret, k, sense_cat, resid;
+    unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    unsigned char * up;
+    struct sg_pt_base * ptvp;
+
+    if (cmddt)
+        inqCmdBlk[1] |= 2;
+    if (evpd)
+        inqCmdBlk[1] |= 1;
+    inqCmdBlk[2] = (unsigned char)pg_op;
+    /* 16 bit allocation length (was 8) is a recent SPC-3 addition */
+    sg_put_unaligned_be16((uint16_t)mx_resp_len, inqCmdBlk + 3);
+    if (verbose) {
+        pr2ws("    inquiry cdb: ");
+        for (k = 0; k < INQUIRY_CMDLEN; ++k)
+            pr2ws("%02x ", inqCmdBlk[k]);
+        pr2ws("\n");
+    }
+    if (resp && (mx_resp_len > 0)) {
+        up = (unsigned char *)resp;
+        up[0] = 0x7f;   /* defensive prefill */
+        if (mx_resp_len > 4)
+            up[4] = 0;
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("inquiry: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, inqCmdBlk, sizeof(inqCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b,
+                               noisy, verbose, &sense_cat);
+    resid = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else if (ret < 4) {
+        if (verbose)
+            pr2ws("inquiry: got too few bytes (%d)\n", ret);
+        ret = SG_LIB_CAT_MALFORMED;
+    } else
+        ret = 0;
+
+    if (resid > 0) {
+        if (resid > mx_resp_len) {
+            pr2ws("inquiry: resid (%d) should never exceed requested "
+                  "len=%d\n", resid, mx_resp_len);
+            return ret ? ret : SG_LIB_CAT_MALFORMED;
+        }
+        /* zero unfilled section of response buffer */
+        memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
+    }
+    return ret;
+}
+
+/* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response.
+ * Returns 0 when successful, various SG_LIB_CAT_* positive values or
+ * -1 -> other errors */
+int
+sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data,
+                  int noisy, int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (inq_data) {
+        memset(inq_data, 0, sizeof(* inq_data));
+        inq_data->peripheral_qualifier = 0x3;
+        inq_data->peripheral_type = 0x1f;
+    }
+    inqCmdBlk[4] = (unsigned char)sizeof(inq_resp);
+    if (verbose) {
+        pr2ws("    inquiry cdb: ");
+        for (k = 0; k < INQUIRY_CMDLEN; ++k)
+            pr2ws("%02x ", inqCmdBlk[k]);
+        pr2ws("\n");
+    }
+    memset(inq_resp, 0, sizeof(inq_resp));
+    inq_resp[0] = 0x7f; /* defensive prefill */
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("inquiry: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, inqCmdBlk, sizeof(inqCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, inq_resp, sizeof(inq_resp));
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "inquiry", res, sizeof(inq_resp),
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else if (ret < 4) {
+        if (verbose)
+            pr2ws("inquiry: got too few bytes (%d)\n", ret);
+        ret = SG_LIB_CAT_MALFORMED;
+    } else
+        ret = 0;
+
+    if (inq_data && (0 == ret)) {
+        inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7;
+        inq_data->peripheral_type = inq_resp[0] & 0x1f;
+        inq_data->byte_1 = inq_resp[1];
+        inq_data->version = inq_resp[2];
+        inq_data->byte_3 = inq_resp[3];
+        inq_data->byte_5 = inq_resp[5];
+        inq_data->byte_6 = inq_resp[6];
+        inq_data->byte_7 = inq_resp[7];
+        memcpy(inq_data->vendor, inq_resp + 8, 8);
+        memcpy(inq_data->product, inq_resp + 16, 16);
+        memcpy(inq_data->revision, inq_resp + 32, 4);
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI TEST UNIT READY command.
+ * 'pack_id' is just for diagnostics, safe to set to 0.
+ * Looks for progress indicator if 'progress' non-NULL;
+ * if found writes value [0..65535] else write -1.
+ * Returns 0 when successful, various SG_LIB_CAT_* positive values or
+ * -1 -> other errors */
+int
+sg_ll_test_unit_ready_progress(int sg_fd, int pack_id, int * progress,
+                               int noisy, int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char turCmdBlk[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (verbose) {
+        pr2ws("    test unit ready cdb: ");
+        for (k = 0; k < TUR_CMDLEN; ++k)
+            pr2ws("%02x ", turCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("test unit ready: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, turCmdBlk, sizeof(turCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_packet_id(ptvp, pack_id);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "test unit ready", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        if (progress) {
+            int slen = get_scsi_pt_sense_len(ptvp);
+
+            if (! sg_get_sense_progress_fld(sense_b, slen, progress))
+                *progress = -1;
+        }
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI TEST UNIT READY command.
+ * 'pack_id' is just for diagnostics, safe to set to 0.
+ * Returns 0 when successful, various SG_LIB_CAT_* positive values or
+ * -1 -> other errors */
+int
+sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy, int verbose)
+{
+    return sg_ll_test_unit_ready_progress(sg_fd, pack_id, NULL, noisy,
+                                          verbose);
+}
+
+/* Invokes a SCSI REQUEST SENSE command. Returns 0 when successful, various
+ * SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_request_sense(int sg_fd, int desc, void * resp, int mx_resp_len,
+                    int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rsCmdBlk[REQUEST_SENSE_CMDLEN] =
+        {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (desc)
+        rsCmdBlk[1] |= 0x1;
+    if (mx_resp_len > 0xff) {
+        pr2ws("mx_resp_len cannot exceed 255\n");
+        return -1;
+    }
+    rsCmdBlk[4] = mx_resp_len & 0xff;
+    if (verbose) {
+        pr2ws("    Request Sense cmd: ");
+        for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k)
+            pr2ws("%02x ", rsCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("request sense: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rsCmdBlk, sizeof(rsCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "request sense", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((mx_resp_len >= 8) && (ret < 8)) {
+            if (verbose)
+                pr2ws("    request sense: got %d bytes in response, too "
+                      "short\n", ret);
+            ret = -1;
+        } else
+            ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI REPORT LUNS command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_report_luns(int sg_fd, int select_report, void * resp, int mx_resp_len,
+                  int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rlCmdBlk[REPORT_LUNS_CMDLEN] =
+                         {REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    rlCmdBlk[2] = select_report & 0xff;
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rlCmdBlk + 6);
+    if (verbose) {
+        pr2ws("    report luns cdb: ");
+        for (k = 0; k < REPORT_LUNS_CMDLEN; ++k)
+            pr2ws("%02x ", rlCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("report luns: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rlCmdBlk, sizeof(rlCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "report luns", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
diff --git a/sg3_utils/lib/sg_cmds_basic2.c b/sg3_utils/lib/sg_cmds_basic2.c
new file mode 100644
index 0000000..adb2916
--- /dev/null
+++ b/sg3_utils/lib/sg_cmds_basic2.c
@@ -0,0 +1,927 @@
+/*
+ * Copyright (c) 1999-2015 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.
+ */
+
+/*
+ * CONTENTS
+ *    Some SCSI commands are executed in many contexts and hence began
+ *    to appear in several sg3_utils utilities. This files centralizes
+ *    some of the low level command execution code. In most cases the
+ *    interpretation of the command response is left to the each
+ *    utility.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pt.h"
+#include "sg_unaligned.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define EBUFF_SZ 256
+
+#define DEF_PT_TIMEOUT 60       /* 60 seconds */
+#define START_PT_TIMEOUT 120    /* 120 seconds == 2 minutes */
+#define LONG_PT_TIMEOUT 7200    /* 7,200 seconds == 120 minutes */
+
+#define SYNCHRONIZE_CACHE_CMD     0x35
+#define SYNCHRONIZE_CACHE_CMDLEN  10
+#define SERVICE_ACTION_IN_16_CMD 0x9e
+#define SERVICE_ACTION_IN_16_CMDLEN 16
+#define READ_CAPACITY_16_SA 0x10
+#define READ_CAPACITY_10_CMD 0x25
+#define READ_CAPACITY_10_CMDLEN 10
+#define MODE_SENSE6_CMD      0x1a
+#define MODE_SENSE6_CMDLEN   6
+#define MODE_SENSE10_CMD     0x5a
+#define MODE_SENSE10_CMDLEN  10
+#define MODE_SELECT6_CMD   0x15
+#define MODE_SELECT6_CMDLEN   6
+#define MODE_SELECT10_CMD   0x55
+#define MODE_SELECT10_CMDLEN  10
+#define LOG_SENSE_CMD     0x4d
+#define LOG_SENSE_CMDLEN  10
+#define LOG_SELECT_CMD     0x4c
+#define LOG_SELECT_CMDLEN  10
+#define START_STOP_CMD          0x1b
+#define START_STOP_CMDLEN       6
+#define PREVENT_ALLOW_CMD    0x1e
+#define PREVENT_ALLOW_CMDLEN   6
+
+#define MODE6_RESP_HDR_LEN 4
+#define MODE10_RESP_HDR_LEN 8
+#define MODE_RESP_ARB_LEN 1024
+
+#define INQUIRY_RESP_INITIAL_LEN 36
+
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group,
+                    unsigned int lba, unsigned int count, int noisy,
+                    int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char scCmdBlk[SYNCHRONIZE_CACHE_CMDLEN] =
+                {SYNCHRONIZE_CACHE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (sync_nv)
+        scCmdBlk[1] |= 4;
+    if (immed)
+        scCmdBlk[1] |= 2;
+    sg_put_unaligned_be32((uint32_t)lba, scCmdBlk + 2);
+    scCmdBlk[6] = group & 0x1f;
+    if (count > 0xffff) {
+        pr2ws("count too big\n");
+        return -1;
+    }
+    sg_put_unaligned_be16((int16_t)count, scCmdBlk + 7);
+
+    if (verbose) {
+        pr2ws("    synchronize cache(10) cdb: ");
+        for (k = 0; k < SYNCHRONIZE_CACHE_CMDLEN; ++k)
+            pr2ws("%02x ", scCmdBlk[k]);
+        pr2ws("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("synchronize cache(10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, scCmdBlk, sizeof(scCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "synchronize cache(10)", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_readcap_16(int sg_fd, int pmi, uint64_t llba, void * resp,
+                 int mx_resp_len, int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rcCmdBlk[SERVICE_ACTION_IN_16_CMDLEN] =
+                        {SERVICE_ACTION_IN_16_CMD, READ_CAPACITY_16_SA,
+                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (pmi) { /* lbs only valid when pmi set */
+        rcCmdBlk[14] |= 1;
+        sg_put_unaligned_be64(llba, rcCmdBlk + 2);
+    }
+    /* Allocation length, no guidance in SBC-2 rev 15b */
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rcCmdBlk + 10);
+    if (verbose) {
+        pr2ws("    read capacity (16) cdb: ");
+        for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k)
+            pr2ws("%02x ", rcCmdBlk[k]);
+        pr2ws("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("read capacity (16): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rcCmdBlk, sizeof(rcCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "read capacity (16)", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI READ CAPACITY (10) command. Returns 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba, void * resp,
+                 int mx_resp_len, int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rcCmdBlk[READ_CAPACITY_10_CMDLEN] =
+                         {READ_CAPACITY_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (pmi) { /* lbs only valid when pmi set */
+        rcCmdBlk[8] |= 1;
+        sg_put_unaligned_be32((uint32_t)lba, rcCmdBlk + 2);
+    }
+    if (verbose) {
+        pr2ws("    read capacity (10) cdb: ");
+        for (k = 0; k < READ_CAPACITY_10_CMDLEN; ++k)
+            pr2ws("%02x ", rcCmdBlk[k]);
+        pr2ws("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("read capacity (10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rcCmdBlk, sizeof(rcCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "read capacity (10)", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code,
+                  void * resp, int mx_resp_len, int noisy, int verbose)
+{
+    int res, ret, k, sense_cat, resid;
+    unsigned char modesCmdBlk[MODE_SENSE6_CMDLEN] =
+        {MODE_SENSE6_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0);
+    modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
+    modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff);
+    modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff);
+    if (mx_resp_len > 0xff) {
+        pr2ws("mx_resp_len too big\n");
+        return -1;
+    }
+    if (verbose) {
+        pr2ws("    mode sense (6) cdb: ");
+        for (k = 0; k < MODE_SENSE6_CMDLEN; ++k)
+            pr2ws("%02x ", modesCmdBlk[k]);
+        pr2ws("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("mode sense (6): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, modesCmdBlk, sizeof(modesCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "mode sense (6)", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    resid = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    mode sense (6): response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+
+    if (resid > 0) {
+        if (resid > mx_resp_len) {
+            pr2ws("mode sense(6): resid (%d) should never exceed requested "
+                  "len=%d\n", resid, mx_resp_len);
+            return ret ? ret : SG_LIB_CAT_MALFORMED;
+        }
+        /* zero unfilled section of response buffer */
+        memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
+    }
+    return ret;
+}
+
+/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code,
+                   int sub_pg_code, void * resp, int mx_resp_len,
+                   int noisy, int verbose)
+{
+    int res, ret, k, sense_cat, resid;
+    unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
+        {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    modesCmdBlk[1] = (unsigned char)((dbd ? 0x8 : 0) | (llbaa ? 0x10 : 0));
+    modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
+    modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff);
+    sg_put_unaligned_be16((int16_t)mx_resp_len, modesCmdBlk + 7);
+    if (mx_resp_len > 0xffff) {
+        pr2ws("mx_resp_len too big\n");
+        return -1;
+    }
+    if (verbose) {
+        pr2ws("    mode sense (10) cdb: ");
+        for (k = 0; k < MODE_SENSE10_CMDLEN; ++k)
+            pr2ws("%02x ", modesCmdBlk[k]);
+        pr2ws("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("mode sense (10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, modesCmdBlk, sizeof(modesCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "mode sense (10)", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    resid = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    mode sense (10): response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+
+    if (resid > 0) {
+        if (resid > mx_resp_len) {
+            pr2ws("mode sense(10): resid (%d) should never exceed requested "
+                  "len=%d\n", resid, mx_resp_len);
+            return ret ? ret : SG_LIB_CAT_MALFORMED;
+        }
+        /* zero unfilled section of response buffer */
+        memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
+    }
+    return ret;
+}
+
+/* Invokes a SCSI MODE SELECT (6) command.  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp, int param_len,
+                   int noisy, int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char modesCmdBlk[MODE_SELECT6_CMDLEN] =
+        {MODE_SELECT6_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    modesCmdBlk[1] = (unsigned char)(((pf << 4) & 0x10) | (sp & 0x1));
+    modesCmdBlk[4] = (unsigned char)(param_len & 0xff);
+    if (param_len > 0xff) {
+        pr2ws("mode select (6): param_len too big\n");
+        return -1;
+    }
+    if (verbose) {
+        pr2ws("    mode select (6) cdb: ");
+        for (k = 0; k < MODE_SELECT6_CMDLEN; ++k)
+            pr2ws("%02x ", modesCmdBlk[k]);
+        pr2ws("\n");
+    }
+    if (verbose > 1) {
+        pr2ws("    mode select (6) parameter list\n");
+        dStrHexErr((const char *)paramp, param_len, -1);
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("mode select (6): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, modesCmdBlk, sizeof(modesCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "mode select (6)", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI MODE SELECT (10) command.  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp, int param_len,
+                    int noisy, int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] =
+        {MODE_SELECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    modesCmdBlk[1] = (unsigned char)(((pf << 4) & 0x10) | (sp & 0x1));
+    sg_put_unaligned_be16((int16_t)param_len, modesCmdBlk + 7);
+    if (param_len > 0xffff) {
+        pr2ws("mode select (10): param_len too big\n");
+        return -1;
+    }
+    if (verbose) {
+        pr2ws("    mode select (10) cdb: ");
+        for (k = 0; k < MODE_SELECT10_CMDLEN; ++k)
+            pr2ws("%02x ", modesCmdBlk[k]);
+        pr2ws("\n");
+    }
+    if (verbose > 1) {
+        pr2ws("    mode select (10) parameter list\n");
+        dStrHexErr((const char *)paramp, param_len, -1);
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("mode select (10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, modesCmdBlk, sizeof(modesCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "mode select (10)", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* MODE SENSE commands yield a response that has block descriptors followed
+ * by mode pages. In most cases users are interested in the first mode page.
+ * This function returns the (byte) offset of the start of the first mode
+ * page. Set mode_sense_6 to 1 for MODE SENSE (6) and 0 for MODE SENSE (10).
+ * Returns >= 0 is successful or -1 if failure. If there is a failure
+ * a message is written to err_buff if err_buff_len > 0. */
+int
+sg_mode_page_offset(const unsigned char * resp, int resp_len,
+                    int mode_sense_6, char * err_buff, int err_buff_len)
+{
+    int bd_len;
+    int calc_len;
+    int offset;
+
+    if ((NULL == resp) || (resp_len < 4) ||
+        ((! mode_sense_6) && (resp_len < 8))) {
+        if ((err_buff_len > 0) && err_buff)
+            snprintf(err_buff, err_buff_len, "given response length too "
+                     "short: %d\n", resp_len);
+        return -1;
+    }
+    if (mode_sense_6) {
+        calc_len = resp[0] + 1;
+        bd_len = resp[3];
+        offset = bd_len + MODE6_RESP_HDR_LEN;
+    } else {
+        calc_len = sg_get_unaligned_be16(resp) + 2;
+        bd_len = sg_get_unaligned_be16(resp + 6);
+        /* LongLBA doesn't change this calculation */
+        offset = bd_len + MODE10_RESP_HDR_LEN;
+    }
+    if ((offset + 2) > resp_len) {
+        if ((err_buff_len > 0) && err_buff)
+            snprintf(err_buff, err_buff_len, "given response length "
+                     "too small, offset=%d given_len=%d bd_len=%d\n",
+                     offset, resp_len, bd_len);
+        offset = -1;
+    } else if ((offset + 2) > calc_len) {
+        if ((err_buff_len > 0) && err_buff)
+            snprintf(err_buff, err_buff_len, "calculated response "
+                     "length too small, offset=%d calc_len=%d bd_len=%d\n",
+                     offset, calc_len, bd_len);
+        offset = -1;
+    }
+    return offset;
+}
+
+/* Fetches current, changeable, default and/or saveable modes pages as
+ * indicated by pcontrol_arr for given pg_code and sub_pg_code. If
+ * mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If
+ * flexible set and mode data length seems wrong then try and
+ * fix (compensating hack for bad device or driver). pcontrol_arr
+ * should have 4 elements for output of current, changeable, default
+ * and saved values respectively. Each element should be NULL or
+ * at least mx_mpage_len bytes long.
+ * Return of 0 -> overall success, various SG_LIB_CAT_* positive values or
+ * -1 -> other errors.
+ * If success_mask pointer is not NULL then first zeros it. Then set bits
+ * 0, 1, 2 and/or 3 if the current, changeable, default and saved values
+ * respectively have been fetched. If error on current page
+ * then stops and returns that error; otherwise continues if an error is
+ * detected but returns the first error encountered.  */
+int
+sg_get_mode_page_controls(int sg_fd, int mode6, int pg_code, int sub_pg_code,
+                          int dbd, int flexible, int mx_mpage_len,
+                          int * success_mask, void * pcontrol_arr[],
+                          int * reported_len, int verbose)
+{
+    int k, n, res, offset, calc_len, xfer_len, resp_mode6;
+    unsigned char buff[MODE_RESP_ARB_LEN];
+    char ebuff[EBUFF_SZ];
+    int first_err = 0;
+
+    if (success_mask)
+        *success_mask = 0;
+    if (reported_len)
+        *reported_len = 0;
+    if (mx_mpage_len < 4)
+        return 0;
+    memset(ebuff, 0, sizeof(ebuff));
+    /* first try to find length of current page response */
+    memset(buff, 0, MODE10_RESP_HDR_LEN);
+    if (mode6)  /* want first 8 bytes just in case */
+        res = sg_ll_mode_sense6(sg_fd, dbd, 0 /* pc */, pg_code,
+                                sub_pg_code, buff, MODE10_RESP_HDR_LEN, 1,
+                                verbose);
+    else
+        res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd,
+                                 0 /* pc */, pg_code, sub_pg_code, buff,
+                                 MODE10_RESP_HDR_LEN, 1, verbose);
+    if (0 != res)
+        return res;
+    n = buff[0];
+    if (reported_len)
+        *reported_len = mode6 ? (n + 1) : (sg_get_unaligned_be16(buff) + 2);
+    resp_mode6 = mode6;
+    if (flexible) {
+        if (mode6 && (n < 3)) {
+            resp_mode6 = 0;
+            if (verbose)
+                pr2ws(">>> msense(6) but resp[0]=%d so try msense(10) "
+                      "response processing\n", n);
+        }
+        if ((0 == mode6) && (n > 5)) {
+            if ((n > 11) && (0 == (n % 2)) && (0 == buff[4]) &&
+                (0 == buff[5]) && (0 == buff[6])) {
+                buff[1] = n;
+                buff[0] = 0;
+                if (verbose)
+                    pr2ws(">>> msense(10) but resp[0]=%d and not msense(6) "
+                          "response so fix length\n", n);
+            } else
+                resp_mode6 = 1;
+        }
+    }
+    if (verbose && (resp_mode6 != mode6))
+        pr2ws(">>> msense(%d) but resp[0]=%d so switch response "
+              "processing\n", (mode6 ? 6 : 10), buff[0]);
+    calc_len = resp_mode6 ? (buff[0] + 1) : (sg_get_unaligned_be16(buff) + 2);
+    if (calc_len > MODE_RESP_ARB_LEN)
+        calc_len = MODE_RESP_ARB_LEN;
+    offset = sg_mode_page_offset(buff, calc_len, resp_mode6,
+                                 ebuff, EBUFF_SZ);
+    if (offset < 0) {
+        if (('\0' != ebuff[0]) && (verbose > 0))
+            pr2ws("sg_get_mode_page_controls: %s\n", ebuff);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    xfer_len = calc_len - offset;
+    if (xfer_len > mx_mpage_len)
+        xfer_len = mx_mpage_len;
+
+    for (k = 0; k < 4; ++k) {
+        if (NULL == pcontrol_arr[k])
+            continue;
+        memset(pcontrol_arr[k], 0, mx_mpage_len);
+        if (mode6)
+            res = sg_ll_mode_sense6(sg_fd, dbd, k /* pc */,
+                                    pg_code, sub_pg_code, buff,
+                                    calc_len, 1, verbose);
+        else
+            res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd,
+                                     k /* pc */, pg_code, sub_pg_code,
+                                     buff, calc_len, 1, verbose);
+        if (0 != res) {
+            if (0 == first_err)
+                first_err = res;
+            if (0 == k)
+                break;  /* if problem on current page, it won't improve */
+            else
+                continue;
+        }
+        if (xfer_len > 0)
+            memcpy(pcontrol_arr[k], buff + offset, xfer_len);
+        if (success_mask)
+            *success_mask |= (1 << k);
+    }
+    return first_err;
+}
+
+/* Invokes a SCSI LOG SENSE command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code,
+                int subpg_code, int paramp, unsigned char * resp,
+                int mx_resp_len, int noisy, int verbose)
+{
+    int res, ret, k, sense_cat, resid;
+    unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] =
+        {LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (mx_resp_len > 0xffff) {
+        pr2ws("mx_resp_len too big\n");
+        return -1;
+    }
+    logsCmdBlk[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0));
+    logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
+    logsCmdBlk[3] = (unsigned char)(subpg_code & 0xff);
+    sg_put_unaligned_be16((int16_t)paramp, logsCmdBlk + 5);
+    sg_put_unaligned_be16((int16_t)mx_resp_len, logsCmdBlk + 7);
+    if (verbose) {
+        pr2ws("    log sense cdb: ");
+        for (k = 0; k < LOG_SENSE_CMDLEN; ++k)
+            pr2ws("%02x ", logsCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("log sense: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, logsCmdBlk, sizeof(logsCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "log sense", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    resid = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((mx_resp_len > 3) && (ret < 4)) {
+            /* resid indicates LOG SENSE response length bad, so zero it */
+            resp[2] = 0;
+            resp[3] = 0;
+        }
+        ret = 0;
+    }
+
+    if (resid > 0) {
+        if (resid > mx_resp_len) {
+            pr2ws("log sense: resid (%d) should never exceed requested "
+                  "len=%d\n", resid, mx_resp_len);
+            return ret ? ret : SG_LIB_CAT_MALFORMED;
+        }
+        /* zero unfilled section of response buffer */
+        memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
+    }
+    return ret;
+}
+
+/* Invokes a SCSI LOG SELECT command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_log_select(int sg_fd, int pcr, int sp, int pc, int pg_code,
+                 int subpg_code, unsigned char * paramp, int param_len,
+                 int noisy, int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char logsCmdBlk[LOG_SELECT_CMDLEN] =
+        {LOG_SELECT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (param_len > 0xffff) {
+        pr2ws("log select: param_len too big\n");
+        return -1;
+    }
+    logsCmdBlk[1] = (unsigned char)((pcr ? 2 : 0) | (sp ? 1 : 0));
+    logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
+    logsCmdBlk[3] = (unsigned char)(subpg_code & 0xff);
+    sg_put_unaligned_be16((int16_t)param_len, logsCmdBlk + 7);
+    if (verbose) {
+        pr2ws("    log select cdb: ");
+        for (k = 0; k < LOG_SELECT_CMDLEN; ++k)
+            pr2ws("%02x ", logsCmdBlk[k]);
+        pr2ws("\n");
+    }
+    if ((verbose > 1) && (param_len > 0)) {
+        pr2ws("    log select parameter list\n");
+        dStrHexErr((const char *)paramp, param_len, -1);
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("log select: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, logsCmdBlk, sizeof(logsCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "log select", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI START STOP UNIT command (SBC + MMC).
+ * Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors.
+ * SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and
+ * format_layer_number(mmc) fields. They also overlap on the noflush(sbc)
+ * and fl(mmc) one bit field. This is the cause of the awkardly named
+ * pc_mod__fl_num and noflush__fl arguments to this function.
+ *  */
+int
+sg_ll_start_stop_unit(int sg_fd, int immed, int pc_mod__fl_num,
+                      int power_cond, int noflush__fl, int loej, int start,
+                      int noisy, int verbose)
+{
+    unsigned char ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    int k, res, ret, sense_cat;
+    struct sg_pt_base * ptvp;
+
+    ssuBlk[1] = immed & 1;
+    ssuBlk[3] = pc_mod__fl_num & 0xf;  /* bits 2 and 3 are reserved in MMC */
+    ssuBlk[4] = ((power_cond & 0xf) << 4) | (noflush__fl ? 0x4 : 0) |
+                 (loej ? 0x2 : 0) | (start ? 0x1 : 0);
+    if (verbose) {
+        pr2ws("    Start stop unit command:");
+        for (k = 0; k < (int)sizeof(ssuBlk); ++k)
+                pr2ws(" %02x", ssuBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("start stop unit: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    res = do_scsi_pt(ptvp, sg_fd, START_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "start stop unit", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+            ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command
+ * [was in SPC-3 but displaced from SPC-4 into SBC-3, MMC-5, SSC-3]
+ * prevent==0 allows removal, prevent==1 prevents removal ...
+ * Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_prevent_allow(int sg_fd, int prevent, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char pCmdBlk[PREVENT_ALLOW_CMDLEN] =
+                {PREVENT_ALLOW_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if ((prevent < 0) || (prevent > 3)) {
+        pr2ws("prevent argument should be 0, 1, 2 or 3\n");
+        return -1;
+    }
+    pCmdBlk[4] |= (prevent & 0x3);
+    if (verbose) {
+        pr2ws("    Prevent allow medium removal cdb: ");
+        for (k = 0; k < PREVENT_ALLOW_CMDLEN; ++k)
+            pr2ws("%02x ", pCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("prevent allow medium removal: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, pCmdBlk, sizeof(pCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "prevent allow medium removal", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+            ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
diff --git a/sg3_utils/lib/sg_cmds_extra.c b/sg3_utils/lib/sg_cmds_extra.c
new file mode 100644
index 0000000..5ccf370
--- /dev/null
+++ b/sg3_utils/lib/sg_cmds_extra.c
@@ -0,0 +1,2071 @@
+/*
+ * Copyright (c) 1999-2016 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pt.h"
+#include "sg_unaligned.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+
+#define DEF_PT_TIMEOUT 60       /* 60 seconds */
+#define LONG_PT_TIMEOUT 7200    /* 7,200 seconds == 120 minutes */
+
+#define SERVICE_ACTION_IN_16_CMD 0x9e
+#define SERVICE_ACTION_IN_16_CMDLEN 16
+#define SERVICE_ACTION_OUT_16_CMD 0x9f
+#define SERVICE_ACTION_OUT_16_CMDLEN 16
+#define MAINTENANCE_IN_CMD 0xa3
+#define MAINTENANCE_IN_CMDLEN 12
+#define MAINTENANCE_OUT_CMD 0xa4
+#define MAINTENANCE_OUT_CMDLEN 12
+
+#define ATA_PT_12_CMD 0xa1
+#define ATA_PT_12_CMDLEN 12
+#define ATA_PT_16_CMD 0x85
+#define ATA_PT_16_CMDLEN 16
+#define FORMAT_UNIT_CMD 0x4
+#define FORMAT_UNIT_CMDLEN 6
+#define PERSISTENT_RESERVE_IN_CMD 0x5e
+#define PERSISTENT_RESERVE_IN_CMDLEN 10
+#define PERSISTENT_RESERVE_OUT_CMD 0x5f
+#define PERSISTENT_RESERVE_OUT_CMDLEN 10
+#define READ_BLOCK_LIMITS_CMD 0x5
+#define READ_BLOCK_LIMITS_CMDLEN 6
+#define READ_BUFFER_CMD 0x3c
+#define READ_BUFFER_CMDLEN 10
+#define READ_DEFECT10_CMD     0x37
+#define READ_DEFECT10_CMDLEN    10
+#define REASSIGN_BLKS_CMD     0x7
+#define REASSIGN_BLKS_CMDLEN  6
+#define RECEIVE_DIAGNOSTICS_CMD   0x1c
+#define RECEIVE_DIAGNOSTICS_CMDLEN  6
+#define THIRD_PARTY_COPY_OUT_CMD 0x83   /* was EXTENDED_COPY_CMD */
+#define THIRD_PARTY_COPY_OUT_CMDLEN 16
+#define THIRD_PARTY_COPY_IN_CMD 0x84     /* was RECEIVE_COPY_RESULTS_CMD */
+#define THIRD_PARTY_COPY_IN_CMDLEN 16
+#define SEND_DIAGNOSTIC_CMD   0x1d
+#define SEND_DIAGNOSTIC_CMDLEN  6
+#define SERVICE_ACTION_IN_12_CMD 0xab
+#define SERVICE_ACTION_IN_12_CMDLEN 12
+#define READ_LONG10_CMD 0x3e
+#define READ_LONG10_CMDLEN 10
+#define UNMAP_CMD 0x42
+#define UNMAP_CMDLEN 10
+#define VERIFY10_CMD 0x2f
+#define VERIFY10_CMDLEN 10
+#define VERIFY16_CMD 0x8f
+#define VERIFY16_CMDLEN 16
+#define WRITE_LONG10_CMD 0x3f
+#define WRITE_LONG10_CMDLEN 10
+#define WRITE_BUFFER_CMD 0x3b
+#define WRITE_BUFFER_CMDLEN 10
+
+#define GET_LBA_STATUS_SA 0x12
+#define READ_LONG_16_SA 0x11
+#define READ_MEDIA_SERIAL_NUM_SA 0x1
+#define REPORT_IDENTIFYING_INFORMATION_SA 0x5
+#define REPORT_TGT_PRT_GRP_SA 0xa
+#define SET_IDENTIFYING_INFORMATION_SA 0x6
+#define SET_TGT_PRT_GRP_SA 0xa
+#define WRITE_LONG_16_SA 0x11
+#define REPORT_REFERRALS_SA 0x13
+#define EXTENDED_COPY_LID1_SA 0x0
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+
+/* Invokes a SCSI GET LBA STATUS command (SBC). Returns 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_get_lba_status(int sg_fd, uint64_t start_llba, void * resp,
+                     int alloc_len, int noisy, int verbose)
+{
+    int k, res, sense_cat, ret;
+    unsigned char getLbaStatCmd[SERVICE_ACTION_IN_16_CMDLEN];
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    memset(getLbaStatCmd, 0, sizeof(getLbaStatCmd));
+    getLbaStatCmd[0] = SERVICE_ACTION_IN_16_CMD;
+    getLbaStatCmd[1] = GET_LBA_STATUS_SA;
+
+    sg_put_unaligned_be64(start_llba, getLbaStatCmd + 2);
+    sg_put_unaligned_be32((uint32_t)alloc_len, getLbaStatCmd + 10);
+    if (verbose) {
+        pr2ws("    Get LBA status cmd: ");
+        for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k)
+            pr2ws("%02x ", getLbaStatCmd[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("get LBA status: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, getLbaStatCmd, sizeof(getLbaStatCmd));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, alloc_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "get LBA status", res, alloc_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    get LBA status: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+int
+sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, int mx_resp_len,
+                         int noisy, int verbose)
+{
+    return sg_ll_report_tgt_prt_grp2(sg_fd, resp, mx_resp_len, 0, noisy,
+                                     verbose);
+}
+
+/* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_report_tgt_prt_grp2(int sg_fd, void * resp, int mx_resp_len,
+                          int extended, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char rtpgCmdBlk[MAINTENANCE_IN_CMDLEN] =
+                         {MAINTENANCE_IN_CMD, REPORT_TGT_PRT_GRP_SA,
+                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (extended) {
+        rtpgCmdBlk[1] |= 0x20;
+    }
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rtpgCmdBlk + 6);
+    if (verbose) {
+        pr2ws("    report target port groups cdb: ");
+        for (k = 0; k < MAINTENANCE_IN_CMDLEN; ++k)
+            pr2ws("%02x ", rtpgCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("report target port groups: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rtpgCmdBlk, sizeof(rtpgCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "report target port group", res,
+                               mx_resp_len, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    report target port group: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI SET TARGET PORT GROUPS command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_set_tgt_prt_grp(int sg_fd, void * paramp, int param_len, int noisy,
+                      int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char stpgCmdBlk[MAINTENANCE_OUT_CMDLEN] =
+                         {MAINTENANCE_OUT_CMD, SET_TGT_PRT_GRP_SA,
+                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be32((uint32_t)param_len, stpgCmdBlk + 6);
+    if (verbose) {
+        pr2ws("    set target port groups cdb: ");
+        for (k = 0; k < MAINTENANCE_OUT_CMDLEN; ++k)
+            pr2ws("%02x ", stpgCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2ws("    set target port groups parameter list:\n");
+            dStrHexErr((const char *)paramp, param_len, -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("set target port groups: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, stpgCmdBlk, sizeof(stpgCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "set target port group", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI REPORT REFERRALS command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_report_referrals(int sg_fd, uint64_t start_llba, int one_seg,
+                       void * resp, int mx_resp_len, int noisy,
+                       int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char repRefCmdBlk[SERVICE_ACTION_IN_16_CMDLEN] =
+                         {SERVICE_ACTION_IN_16_CMD, REPORT_REFERRALS_SA,
+                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be64(start_llba, repRefCmdBlk + 2);
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, repRefCmdBlk + 10);
+    repRefCmdBlk[14] = one_seg & 0x1;
+    if (verbose) {
+        pr2ws("    report referrals cdb: ");
+        for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k)
+            pr2ws("%02x ", repRefCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("report target port groups: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, repRefCmdBlk, sizeof(repRefCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "report referrals", res,
+                               mx_resp_len, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    report referrals: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can
+ * take a long time, if so set long_duration flag in which case the timout
+ * is set to 7200 seconds; if the value of long_duration is > 7200 then that
+ * value is taken as the timeout value in seconds. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit, int devofl_bit,
+                int unitofl_bit, int long_duration, void * paramp,
+                int param_len, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat, tmout;
+    unsigned char senddiagCmdBlk[SEND_DIAGNOSTIC_CMDLEN] =
+        {SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    senddiagCmdBlk[1] = (unsigned char)((sf_code << 5) | (pf_bit << 4) |
+                        (sf_bit << 2) | (devofl_bit << 1) | unitofl_bit);
+    sg_put_unaligned_be16((uint16_t)param_len, senddiagCmdBlk + 3);
+
+    if (verbose) {
+        pr2ws("    Send diagnostic cmd: ");
+        for (k = 0; k < SEND_DIAGNOSTIC_CMDLEN; ++k)
+            pr2ws("%02x ", senddiagCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2ws("    Send diagnostic parameter list:\n");
+            dStrHexErr((const char *)paramp, param_len, -1);
+        }
+    }
+    if (long_duration > LONG_PT_TIMEOUT)
+        tmout = long_duration;
+    else
+        tmout = long_duration ? LONG_PT_TIMEOUT : DEF_PT_TIMEOUT;
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("send diagnostic: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, senddiagCmdBlk, sizeof(senddiagCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, tmout, verbose);
+    ret = sg_cmds_process_resp(ptvp, "send diagnostic", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI RECEIVE DIAGNOSTIC RESULTS command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp,
+                   int mx_resp_len, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char rcvdiagCmdBlk[RECEIVE_DIAGNOSTICS_CMDLEN] =
+        {RECEIVE_DIAGNOSTICS_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    rcvdiagCmdBlk[1] = (unsigned char)(pcv ? 0x1 : 0);
+    rcvdiagCmdBlk[2] = (unsigned char)(pg_code);
+    sg_put_unaligned_be16((uint16_t)mx_resp_len, rcvdiagCmdBlk + 3);
+
+    if (verbose) {
+        pr2ws("    Receive diagnostic results cmd: ");
+        for (k = 0; k < RECEIVE_DIAGNOSTICS_CMDLEN; ++k)
+            pr2ws("%02x ", rcvdiagCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("receive diagnostic results: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rcvdiagCmdBlk, sizeof(rcvdiagCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "receive diagnostic results", res,
+                               mx_resp_len, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    receive diagnostic results: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 -> success
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist, int dl_format,
+                    void * resp, int mx_resp_len, int noisy, int verbose)
+{
+    int res, k, ret, sense_cat;
+    unsigned char rdefCmdBlk[READ_DEFECT10_CMDLEN] =
+        {READ_DEFECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    rdefCmdBlk[2] = (unsigned char)(((req_plist << 4) & 0x10) |
+                         ((req_glist << 3) & 0x8) | (dl_format & 0x7));
+    sg_put_unaligned_be16((uint16_t)mx_resp_len, rdefCmdBlk + 7);
+    if (mx_resp_len > 0xffff) {
+        pr2ws("mx_resp_len too big\n");
+        return -1;
+    }
+    if (verbose) {
+        pr2ws("    read defect (10) cdb: ");
+        for (k = 0; k < READ_DEFECT10_CMDLEN; ++k)
+            pr2ws("%02x ", rdefCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("read defect (10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rdefCmdBlk, sizeof(rdefCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "read defect (10)", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    read defect (10): response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_read_media_serial_num(int sg_fd, void * resp, int mx_resp_len,
+                            int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char rmsnCmdBlk[SERVICE_ACTION_IN_12_CMDLEN] =
+                         {SERVICE_ACTION_IN_12_CMD, READ_MEDIA_SERIAL_NUM_SA,
+                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rmsnCmdBlk + 6);
+    if (verbose) {
+        pr2ws("    read media serial number cdb: ");
+        for (k = 0; k < SERVICE_ACTION_IN_12_CMDLEN; ++k)
+            pr2ws("%02x ", rmsnCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("read media serial number: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rmsnCmdBlk, sizeof(rmsnCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "read media serial number", res,
+                               mx_resp_len, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    read media serial number: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI REPORT IDENTIFYING INFORMATION command. This command was
+ * called REPORT DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_report_id_info(int sg_fd, int itype, void * resp, int max_resp_len,
+                     int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char riiCmdBlk[MAINTENANCE_IN_CMDLEN] = {MAINTENANCE_IN_CMD,
+                        REPORT_IDENTIFYING_INFORMATION_SA,
+                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be32((uint32_t)max_resp_len, riiCmdBlk + 6);
+    riiCmdBlk[10] |= (itype << 1) & 0xfe;
+
+    if (verbose) {
+        pr2ws("    Report identifying information cdb: ");
+        for (k = 0; k < MAINTENANCE_IN_CMDLEN; ++k)
+            pr2ws("%02x ", riiCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("report identifying information: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, riiCmdBlk, sizeof(riiCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, max_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "report identifying information", res,
+                               max_resp_len, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    report identifying information: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI SET IDENTIFYING INFORMATION command. This command was
+ * called SET DEVICE IDENTIFIER prior to spc4r07. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_set_id_info(int sg_fd, int itype, void * paramp, int param_len,
+                  int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char siiCmdBlk[MAINTENANCE_OUT_CMDLEN] = {MAINTENANCE_OUT_CMD,
+                         SET_IDENTIFYING_INFORMATION_SA,
+                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be32((uint32_t)param_len, siiCmdBlk + 6);
+    siiCmdBlk[10] |= (itype << 1) & 0xfe;
+    if (verbose) {
+        pr2ws("    Set identifying information cdb: ");
+        for (k = 0; k < MAINTENANCE_OUT_CMDLEN; ++k)
+            pr2ws("%02x ", siiCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2ws("    Set identifying information parameter list:\n");
+            dStrHexErr((const char *)paramp, param_len, -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("Set identifying information: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, siiCmdBlk, sizeof(siiCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "set identifying information", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a FORMAT UNIT (SBC-3) command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_format_unit(int sg_fd, int fmtpinfo, int longlist, int fmtdata,
+                  int cmplst, int dlist_format, int timeout_secs,
+                  void * paramp, int param_len, int noisy, int verbose)
+{
+    return sg_ll_format_unit2(sg_fd, fmtpinfo, longlist, fmtdata, cmplst,
+                              dlist_format, 0, timeout_secs, paramp,
+                              param_len, noisy, verbose);
+}
+
+/* Invokes a FORMAT UNIT (SBC-4) command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors.
+ * FFMT field added in sbc4r10 [20160121] */
+int
+sg_ll_format_unit2(int sg_fd, int fmtpinfo, int longlist, int fmtdata,
+                   int cmplst, int dlist_format, int ffmt, int timeout_secs,
+                   void * paramp, int param_len, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat, tmout;
+    unsigned char fuCmdBlk[FORMAT_UNIT_CMDLEN] =
+                {FORMAT_UNIT_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (fmtpinfo)
+        fuCmdBlk[1] |= (fmtpinfo << 6);
+    if (longlist)
+        fuCmdBlk[1] |= 0x20;
+    if (fmtdata)
+        fuCmdBlk[1] |= 0x10;
+    if (cmplst)
+        fuCmdBlk[1] |= 0x8;
+    if (dlist_format)
+        fuCmdBlk[1] |= (dlist_format & 0x7);
+    if (ffmt)
+        fuCmdBlk[4] |= (ffmt & 0x3);
+    tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT;
+    if (verbose) {
+        pr2ws("    format unit cdb: ");
+        for (k = 0; k < 6; ++k)
+            pr2ws("%02x ", fuCmdBlk[k]);
+        pr2ws("\n");
+    }
+    if ((verbose > 1) && (param_len > 0)) {
+        pr2ws("    format parameter list:\n");
+        dStrHexErr((const char *)paramp, param_len, -1);
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("format unit: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, fuCmdBlk, sizeof(fuCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, tmout, verbose);
+    ret = sg_cmds_process_resp(ptvp, "format unit", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI REASSIGN BLOCKS command.  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_reassign_blocks(int sg_fd, int longlba, int longlist, void * paramp,
+                      int param_len, int noisy, int verbose)
+{
+    int res, k, ret, sense_cat;
+    unsigned char reassCmdBlk[REASSIGN_BLKS_CMDLEN] =
+        {REASSIGN_BLKS_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    reassCmdBlk[1] = (unsigned char)(((longlba << 1) & 0x2) |
+                     (longlist & 0x1));
+    if (verbose) {
+        pr2ws("    reassign blocks cdb: ");
+        for (k = 0; k < REASSIGN_BLKS_CMDLEN; ++k)
+            pr2ws("%02x ", reassCmdBlk[k]);
+        pr2ws("\n");
+    }
+    if (verbose > 1) {
+        pr2ws("    reassign blocks parameter list\n");
+        dStrHexErr((const char *)paramp, param_len, -1);
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("reassign blocks: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, reassCmdBlk, sizeof(reassCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "reassign blocks", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI PERSISTENT RESERVE IN command (SPC). Returns 0
+ * when successful, various SG_LIB_CAT_* positive values or
+ * -1 -> other errors */
+int
+sg_ll_persistent_reserve_in(int sg_fd, int rq_servact, void * resp,
+                            int mx_resp_len, int noisy, int verbose)
+{
+    int res, k, ret, sense_cat;
+    unsigned char prinCmdBlk[PERSISTENT_RESERVE_IN_CMDLEN] =
+                 {PERSISTENT_RESERVE_IN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (rq_servact > 0)
+        prinCmdBlk[1] = (unsigned char)(rq_servact & 0x1f);
+    sg_put_unaligned_be16((uint16_t)mx_resp_len, prinCmdBlk + 7);
+
+    if (verbose) {
+        pr2ws("    Persistent Reservation In cmd: ");
+        for (k = 0; k < PERSISTENT_RESERVE_IN_CMDLEN; ++k)
+            pr2ws("%02x ", prinCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("persistent reservation in: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, prinCmdBlk, sizeof(prinCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "persistent reservation in", res,
+                               mx_resp_len, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    persistent reserve in: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI PERSISTENT RESERVE OUT command (SPC). Returns 0
+ * when successful, various SG_LIB_CAT_* positive values or
+ * -1 -> other errors */
+int
+sg_ll_persistent_reserve_out(int sg_fd, int rq_servact, int rq_scope,
+                             unsigned int rq_type, void * paramp,
+                             int param_len, int noisy, int verbose)
+{
+    int res, k, ret, sense_cat;
+    unsigned char proutCmdBlk[PERSISTENT_RESERVE_OUT_CMDLEN] =
+                 {PERSISTENT_RESERVE_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (rq_servact > 0)
+        proutCmdBlk[1] = (unsigned char)(rq_servact & 0x1f);
+    proutCmdBlk[2] = (((rq_scope & 0xf) << 4) | (rq_type & 0xf));
+    sg_put_unaligned_be16((uint16_t)param_len, proutCmdBlk + 7);
+
+    if (verbose) {
+        pr2ws("    Persistent Reservation Out cmd: ");
+        for (k = 0; k < PERSISTENT_RESERVE_OUT_CMDLEN; ++k)
+            pr2ws("%02x ", proutCmdBlk[k]);
+        pr2ws("\n");
+        if (verbose > 1) {
+            pr2ws("    Persistent Reservation Out parameters:\n");
+            dStrHexErr((const char *)paramp, param_len, 0);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("persistent reserve out: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, proutCmdBlk, sizeof(proutCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "persistent reserve out", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+static int
+has_blk_ili(unsigned char * sensep, int sb_len)
+{
+    int resp_code;
+    const unsigned char * cup;
+
+    if (sb_len < 8)
+        return 0;
+    resp_code = (0x7f & sensep[0]);
+    if (resp_code >= 0x72) { /* descriptor format */
+        /* find block command descriptor */
+        if ((cup = sg_scsi_sense_desc_find(sensep, sb_len, 0x5)))
+            return ((cup[3] & 0x20) ? 1 : 0);
+    } else /* fixed */
+        return ((sensep[2] & 0x20) ? 1 : 0);
+    return 0;
+}
+
+/* Invokes a SCSI READ LONG (10) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_read_long10(int sg_fd, int pblock, int correct, unsigned int lba,
+                  void * resp, int xfer_len, int * offsetp, int noisy,
+                  int verbose)
+{
+    int k, res, sense_cat, ret;
+    unsigned char readLongCmdBlk[READ_LONG10_CMDLEN];
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    memset(readLongCmdBlk, 0, READ_LONG10_CMDLEN);
+    readLongCmdBlk[0] = READ_LONG10_CMD;
+    if (pblock)
+        readLongCmdBlk[1] |= 0x4;
+    if (correct)
+        readLongCmdBlk[1] |= 0x2;
+
+    sg_put_unaligned_be32((uint32_t)lba, readLongCmdBlk + 2);
+    sg_put_unaligned_be16((uint16_t)xfer_len, readLongCmdBlk + 7);
+    if (verbose) {
+        pr2ws("    Read Long (10) cmd: ");
+        for (k = 0; k < READ_LONG10_CMDLEN; ++k)
+            pr2ws("%02x ", readLongCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("read long (10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, readLongCmdBlk, sizeof(readLongCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, xfer_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "read long (10)", res, xfer_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_ILLEGAL_REQ:
+            {
+                int valid, slen, ili;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                ili = has_blk_ili(sense_b, slen);
+                if (valid && ili) {
+                    if (offsetp)
+                        *offsetp = (int)(int64_t)ull;
+                    ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO;
+                } else {
+                    if (verbose > 1)
+                        pr2ws("  info field: 0x%" PRIx64 ",  valid: %d, "
+                              "ili: %d\n", ull, valid, ili);
+                    ret = SG_LIB_CAT_ILLEGAL_REQ;
+                }
+            }
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    read long(10): response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI READ LONG (16) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_read_long16(int sg_fd, int pblock, int correct, uint64_t llba,
+                  void * resp, int xfer_len, int * offsetp, int noisy,
+                  int verbose)
+{
+    int k, res, sense_cat, ret;
+    unsigned char readLongCmdBlk[SERVICE_ACTION_IN_16_CMDLEN];
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    memset(readLongCmdBlk, 0, sizeof(readLongCmdBlk));
+    readLongCmdBlk[0] = SERVICE_ACTION_IN_16_CMD;
+    readLongCmdBlk[1] = READ_LONG_16_SA;
+    if (pblock)
+        readLongCmdBlk[14] |= 0x2;
+    if (correct)
+        readLongCmdBlk[14] |= 0x1;
+
+    sg_put_unaligned_be64(llba, readLongCmdBlk + 2);
+    sg_put_unaligned_be16((uint16_t)xfer_len, readLongCmdBlk + 12);
+    if (verbose) {
+        pr2ws("    Read Long (16) cmd: ");
+        for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k)
+            pr2ws("%02x ", readLongCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("read long (16): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, readLongCmdBlk, sizeof(readLongCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, xfer_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "read long (16)", res, xfer_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_ILLEGAL_REQ:
+            {
+                int valid, slen, ili;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                ili = has_blk_ili(sense_b, slen);
+                if (valid && ili) {
+                    if (offsetp)
+                        *offsetp = (int)(int64_t)ull;
+                    ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO;
+                } else {
+                    if (verbose > 1)
+                        pr2ws("  info field: 0x%" PRIx64 ",  valid: %d, "
+                              "ili: %d\n", ull, valid, ili);
+                    ret = SG_LIB_CAT_ILLEGAL_REQ;
+                }
+            }
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    read long(16): response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI WRITE LONG (10) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_write_long10(int sg_fd, int cor_dis, int wr_uncor, int pblock,
+                   unsigned int lba, void * data_out, int xfer_len,
+                   int * offsetp, int noisy, int verbose)
+{
+    int k, res, sense_cat, ret;
+    unsigned char writeLongCmdBlk[WRITE_LONG10_CMDLEN];
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    memset(writeLongCmdBlk, 0, WRITE_LONG10_CMDLEN);
+    writeLongCmdBlk[0] = WRITE_LONG10_CMD;
+    if (cor_dis)
+        writeLongCmdBlk[1] |= 0x80;
+    if (wr_uncor)
+        writeLongCmdBlk[1] |= 0x40;
+    if (pblock)
+        writeLongCmdBlk[1] |= 0x20;
+
+    sg_put_unaligned_be32((uint32_t)lba, writeLongCmdBlk + 2);
+    sg_put_unaligned_be16((uint16_t)xfer_len, writeLongCmdBlk + 7);
+    if (verbose) {
+        pr2ws("    Write Long (10) cmd: ");
+        for (k = 0; k < (int)sizeof(writeLongCmdBlk); ++k)
+            pr2ws("%02x ", writeLongCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("write long(10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, writeLongCmdBlk, sizeof(writeLongCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, xfer_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "write long(10)", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_ILLEGAL_REQ:
+            {
+                int valid, slen, ili;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                ili = has_blk_ili(sense_b, slen);
+                if (valid && ili) {
+                    if (offsetp)
+                        *offsetp = (int)(int64_t)ull;
+                    ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO;
+                } else {
+                    if (verbose > 1)
+                        pr2ws("  info field: 0x%" PRIx64 ",  valid: %d, "
+                              "ili: %d\n", ull, valid, ili);
+                    ret = SG_LIB_CAT_ILLEGAL_REQ;
+                }
+            }
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI WRITE LONG (16) command (SBC). Note that 'xfer_len'
+ * is in bytes. Returns 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_write_long16(int sg_fd, int cor_dis, int wr_uncor, int pblock,
+                   uint64_t llba, void * data_out, int xfer_len,
+                   int * offsetp, int noisy, int verbose)
+{
+    int k, res, sense_cat, ret;
+    unsigned char writeLongCmdBlk[SERVICE_ACTION_OUT_16_CMDLEN];
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    memset(writeLongCmdBlk, 0, sizeof(writeLongCmdBlk));
+    writeLongCmdBlk[0] = SERVICE_ACTION_OUT_16_CMD;
+    writeLongCmdBlk[1] = WRITE_LONG_16_SA;
+    if (cor_dis)
+        writeLongCmdBlk[1] |= 0x80;
+    if (wr_uncor)
+        writeLongCmdBlk[1] |= 0x40;
+    if (pblock)
+        writeLongCmdBlk[1] |= 0x20;
+
+    sg_put_unaligned_be64(llba, writeLongCmdBlk + 2);
+    sg_put_unaligned_be16((uint16_t)xfer_len, writeLongCmdBlk + 12);
+    if (verbose) {
+        pr2ws("    Write Long (16) cmd: ");
+        for (k = 0; k < SERVICE_ACTION_OUT_16_CMDLEN; ++k)
+            pr2ws("%02x ", writeLongCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("write long(16): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, writeLongCmdBlk, sizeof(writeLongCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, xfer_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "write long(16)", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_ILLEGAL_REQ:
+            {
+                int valid, slen, ili;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                ili = has_blk_ili(sense_b, slen);
+                if (valid && ili) {
+                    if (offsetp)
+                        *offsetp = (int)(int64_t)ull;
+                    ret = SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO;
+                } else {
+                    if (verbose > 1)
+                        pr2ws("  info field: 0x%" PRIx64 ",  valid: %d, "
+                              "ili: %d\n", ull, valid, ili);
+                    ret = SG_LIB_CAT_ILLEGAL_REQ;
+                }
+            }
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI VERIFY (10) command (SBC and MMC).
+ * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
+ * Returns of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_verify10(int sg_fd, int vrprotect, int dpo, int bytchk,
+               unsigned int lba, int veri_len, void * data_out,
+               int data_out_len, unsigned int * infop, int noisy,
+               int verbose)
+{
+    int k, res, ret, sense_cat, slen;
+    unsigned char vCmdBlk[VERIFY10_CMDLEN] =
+                {VERIFY10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    /* N.B. BYTCHK field expanded to 2 bits sbc3r34 */
+    vCmdBlk[1] = ((vrprotect & 0x7) << 5) | ((dpo & 0x1) << 4) |
+                 ((bytchk & 0x3) << 1) ;
+    sg_put_unaligned_be32((uint32_t)lba, vCmdBlk + 2);
+    sg_put_unaligned_be16((uint16_t)veri_len, vCmdBlk + 7);
+    if (verbose > 1) {
+        pr2ws("    Verify(10) cdb: ");
+        for (k = 0; k < VERIFY10_CMDLEN; ++k)
+            pr2ws("%02x ", vCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 3) && bytchk && data_out && (data_out_len > 0)) {
+            k = data_out_len > 4104 ? 4104 : data_out_len;
+            pr2ws("    data_out buffer%s\n",
+                  (data_out_len > 4104 ? ", first 4104 bytes" : ""));
+            dStrHexErr((const char *)data_out, k, verbose < 5);
+        }
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("verify (10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, vCmdBlk, sizeof(vCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    if (data_out_len > 0)
+        set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, data_out_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "verify (10)", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_MEDIUM_HARD:
+            {
+                int valid;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                if (valid) {
+                    if (infop)
+                        *infop = (unsigned int)ull;
+                    ret = SG_LIB_CAT_MEDIUM_HARD_WITH_INFO;
+                } else
+                    ret = SG_LIB_CAT_MEDIUM_HARD;
+            }
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI VERIFY (16) command (SBC and MMC).
+ * Note that 'veri_len' is in blocks while 'data_out_len' is in bytes.
+ * Returns of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_verify16(int sg_fd, int vrprotect, int dpo, int bytchk, uint64_t llba,
+               int veri_len, int group_num, void * data_out,
+               int data_out_len, uint64_t * infop, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat, slen;
+    unsigned char vCmdBlk[VERIFY16_CMDLEN] =
+                {VERIFY16_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    /* N.B. BYTCHK field expanded to 2 bits sbc3r34 */
+    vCmdBlk[1] = ((vrprotect & 0x7) << 5) | ((dpo & 0x1) << 4) |
+                 ((bytchk & 0x3) << 1) ;
+    sg_put_unaligned_be64(llba, vCmdBlk + 2);
+    sg_put_unaligned_be32((uint32_t)veri_len, vCmdBlk + 10);
+    vCmdBlk[14] = group_num & 0x1f;
+    if (verbose > 1) {
+        pr2ws("    Verify(16) cdb: ");
+        for (k = 0; k < VERIFY16_CMDLEN; ++k)
+            pr2ws("%02x ", vCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 3) && bytchk && data_out && (data_out_len > 0)) {
+            k = data_out_len > 4104 ? 4104 : data_out_len;
+            pr2ws("    data_out buffer%s\n",
+                  (data_out_len > 4104 ? ", first 4104 bytes" : ""));
+            dStrHexErr((const char *)data_out, k, verbose < 5);
+        }
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("verify (16): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, vCmdBlk, sizeof(vCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    if (data_out_len > 0)
+        set_scsi_pt_data_out(ptvp, (unsigned char *)data_out, data_out_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "verify (16)", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_MEDIUM_HARD:
+            {
+                int valid;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                if (valid) {
+                    if (infop)
+                        *infop = ull;
+                    ret = SG_LIB_CAT_MEDIUM_HARD_WITH_INFO;
+                } else
+                    ret = SG_LIB_CAT_MEDIUM_HARD;
+            }
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a ATA PASS-THROUGH (12 or 16) SCSI command (SAT). If cdb_len
+ * is 12 then a ATA PASS-THROUGH (12) command is called. If cdb_len is 16
+ * then a ATA PASS-THROUGH (16) command is called. If cdb_len is any other
+ * value -1 is returned. After copying from cdbp to an internal buffer,
+ * the first byte (i.e. offset 0) is set to 0xa1 if cdb_len is 12; or is
+ * set to 0x85 if cdb_len is 16. The last byte (offset 11 or offset 15) is
+ * set to 0x0 in the internal buffer. If timeout_secs <= 0 then the timeout
+ * is set to 60 seconds. For data in or out transfers set dinp or doutp,
+ * and dlen to the number of bytes to transfer. If dlen is zero then no data
+ * transfer is assumed. If sense buffer obtained then it is written to
+ * sensep, else sensep[0] is set to 0x0. If ATA return descriptor is obtained
+ * then written to ata_return_dp, else ata_return_dp[0] is set to 0x0. Either
+ * sensep or ata_return_dp (or both) may be NULL pointers. Returns SCSI
+ * status value (>= 0) or -1 if other error. Users are expected to check the
+ * sense buffer themselves. If available the data in resid is written to
+ * residp. Note in SAT-2 and later, fixed format sense data may be placed in
+ * *sensep in which case sensep[0]==0x70 .
+ */
+int
+sg_ll_ata_pt(int sg_fd, const unsigned char * cdbp, int cdb_len,
+             int timeout_secs, void * dinp, void * doutp, int dlen,
+             unsigned char * sensep, int max_sense_len,
+             unsigned char * ata_return_dp, int max_ata_return_len,
+             int * residp, int verbose)
+{
+    int k, res, slen, duration;
+    unsigned char aptCmdBlk[ATA_PT_16_CMDLEN] =
+                {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    unsigned char * sp;
+    const unsigned char * ucp;
+    struct sg_pt_base * ptvp;
+    const char * cnamep;
+    char b[256];
+    int ret = -1;
+
+    b[0] = '\0';
+    cnamep = (12 == cdb_len) ?
+             "ATA pass through (12)" : "ATA pass through (16)";
+    if ((NULL == cdbp) || ((12 != cdb_len) && (16 != cdb_len))) {
+        if (verbose) {
+            if (NULL == cdbp)
+                pr2ws("%s NULL cdb pointer\n", cnamep);
+            else
+                pr2ws("cdb_len must be 12 or 16\n");
+        }
+        return -1;
+    }
+    aptCmdBlk[0] = (12 == cdb_len) ? ATA_PT_12_CMD : ATA_PT_16_CMD;
+    if (sensep && (max_sense_len >= (int)sizeof(sense_b))) {
+        sp = sensep;
+        slen = max_sense_len;
+    } else {
+        sp = sense_b;
+        slen = sizeof(sense_b);
+    }
+    if (12 == cdb_len)
+        memcpy(aptCmdBlk + 1, cdbp + 1, ((cdb_len > 11) ? 10 : (cdb_len - 1)));
+    else
+        memcpy(aptCmdBlk + 1, cdbp + 1, ((cdb_len > 15) ? 14 : (cdb_len - 1)));
+    if (verbose) {
+        pr2ws("    %s cdb: ", cnamep);
+        for (k = 0; k < cdb_len; ++k)
+            pr2ws("%02x ", aptCmdBlk[k]);
+        pr2ws("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("%s: out of memory\n", cnamep);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, aptCmdBlk, cdb_len);
+    set_scsi_pt_sense(ptvp, sp, slen);
+    if (dlen > 0) {
+        if (dinp)
+            set_scsi_pt_data_in(ptvp, (unsigned char *)dinp, dlen);
+        else if (doutp)
+            set_scsi_pt_data_out(ptvp, (unsigned char *)doutp, dlen);
+    }
+    res = do_scsi_pt(ptvp, sg_fd,
+                     ((timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT),
+                     verbose);
+    if (SCSI_PT_DO_BAD_PARAMS == res) {
+        if (verbose)
+            pr2ws("%s: bad parameters\n", cnamep);
+        goto out;
+    } else if (SCSI_PT_DO_TIMEOUT == res) {
+        if (verbose)
+            pr2ws("%s: timeout\n", cnamep);
+        goto out;
+    } else if (res > 2) {
+        if (verbose)
+            pr2ws("%s: do_scsi_pt: errno=%d\n", cnamep, -res);
+    }
+
+    if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0))
+        pr2ws("      duration=%d ms\n", duration);
+
+    switch (get_scsi_pt_result_category(ptvp)) {
+    case SCSI_PT_RESULT_GOOD:
+        if ((sensep) && (max_sense_len > 0))
+            *sensep = 0;
+        if ((ata_return_dp) && (max_ata_return_len > 0))
+            *ata_return_dp = 0;
+        if (residp && (dlen > 0))
+            *residp = get_scsi_pt_resid(ptvp);
+        ret = 0;
+        break;
+    case SCSI_PT_RESULT_STATUS: /* other than GOOD + CHECK CONDITION */
+        if ((sensep) && (max_sense_len > 0))
+            *sensep = 0;
+        if ((ata_return_dp) && (max_ata_return_len > 0))
+            *ata_return_dp = 0;
+        ret = get_scsi_pt_status_response(ptvp);
+        break;
+    case SCSI_PT_RESULT_SENSE:
+        if (sensep && (sp != sensep)) {
+            k = get_scsi_pt_sense_len(ptvp);
+            k = (k > max_sense_len) ? max_sense_len : k;
+            memcpy(sensep, sp, k);
+        }
+        if (ata_return_dp && (max_ata_return_len > 0))  {
+            /* search for ATA return descriptor */
+            ucp = sg_scsi_sense_desc_find(sp, slen, 0x9);
+            if (ucp) {
+                k = ucp[1] + 2;
+                k = (k > max_ata_return_len) ? max_ata_return_len : k;
+                memcpy(ata_return_dp, ucp, k);
+            } else
+                ata_return_dp[0] = 0x0;
+        }
+        if (residp && (dlen > 0))
+            *residp = get_scsi_pt_resid(ptvp);
+        ret = get_scsi_pt_status_response(ptvp);
+        break;
+    case SCSI_PT_RESULT_TRANSPORT_ERR:
+        if (verbose)
+            pr2ws("%s: transport error: %s\n", cnamep,
+                  get_scsi_pt_transport_err_str(ptvp, sizeof(b), b));
+        break;
+    case SCSI_PT_RESULT_OS_ERR:
+        if (verbose)
+            pr2ws("%s: os error: %s\n", cnamep,
+                  get_scsi_pt_os_err_str(ptvp, sizeof(b) , b));
+        break;
+    default:
+        if (verbose)
+            pr2ws("%s: unknown pt_result_category=%d\n", cnamep,
+                  get_scsi_pt_result_category(ptvp));
+        break;
+    }
+
+out:
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI READ BUFFER command (SPC). Return of 0 -> success
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_read_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
+                  void * resp, int mx_resp_len, int noisy, int verbose)
+{
+    int res, k, ret, sense_cat;
+    unsigned char rbufCmdBlk[READ_BUFFER_CMDLEN] =
+        {READ_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    rbufCmdBlk[1] = (unsigned char)(mode & 0x1f);
+    rbufCmdBlk[2] = (unsigned char)(buffer_id & 0xff);
+    sg_put_unaligned_be24((uint32_t)buffer_offset, rbufCmdBlk + 3);
+    sg_put_unaligned_be24((uint32_t)mx_resp_len, rbufCmdBlk + 6);
+    if (verbose) {
+        pr2ws("    read buffer cdb: ");
+        for (k = 0; k < READ_BUFFER_CMDLEN; ++k)
+            pr2ws("%02x ", rbufCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("read buffer: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rbufCmdBlk, sizeof(rbufCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "read buffer", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    read buffer: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> success
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_write_buffer(int sg_fd, int mode, int buffer_id, int buffer_offset,
+                   void * paramp, int param_len, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char wbufCmdBlk[WRITE_BUFFER_CMDLEN] =
+        {WRITE_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    wbufCmdBlk[1] = (unsigned char)(mode & 0x1f);
+    wbufCmdBlk[2] = (unsigned char)(buffer_id & 0xff);
+    sg_put_unaligned_be24((uint32_t)buffer_offset, wbufCmdBlk + 3);
+    sg_put_unaligned_be24((uint32_t)param_len, wbufCmdBlk + 6);
+    if (verbose) {
+        pr2ws("    Write buffer cmd: ");
+        for (k = 0; k < WRITE_BUFFER_CMDLEN; ++k)
+            pr2ws("%02x ", wbufCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2ws("    Write buffer parameter list%s:\n",
+                  ((param_len > 256) ? " (first 256 bytes)" : ""));
+            dStrHexErr((const char *)paramp,
+                       ((param_len > 256) ? 256 : param_len), -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("write buffer: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, wbufCmdBlk, sizeof(wbufCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "write buffer", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI UNMAP command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_unmap(int sg_fd, int group_num, int timeout_secs, void * paramp,
+            int param_len, int noisy, int verbose)
+{
+    return sg_ll_unmap_v2(sg_fd, 0, group_num, timeout_secs, paramp,
+                          param_len, noisy, verbose);
+}
+
+/* Invokes a SCSI UNMAP (SBC-3) command. Version 2 adds anchor field
+ * (sbc3r22). Otherwise same as sg_ll_unmap() . */
+int
+sg_ll_unmap_v2(int sg_fd, int anchor, int group_num, int timeout_secs,
+               void * paramp, int param_len, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat, tmout;
+    unsigned char uCmdBlk[UNMAP_CMDLEN] =
+                         {UNMAP_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (anchor)
+        uCmdBlk[1] |= 0x1;
+    tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT;
+    uCmdBlk[6] = group_num & 0x1f;
+    sg_put_unaligned_be16((uint16_t)param_len, uCmdBlk + 7);
+    if (verbose) {
+        pr2ws("    unmap cdb: ");
+        for (k = 0; k < UNMAP_CMDLEN; ++k)
+            pr2ws("%02x ", uCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2ws("    unmap parameter list:\n");
+            dStrHexErr((const char *)paramp, param_len, -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("unmap: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, uCmdBlk, sizeof(uCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, tmout, verbose);
+    ret = sg_cmds_process_resp(ptvp, "unmap", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI READ BLOCK LIMITS command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_read_block_limits(int sg_fd, void * resp, int mx_resp_len,
+                        int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rlCmdBlk[READ_BLOCK_LIMITS_CMDLEN] =
+      {READ_BLOCK_LIMITS_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (verbose) {
+        pr2ws("    read block limits cdb: ");
+        for (k = 0; k < READ_BLOCK_LIMITS_CMDLEN; ++k)
+            pr2ws("%02x ", rlCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("read block limits: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rlCmdBlk, sizeof(rlCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "read block limits", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2ws("    read block limits: response%s\n",
+                  (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI RECEIVE COPY RESULTS command. Actually cover all current
+ * uses of opcode 0x84 (Third-party copy IN). Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_receive_copy_results(int sg_fd, int sa, int list_id, void * resp,
+                           int mx_resp_len, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char rcvcopyresCmdBlk[THIRD_PARTY_COPY_IN_CMDLEN] =
+      {THIRD_PARTY_COPY_IN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+    char b[64];
+
+    sg_get_opcode_sa_name(THIRD_PARTY_COPY_IN_CMD, sa, 0, (int)sizeof(b), b);
+    rcvcopyresCmdBlk[1] = (unsigned char)(sa & 0x1f);
+    if (sa <= 4)        /* LID1 variants */
+        rcvcopyresCmdBlk[2] = (unsigned char)(list_id);
+    else if ((sa >= 5) && (sa <= 7))    /* LID4 variants */
+        sg_put_unaligned_be32((uint32_t)list_id, rcvcopyresCmdBlk + 2);
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rcvcopyresCmdBlk + 10);
+
+    if (verbose) {
+        pr2ws("    %s cmd: ", b);
+        for (k = 0; k < THIRD_PARTY_COPY_IN_CMDLEN; ++k)
+            pr2ws("%02x ", rcvcopyresCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("%s: out of memory\n", b);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rcvcopyresCmdBlk, sizeof(rcvcopyresCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, b, res, mx_resp_len, sense_b, noisy,
+                               verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+
+/* SPC-4 rev 35 and later calls this opcode (0x83) "Third-party copy OUT"
+ * The original EXTENDED COPY command (now called EXTENDED COPY (LID1))
+ * is the only one supported by sg_ll_extended_copy(). See function
+ * sg_ll_3party_copy_out() for the other service actions ( > 0 ). */
+
+/* Invokes a SCSI EXTENDED COPY (LID1) command. Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_extended_copy(int sg_fd, void * paramp, int param_len, int noisy,
+                    int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char xcopyCmdBlk[THIRD_PARTY_COPY_OUT_CMDLEN] =
+      {THIRD_PARTY_COPY_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+    const char * opcode_name = "Extended copy (LID1)";
+
+    xcopyCmdBlk[1] = (unsigned char)(EXTENDED_COPY_LID1_SA & 0x1f);
+    sg_put_unaligned_be32((uint32_t)param_len, xcopyCmdBlk + 10);
+
+    if (verbose) {
+        pr2ws("    %s cmd: ", opcode_name);
+        for (k = 0; k < THIRD_PARTY_COPY_OUT_CMDLEN; ++k)
+            pr2ws("%02x ", xcopyCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2ws("    %s parameter list:\n", opcode_name);
+            dStrHexErr((const char *)paramp, param_len, -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("%s: out of memory\n", opcode_name);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, xcopyCmdBlk, sizeof(xcopyCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, opcode_name, res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Handles various service actions associated with opcode 0x83 which is
+ * called THIRD PARTY COPY OUT. These include the EXTENDED COPY(LID1 and
+ * LID4), POPULATE TOKEN and WRITE USING TOKEN commands.
+ * Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+int
+sg_ll_3party_copy_out(int sg_fd, int sa, unsigned int list_id, int group_num,
+                      int timeout_secs, void * paramp, int param_len,
+                      int noisy, int verbose)
+{
+    int k, res, ret, sense_cat, tmout;
+    unsigned char xcopyCmdBlk[THIRD_PARTY_COPY_OUT_CMDLEN] =
+      {THIRD_PARTY_COPY_OUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+    char cname[80];
+
+    sg_get_opcode_sa_name(THIRD_PARTY_COPY_OUT_CMD, sa, 0, sizeof(cname),
+                          cname);
+    xcopyCmdBlk[1] = (unsigned char)(sa & 0x1f);
+    switch (sa) {
+    case 0x0:   /* XCOPY(LID1) */
+    case 0x1:   /* XCOPY(LID4) */
+        sg_put_unaligned_be32((uint32_t)param_len, xcopyCmdBlk + 10);
+        break;
+    case 0x10:  /* POPULATE TOKEN (SBC-3) */
+    case 0x11:  /* WRITE USING TOKEN (SBC-3) */
+        sg_put_unaligned_be32((uint32_t)list_id, xcopyCmdBlk + 6);
+        sg_put_unaligned_be32((uint32_t)param_len, xcopyCmdBlk + 10);
+        xcopyCmdBlk[14] = (unsigned char)(group_num & 0x1f);
+        break;
+    case 0x1c:  /* COPY OPERATION ABORT */
+        sg_put_unaligned_be32((uint32_t)list_id, xcopyCmdBlk + 2);
+        break;
+    default:
+        pr2ws("sg_ll_3party_copy_out: unknown service action 0x%x\n", sa);
+        return -1;
+    }
+    tmout = (timeout_secs > 0) ? timeout_secs : DEF_PT_TIMEOUT;
+
+    if (verbose) {
+        pr2ws("    %s cmd: ", cname);
+        for (k = 0; k < THIRD_PARTY_COPY_OUT_CMDLEN; ++k)
+            pr2ws("%02x ", xcopyCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2ws("    %s parameter list:\n", cname);
+            dStrHexErr((const char *)paramp, param_len, -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("%s: out of memory\n", cname);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, xcopyCmdBlk, sizeof(xcopyCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, tmout, verbose);
+    ret = sg_cmds_process_resp(ptvp, cname, res, 0, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
diff --git a/sg3_utils/lib/sg_cmds_mmc.c b/sg3_utils/lib/sg_cmds_mmc.c
new file mode 100644
index 0000000..8bb8569
--- /dev/null
+++ b/sg3_utils/lib/sg_cmds_mmc.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2008-2015 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_mmc.h"
+#include "sg_pt.h"
+#include "sg_unaligned.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+
+#define DEF_PT_TIMEOUT 60       /* 60 seconds */
+
+#define GET_CONFIG_CMD 0x46
+#define GET_CONFIG_CMD_LEN 10
+#define GET_PERFORMANCE_CMD 0xac
+#define GET_PERFORMANCE_CMD_LEN 12
+#define SET_CD_SPEED_CMD 0xbb
+#define SET_CD_SPEED_CMDLEN 12
+#define SET_STREAMING_CMD 0xb6
+#define SET_STREAMING_CMDLEN 12
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+/* Invokes a SCSI SET CD SPEED command (MMC).
+ * Return of 0 -> success, SG_LIB_CAT_INVALID_OP -> command not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+int
+sg_ll_set_cd_speed(int sg_fd, int rot_control, int drv_read_speed,
+                   int drv_write_speed, int noisy, int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char scsCmdBlk[SET_CD_SPEED_CMDLEN] = {SET_CD_SPEED_CMD, 0,
+                                         0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    scsCmdBlk[1] |= (rot_control & 0x3);
+    sg_put_unaligned_be16((uint16_t)drv_read_speed, scsCmdBlk + 2);
+    sg_put_unaligned_be16((uint16_t)drv_write_speed, scsCmdBlk + 4);
+
+    if (verbose) {
+        pr2ws("    set cd speed cdb: ");
+        for (k = 0; k < SET_CD_SPEED_CMDLEN; ++k)
+            pr2ws("%02x ", scsCmdBlk[k]);
+        pr2ws("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("set cd speed: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, scsCmdBlk, sizeof(scsCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "set cd speed", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_NOT_READY:
+        case SG_LIB_CAT_UNIT_ATTENTION:
+        case SG_LIB_CAT_INVALID_OP:
+        case SG_LIB_CAT_ILLEGAL_REQ:
+        case SG_LIB_CAT_ABORTED_COMMAND:
+            ret = sense_cat;
+            break;
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = -1;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI GET CONFIGURATION command (MMC-3,4,5).
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
+ * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
+int
+sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
+                 int mx_resp_len, int noisy, int verbose)
+{
+    int res, k, ret, sense_cat;
+    unsigned char gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0,
+                                                  0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if ((rt < 0) || (rt > 3)) {
+        pr2ws("Bad rt value: %d\n", rt);
+        return -1;
+    }
+    gcCmdBlk[1] = (rt & 0x3);
+    if ((starting < 0) || (starting > 0xffff)) {
+        pr2ws("Bad starting field number: 0x%x\n", starting);
+        return -1;
+    }
+    sg_put_unaligned_be16((uint16_t)starting, gcCmdBlk + 2);
+    if ((mx_resp_len < 0) || (mx_resp_len > 0xffff)) {
+        pr2ws("Bad mx_resp_len: 0x%x\n", starting);
+        return -1;
+    }
+    sg_put_unaligned_be16((uint16_t)mx_resp_len, gcCmdBlk + 7);
+
+    if (verbose) {
+        pr2ws("    Get Configuration cdb: ");
+        for (k = 0; k < GET_CONFIG_CMD_LEN; ++k)
+            pr2ws("%02x ", gcCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("get configuration: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, gcCmdBlk, sizeof(gcCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "get configuration", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_INVALID_OP:
+        case SG_LIB_CAT_ILLEGAL_REQ:
+        case SG_LIB_CAT_UNIT_ATTENTION:
+        case SG_LIB_CAT_ABORTED_COMMAND:
+            ret = sense_cat;
+            break;
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = -1;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 3)) {
+            unsigned char * ucp;
+            int len;
+
+            ucp = (unsigned char *)resp;
+            len = sg_get_unaligned_be32(ucp + 0);
+            if (len < 0)
+                len = 0;
+            len = (ret < len) ? ret : len;
+            pr2ws("    get configuration: response%s\n",
+                  (len > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (len > 256 ? 256 : len), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI GET PERFORMANCE command (MMC-3...6).
+ * Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
+ * supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_ABORTED_COMMAND, else -1 */
+int
+sg_ll_get_performance(int sg_fd, int data_type, unsigned int starting_lba,
+                      int max_num_desc, int ttype, void * resp,
+                      int mx_resp_len, int noisy, int verbose)
+{
+    int res, k, ret, sense_cat;
+    unsigned char gpCmdBlk[GET_PERFORMANCE_CMD_LEN] = {GET_PERFORMANCE_CMD, 0,
+                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if ((data_type < 0) || (data_type > 0x1f)) {
+        pr2ws("Bad data_type value: %d\n", data_type);
+        return -1;
+    }
+    gpCmdBlk[1] = (data_type & 0x1f);
+    sg_put_unaligned_be32((uint32_t)starting_lba, gpCmdBlk + 2);
+    if ((max_num_desc < 0) || (max_num_desc > 0xffff)) {
+        pr2ws("Bad max_num_desc: 0x%x\n", max_num_desc);
+        return -1;
+    }
+    sg_put_unaligned_be16((uint16_t)max_num_desc, gpCmdBlk + 8);
+    if ((ttype < 0) || (ttype > 0xff)) {
+        pr2ws("Bad type: 0x%x\n", ttype);
+        return -1;
+    }
+    gpCmdBlk[10] = (unsigned char)ttype;
+
+    if (verbose) {
+        pr2ws("    Get Performance cdb: ");
+        for (k = 0; k < GET_PERFORMANCE_CMD_LEN; ++k)
+            pr2ws("%02x ", gpCmdBlk[k]);
+        pr2ws("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("get performance: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, gpCmdBlk, sizeof(gpCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "get performance", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_INVALID_OP:
+        case SG_LIB_CAT_ILLEGAL_REQ:
+        case SG_LIB_CAT_UNIT_ATTENTION:
+        case SG_LIB_CAT_ABORTED_COMMAND:
+            ret = sense_cat;
+            break;
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = -1;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 3)) {
+            unsigned char * ucp;
+            int len;
+
+            ucp = (unsigned char *)resp;
+            len = sg_get_unaligned_be32(ucp + 0);
+            if (len < 0)
+                len = 0;
+            len = (ret < len) ? ret : len;
+            pr2ws("    get performance:: response%s\n",
+                  (len > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (len > 256 ? 256 : len), -1);
+        }
+        ret = 0;
+    }
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI SET STREAMING command (MMC). Return of 0 -> success,
+ * SG_LIB_CAT_INVALID_OP -> Set Streaming not supported,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_ABORTED_COMMAND,
+ * SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_NOT_READY -> device not ready,
+ * -1 -> other failure */
+int
+sg_ll_set_streaming(int sg_fd, int type, void * paramp, int param_len,
+                    int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    unsigned char ssCmdBlk[SET_STREAMING_CMDLEN] =
+                 {SET_STREAMING_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    ssCmdBlk[8] = type;
+    sg_put_unaligned_be16((uint16_t)param_len, ssCmdBlk + 9);
+    if (verbose) {
+        pr2ws("    set streaming cdb: ");
+        for (k = 0; k < SET_STREAMING_CMDLEN; ++k)
+            pr2ws("%02x ", ssCmdBlk[k]);
+        pr2ws("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2ws("    set streaming parameter list:\n");
+            dStrHexErr((const char *)paramp, param_len, -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2ws("set streaming: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, ssCmdBlk, sizeof(ssCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "set streaming", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_NOT_READY:
+        case SG_LIB_CAT_INVALID_OP:
+        case SG_LIB_CAT_ILLEGAL_REQ:
+        case SG_LIB_CAT_UNIT_ATTENTION:
+        case SG_LIB_CAT_ABORTED_COMMAND:
+            ret = sense_cat;
+            break;
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = -1;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
diff --git a/sg3_utils/lib/sg_io_linux.c b/sg3_utils/lib/sg_io_linux.c
new file mode 100644
index 0000000..e8d4778
--- /dev/null
+++ b/sg3_utils/lib/sg_io_linux.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 1999-2015 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef SG_LIB_LINUX
+
+#include "sg_io_linux.h"
+
+
+/* Version 1.06 20151217 */
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+
+void
+sg_print_masked_status(int masked_status)
+{
+    int scsi_status = (masked_status << 1) & 0x7e;
+
+    sg_print_scsi_status(scsi_status);
+}
+
+static const char * linux_host_bytes[] = {
+    "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT",
+    "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR",
+    "DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR",
+    "DID_IMM_RETRY", "DID_REQUEUE", "DID_TRANSPORT_DISRUPTED",
+    "DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE", "DID_NEXUS_FAILURE",
+    "DID_ALLOC_FAILURE", "DID_MEDIUM_ERROR",
+};
+
+#define LINUX_HOST_BYTES_SZ \
+        (int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0]))
+
+void
+sg_print_host_status(int host_status)
+{
+    pr2ws("Host_status=0x%02x ", host_status);
+    if ((host_status < 0) || (host_status >= LINUX_HOST_BYTES_SZ))
+        pr2ws("is invalid ");
+    else
+        pr2ws("[%s] ", linux_host_bytes[host_status]);
+}
+
+static const char * linux_driver_bytes[] = {
+    "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
+    "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
+    "DRIVER_SENSE"
+};
+
+#define LINUX_DRIVER_BYTES_SZ \
+    (int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0]))
+
+#if 0
+static const char * linux_driver_suggests[] = {
+    "SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
+    "SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
+    "SUGGEST_SENSE"
+};
+
+#define LINUX_DRIVER_SUGGESTS_SZ \
+    (int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0]))
+#endif
+
+
+void
+sg_print_driver_status(int driver_status)
+{
+    int driv;
+    const char * driv_cp = "invalid";
+
+    driv = driver_status & SG_LIB_DRIVER_MASK;
+    if (driv < LINUX_DRIVER_BYTES_SZ)
+        driv_cp = linux_driver_bytes[driv];
+#if 0
+    sugg = (driver_status & SG_LIB_SUGGEST_MASK) >> 4;
+    if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
+        sugg_cp = linux_driver_suggests[sugg];
+#endif
+    pr2ws("Driver_status=0x%02x", driver_status);
+    pr2ws(" [%s] ", driv_cp);
+}
+
+/* Returns 1 if no errors found and thus nothing printed; otherwise
+   prints error/warning (prefix by 'leadin') and returns 0. */
+static int
+sg_linux_sense_print(const char * leadin, int scsi_status, int host_status,
+                     int driver_status, const unsigned char * sense_buffer,
+                     int sb_len, int raw_sinfo)
+{
+    int done_leadin = 0;
+    int done_sense = 0;
+
+    scsi_status &= 0x7e; /*sanity */
+    if ((0 == scsi_status) && (0 == host_status) && (0 == driver_status))
+        return 1;       /* No problems */
+    if (0 != scsi_status) {
+        if (leadin)
+            pr2ws("%s: ", leadin);
+        done_leadin = 1;
+        pr2ws("SCSI status: ");
+        sg_print_scsi_status(scsi_status);
+        pr2ws("\n");
+        if (sense_buffer && ((scsi_status == SAM_STAT_CHECK_CONDITION) ||
+                             (scsi_status == SAM_STAT_COMMAND_TERMINATED))) {
+            /* SAM_STAT_COMMAND_TERMINATED is obsolete */
+            sg_print_sense(0, sense_buffer, sb_len, raw_sinfo);
+            done_sense = 1;
+        }
+    }
+    if (0 != host_status) {
+        if (leadin && (! done_leadin))
+            pr2ws("%s: ", leadin);
+        if (done_leadin)
+            pr2ws("plus...: ");
+        else
+            done_leadin = 1;
+        sg_print_host_status(host_status);
+        pr2ws("\n");
+    }
+    if (0 != driver_status) {
+        if (done_sense &&
+            (SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status)))
+            return 0;
+        if (leadin && (! done_leadin))
+            pr2ws("%s: ", leadin);
+        if (done_leadin)
+            pr2ws("plus...: ");
+        else
+            done_leadin = 1;
+        sg_print_driver_status(driver_status);
+        pr2ws("\n");
+        if (sense_buffer && (! done_sense) &&
+            (SG_LIB_DRIVER_SENSE == (SG_LIB_DRIVER_MASK & driver_status)))
+            sg_print_sense(0, sense_buffer, sb_len, raw_sinfo);
+    }
+    return 0;
+}
+
+#ifdef SG_IO
+
+int
+sg_normalize_sense(const struct sg_io_hdr * hp,
+                   struct sg_scsi_sense_hdr * sshp)
+{
+    if ((NULL == hp) || (0 == hp->sb_len_wr)) {
+        if (sshp)
+            memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr));
+        return 0;
+    }
+    return sg_scsi_normalize_sense(hp->sbp, hp->sb_len_wr, sshp);
+}
+
+/* Returns 1 if no errors found and thus nothing printed; otherwise
+   returns 0. */
+int
+sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp,
+                int raw_sinfo)
+{
+    return sg_linux_sense_print(leadin, hp->status, hp->host_status,
+                                hp->driver_status, hp->sbp, hp->sb_len_wr,
+                                raw_sinfo);
+}
+#endif
+
+/* Returns 1 if no errors found and thus nothing printed; otherwise
+   returns 0. */
+int
+sg_chk_n_print(const char * leadin, int masked_status, int host_status,
+               int driver_status, const unsigned char * sense_buffer,
+               int sb_len, int raw_sinfo)
+{
+    int scsi_status = (masked_status << 1) & 0x7e;
+
+    return sg_linux_sense_print(leadin, scsi_status, host_status,
+                                driver_status, sense_buffer, sb_len,
+                                raw_sinfo);
+}
+
+#ifdef SG_IO
+int
+sg_err_category3(struct sg_io_hdr * hp)
+{
+    return sg_err_category_new(hp->status, hp->host_status,
+                               hp->driver_status, hp->sbp, hp->sb_len_wr);
+}
+#endif
+
+int
+sg_err_category(int masked_status, int host_status, int driver_status,
+                const unsigned char * sense_buffer, int sb_len)
+{
+    int scsi_status = (masked_status << 1) & 0x7e;
+
+    return sg_err_category_new(scsi_status, host_status, driver_status,
+                               sense_buffer, sb_len);
+}
+
+int
+sg_err_category_new(int scsi_status, int host_status, int driver_status,
+                    const unsigned char * sense_buffer, int sb_len)
+{
+    int masked_driver_status = (SG_LIB_DRIVER_MASK & driver_status);
+
+    scsi_status &= 0x7e;
+    if ((0 == scsi_status) && (0 == host_status) &&
+        (0 == masked_driver_status))
+        return SG_LIB_CAT_CLEAN;
+    if ((SAM_STAT_CHECK_CONDITION == scsi_status) ||
+        (SAM_STAT_COMMAND_TERMINATED == scsi_status) ||
+        (SG_LIB_DRIVER_SENSE == masked_driver_status))
+        return sg_err_category_sense(sense_buffer, sb_len);
+    if (0 != host_status) {
+        if ((SG_LIB_DID_NO_CONNECT == host_status) ||
+            (SG_LIB_DID_BUS_BUSY == host_status) ||
+            (SG_LIB_DID_TIME_OUT == host_status))
+            return SG_LIB_CAT_TIMEOUT;
+        if (SG_LIB_DID_NEXUS_FAILURE == host_status)
+            return SG_LIB_CAT_RES_CONFLICT;
+    }
+    if (SG_LIB_DRIVER_TIMEOUT == masked_driver_status)
+        return SG_LIB_CAT_TIMEOUT;
+    return SG_LIB_CAT_OTHER;
+}
+
+#endif  /* if SG_LIB_LINUX defined */
diff --git a/sg3_utils/lib/sg_lib.c b/sg3_utils/lib/sg_lib.c
new file mode 100644
index 0000000..72f6339
--- /dev/null
+++ b/sg3_utils/lib/sg_lib.c
@@ -0,0 +1,2741 @@
+/*
+ * Copyright (c) 1999-2016 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.
+ */
+
+/* NOTICE:
+ *    On 5th October 2004 (v1.00) this file name was changed from sg_err.c
+ *    to sg_lib.c and the previous GPL was changed to a FreeBSD license.
+ *    The intention is to maintain this file and the related sg_lib.h file
+ *    as open source and encourage their unencumbered use.
+ *
+ * CONTRIBUTIONS:
+ *    This file started out as a copy of SCSI opcodes, sense keys and
+ *    additional sense codes (ASC/ASCQ) kept in the Linux SCSI subsystem
+ *    in the kernel source file: drivers/scsi/constant.c . That file
+ *    bore this notice: "Copyright (C) 1993, 1994, 1995 Eric Youngdale"
+ *    and a GPL notice.
+ *
+ *    Much of the data in this file is derived from SCSI draft standards
+ *    found at http://www.t10.org with the "SCSI Primary Commands-4" (SPC-4)
+ *    being the central point of reference.
+ *
+ *    Contributions:
+ *      sense key specific field decoding [Trent Piepho 20031116]
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_unaligned.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* sg_lib_version_str (and datestamp) defined in sg_lib_data.c file */
+
+#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d  /* corresponding ASC is 0 */
+
+FILE * sg_warnings_strm = NULL;        /* would like to default to stderr */
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+#ifdef __GNUC__
+static int my_snprintf(char * cp, int cp_max_len, const char * fmt, ...)
+                       __attribute__ ((format (printf, 3, 4)));
+#else
+static int my_snprintf(char * cp, int cp_max_len, const char * fmt, ...);
+#endif
+
+/* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of
+ * functions. Returns number number of chars placed in cp excluding the
+ * trailing null char. So for cp_max_len > 0 the return value is always
+ * < cp_max_len; for cp_max_len <= 1 the return value is 0 and no chars
+ * are written to cp. Note this means that when cp_max_len = 1, this
+ * function assumes that cp[0] is the null character and does nothing
+ * (and returns 0).  */
+static int
+my_snprintf(char * cp, int cp_max_len, const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    if (cp_max_len < 2)
+        return 0;
+    va_start(args, fmt);
+    n = vsnprintf(cp, cp_max_len, fmt, args);
+    va_end(args);
+    return (n < cp_max_len) ? n : (cp_max_len - 1);
+}
+
+/* Searches 'arr' for match on 'value' then 'peri_type'. If matches
+   'value' but not 'peri_type' then yields first 'value' match entry.
+   Last element of 'arr' has NULL 'name'. If no match returns NULL. */
+static const struct sg_lib_value_name_t *
+get_value_name(const struct sg_lib_value_name_t * arr, int value,
+               int peri_type)
+{
+    const struct sg_lib_value_name_t * vp = arr;
+    const struct sg_lib_value_name_t * holdp;
+
+    for (; vp->name; ++vp) {
+        if (value == vp->value) {
+            if (peri_type == vp->peri_dev_type)
+                return vp;
+            holdp = vp;
+            while ((vp + 1)->name && (value == (vp + 1)->value)) {
+                ++vp;
+                if (peri_type == vp->peri_dev_type)
+                    return vp;
+            }
+            return holdp;
+        }
+    }
+    return NULL;
+}
+
+/* If this function is not called, sg_warnings_strm will be NULL and all users
+ * (mainly fprintf() ) need to check and substitute stderr as required */
+void
+sg_set_warnings_strm(FILE * warnings_strm)
+{
+    sg_warnings_strm = warnings_strm;
+}
+
+#define CMD_NAME_LEN 128
+
+void
+sg_print_command(const unsigned char * command)
+{
+    int k, sz;
+    char buff[CMD_NAME_LEN];
+
+    sg_get_command_name(command, 0, CMD_NAME_LEN, buff);
+    buff[CMD_NAME_LEN - 1] = '\0';
+
+    pr2ws("%s [", buff);
+    if (SG_VARIABLE_LENGTH_CMD == command[0])
+        sz = command[7] + 8;
+    else
+        sz = sg_get_command_size(command[0]);
+    for (k = 0; k < sz; ++k)
+        pr2ws("%02x ", command[k]);
+    pr2ws("]\n");
+}
+
+void
+sg_get_scsi_status_str(int scsi_status, int buff_len, char * buff)
+{
+    const char * ccp = NULL;
+    int unknown = 0;
+
+    if ((NULL == buff) || (buff_len < 1))
+        return;
+    else if (1 ==  buff_len) {
+        buff[0] = '\0';
+        return;
+    }
+    scsi_status &= 0x7e; /* sanitize as much as possible */
+    switch (scsi_status) {
+        case 0: ccp = "Good"; break;
+        case 0x2: ccp = "Check Condition"; break;
+        case 0x4: ccp = "Condition Met"; break;
+        case 0x8: ccp = "Busy"; break;
+        case 0x10: ccp = "Intermediate (obsolete)"; break;
+        case 0x14: ccp = "Intermediate-Condition Met (obsolete)"; break;
+        case 0x18: ccp = "Reservation Conflict"; break;
+        case 0x22: ccp = "Command Terminated (obsolete)"; break;
+        case 0x28: ccp = "Task set Full"; break;
+        case 0x30: ccp = "ACA Active"; break;
+        case 0x40: ccp = "Task Aborted"; break;
+        default:
+            unknown = 1;
+            break;
+    }
+    if (unknown)
+        my_snprintf(buff, buff_len, "Unknown status [0x%x]", scsi_status);
+    else
+        my_snprintf(buff, buff_len, "%s", ccp);
+}
+
+void
+sg_print_scsi_status(int scsi_status)
+{
+    char buff[128];
+
+    sg_get_scsi_status_str(scsi_status, sizeof(buff) - 1, buff);
+    buff[sizeof(buff) - 1] = '\0';
+    pr2ws("%s ", buff);
+}
+
+/* Get sense key from sense buffer. If successful returns a sense key value
+ * between 0 and 15. If sense buffer cannot be decode, returns -1 . */
+int
+sg_get_sense_key(const unsigned char * sensep, int sense_len)
+{
+    if ((NULL == sensep) || (sense_len < 2))
+        return -1;
+    switch (sensep[0] & 0x7f) {
+    case 0x70:
+    case 0x71:
+        return (sense_len < 3) ? -1 : (sensep[2] & 0xf);
+    case 0x72:
+    case 0x73:
+        return sensep[1] & 0xf;
+    default:
+        return -1;
+    }
+}
+
+/* Yield string associated with sense_key value. Returns 'buff'. */
+char *
+sg_get_sense_key_str(int sense_key, int buff_len, char * buff)
+{
+    if (1 == buff_len) {
+        buff[0] = '\0';
+        return buff;
+    }
+    if ((sense_key >= 0) && (sense_key < 16))
+         my_snprintf(buff, buff_len, "%s", sg_lib_sense_key_desc[sense_key]);
+    else
+         my_snprintf(buff, buff_len, "invalid value: 0x%x", sense_key);
+    return buff;
+}
+
+/* Yield string associated with ASC/ASCQ values. Returns 'buff'. */
+char *
+sg_get_asc_ascq_str(int asc, int ascq, int buff_len, char * buff)
+{
+    int k, num, rlen;
+    int found = 0;
+    struct sg_lib_asc_ascq_t * eip;
+    struct sg_lib_asc_ascq_range_t * ei2p;
+
+    if (1 == buff_len) {
+        buff[0] = '\0';
+        return buff;
+    }
+    for (k = 0; sg_lib_asc_ascq_range[k].text; ++k) {
+        ei2p = &sg_lib_asc_ascq_range[k];
+        if ((ei2p->asc == asc) &&
+            (ascq >= ei2p->ascq_min)  &&
+            (ascq <= ei2p->ascq_max)) {
+            found = 1;
+            num = my_snprintf(buff, buff_len, "Additional sense: ");
+            rlen = buff_len - num;
+            num += my_snprintf(buff + num, ((rlen > 0) ? rlen : 0),
+                               ei2p->text, ascq);
+        }
+    }
+    if (found)
+        return buff;
+
+    for (k = 0; sg_lib_asc_ascq[k].text; ++k) {
+        eip = &sg_lib_asc_ascq[k];
+        if (eip->asc == asc &&
+            eip->ascq == ascq) {
+            found = 1;
+            my_snprintf(buff, buff_len, "Additional sense: %s", eip->text);
+        }
+    }
+    if (! found) {
+        if (asc >= 0x80)
+            my_snprintf(buff, buff_len, "vendor specific ASC=%02x, "
+                        "ASCQ=%02x (hex)", asc, ascq);
+        else if (ascq >= 0x80)
+            my_snprintf(buff, buff_len, "ASC=%02x, vendor specific "
+                        "qualification ASCQ=%02x (hex)", asc, ascq);
+        else
+            my_snprintf(buff, buff_len, "ASC=%02x, ASCQ=%02x (hex)", asc,
+                        ascq);
+    }
+    return buff;
+}
+
+/* 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 add_sb_len, add_d_len, desc_len, k;
+    const unsigned char * descp;
+
+    if ((sense_len < 8) || (0 == (add_sb_len = sensep[7])))
+        return NULL;
+    if ((sensep[0] < 0x72) || (sensep[0] > 0x73))
+        return NULL;
+    add_sb_len = (add_sb_len < (sense_len - 8)) ?
+                         add_sb_len : (sense_len - 8);
+    descp = &sensep[8];
+    for (desc_len = 0, k = 0; k < add_sb_len; k += desc_len) {
+        descp += desc_len;
+        add_d_len = (k < (add_sb_len - 1)) ? descp[1]: -1;
+        desc_len = add_d_len + 2;
+        if (descp[0] == desc_type)
+            return descp;
+        if (add_d_len < 0) /* short descriptor ?? */
+            break;
+    }
+    return NULL;
+}
+
+/* Returns 1 if valid bit set, 0 if valid bit clear. Irrespective the
+ * information field is written out via 'info_outp' (except when it is
+ * NULL). Handles both fixed and descriptor sense formats. */
+int
+sg_get_sense_info_fld(const unsigned char * sensep, int sb_len,
+                      uint64_t * info_outp)
+{
+    const unsigned char * ucp;
+    uint64_t ull;
+
+    if (info_outp)
+        *info_outp = 0;
+    if (sb_len < 7)
+        return 0;
+    switch (sensep[0] & 0x7f) {
+    case 0x70:
+    case 0x71:
+        if (info_outp)
+            *info_outp = sg_get_unaligned_be32(sensep + 3);
+        return (sensep[0] & 0x80) ? 1 : 0;
+    case 0x72:
+    case 0x73:
+        ucp = sg_scsi_sense_desc_find(sensep, sb_len, 0 /* info desc */);
+        if (ucp && (0xa == ucp[1])) {
+            ull = sg_get_unaligned_be64(ucp + 4);
+            if (info_outp)
+                *info_outp = ull;
+            return !!(ucp[2] & 0x80);   /* since spc3r23 should be set */
+        } else
+            return 0;
+    default:
+        return 0;
+    }
+}
+
+/* Returns 1 if any of the 3 bits (i.e. FILEMARK, EOM or ILI) are set.
+ * In descriptor format if the stream commands descriptor not found
+ * then returns 0. Writes 1 or 0 corresponding to these bits to the
+ * last three arguments if they are non-NULL. */
+int
+sg_get_sense_filemark_eom_ili(const unsigned char * sensep, int sb_len,
+                              int * filemark_p, int * eom_p, int * ili_p)
+{
+    const unsigned char * ucp;
+
+    if (sb_len < 7)
+        return 0;
+    switch (sensep[0] & 0x7f) {
+    case 0x70:
+    case 0x71:
+        if (sensep[2] & 0xe0) {
+            if (filemark_p)
+                *filemark_p = !!(sensep[2] & 0x80);
+            if (eom_p)
+                *eom_p = !!(sensep[2] & 0x40);
+            if (ili_p)
+                *ili_p = !!(sensep[2] & 0x20);
+            return 1;
+        } else
+            return 0;
+    case 0x72:
+    case 0x73:
+       /* Look for stream commands sense data descriptor */
+        ucp = sg_scsi_sense_desc_find(sensep, sb_len, 4);
+        if (ucp && (ucp[1] >= 2)) {
+            if (ucp[3] & 0xe0) {
+                if (filemark_p)
+                    *filemark_p = !!(ucp[3] & 0x80);
+                if (eom_p)
+                    *eom_p = !!(ucp[3] & 0x40);
+                if (ili_p)
+                    *ili_p = !!(ucp[3] & 0x20);
+                return 1;
+            }
+        }
+        return 0;
+    default:
+        return 0;
+    }
+}
+
+/* Returns 1 if SKSV is set and sense key is NO_SENSE or NOT_READY. Also
+ * returns 1 if progress indication sense data descriptor found. Places
+ * progress field from sense data where progress_outp points. If progress
+ * field is not available returns 0 and *progress_outp is unaltered. Handles
+ * both fixed and descriptor sense formats.
+ * Hint: if 1 is returned *progress_outp may be multiplied by 100 then
+ * divided by 65536 to get the percentage completion. */
+int
+sg_get_sense_progress_fld(const unsigned char * sensep, int sb_len,
+                          int * progress_outp)
+{
+    const unsigned char * ucp;
+    int sk, sk_pr;
+
+    if (sb_len < 7)
+        return 0;
+    switch (sensep[0] & 0x7f) {
+    case 0x70:
+    case 0x71:
+        sk = (sensep[2] & 0xf);
+        if ((sb_len < 18) ||
+            ((SPC_SK_NO_SENSE != sk) && (SPC_SK_NOT_READY != sk)))
+            return 0;
+        if (sensep[15] & 0x80) {        /* SKSV bit set */
+            if (progress_outp)
+                *progress_outp = sg_get_unaligned_be16(sensep + 16);
+            return 1;
+        } else
+            return 0;
+    case 0x72:
+    case 0x73:
+        /* sense key specific progress (0x2) or progress descriptor (0xa) */
+        sk = (sensep[1] & 0xf);
+        sk_pr = (SPC_SK_NO_SENSE == sk) || (SPC_SK_NOT_READY == sk);
+        if (sk_pr && ((ucp = sg_scsi_sense_desc_find(sensep, sb_len, 2))) &&
+            (0x6 == ucp[1]) && (0x80 & ucp[4])) {
+            if (progress_outp)
+                *progress_outp = sg_get_unaligned_be16(ucp + 5);
+            return 1;
+        } else if (((ucp = sg_scsi_sense_desc_find(sensep, sb_len, 0xa))) &&
+                   ((0x6 == ucp[1]))) {
+            if (progress_outp)
+                *progress_outp = sg_get_unaligned_be16(ucp + 6);
+            return 1;
+        } else
+            return 0;
+    default:
+        return 0;
+    }
+}
+
+char *
+sg_get_pdt_str(int pdt, int buff_len, char * buff)
+{
+    if ((pdt < 0) || (pdt > 31))
+        my_snprintf(buff, buff_len, "bad pdt");
+    else
+        my_snprintf(buff, buff_len, "%s", sg_lib_pdt_strs[pdt]);
+    return buff;
+}
+
+int
+sg_lib_pdt_decay(int pdt)
+{
+    if ((pdt < 0) || (pdt > 31))
+        return pdt;
+    return sg_lib_pdt_decay_arr[pdt];
+}
+
+char *
+sg_get_trans_proto_str(int tpi, int buff_len, char * buff)
+{
+    if ((tpi < 0) || (tpi > 15))
+        my_snprintf(buff, buff_len, "bad tpi");
+    else
+        my_snprintf(buff, buff_len, "%s", sg_lib_transport_proto_strs[tpi]);
+    return buff;
+}
+
+static const char * desig_code_set_str_arr[] =
+{
+    "Reserved [0x0]",
+    "Binary",
+    "ASCII",
+    "UTF-8",
+    "Reserved [0x4]", "Reserved [0x5]", "Reserved [0x6]", "Reserved [0x7]",
+    "Reserved [0x8]", "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]",
+    "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
+};
+
+const char *
+sg_get_desig_code_set_str(int val)
+{
+    if ((val >= 0) && (val < 16))
+        return desig_code_set_str_arr[val];
+    else
+        return NULL;
+}
+
+static const char * desig_assoc_str_arr[] =
+{
+    "Addressed logical unit",
+    "Target port",      /* that received request; unless SCSI ports VPD */
+    "Target device that contains addressed lu",
+    "Reserved [0x3]",
+};
+
+const char *
+sg_get_desig_assoc_str(int val)
+{
+    if ((val >= 0) && (val < 4))
+        return desig_assoc_str_arr[val];
+    else
+        return NULL;
+}
+
+static const char * desig_type_str_arr[] =
+{
+    "vendor specific [0x0]",
+    "T10 vendor identification",
+    "EUI-64 based",
+    "NAA",
+    "Relative target port",
+    "Target port group",        /* spc4r09: _primary_ target port group */
+    "Logical unit group",
+    "MD5 logical unit identifier",
+    "SCSI name string",
+    "Protocol specific port identifier",        /* spc4r36 */
+    "UUID identifier",          /* spc5r08 */
+    "Reserved [0xb]",
+    "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
+};
+
+const char *
+sg_get_desig_type_str(int val)
+{
+    if ((val >= 0) && (val < 16))
+        return desig_type_str_arr[val];
+    else
+        return NULL;
+}
+
+int
+sg_get_designation_descriptor_str(const char * leadin,
+                                  const unsigned char * ddp, int dd_len,
+                                  int print_assoc, int do_long, int blen,
+                                  char * b)
+{
+    int m, p_id, piv, c_set, assoc, desig_type, ci_off, c_id, d_id, naa;
+    int vsi, k, n, dlen;
+    const unsigned char * ip;
+    uint64_t vsei;
+    uint64_t id_ext;
+    char e[64];
+    const char * cp;
+    const char * lip = "";
+
+    n = 0;
+    if (leadin)
+        lip = leadin;
+    if (dd_len < 4) {
+        n += my_snprintf(b + n, blen - n, "%sdesignator desc too short: "
+                         "got length of %d want 4 or more\n", lip, dd_len);
+        return n;
+    }
+    dlen = ddp[3];
+    if (dlen > (dd_len - 4)) {
+        n += my_snprintf(b + n, blen - n, "%sdesignator too long: says it "
+                         "is %d bytes, but given %d bytes\n", lip, dlen,
+                         dd_len - 4);
+        return n;
+    }
+    ip = ddp + 4;
+    p_id = ((ddp[0] >> 4) & 0xf);
+    c_set = (ddp[0] & 0xf);
+    piv = ((ddp[1] & 0x80) ? 1 : 0);
+    assoc = ((ddp[1] >> 4) & 0x3);
+    desig_type = (ddp[1] & 0xf);
+    if (print_assoc && ((cp = sg_get_desig_assoc_str(assoc))))
+        n += my_snprintf(b + n, blen - n, "%s  %s:\n", lip, cp);
+    n += my_snprintf(b + n, blen - n, "%s    designator type: ", lip);
+    cp = sg_get_desig_type_str(desig_type);
+    if (cp)
+        n += my_snprintf(b + n, blen - n, "%s", cp);
+    n += my_snprintf(b + n, blen - n, ",  code set: ");
+    cp = sg_get_desig_code_set_str(c_set);
+    if (cp)
+        n += my_snprintf(b + n, blen - n, "%s", cp);
+    n += my_snprintf(b + n, blen - n, "\n");
+    if (piv && ((1 == assoc) || (2 == assoc)))
+        n += my_snprintf(b + n, blen - n, "%s     transport: %s\n", lip,
+                         sg_get_trans_proto_str(p_id, sizeof(e), e));
+    /* printf("    associated with the %s\n", sdparm_assoc_arr[assoc]); */
+    switch (desig_type) {
+    case 0: /* vendor specific */
+        k = 0;
+        if ((1 == c_set) || (2 == c_set)) { /* ASCII or UTF-8 */
+            for (k = 0; (k < dlen) && isprint(ip[k]); ++k)
+                ;
+            if (k >= dlen)
+                k = 1;
+        }
+        if (k)
+            n += my_snprintf(b + n, blen - n, "%s      vendor specific: "
+                             "%.*s\n", lip, dlen, ip);
+        else {
+            n += my_snprintf(b + n, blen - n, "%s      vendor specific:\n",
+                             lip);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+        }
+        break;
+    case 1: /* T10 vendor identification */
+        n += my_snprintf(b + n, blen - n, "%s      vendor id: %.8s\n", lip,
+                         ip);
+        if (dlen > 8) {
+            if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
+                n += my_snprintf(b + n, blen - n, "%s      vendor specific: "
+                                 "%.*s\n", lip, dlen - 8, ip + 8);
+            } else {
+                n += my_snprintf(b + n, blen - n, "%s      vendor specific: "
+                                 "0x", lip);
+                for (m = 8; m < dlen; ++m)
+                    n += my_snprintf(b + n, blen - n, "%02x",
+                                     (unsigned int)ip[m]);
+                n += my_snprintf(b + n, blen - n, "\n");
+            }
+        }
+        break;
+    case 2: /* EUI-64 based */
+        if (! do_long) {
+            if ((8 != dlen) && (12 != dlen) && (16 != dlen)) {
+                n += my_snprintf(b + n, blen - n, "%s      << expect 8, 12 "
+                                 "and 16 byte EUI, got %d >>\n", lip, dlen);
+                 n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n,
+                                 b + n);
+                break;
+            }
+            n += my_snprintf(b + n, blen - n, "%s      0x", lip);
+            for (m = 0; m < dlen; ++m)
+                n += my_snprintf(b + n, blen - n, "%02x", (unsigned int)ip[m]);
+            n += my_snprintf(b + n, blen - n, "\n");
+            break;
+        }
+        n += my_snprintf(b + n, blen - n, "%s      EUI-64 based %d byte "
+                         "identifier\n", lip, dlen);
+        if (1 != c_set) {
+            n += my_snprintf(b + n, blen - n, "%s      << expected binary "
+                             "code_set (1) >>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        ci_off = 0;
+        if (16 == dlen) {
+            ci_off = 8;
+            id_ext = sg_get_unaligned_be64(ip);
+            n += my_snprintf(b + n, blen - n, "%s      Identifier extension: "
+                             "0x%" PRIx64 "\n", lip, id_ext);
+        } else if ((8 != dlen) && (12 != dlen)) {
+            n += my_snprintf(b + n, blen - n, "%s      << can only decode 8, "
+                             "12 and 16 byte ids >>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        c_id = sg_get_unaligned_be24(ip + ci_off);
+        n += my_snprintf(b + n, blen - n, "%s      IEEE Company_id: 0x%x\n",
+                         lip, c_id);
+        vsei = 0;
+        for (m = 0; m < 5; ++m) {
+            if (m > 0)
+                vsei <<= 8;
+            vsei |= ip[ci_off + 3 + m];
+        }
+        n += my_snprintf(b + n, blen - n, "%s      Vendor Specific Extension "
+                         "Identifier: 0x%" PRIx64 "\n", lip, vsei);
+        if (12 == dlen) {
+            d_id = sg_get_unaligned_be32(ip + 8);
+            n += my_snprintf(b + n, blen - n, "%s      Directory ID: 0x%x\n",
+                             lip, d_id);
+        }
+        break;
+    case 3: /* NAA <n> */
+        if (1 != c_set) {
+            n += my_snprintf(b + n, blen - n, "%s      << unexpected code "
+                             "set %d for NAA >>\n", lip, c_set);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        naa = (ip[0] >> 4) & 0xff;
+        switch (naa) {
+        case 2:         /* NAA 2: IEEE Extended */
+            if (8 != dlen) {
+                n += my_snprintf(b + n, blen - n, "%s      << unexpected NAA "
+                                 "2 identifier length: 0x%x >>\n", lip, dlen);
+                n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n,
+                                b + n);
+                break;
+            }
+            d_id = (((ip[0] & 0xf) << 8) | ip[1]);
+            c_id = sg_get_unaligned_be24(ip + 2);
+            vsi = sg_get_unaligned_be24(ip + 5);
+            if (do_long) {
+                n += my_snprintf(b + n, blen - n, "%s      NAA 2, vendor "
+                                 "specific identifier A: 0x%x\n", lip, d_id);
+                n += my_snprintf(b + n, blen - n, "%s      IEEE Company_id: "
+                                 "0x%x\n", lip, c_id);
+                n += my_snprintf(b + n, blen - n, "%s      vendor specific "
+                                 "identifier B: 0x%x\n", lip, vsi);
+                n += my_snprintf(b + n, blen - n, "%s      [0x", lip);
+                for (m = 0; m < 8; ++m)
+                    n += my_snprintf(b + n, blen - n, "%02x",
+                                     (unsigned int)ip[m]);
+                n += my_snprintf(b + n, blen - n, "]\n");
+            }
+            n += my_snprintf(b + n, blen - n, "%s      0x", lip);
+            for (m = 0; m < 8; ++m)
+                n += my_snprintf(b + n, blen - n, "%02x", (unsigned int)ip[m]);
+            n += my_snprintf(b + n, blen - n, "\n");
+            break;
+        case 3:         /* NAA 3: Locally assigned */
+            if (8 != dlen) {
+                n += my_snprintf(b + n, blen - n, "%s      << unexpected NAA "
+                                 "3 identifier length: 0x%x >>\n", lip, dlen);
+                n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n,
+                                b + n);
+                break;
+            }
+            if (do_long)
+                n += my_snprintf(b + n, blen - n, "%s      NAA 3, Locally "
+                                 "assigned:\n", lip);
+            n += my_snprintf(b + n, blen - n, "%s      0x", lip);
+            for (m = 0; m < 8; ++m)
+                n += my_snprintf(b + n, blen - n, "%02x", (unsigned int)ip[m]);
+            n += my_snprintf(b + n, blen - n, "\n");
+            break;
+        case 5:         /* NAA 5: IEEE Registered */
+            if (8 != dlen) {
+                n += my_snprintf(b + n, blen - n, "%s      << unexpected NAA "
+                                 "5 identifier length: 0x%x >>\n", lip, dlen);
+                n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n,
+                                b + n);
+                break;
+            }
+            c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
+                    (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
+            vsei = ip[3] & 0xf;
+            for (m = 1; m < 5; ++m) {
+                vsei <<= 8;
+                vsei |= ip[3 + m];
+            }
+            if (do_long) {
+                n += my_snprintf(b + n, blen - n, "%s      NAA 5, IEEE "
+                                 "Company_id: 0x%x\n", lip, c_id);
+                n += my_snprintf(b + n, blen - n, "%s      Vendor Specific "
+                                 "Identifier: 0x%" PRIx64 "\n", lip, vsei);
+                n += my_snprintf(b + n, blen - n, "%s      [0x", lip);
+                for (m = 0; m < 8; ++m)
+                    n += my_snprintf(b + n, blen - n, "%02x",
+                                     (unsigned int)ip[m]);
+                n += my_snprintf(b + n, blen - n, "]\n");
+            } else {
+                n += my_snprintf(b + n, blen - n, "%s      0x", lip);
+                for (m = 0; m < 8; ++m)
+                    n += my_snprintf(b + n, blen - n, "%02x",
+                                     (unsigned int)ip[m]);
+                n += my_snprintf(b + n, blen - n, "\n");
+            }
+            break;
+        case 6:         /* NAA 6: IEEE Registered extended */
+            if (16 != dlen) {
+                n += my_snprintf(b + n, blen - n, "%s      << unexpected NAA "
+                                 "6 identifier length: 0x%x >>\n", lip, dlen);
+                n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n,
+                                b + n);
+                break;
+            }
+            c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
+                    (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
+            vsei = ip[3] & 0xf;
+            for (m = 1; m < 5; ++m) {
+                vsei <<= 8;
+                vsei |= ip[3 + m];
+            }
+            if (do_long) {
+                n += my_snprintf(b + n, blen - n, "%s      NAA 6, IEEE "
+                                 "Company_id: 0x%x\n", lip, c_id);
+                n += my_snprintf(b + n, blen - n, "%s      Vendor Specific "
+                                 "Identifier: 0x%" PRIx64 "\n", lip, vsei);
+                vsei = sg_get_unaligned_be64(ip + 8);
+                n += my_snprintf(b + n, blen - n, "%s      Vendor Specific "
+                                 "Identifier Extension: 0x%" PRIx64 "\n", lip,
+                                 vsei);
+                n += my_snprintf(b + n, blen - n, "%s      [0x", lip);
+                for (m = 0; m < 16; ++m)
+                    n += my_snprintf(b + n, blen - n, "%02x",
+                                     (unsigned int)ip[m]);
+                n += my_snprintf(b + n, blen - n, "]\n");
+            } else {
+                n += my_snprintf(b + n, blen - n, "%s      0x", lip);
+                for (m = 0; m < 16; ++m)
+                    n += my_snprintf(b + n, blen - n, "%02x",
+                                     (unsigned int)ip[m]);
+                n += my_snprintf(b + n, blen - n, "\n");
+            }
+            break;
+        default:
+            n += my_snprintf(b + n, blen - n, "%s      << unexpected NAA "
+                             "[0x%x] >>\n", lip, naa);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        break;
+    case 4: /* Relative target port */
+        if ((1 != c_set) || (1 != assoc) || (4 != dlen)) {
+            n += my_snprintf(b + n, blen - n, "%s      << expected binary "
+                             "code_set, target port association, length 4 "
+                             ">>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, "", 0, blen - n, b + n);
+            break;
+        }
+        d_id = sg_get_unaligned_be16(ip + 2);
+        n += my_snprintf(b + n, blen - n, "%s      Relative target port: "
+                         "0x%x\n", lip, d_id);
+        break;
+    case 5: /* (primary) Target port group */
+        if ((1 != c_set) || (1 != assoc) || (4 != dlen)) {
+            n += my_snprintf(b + n, blen - n, "%s      << expected binary "
+                             "code_set, target port association, length 4 "
+                             ">>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        d_id = sg_get_unaligned_be16(ip + 2);
+        n += my_snprintf(b + n, blen - n, "%s      Target port group: 0x%x\n",
+                         lip, d_id);
+        break;
+    case 6: /* Logical unit group */
+        if ((1 != c_set) || (0 != assoc) || (4 != dlen)) {
+            n += my_snprintf(b + n, blen - n, "%s      << expected binary "
+                             "code_set, logical unit association, length "
+                             "4 >>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        d_id = sg_get_unaligned_be16(ip + 2);
+        n += my_snprintf(b + n, blen - n, "%s      Logical unit group: "
+                         "0x%x\n", lip, d_id);
+        break;
+    case 7: /* MD5 logical unit identifier */
+        if ((1 != c_set) || (0 != assoc)) {
+            n += my_snprintf(b + n, blen - n, "%s      << expected binary "
+                             "code_set, logical unit association >>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, "", 0, blen - n, b + n);
+            break;
+        }
+        n += my_snprintf(b + n, blen - n, "%s      MD5 logical unit "
+                         "identifier:\n", lip);
+        n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+        break;
+    case 8: /* SCSI name string */
+        if (3 != c_set) {
+            n += my_snprintf(b + n, blen - n, "%s      << expected UTF-8 "
+                             "code_set >>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        n += my_snprintf(b + n, blen - n, "%s      SCSI name string:\n", lip);
+        /* does %s print out UTF-8 ok??
+         * Seems to depend on the locale. Looks ok here with my
+         * locale setting: en_AU.UTF-8
+         */
+        n += my_snprintf(b + n, blen - n, "%s      %s\n", lip,
+                         (const char *)ip);
+        break;
+    case 9: /* Protocol specific port identifier */
+        /* added in spc4r36, PIV must be set, proto_id indicates */
+        /* whether UAS (USB) or SOP (PCIe) or ... */
+        if (! piv)
+            n += my_snprintf(b + n, blen - n, " %s      >>>> Protocol "
+                             "specific port identifier expects protocol\n"
+                             "%s           identifier to be valid and it is "
+                             "not\n", lip, lip);
+        if (TPROTO_UAS == p_id) {
+            n += my_snprintf(b + n, blen - n, "%s      USB device address: "
+                             "0x%x\n", lip, 0x7f & ip[0]);
+            n += my_snprintf(b + n, blen - n, "%s      USB interface number: "
+                             "0x%x\n", lip, ip[2]);
+        } else if (TPROTO_SOP == p_id) {
+            n += my_snprintf(b + n, blen - n, "%s      PCIe routing ID, bus "
+                             "number: 0x%x\n", lip, ip[0]);
+            n += my_snprintf(b + n, blen - n, "%s          function number: "
+                             "0x%x\n", lip, ip[1]);
+            n += my_snprintf(b + n, blen - n, "%s          [or device "
+                             "number: 0x%x, function number: 0x%x]\n", lip,
+                             (0x1f & (ip[1] >> 3)), 0x7 & ip[1]);
+        } else
+            n += my_snprintf(b + n, blen - n, "%s      >>>> unexpected "
+                             "protocol indentifier: %s\n%s           with "
+                             "Protocol specific port identifier\n", lip,
+                   sg_get_trans_proto_str(p_id, sizeof(e), e), lip);
+        break;
+    case 0xa: /* UUID identifier */
+        if (1 != c_set) {
+            n += my_snprintf(b + n, blen - n, "%s      << expected binary "
+                             "code_set >>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        if ((1 != ((ip[0] >> 4) & 0xf)) || (18 != dlen)) {
+            n += my_snprintf(b + n, blen - n, "%s      << expected locally "
+                             "assigned UUID, 16 bytes long >>\n", lip);
+            n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+            break;
+        }
+        n += my_snprintf(b + n, blen - n, "%s      Locally assigned UUID: ",
+                         lip);
+        for (m = 0; m < 16; ++m) {
+            if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
+                n += my_snprintf(b + n, blen - n, "-");
+            n += my_snprintf(b + n, blen - n, "%02x", (unsigned int)ip[2 + m]);
+        }
+        n += my_snprintf(b + n, blen - n, "\n");
+        if (do_long) {
+            n += my_snprintf(b + n, blen - n, "%s      [0x", lip);
+            for (m = 0; m < 16; ++m)
+                n += my_snprintf(b + n, blen - n, "%02x",
+                                 (unsigned int)ip[2 + m]);
+            n += my_snprintf(b + n, blen - n, "]\n");
+        }
+        break;
+    default: /* reserved */
+        n += my_snprintf(b + n, blen - n, "%s      reserved "
+                         "designator=0x%x\n", lip, desig_type);
+        n += dStrHexStr((const char *)ip, dlen, lip, 0, blen - n, b + n);
+        break;
+    }
+    return n;
+}
+
+static int
+decode_sks(const char * leadin, const unsigned char * descp, int add_d_len,
+           int sense_key, int * processedp, int blen, char * b)
+{
+    int progress, pr, rem, n;
+    const char * lip = "";
+
+    n = 0;
+    if (leadin)
+        lip = leadin;
+    switch (sense_key) {
+    case SPC_SK_ILLEGAL_REQUEST:
+        if (add_d_len < 6) {
+            n += my_snprintf(b + n, blen - n, "Field pointer: ");
+            goto too_short;
+        }
+        /* abbreviate to fit on one line */
+        n += my_snprintf(b + n, blen - n, "Field pointer:\n");
+        n += my_snprintf(b + n, blen - n, "%s        Error in %s: byte %d",
+                         lip, (descp[4] & 0x40) ? "Command" :
+                                                  "Data parameters",
+                         sg_get_unaligned_be16(descp + 5));
+        if (descp[4] & 0x08) {
+            n += my_snprintf(b + n, blen - n, " bit %d\n",
+                             descp[4] & 0x07);
+        } else
+            n += my_snprintf(b + n, blen - n, "\n");
+        break;
+    case SPC_SK_HARDWARE_ERROR:
+    case SPC_SK_MEDIUM_ERROR:
+    case SPC_SK_RECOVERED_ERROR:
+        n += my_snprintf(b + n, blen - n, "Actual retry count: ");
+        if (add_d_len < 6)
+            goto too_short;
+        n += my_snprintf(b + n, blen - n,"%u\n",
+                         sg_get_unaligned_be16(descp + 5));
+        break;
+    case SPC_SK_NO_SENSE:
+    case SPC_SK_NOT_READY:
+        n += my_snprintf(b + n, blen - n, "Progress indication: ");
+        if (add_d_len < 6)
+            goto too_short;
+        progress = sg_get_unaligned_be16(descp + 5);
+        pr = (progress * 100) / 65536;
+        rem = ((progress * 100) % 65536) / 656;
+        n += my_snprintf(b + n, blen - n, "%d.%02d%%\n", pr, rem);
+        break;
+    case SPC_SK_COPY_ABORTED:
+        n += my_snprintf(b + n, blen - n, "Segment pointer:\n");
+        if (add_d_len < 6)
+            goto too_short;
+        n += my_snprintf(b + n, blen - n, "%s        Relative to start of "
+                         "%s, byte %d", lip,
+                         (descp[4] & 0x20) ? "segment descriptor" :
+                                             "parameter list",
+                         sg_get_unaligned_be16(descp + 5));
+        if (descp[4] & 0x08)
+            n += my_snprintf(b + n, blen - n, " bit %d\n",
+                             descp[4] & 0x07);
+        else
+            n += my_snprintf(b + n, blen - n, "\n");
+        break;
+    case SPC_SK_UNIT_ATTENTION:
+        n += my_snprintf(b + n, blen - n, "Unit attention condition "
+                         "queue:\n");
+        n += my_snprintf(b + n, blen - n, "%s        overflow flag is %d\n",
+                         lip, !!(descp[4] & 0x1));
+        break;
+    default:
+        n += my_snprintf(b + n, blen - n, "Sense_key: 0x%x "
+                         "unexpected\n", sense_key);
+        *processedp = 0;
+        break;
+    }
+    return n;
+
+too_short:
+    n += my_snprintf(b + n, blen - n, "%s\n", "   >> descriptor too short");
+    *processedp = 0;
+    return n;
+}
+
+#define TPGS_STATE_OPTIMIZED 0x0
+#define TPGS_STATE_NONOPTIMIZED 0x1
+#define TPGS_STATE_STANDBY 0x2
+#define TPGS_STATE_UNAVAILABLE 0x3
+#define TPGS_STATE_OFFLINE 0xe
+#define TPGS_STATE_TRANSITIONING 0xf
+
+static int
+decode_tpgs_state(int st, char * b, int blen)
+{
+    switch (st) {
+    case TPGS_STATE_OPTIMIZED:
+        return my_snprintf(b, blen, "active/optimized");
+    case TPGS_STATE_NONOPTIMIZED:
+        return my_snprintf(b, blen, "active/non optimized");
+    case TPGS_STATE_STANDBY:
+        return my_snprintf(b, blen, "standby");
+    case TPGS_STATE_UNAVAILABLE:
+        return my_snprintf(b, blen, "unavailable");
+    case TPGS_STATE_OFFLINE:
+        return my_snprintf(b, blen, "offline");
+    case TPGS_STATE_TRANSITIONING:
+        return my_snprintf(b, blen, "transitioning between states");
+    default:
+        return my_snprintf(b, blen, "unknown: 0x%x", st);
+    }
+}
+
+static int
+uds_referral_descriptor_str(char * b, int blen, const unsigned char * dp,
+                            int alen, const char * leadin)
+{
+    int n = 0;
+    int dlen = alen - 2;
+    int k, j, g, f, tpgd;
+    const unsigned char * tp;
+    uint64_t ull;
+    char c[40];
+    const char * lip = "";
+
+    if (leadin)
+        lip = leadin;
+    n += my_snprintf(b + n, blen - n, "%s   Not all referrals: %d\n", lip,
+                     !!(dp[2] & 0x1));
+    dp += 4;
+    for (k = 0, f = 1; (k + 4) < dlen; k += g, dp += g, ++f) {
+        tpgd = dp[3];
+        g = (tpgd * 4) + 20;
+        n += my_snprintf(b + n, blen - n, "%s    Descriptor %d\n", lip, f);
+        if ((k + g) > dlen) {
+            n += my_snprintf(b + n, blen - n, "%s      truncated descriptor, "
+                             "stop\n", lip);
+            return n;
+        }
+        ull = sg_get_unaligned_be64(dp + 4);
+        n += my_snprintf(b + n, blen - n, "%s      first uds LBA: 0x%" PRIx64
+                         "\n", lip, ull);
+        ull = sg_get_unaligned_be64(dp + 12);
+        n += my_snprintf(b + n, blen - n, "%s      last uds LBA:  0x%" PRIx64
+                         "\n", lip, ull);
+        for (j = 0; j < tpgd; ++j) {
+            tp = dp + 20 + (j * 4);
+            decode_tpgs_state(tp[0] & 0xf, c, sizeof(c));
+            n += my_snprintf(b + n, blen - n, "%s        tpg: %d  state: "
+                             "%s\n", lip, sg_get_unaligned_be16(tp + 2), c);
+        }
+    }
+    return n;
+}
+
+static const char * dd_usage_reason_str_arr[] = {
+    "Unknown",
+    "resend this and further commands to:",
+    "resend this command to:",
+    "new subsiduary lu added to this administrative lu:",
+    "administrative lu associated with a preferred binding:",
+   };
+
+
+/* Decode descriptor format sense descriptors (assumes sense buffer is
+ * in descriptor format) */
+int
+sg_get_sense_descriptors_str(const char * leadin,
+                             const unsigned char * sense_buffer, int sb_len,
+                             int blen, char * b)
+{
+    int add_sb_len, add_d_len, desc_len, k, j, sense_key, processed;
+    int n, progress, pr, rem;
+    const unsigned char * descp;
+    const char * lip = "";
+    const char * dtsp = "   >> descriptor too short";
+    const char * eccp = "Extended copy command";
+    const char * ddp = "destination device";
+    char z[64];
+
+    if ((NULL == b) || (blen <= 0))
+        return 0;
+    b[0] = '\0';
+    if (leadin) {
+        lip = leadin;
+        snprintf(z, sizeof(z), "%.60s  ", lip);
+    } else
+         snprintf(z, sizeof(z), "  ");
+    if ((sb_len < 8) || (0 == (add_sb_len = sense_buffer[7])))
+        return 0;
+    add_sb_len = (add_sb_len < (sb_len - 8)) ? add_sb_len : (sb_len - 8);
+    sense_key = (sense_buffer[1] & 0xf);
+
+    for (descp = (sense_buffer + 8), k = 0, n = 0;
+         (k < add_sb_len) && (n < blen);
+         k += desc_len, descp += desc_len) {
+        add_d_len = (k < (add_sb_len - 1)) ? descp[1] : -1;
+        if ((k + add_d_len + 2) > add_sb_len)
+            add_d_len = add_sb_len - k - 2;
+        desc_len = add_d_len + 2;
+        n += my_snprintf(b + n, blen - n, "%s  Descriptor type: ", lip);
+        processed = 1;
+        switch (descp[0]) {
+        case 0:
+            n += my_snprintf(b + n, blen - n, "Information: ");
+            if ((add_d_len >= 10) && (0x80 & descp[2])) {
+                n += my_snprintf(b + n, blen - n, "0x");
+                for (j = 0; j < 8; ++j)
+                    n += my_snprintf(b + n, blen - n, "%02x", descp[4 + j]);
+                n += my_snprintf(b + n, blen - n, "\n");
+            } else {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+            }
+            break;
+        case 1:
+            n += my_snprintf(b + n, blen - n, "Command specific: ");
+            if (add_d_len >= 10) {
+                n += my_snprintf(b + n, blen - n, "0x");
+                for (j = 0; j < 8; ++j)
+                    n += my_snprintf(b + n, blen - n, "%02x", descp[4 + j]);
+                n += my_snprintf(b + n, blen - n, "\n");
+            } else {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+            }
+            break;
+        case 2:         /* Sense Key Specific */
+            n += my_snprintf(b + n, blen - n, "Sense key specific: ");
+            n += decode_sks(lip, descp, add_d_len, sense_key, &processed,
+                            blen - n, b + n);
+            break;
+        case 3:
+            n += my_snprintf(b + n, blen - n, "Field replaceable unit code: ");
+            if (add_d_len >= 2)
+                n += my_snprintf(b + n, blen - n, "0x%x\n", descp[3]);
+            else {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+            }
+            break;
+        case 4:
+            n += my_snprintf(b + n, blen - n, "Stream commands: ");
+            if (add_d_len >= 2) {
+                if (descp[3] & 0x80)
+                    n += my_snprintf(b + n, blen - n, "FILEMARK");
+                if (descp[3] & 0x40)
+                    n += my_snprintf(b + n, blen - n, "End Of Medium (EOM)");
+                if (descp[3] & 0x20)
+                    n += my_snprintf(b + n, blen - n, "Incorrect Length "
+                                     "Indicator (ILI)");
+                n += my_snprintf(b + n, blen - n, "\n");
+            } else {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+            }
+            break;
+        case 5:
+            n += my_snprintf(b + n, blen - n, "Block commands: ");
+            if (add_d_len >= 2)
+                n += my_snprintf(b + n, blen - n, "Incorrect Length "
+                                 "Indicator (ILI) %s\n",
+                                 (descp[3] & 0x20) ? "set" : "clear");
+            else {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+            }
+            break;
+        case 6:
+            n += my_snprintf(b + n, blen - n, "OSD object identification\n");
+            processed = 0;
+            break;
+        case 7:
+            n += my_snprintf(b + n, blen - n, "OSD response integrity check "
+                             "value\n");
+            processed = 0;
+            break;
+        case 8:
+            n += my_snprintf(b + n, blen - n, "OSD attribute "
+                             "identification\n");
+            processed = 0;
+            break;
+        case 9:         /* this is defined in SAT (SAT-2) */
+            n += my_snprintf(b + n, blen - n, "ATA Status Return: ");
+            if (add_d_len >= 12) {
+                int extend, count;
+
+                extend = descp[2] & 1;
+                count = descp[5] + (extend ? (descp[4] << 8) : 0);
+                n += my_snprintf(b + n, blen - n, "extend=%d error=0x%x "
+                                 "\n%s        count=0x%x ", extend,
+                                 descp[3], lip, count);
+                if (extend)
+                    n += my_snprintf(b + n, blen - n,
+                                     "lba=0x%02x%02x%02x%02x%02x%02x ",
+                                     descp[10], descp[8], descp[6],
+                                     descp[11], descp[9], descp[7]);
+                else
+                    n += my_snprintf(b + n, blen - n,
+                                     "lba=0x%02x%02x%02x ",
+                                     descp[11], descp[9], descp[7]);
+                n += my_snprintf(b + n, blen - n, "device=0x%x status=0x%x\n",
+                                 descp[12], descp[13]);
+            } else {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+            }
+            break;
+        case 0xa:
+           /* Added in SPC-4 rev 17, became 'Another ...' in rev 34 */
+            n += my_snprintf(b + n, blen - n, "Another progress "
+                             "indication: ");
+            if (add_d_len < 6) {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+                break;
+            }
+            progress = sg_get_unaligned_be16(descp + 6);
+            pr = (progress * 100) / 65536;
+            rem = ((progress * 100) % 65536) / 656;
+            n += my_snprintf(b + n, blen - n, "%d.02%d%%\n", pr, rem);
+            n += my_snprintf(b + n, blen - n, "%s        [sense_key=0x%x "
+                             "asc,ascq=0x%x,0x%x]\n", lip, descp[2], descp[3],
+                             descp[4]);
+            break;
+        case 0xb:       /* Added in SPC-4 rev 23, defined in SBC-3 rev 22 */
+            n += my_snprintf(b + n, blen - n, "User data segment referral: ");
+            if (add_d_len < 2) {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+                break;
+            }
+            n += my_snprintf(b + n, blen - n, "\n");
+            n += uds_referral_descriptor_str(b + n, blen - n, descp,
+                                             add_d_len, lip);
+            break;
+        case 0xc:       /* Added in SPC-4 rev 28 */
+            n += my_snprintf(b + n, blen - n, "Forwarded sense data\n");
+            if (add_d_len < 2) {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+                break;
+            }
+            n += my_snprintf(b + n, blen - n, "%s    FSDT: %s\n", lip,
+                             (descp[2] & 0x80) ? "set" : "clear");
+            j = descp[2] & 0xf;
+            n += my_snprintf(b + n, blen - n, "%s    Sense data source: ",
+                             lip);
+            switch (j) {
+            case 0:
+                n += my_snprintf(b + n, blen - n, "%s source device\n", eccp);
+                break;
+            case 1:
+            case 2:
+            case 3:
+            case 4:
+            case 5:
+            case 6:
+            case 7:
+                n += my_snprintf(b + n, blen - n, "%s %s %d\n", eccp, ddp,
+                                 j - 1);
+                break;
+            default:
+                n += my_snprintf(b + n, blen - n, "unknown [%d]\n", j);
+            }
+            {
+                char c[480];
+
+                sg_get_scsi_status_str(descp[3], sizeof(c) - 1, c);
+                c[sizeof(c) - 1] = '\0';
+                n += my_snprintf(b + n, blen - n, "%s    Forwarded status: "
+                                 "%s\n", lip, c);
+                if (add_d_len > 2) {
+                    /* recursing; hope not to get carried away */
+                    n += my_snprintf(b + n, blen - n, "%s vvvvvvvvvvvvvvvv\n",
+                                     lip);
+                    sg_get_sense_str(lip, descp + 4, add_d_len - 2, 0,
+                                     sizeof(c), c);
+                    n += my_snprintf(b + n, blen - n, "%s", c);
+                    n += my_snprintf(b + n, blen - n, "%s ^^^^^^^^^^^^^^^^\n",
+                                     lip);
+                }
+            }
+            break;
+        case 0xd:       /* Added in SBC-3 rev 36d */
+            /* this descriptor combines descriptors 0, 1, 2 and 3 */
+            n += my_snprintf(b + n, blen - n, "Direct-access block device\n");
+            if (add_d_len < 28) {
+                n += my_snprintf(b + n, blen - n, "%s\n", dtsp);
+                processed = 0;
+                break;
+            }
+            if (0x20 & descp[2])
+                n += my_snprintf(b + n, blen - n, "%s    ILI (incorrect "
+                                 "length indication) set\n", lip);
+            if (0x80 & descp[4]) {
+                n += my_snprintf(b + n, blen - n, "%s    Sense key "
+                                 "specific: ", lip);
+                n += decode_sks(lip, descp, add_d_len, sense_key, &processed,
+                                blen - n, b + n);
+            }
+            n += my_snprintf(b + n, blen - n, "%s    Field replaceable unit "
+                             "code: 0x%x\n", lip, descp[7]);
+            if (0x80 & descp[2]) {
+                n += my_snprintf(b + n, blen - n, "%s    Information: 0x",
+                                 lip);
+                for (j = 0; j < 8; ++j)
+                    n += my_snprintf(b + n, blen - n, "%02x", descp[8 + j]);
+                n += my_snprintf(b + n, blen - n, "\n");
+            }
+            n += my_snprintf(b + n, blen - n, "%s    Command specific: 0x",
+                             lip);
+            for (j = 0; j < 8; ++j)
+                n += my_snprintf(b + n, blen - n, "%02x", descp[16 + j]);
+            n += my_snprintf(b + n, blen - n, "\n");
+            break;
+        case 0xe:       /* Added in SPC-5 rev 6 (for bind/unbind) */
+            n += my_snprintf(b + n, blen - n, "Device designation\n");
+            j = (int)(sizeof(dd_usage_reason_str_arr) /
+                      sizeof(dd_usage_reason_str_arr[0]));
+            if (descp[3] < j)
+                n += my_snprintf(b + n, blen - n, "%s    Usage reason: %s\n",
+                                 lip, dd_usage_reason_str_arr[descp[3]]);
+            else
+                n += my_snprintf(b + n, blen - n, "%s    Usage reason: "
+                                 "reserved[%d]\n", lip, descp[3]);
+            n += sg_get_designation_descriptor_str(z, descp + 4, descp[1] - 2,
+                                                   1, 0, blen - n, b + n);
+            break;
+        default:
+            if (descp[0] >= 0x80)
+                n += my_snprintf(b + n, blen - n, "Vendor specific [0x%x]\n",
+                                 descp[0]);
+            else
+                n += my_snprintf(b + n, blen - n, "Unknown [0x%x]\n",
+                                 descp[0]);
+            processed = 0;
+            break;
+        }
+        if (! processed) {
+            if (add_d_len > 0) {
+                n += my_snprintf(b + n, blen - n, "%s    ", lip);
+                for (j = 0; j < add_d_len; ++j) {
+                    if ((j > 0) && (0 == (j % 24)))
+                        n += my_snprintf(b + n, blen - n, "\n%s    ", lip);
+                    n += my_snprintf(b + n, blen - n, "%02x ", descp[j + 2]);
+                }
+                n += my_snprintf(b + n, blen - n, "\n");
+            }
+        }
+        if (add_d_len < 0)
+            n += my_snprintf(b + n, blen - n, "%s    short descriptor\n", lip);
+    }
+    return n;
+}
+
+/* Decode SAT ATA PASS-THROUGH fixed format sense */
+static int
+sg_get_sense_sat_pt_fixed_str(const char * leadin, const unsigned char * sp,
+                              int slen, int blen, char * b)
+{
+    int n = 0;
+    const char * lip = "";
+
+    if ((blen < 1) || (slen < 12))
+        return n;
+    if (leadin)
+        lip = leadin;
+    if (SPC_SK_RECOVERED_ERROR != (0xf & sp[2]))
+        n += my_snprintf(b + n, blen - n, "%s  >> expected Sense key: "
+                         "Recovered Error ??\n", lip);
+    n += my_snprintf(b + n, blen - n, "%s  error=0x%x, status=0x%x, "
+                     "device=0x%x, sector_count(7:0)=0x%x%c\n", lip, sp[3],
+                     sp[4], sp[5], sp[6], ((0x40 & sp[8]) ? '+' : ' '));
+    n += my_snprintf(b + n, blen - n, "%s  extend=%d, log_index=0x%x, "
+                     "lba_high,mid,low(7:0)=0x%x,0x%x,0x%x%c\n", lip,
+                     (!!(0x80 & sp[8])), (0xf & sp[8]), sp[9], sp[10], sp[11],
+                     ((0x20 & sp[8]) ? '+' : ' '));
+    return n;
+}
+
+/* Fetch sense information */
+int
+sg_get_sense_str(const char * leadin, const unsigned char * sense_buffer,
+                 int sb_len, int raw_sinfo, int buff_len, char * buff)
+{
+    int len, valid, progress, n, r, pr, rem, blen;
+    unsigned int info;
+    int descriptor_format = 0;
+    int sdat_ovfl = 0;
+    const char * ebp = NULL;
+    char error_buff[64];
+    char b[256];
+    struct sg_scsi_sense_hdr ssh;
+    const char * lip = "";
+
+    if ((NULL == buff) || (buff_len <= 0))
+        return 0;
+    else if (1 == buff_len) {
+        buff[0] = '\0';
+        return 0;
+    }
+    blen = sizeof(b);
+    n = 0;
+    if (leadin)
+        lip = leadin;
+    if ((NULL == sense_buffer) || (sb_len < 1)) {
+            n += my_snprintf(buff, buff_len, "%s >>> sense buffer empty\n",
+                             lip);
+            return n;
+    }
+    len = sb_len;
+    if (sg_scsi_normalize_sense(sense_buffer, sb_len, &ssh)) {
+        switch (ssh.response_code) {
+        case 0x70:      /* fixed, current */
+            ebp = "Fixed format, current";
+            len = (sb_len > 7) ? (sense_buffer[7] + 8) : sb_len;
+            len = (len > sb_len) ? sb_len : len;
+            sdat_ovfl = (len > 2) ? !!(sense_buffer[2] & 0x10) : 0;
+            break;
+        case 0x71:      /* fixed, deferred */
+            /* error related to a previous command */
+            ebp = "Fixed format, <<<deferred>>>";
+            len = (sb_len > 7) ? (sense_buffer[7] + 8) : sb_len;
+            len = (len > sb_len) ? sb_len : len;
+            sdat_ovfl = (len > 2) ? !!(sense_buffer[2] & 0x10) : 0;
+            break;
+        case 0x72:      /* descriptor, current */
+            descriptor_format = 1;
+            ebp = "Descriptor format, current";
+            sdat_ovfl = (sb_len > 4) ? !!(sense_buffer[4] & 0x80) : 0;
+            break;
+        case 0x73:      /* descriptor, deferred */
+            descriptor_format = 1;
+            ebp = "Descriptor format, <<<deferred>>>";
+            sdat_ovfl = (sb_len > 4) ? !!(sense_buffer[4] & 0x80) : 0;
+            break;
+        case 0x0:
+            ebp = "Response code: 0x0 (?)";
+            break;
+        default:
+            my_snprintf(error_buff, sizeof(error_buff),
+                        "Unknown response code: 0x%x", ssh.response_code);
+            ebp = error_buff;
+            break;
+        }
+        n += my_snprintf(buff + n, buff_len - n, "%s%s; Sense key: %s\n",
+                         lip, ebp, sg_lib_sense_key_desc[ssh.sense_key]);
+        if (sdat_ovfl)
+            n += my_snprintf(buff + n, buff_len - n, "%s<<<Sense data "
+                             "overflow>>>\n", lip);
+        if (descriptor_format) {
+            n += my_snprintf(buff + n, buff_len - n, "%s%s\n", lip,
+                             sg_get_asc_ascq_str(ssh.asc, ssh.ascq,
+                                                 sizeof(b), b));
+            n += sg_get_sense_descriptors_str(lip, sense_buffer, len,
+                                              buff_len - n, buff + n);
+        } else if ((len > 12) && (0 == ssh.asc) &&
+                   (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
+            /* SAT ATA PASS-THROUGH fixed format */
+            n += my_snprintf(buff + n, buff_len - n, "%s%s\n", lip,
+                             sg_get_asc_ascq_str(ssh.asc, ssh.ascq,
+                             sizeof(b), b));
+            n += sg_get_sense_sat_pt_fixed_str(lip, sense_buffer, len,
+                                               buff_len - n, buff + n);
+        } else if (len > 2) {   /* fixed format */
+            if (len > 12)
+                n += my_snprintf(buff + n, buff_len - n, "%s%s\n", lip,
+                                 sg_get_asc_ascq_str(ssh.asc, ssh.ascq,
+                                                     sizeof(b), b));
+            r = 0;
+            valid = sense_buffer[0] & 0x80;
+            if (strlen(lip) > 0)
+                r += my_snprintf(b + r, blen - r, "%s", lip);
+            if (len > 6) {
+                info = sg_get_unaligned_be32(sense_buffer + 3);
+                if (valid)
+                    r += my_snprintf(b + r, blen - r, "  Info fld=0x%x [%u] ",
+                                     info, info);
+                else if (info > 0)
+                    r += my_snprintf(b + r, blen - r, "  Valid=0, Info "
+                                     "fld=0x%x [%u] ", info, info);
+            } else
+                info = 0;
+            if (sense_buffer[2] & 0xe0) {
+                if (sense_buffer[2] & 0x80)
+                   r += my_snprintf(b + r, blen - r, " FMK");
+                            /* current command has read a filemark */
+                if (sense_buffer[2] & 0x40)
+                   r += my_snprintf(b + r, blen - r, " EOM");
+                            /* end-of-medium condition exists */
+                if (sense_buffer[2] & 0x20)
+                   r += my_snprintf(b + r, blen - r, " ILI");
+                            /* incorrect block length requested */
+                r += my_snprintf(b + r, blen - r, "\n");
+            } else if (valid || (info > 0))
+                r += my_snprintf(b + r, blen - r, "\n");
+            if ((len >= 14) && sense_buffer[14])
+                r += my_snprintf(b + r, blen - r, "%s  Field replaceable unit "
+                                 "code: %d\n", lip, sense_buffer[14]);
+            if ((len >= 18) && (sense_buffer[15] & 0x80)) {
+                /* sense key specific decoding */
+                switch (ssh.sense_key) {
+                case SPC_SK_ILLEGAL_REQUEST:
+                    r += my_snprintf(b + r, blen - r, "%s  Sense Key "
+                                     "Specific: Error in %s: byte %d", lip,
+                             ((sense_buffer[15] & 0x40) ? "Command" :
+                                                          "Data parameters"),
+                             sg_get_unaligned_be16(sense_buffer + 16));
+                    if (sense_buffer[15] & 0x08)
+                        r += my_snprintf(b + r, blen - r, " bit %d\n",
+                                         sense_buffer[15] & 0x07);
+                    else
+                        r += my_snprintf(b + r, blen - r, "\n");
+                    break;
+                case SPC_SK_NO_SENSE:
+                case SPC_SK_NOT_READY:
+                    progress = sg_get_unaligned_be16(sense_buffer + 16);
+                    pr = (progress * 100) / 65536;
+                    rem = ((progress * 100) % 65536) / 656;
+                    r += my_snprintf(b + r, blen - r, "%s  Progress "
+                                     "indication: %d.%02d%%\n", lip, pr, rem);
+                    break;
+                case SPC_SK_HARDWARE_ERROR:
+                case SPC_SK_MEDIUM_ERROR:
+                case SPC_SK_RECOVERED_ERROR:
+                    r += my_snprintf(b + r, blen - r, "%s  Actual retry "
+                                     "count: " "0x%02x%02x\n", lip,
+                                     sense_buffer[16], sense_buffer[17]);
+                    break;
+                case SPC_SK_COPY_ABORTED:
+                    r += my_snprintf(b + r, blen - r, "%s  Segment pointer: ",
+                                     lip);
+                    r += my_snprintf(b + r, blen - r, "Relative to start of "
+                                     "%s, byte %d",
+                                     ((sense_buffer[15] & 0x20) ?
+                                      "segment descriptor" : "parameter list"),
+                                     sg_get_unaligned_be16(sense_buffer + 16));
+                    if (sense_buffer[15] & 0x08)
+                        r += my_snprintf(b + r, blen - r, " bit %d\n",
+                                         sense_buffer[15] & 0x07);
+                    else
+                        r += my_snprintf(b + r, blen - r, "\n");
+                    break;
+                case SPC_SK_UNIT_ATTENTION:
+                    r += my_snprintf(b + r, blen - r, "%s  Unit attention "
+                                     "condition queue: ", lip);
+                    r += my_snprintf(b + r, blen - r, "overflow flag is %d\n",
+                                     !!(sense_buffer[15] & 0x1));
+                    break;
+                default:
+                    r += my_snprintf(b + r, blen - r, "%s  Sense_key: 0x%x "
+                                     "unexpected\n", lip, ssh.sense_key);
+                    break;
+                }
+            }
+            if (r > 0)
+                n += my_snprintf(buff + n, buff_len - n, "%s", b);
+        } else
+            n += my_snprintf(buff + n, buff_len - n, "%s fixed descriptor "
+                             "length too short, len=%d\n", lip, len);
+    } else {    /* non-extended SCSI-1 sense data ?? */
+        if (sb_len < 4) {
+            n += my_snprintf(buff + n, buff_len - n, "%ssense buffer too "
+                             "short (4 byte minimum)\n", lip);
+            return n;
+        }
+        r = 0;
+        if (strlen(lip) > 0)
+            r += my_snprintf(b + r, blen - r, "%s", lip);
+        r += my_snprintf(b + r, blen - r, "Probably uninitialized data.\n%s  "
+                         "Try to view as SCSI-1 non-extended sense:\n", lip);
+        r += my_snprintf(b + r, blen - r, "  AdValid=%d  Error class=%d  "
+                         "Error code=%d\n", !!(sense_buffer[0] & 0x80),
+                         ((sense_buffer[0] >> 4) & 0x7),
+                         (sense_buffer[0] & 0xf));
+        if (sense_buffer[0] & 0x80)
+            r += my_snprintf(b + r, blen - r, "%s  lba=0x%x\n", lip,
+                     sg_get_unaligned_be24(sense_buffer + 1) & 0x1fffff);
+        n += my_snprintf(buff + n, buff_len - n, "%s\n", b);
+        len = sb_len;
+        if (len > 32)
+            len = 32;   /* trim in case there is a lot of rubbish */
+    }
+    if (raw_sinfo) {
+        char z[64];
+
+        n += my_snprintf(buff + n, buff_len - n, "%s Raw sense data (in hex):"
+                         "\n", lip);
+        if (n >= (buff_len - 1))
+            return n;
+        snprintf(z, sizeof(z), "%.50s        ", lip);
+        n += dStrHexStr((const char *)sense_buffer, len, z,  0,
+                        buff_len - n, buff + n);
+    }
+    return n;
+}
+
+/* Print sense information */
+void
+sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
+               int sb_len, int raw_sinfo)
+{
+    char b[2048];
+
+    sg_get_sense_str(leadin, sense_buffer, sb_len, raw_sinfo, sizeof(b), b);
+    pr2ws("%s", b);
+}
+
+/* See description in sg_lib.h header file */
+int
+sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len,
+                        struct sg_scsi_sense_hdr * sshp)
+{
+    if (sshp)
+        memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr));
+    if ((NULL == sensep) || (0 == sb_len) || (0x70 != (0x70 & sensep[0])))
+        return 0;
+    if (sshp) {
+        sshp->response_code = (0x7f & sensep[0]);
+        if (sshp->response_code >= 0x72) {  /* descriptor format */
+            if (sb_len > 1)
+                sshp->sense_key = (0xf & sensep[1]);
+            if (sb_len > 2)
+                sshp->asc = sensep[2];
+            if (sb_len > 3)
+                sshp->ascq = sensep[3];
+            if (sb_len > 7)
+                sshp->additional_length = sensep[7];
+        } else {                              /* fixed format */
+            if (sb_len > 2)
+                sshp->sense_key = (0xf & sensep[2]);
+            if (sb_len > 7) {
+                sb_len = (sb_len < (sensep[7] + 8)) ? sb_len :
+                                                      (sensep[7] + 8);
+                if (sb_len > 12)
+                    sshp->asc = sensep[12];
+                if (sb_len > 13)
+                    sshp->ascq = sensep[13];
+            }
+        }
+    }
+    return 1;
+}
+
+/* Returns a SG_LIB_CAT_* value. If cannot decode sense_buffer or a less
+ * common sense key then return SG_LIB_CAT_SENSE .*/
+int
+sg_err_category_sense(const unsigned char * sense_buffer, int sb_len)
+{
+    struct sg_scsi_sense_hdr ssh;
+
+    if ((sense_buffer && (sb_len > 2)) &&
+        (sg_scsi_normalize_sense(sense_buffer, sb_len, &ssh))) {
+        switch (ssh.sense_key) {        /* 0 to 0x1f */
+        case SPC_SK_NO_SENSE:
+            return SG_LIB_CAT_NO_SENSE;
+        case SPC_SK_RECOVERED_ERROR:
+            return SG_LIB_CAT_RECOVERED;
+        case SPC_SK_NOT_READY:
+            return SG_LIB_CAT_NOT_READY;
+        case SPC_SK_MEDIUM_ERROR:
+        case SPC_SK_HARDWARE_ERROR:
+        case SPC_SK_BLANK_CHECK:
+            return SG_LIB_CAT_MEDIUM_HARD;
+        case SPC_SK_UNIT_ATTENTION:
+            return SG_LIB_CAT_UNIT_ATTENTION;
+            /* used to return SG_LIB_CAT_MEDIA_CHANGED when ssh.asc==0x28 */
+        case SPC_SK_ILLEGAL_REQUEST:
+            if ((0x20 == ssh.asc) && (0x0 == ssh.ascq))
+                return SG_LIB_CAT_INVALID_OP;
+            else
+                return SG_LIB_CAT_ILLEGAL_REQ;
+            break;
+        case SPC_SK_ABORTED_COMMAND:
+            if (0x10 == ssh.asc)
+                return SG_LIB_CAT_PROTECTION;
+            else
+                return SG_LIB_CAT_ABORTED_COMMAND;
+        case SPC_SK_MISCOMPARE:
+            return SG_LIB_CAT_MISCOMPARE;
+        case SPC_SK_DATA_PROTECT:
+            return SG_LIB_CAT_DATA_PROTECT;
+        case SPC_SK_COPY_ABORTED:
+            return SG_LIB_CAT_COPY_ABORTED;
+        case SPC_SK_COMPLETED:
+        case SPC_SK_VOLUME_OVERFLOW:
+            return SG_LIB_CAT_SENSE;
+        default:
+            ;   /* reserved and vendor specific sense keys fall through */
+        }
+    }
+    return SG_LIB_CAT_SENSE;
+}
+
+/* Beware: gives wrong answer for variable length command (opcode=0x7f) */
+int
+sg_get_command_size(unsigned char opcode)
+{
+    switch ((opcode >> 5) & 0x7) {
+    case 0:
+        return 6;
+    case 1: case 2: case 6: case 7:
+        return 10;
+    case 3: case 5:
+        return 12;
+        break;
+    case 4:
+        return 16;
+    default:
+        return 10;
+    }
+}
+
+void
+sg_get_command_name(const unsigned char * cmdp, int peri_type, int buff_len,
+                    char * buff)
+{
+    int service_action;
+
+    if ((NULL == buff) || (buff_len < 1))
+        return;
+    else if (1 == buff_len) {
+        buff[0] = '\0';
+        return;
+    }
+    if (NULL == cmdp) {
+        my_snprintf(buff, buff_len, "%s", "<null> command pointer");
+        return;
+    }
+    service_action = (SG_VARIABLE_LENGTH_CMD == cmdp[0]) ?
+                     sg_get_unaligned_be16(cmdp + 8) : (cmdp[1] & 0x1f);
+    sg_get_opcode_sa_name(cmdp[0], service_action, peri_type, buff_len, buff);
+}
+
+struct op_code2sa_t {
+    int op_code;
+    int pdt_match;      /* -1->all; 0->disk,ZBC,RCB, 1->tape+adc+smc */
+    struct sg_lib_value_name_t * arr;
+    const char * prefix;
+};
+
+static struct op_code2sa_t op_code2sa_arr[] = {
+    {SG_VARIABLE_LENGTH_CMD, -1, sg_lib_variable_length_arr, NULL},
+    {SG_MAINTENANCE_IN, -1, sg_lib_maint_in_arr, NULL},
+    {SG_MAINTENANCE_OUT, -1, sg_lib_maint_out_arr, NULL},
+    {SG_SERVICE_ACTION_IN_12, -1, sg_lib_serv_in12_arr, NULL},
+    {SG_SERVICE_ACTION_OUT_12, -1, sg_lib_serv_out12_arr, NULL},
+    {SG_SERVICE_ACTION_IN_16, -1, sg_lib_serv_in16_arr, NULL},
+    {SG_SERVICE_ACTION_OUT_16, -1, sg_lib_serv_out16_arr, NULL},
+    {SG_SERVICE_ACTION_BIDI, -1, sg_lib_serv_bidi_arr, NULL},
+    {SG_PERSISTENT_RESERVE_IN, -1, sg_lib_pr_in_arr, "Persistent reserve in"},
+    {SG_PERSISTENT_RESERVE_OUT, -1, sg_lib_pr_out_arr,
+     "Persistent reserve out"},
+    {SG_3PARTY_COPY_OUT, -1, sg_lib_xcopy_sa_arr, NULL},
+    {SG_3PARTY_COPY_IN, -1, sg_lib_rec_copy_sa_arr, NULL},
+    {SG_READ_BUFFER, -1, sg_lib_read_buff_arr, "Read buffer"},
+    {SG_READ_ATTRIBUTE, -1, sg_lib_read_attr_arr, "Read attribute"},
+    {SG_READ_POSITION, 1, sg_lib_read_pos_arr, "Read position"},
+    {SG_SANITIZE, 0, sg_lib_sanitize_sa_arr, "Sanitize"},
+    {SG_WRITE_BUFFER, -1, sg_lib_write_buff_arr, "Write buffer"},
+    {SG_ZONING_IN, 0, sg_lib_zoning_in_arr, NULL},
+    {SG_ZONING_OUT, 0, sg_lib_zoning_out_arr, NULL},
+    {0xffff, -1, NULL, NULL},
+};
+
+void
+sg_get_opcode_sa_name(unsigned char cmd_byte0, int service_action,
+                      int peri_type, int buff_len, char * buff)
+{
+    int d_pdt;
+    const struct sg_lib_value_name_t * vnp;
+    const struct op_code2sa_t * osp;
+    char b[80];
+
+    if ((NULL == buff) || (buff_len < 1))
+        return;
+    else if (1 == buff_len) {
+        buff[0] = '\0';
+        return;
+    }
+
+    d_pdt = sg_lib_pdt_decay(peri_type);
+    for (osp = op_code2sa_arr; osp->arr; ++osp) {
+        if ((int)cmd_byte0 == osp->op_code) {
+            if ((osp->pdt_match < 0) || (d_pdt == osp->pdt_match)) {
+                vnp = get_value_name(osp->arr, service_action, peri_type);
+                if (vnp) {
+                    if (osp->prefix)
+                        my_snprintf(buff, buff_len, "%s, %s", osp->prefix,
+                                    vnp->name);
+                    else
+                        my_snprintf(buff, buff_len, "%s", vnp->name);
+                } else {
+                    sg_get_opcode_name(cmd_byte0, peri_type, sizeof(b), b);
+                    my_snprintf(buff, buff_len, "%s service action=0x%x",
+                                b, service_action);
+                }
+            } else
+                sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff);
+            return;
+        }
+    }
+    sg_get_opcode_name(cmd_byte0, peri_type, buff_len, buff);
+}
+
+void
+sg_get_opcode_name(unsigned char cmd_byte0, int peri_type, int buff_len,
+                   char * buff)
+{
+    const struct sg_lib_value_name_t * vnp;
+    int grp;
+
+    if ((NULL == buff) || (buff_len < 1))
+        return;
+    else if (1 == buff_len) {
+        buff[0] = '\0';
+        return;
+    }
+    if (SG_VARIABLE_LENGTH_CMD == cmd_byte0) {
+        my_snprintf(buff, buff_len, "%s", "Variable length");
+        return;
+    }
+    grp = (cmd_byte0 >> 5) & 0x7;
+    switch (grp) {
+    case 0:
+    case 1:
+    case 2:
+    case 4:
+    case 5:
+        vnp = get_value_name(sg_lib_normal_opcodes, cmd_byte0, peri_type);
+        if (vnp)
+            my_snprintf(buff, buff_len, "%s", vnp->name);
+        else
+            my_snprintf(buff, buff_len, "Opcode=0x%x", (int)cmd_byte0);
+        break;
+    case 3:
+        my_snprintf(buff, buff_len, "Reserved [0x%x]", (int)cmd_byte0);
+        break;
+    case 6:
+    case 7:
+        my_snprintf(buff, buff_len, "Vendor specific [0x%x]", (int)cmd_byte0);
+        break;
+    default:
+        my_snprintf(buff, buff_len, "Opcode=0x%x", (int)cmd_byte0);
+        break;
+    }
+}
+
+/* Iterates to next designation descriptor in the device identification
+ * VPD page. The 'initial_desig_desc' should point to start of first
+ * descriptor with 'page_len' being the number of valid bytes in that
+ * and following descriptors. To start, 'off' should point to a negative
+ * value, thereafter it should point to the value yielded by the previous
+ * call. If 0 returned then 'initial_desig_desc + *off' should be a valid
+ * descriptor; returns -1 if normal end condition and -2 for an abnormal
+ * termination. Matches association, designator_type and/or code_set when
+ * any of those values are greater than or equal to zero. */
+int
+sg_vpd_dev_id_iter(const unsigned char * initial_desig_desc, int page_len,
+                   int * off, int m_assoc, int m_desig_type, int m_code_set)
+{
+    const unsigned char * ucp;
+    int k, c_set, assoc, desig_type;
+
+    for (k = *off, ucp = initial_desig_desc ; (k + 3) < page_len; ) {
+        k = (k < 0) ? 0 : (k + ucp[k + 3] + 4);
+        if ((k + 4) > page_len)
+            break;
+        c_set = (ucp[k] & 0xf);
+        if ((m_code_set >= 0) && (m_code_set != c_set))
+            continue;
+        assoc = ((ucp[k + 1] >> 4) & 0x3);
+        if ((m_assoc >= 0) && (m_assoc != assoc))
+            continue;
+        desig_type = (ucp[k + 1] & 0xf);
+        if ((m_desig_type >= 0) && (m_desig_type != desig_type))
+            continue;
+        *off = k;
+        return 0;
+    }
+    return (k == page_len) ? -1 : -2;
+}
+
+static const char * bad_sense_cat = "Bad sense category";
+
+/* Yield string associated with sense++ category. Returns 'buff' (or pointer
+ * to "Bad sense category" if 'buff' is NULL). If sense_cat unknown then
+ * yield "Sense category: <sense_cat>" string. */
+const char *
+sg_get_category_sense_str(int sense_cat, int buff_len, char * buff,
+                          int verbose)
+{
+    int n;
+
+    if (NULL == buff)
+        return bad_sense_cat;
+    if (buff_len <= 0)
+        return buff;
+    switch (sense_cat) {
+    case SG_LIB_CAT_CLEAN:              /* 0 */
+        snprintf(buff, buff_len, "No errors");
+        break;
+    case SG_LIB_SYNTAX_ERROR:           /* 1 */
+        snprintf(buff, buff_len, "Syntax error");
+        break;
+    case SG_LIB_CAT_NOT_READY:          /* 2 */
+        n = snprintf(buff, buff_len, "Not ready");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key");
+        break;
+    case SG_LIB_CAT_MEDIUM_HARD:        /* 3 */
+        n = snprintf(buff, buff_len, "Medium or hardware error");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key (plus blank check)");
+        break;
+    case SG_LIB_CAT_ILLEGAL_REQ:        /* 5 */
+        n = snprintf(buff, buff_len, "Illegal request");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key, apart from Invalid "
+                     "opcode");
+        break;
+    case SG_LIB_CAT_UNIT_ATTENTION:     /* 6 */
+        n = snprintf(buff, buff_len, "Unit attention");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key");
+        break;
+    case SG_LIB_CAT_DATA_PROTECT:       /* 7 */
+        n = snprintf(buff, buff_len, "Data protect");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key, write protected "
+                     "media?");
+        break;
+    case SG_LIB_CAT_INVALID_OP:         /* 9 */
+        n = snprintf(buff, buff_len, "Illegal request, invalid opcode");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key");
+        break;
+    case SG_LIB_CAT_COPY_ABORTED:       /* 10 */
+        n = snprintf(buff, buff_len, "Copy aborted");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key");
+        break;
+    case SG_LIB_CAT_ABORTED_COMMAND:    /* 11 */
+        n = snprintf(buff, buff_len, "Aborted command");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key, other than "
+                     "protection related (asc=0x10)");
+        break;
+    case SG_LIB_CAT_MISCOMPARE:         /* 14 */
+        n = snprintf(buff, buff_len, "Miscompare");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key");
+        break;
+    case SG_LIB_FILE_ERROR:             /* 15 */
+        snprintf(buff, buff_len, "File error");
+        break;
+    case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:  /* 17 */
+        snprintf(buff, buff_len, "Illegal request with info");
+        break;
+    case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO:  /* 18 */
+        snprintf(buff, buff_len, "Medium or hardware error with info");
+        break;
+    case SG_LIB_CAT_NO_SENSE:           /* 20 */
+        n = snprintf(buff, buff_len, "No sense key");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " probably additional sense "
+                     "information");
+        break;
+    case SG_LIB_CAT_RECOVERED:          /* 21 */
+        n = snprintf(buff, buff_len, "Recovered error");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " sense key");
+        break;
+    case SG_LIB_CAT_RES_CONFLICT:       /* 24 */
+        n = snprintf(buff, buff_len, "Reservation conflict");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " SCSI status");
+        break;
+    case SG_LIB_CAT_CONDITION_MET:      /* 25 */
+        n = snprintf(buff, buff_len, "Condition met");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " SCSI status");
+        break;
+    case SG_LIB_CAT_BUSY:               /* 26 */
+        n = snprintf(buff, buff_len, "Busy");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " SCSI status");
+        break;
+    case SG_LIB_CAT_TS_FULL:            /* 27 */
+        n = snprintf(buff, buff_len, "Task set full");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " SCSI status");
+        break;
+    case SG_LIB_CAT_ACA_ACTIVE:         /* 28 */
+        n = snprintf(buff, buff_len, "ACA active");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " SCSI status");
+        break;
+    case SG_LIB_CAT_TASK_ABORTED:       /* 29 */
+        n = snprintf(buff, buff_len, "Task aborted");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " SCSI status");
+        break;
+    case SG_LIB_CAT_TIMEOUT:            /* 33 */
+        snprintf(buff, buff_len, "SCSI command timeout");
+        break;
+    case SG_LIB_CAT_PROTECTION:         /* 40 */
+        n = snprintf(buff, buff_len, "Aborted command, protection");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " information (PI) problem");
+        break;
+    case SG_LIB_CAT_PROTECTION_WITH_INFO: /* 41 */
+        n = snprintf(buff, buff_len, "Aborted command with info, protection");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " information (PI) problem");
+        break;
+    case SG_LIB_CAT_MALFORMED:          /* 97 */
+        n = snprintf(buff, buff_len, "Malformed response");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, " to SCSI command");
+        break;
+    case SG_LIB_CAT_SENSE:              /* 98 */
+        n = snprintf(buff, buff_len, "Some other sense data problem");
+        if (verbose && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, ", try '-v' option for more "
+                     "information");
+        break;
+    case SG_LIB_CAT_OTHER:              /* 99 */
+        n = snprintf(buff, buff_len, "Some other error/warning has occurred");
+        if ((0 == verbose) && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, ", possible transport of driver "
+                     "issue");
+        break;
+    default:
+        n = snprintf(buff, buff_len, "Sense category: %d", sense_cat);
+        if ((0 == verbose) && (n < (buff_len - 1)))
+            snprintf(buff + n, buff_len - n, ", try '-v' option for more "
+                     "information");
+        break;
+    }
+    return buff;
+}
+
+/* safe_strerror() contributed by Clayton Weaver <cgweav at email dot com>
+ * Allows for situation in which strerror() is given a wild value (or the
+ * C library is incomplete) and returns NULL. Still not thread safe.
+ */
+
+static char safe_errbuf[64] = {'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ',
+                               'e', 'r', 'r', 'n', 'o', ':', ' ', 0};
+
+char *
+safe_strerror(int errnum)
+{
+    size_t len;
+    char * errstr;
+
+    if (errnum < 0)
+        errnum = -errnum;
+    errstr = strerror(errnum);
+    if (NULL == errstr) {
+        len = strlen(safe_errbuf);
+        my_snprintf(safe_errbuf + len, sizeof(safe_errbuf) - len, "%i",
+                    errnum);
+        return safe_errbuf;
+    }
+    return errstr;
+}
+
+static void
+trimTrailingSpaces(char * b)
+{
+    int k;
+
+    for (k = ((int)strlen(b) - 1); k >= 0; --k) {
+        if (' ' != b[k])
+            break;
+    }
+    if ('\0' != b[k + 1])
+        b[k + 1] = '\0';
+}
+
+/* Note the ASCII-hex output goes to stdout. [Most other output from functions
+ * in this file go to sg_warnings_strm (default stderr).]
+ * 'no_ascii' allows for 3 output types:
+ *     > 0     each line has address then up to 16 ASCII-hex bytes
+ *     = 0     in addition, the bytes are listed in ASCII to the right
+ *     < 0     only the ASCII-hex bytes are listed (i.e. without address) */
+static void
+dStrHexFp(const char* str, int len, int no_ascii, FILE * fp)
+{
+    const char * p = str;
+    const char * formatstr;
+    unsigned char c;
+    char buff[82];
+    int a = 0;
+    int bpstart = 5;
+    const int cpstart = 60;
+    int cpos = cpstart;
+    int bpos = bpstart;
+    int i, k, blen;
+
+    if (len <= 0)
+        return;
+    blen = (int)sizeof(buff);
+    if (0 == no_ascii)  /* address at left and ASCII at right */
+        formatstr = "%.76s\n";
+    else if (no_ascii > 0)
+        formatstr = "%s\n";     /* was: "%.58s\n" */
+    else /* negative: no address at left and no ASCII at right */
+        formatstr = "%s\n";     /* was: "%.48s\n"; */
+    memset(buff, ' ', 80);
+    buff[80] = '\0';
+    if (no_ascii < 0) {
+        bpstart = 0;
+        bpos = bpstart;
+        for (k = 0; k < len; k++) {
+            c = *p++;
+            if (bpos == (bpstart + (8 * 3)))
+                bpos++;
+            my_snprintf(&buff[bpos], blen - bpos, "%.2x",
+                        (int)(unsigned char)c);
+            buff[bpos + 2] = ' ';
+            if ((k > 0) && (0 == ((k + 1) % 16))) {
+                trimTrailingSpaces(buff);
+                fprintf(fp, formatstr, buff);
+                bpos = bpstart;
+                memset(buff, ' ', 80);
+            } else
+                bpos += 3;
+        }
+        if (bpos > bpstart) {
+            buff[bpos + 2] = '\0';
+            trimTrailingSpaces(buff);
+            fprintf(fp, "%s\n", buff);
+        }
+        return;
+    }
+    /* no_ascii>=0, start each line with address (offset) */
+    k = my_snprintf(buff + 1, blen - 1, "%.2x", a);
+    buff[k + 1] = ' ';
+
+    for (i = 0; i < len; i++) {
+        c = *p++;
+        bpos += 3;
+        if (bpos == (bpstart + (9 * 3)))
+            bpos++;
+        my_snprintf(&buff[bpos], blen - bpos, "%.2x", (int)(unsigned char)c);
+        buff[bpos + 2] = ' ';
+        if (no_ascii)
+            buff[cpos++] = ' ';
+        else {
+            if ((c < ' ') || (c >= 0x7f))
+                c = '.';
+            buff[cpos++] = c;
+        }
+        if (cpos > (cpstart + 15)) {
+            if (no_ascii)
+                trimTrailingSpaces(buff);
+            fprintf(fp, formatstr, buff);
+            bpos = bpstart;
+            cpos = cpstart;
+            a += 16;
+            memset(buff, ' ', 80);
+            k = my_snprintf(buff + 1, blen - 1, "%.2x", a);
+            buff[k + 1] = ' ';
+        }
+    }
+    if (cpos > cpstart) {
+        buff[cpos] = '\0';
+        if (no_ascii)
+            trimTrailingSpaces(buff);
+        fprintf(fp, "%s\n", buff);
+    }
+}
+
+void
+dStrHex(const char* str, int len, int no_ascii)
+{
+    dStrHexFp(str, len, no_ascii, stdout);
+}
+
+void
+dStrHexErr(const char* str, int len, int no_ascii)
+{
+    dStrHexFp(str, len, no_ascii,
+              (sg_warnings_strm ? sg_warnings_strm : stderr));
+}
+
+/* Read 'len' bytes from 'str' and output as ASCII-Hex bytes (space
+ * separated) to 'b' not to exceed 'b_len' characters. Each line
+ * starts with 'leadin' (NULL for no leadin) and there are 16 bytes
+ * per line with an extra space between the 8th and 9th bytes. 'format'
+ * is unused (currently), set to 0 . Returns number of bytes written
+ * to 'b' excluding the trailing '\0'. */
+int
+dStrHexStr(const char* str, int len, const char * leadin, int format,
+           int b_len, char * b)
+{
+    const char * p = str;
+    unsigned char c;
+    char buff[122];
+    int bpstart, bpos, k, n;
+
+    if (len <= 0) {
+        if (b_len > 0)
+            b[0] = '\0';
+        return 0;
+    }
+    if (0 != format) {
+        ;       /* do nothing different for now */
+    }
+    if (leadin) {
+        bpstart = strlen(leadin);
+        /* Cap leadin at 60 characters */
+        if (bpstart > 60)
+            bpstart = 60;
+    } else
+        bpstart = 0;
+    bpos = bpstart;
+    n = 0;
+    memset(buff, ' ', 120);
+    buff[120] = '\0';
+    if (bpstart > 0)
+        memcpy(buff, leadin, bpstart);
+    for (k = 0; k < len; k++) {
+        c = *p++;
+        if (bpos == (bpstart + (8 * 3)))
+            bpos++;
+        my_snprintf(&buff[bpos], (int)sizeof(buff) - bpos, "%.2x",
+                    (int)(unsigned char)c);
+        buff[bpos + 2] = ' ';
+        if ((k > 0) && (0 == ((k + 1) % 16))) {
+            trimTrailingSpaces(buff);
+            n += my_snprintf(b + n, b_len - n, "%s\n", buff);
+            if (n >= (b_len - 1))
+                return n;
+            bpos = bpstart;
+            memset(buff, ' ', 120);
+            if (bpstart > 0)
+                memcpy(buff, leadin, bpstart);
+        } else
+            bpos += 3;
+    }
+    if (bpos > bpstart) {
+        trimTrailingSpaces(buff);
+        n += my_snprintf(b + n, b_len - n, "%s\n", buff);
+    }
+    return n;
+}
+
+/* Returns 1 when executed on big endian machine; else returns 0.
+ * Useful for displaying ATA identify words (which need swapping on a
+ * big endian machine). */
+int
+sg_is_big_endian()
+{
+    union u_t {
+        unsigned short s;
+        unsigned char c[sizeof(unsigned short)];
+    } u;
+
+    u.s = 0x0102;
+    return (u.c[0] == 0x01);     /* The lowest address contains
+                                    the most significant byte */
+}
+
+static unsigned short
+swapb_ushort(unsigned short u)
+{
+    unsigned short r;
+
+    r = (u >> 8) & 0xff;
+    r |= ((u & 0xff) << 8);
+    return r;
+}
+
+/* Note the ASCII-hex output goes to stdout. [Most other output from functions
+ * in this file go to sg_warnings_strm (default stderr).]
+ * 'no_ascii' allows for 3 output types:
+ *     > 0     each line has address then up to 8 ASCII-hex 16 bit words
+ *     = 0     in addition, the ASCI bytes pairs are listed to the right
+ *     = -1    only the ASCII-hex words are listed (i.e. without address)
+ *     = -2    only the ASCII-hex words, formatted for "hdparm --Istdin"
+ *     < -2    same as -1
+ * If 'swapb' non-zero then bytes in each word swapped. Needs to be set
+ * for ATA IDENTIFY DEVICE response on big-endian machines. */
+void
+dWordHex(const unsigned short* words, int num, int no_ascii, int swapb)
+{
+    const unsigned short * p = words;
+    unsigned short c;
+    char buff[82];
+    unsigned char upp, low;
+    int a = 0;
+    const int bpstart = 3;
+    const int cpstart = 52;
+    int cpos = cpstart;
+    int bpos = bpstart;
+    int i, k, blen;
+
+    if (num <= 0)
+        return;
+    blen = (int)sizeof(buff);
+    memset(buff, ' ', 80);
+    buff[80] = '\0';
+    if (no_ascii < 0) {
+        for (k = 0; k < num; k++) {
+            c = *p++;
+            if (swapb)
+                c = swapb_ushort(c);
+            bpos += 5;
+            my_snprintf(&buff[bpos], blen - bpos, "%.4x", (unsigned int)c);
+            buff[bpos + 4] = ' ';
+            if ((k > 0) && (0 == ((k + 1) % 8))) {
+                if (-2 == no_ascii)
+                    printf("%.39s\n", buff +8);
+                else
+                    printf("%.47s\n", buff);
+                bpos = bpstart;
+                memset(buff, ' ', 80);
+            }
+        }
+        if (bpos > bpstart) {
+            if (-2 == no_ascii)
+                printf("%.39s\n", buff +8);
+            else
+                printf("%.47s\n", buff);
+        }
+        return;
+    }
+    /* no_ascii>=0, start each line with address (offset) */
+    k = my_snprintf(buff + 1, blen - 1, "%.2x", a);
+    buff[k + 1] = ' ';
+
+    for (i = 0; i < num; i++) {
+        c = *p++;
+        if (swapb)
+            c = swapb_ushort(c);
+        bpos += 5;
+        my_snprintf(&buff[bpos], blen - bpos, "%.4x", (unsigned int)c);
+        buff[bpos + 4] = ' ';
+        if (no_ascii) {
+            buff[cpos++] = ' ';
+            buff[cpos++] = ' ';
+            buff[cpos++] = ' ';
+        } else {
+            upp = (c >> 8) & 0xff;
+            low = c & 0xff;
+            if ((upp < 0x20) || (upp >= 0x7f))
+                upp = '.';
+            buff[cpos++] = upp;
+            if ((low < 0x20) || (low >= 0x7f))
+                low = '.';
+            buff[cpos++] = low;
+            buff[cpos++] = ' ';
+        }
+        if (cpos > (cpstart + 23)) {
+            printf("%.76s\n", buff);
+            bpos = bpstart;
+            cpos = cpstart;
+            a += 8;
+            memset(buff, ' ', 80);
+            k = my_snprintf(buff + 1, blen - 1, "%.2x", a);
+            buff[k + 1] = ' ';
+        }
+    }
+    if (cpos > cpstart)
+        printf("%.76s\n", buff);
+}
+
+/* If the number in 'buf' can be decoded or the multiplier is unknown
+ * then -1 is returned. Accepts a hex prefix (0x or 0X) or a decimal
+ * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)).
+ * Main (SI) multipliers supported: K, M, G. Ignore leading spaces and
+ * tabs; accept comma, space, tab and hash as terminator. */
+int
+sg_get_num(const char * buf)
+{
+    int res, num, n, len;
+    unsigned int unum;
+    char * cp;
+    const char * b;
+    char c = 'c';
+    char c2, c3;
+    char lb[16];
+
+    if ((NULL == buf) || ('\0' == buf[0]))
+        return -1;
+    len = strlen(buf);
+    n = strspn(buf, " \t");
+    if (n > 0) {
+        if (n == len)
+            return -1;
+        buf += n;
+        len -=n;
+    }
+    /* following hack to keep C++ happy */
+    cp = strpbrk((char *)buf, " \t,#");
+    if (cp) {
+        len = cp - buf;
+        n = (int)sizeof(lb) - 1;
+        len = (len < n) ? len : n;
+        memcpy(lb, buf, len);
+        lb[len] = '\0';
+        b = lb;
+    } else
+        b = buf;
+    if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) {
+        res = sscanf(b + 2, "%x", &unum);
+        num = unum;
+    } else if ('H' == toupper((int)b[len - 1])) {
+        res = sscanf(b, "%x", &unum);
+        num = unum;
+    } else
+        res = sscanf(b, "%d%c%c%c", &num, &c, &c2, &c3);
+    if (res < 1)
+        return -1LL;
+    else if (1 == res)
+        return num;
+    else {
+        if (res > 2)
+            c2 = toupper((int)c2);
+        if (res > 3)
+            c3 = toupper((int)c3);
+        switch (toupper((int)c)) {
+        case 'C':
+            return num;
+        case 'W':
+            return num * 2;
+        case 'B':
+            return num * 512;
+        case 'K':
+            if (2 == res)
+                return num * 1024;
+            if (('B' == c2) || ('D' == c2))
+                return num * 1000;
+            if (('I' == c2) && (4 == res) && ('B' == c3))
+                return num * 1024;
+            return -1;
+        case 'M':
+            if (2 == res)
+                return num * 1048576;
+            if (('B' == c2) || ('D' == c2))
+                return num * 1000000;
+            if (('I' == c2) && (4 == res) && ('B' == c3))
+                return num * 1048576;
+            return -1;
+        case 'G':
+            if (2 == res)
+                return num * 1073741824;
+            if (('B' == c2) || ('D' == c2))
+                return num * 1000000000;
+            if (('I' == c2) && (4 == res) && ('B' == c3))
+                return num * 1073741824;
+            return -1;
+        case 'X':
+            cp = (char *)strchr(b, 'x');
+            if (NULL == cp)
+                cp = (char *)strchr(b, 'X');
+            if (cp) {
+                n = sg_get_num(cp + 1);
+                if (-1 != n)
+                    return num * n;
+            }
+            return -1;
+        default:
+            pr2ws("unrecognized multiplier\n");
+            return -1;
+        }
+    }
+}
+
+/* If the number in 'buf' can not be decoded then -1 is returned. Accepts a
+ * hex prefix (0x or 0X) or a 'h' (or 'H') suffix; otherwise decimal is
+ * assumed. Does not accept multipliers. Accept a comma (","), a whitespace
+ * or newline as terminator. */
+int
+sg_get_num_nomult(const char * buf)
+{
+    int res, len, num;
+    unsigned int unum;
+    char * commap;
+
+    if ((NULL == buf) || ('\0' == buf[0]))
+        return -1;
+    len = strlen(buf);
+    commap = (char *)strchr(buf + 1, ',');
+    if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
+        res = sscanf(buf + 2, "%x", &unum);
+        num = unum;
+    } else if (commap && ('H' == toupper((int)*(commap - 1)))) {
+        res = sscanf(buf, "%x", &unum);
+        num = unum;
+    } else if ((NULL == commap) && ('H' == toupper((int)buf[len - 1]))) {
+        res = sscanf(buf, "%x", &unum);
+        num = unum;
+    } else
+        res = sscanf(buf, "%d", &num);
+    if (1 == res)
+        return num;
+    else
+        return -1;
+}
+
+/* If the number in 'buf' can be decoded or the multiplier is unknown
+ * then -1LL is returned. Accepts a hex prefix (0x or 0X) or a decimal
+ * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)).
+ * Main (SI) multipliers supported: K, M, G, T, P. Ignore leading spaces
+ * and tabs; accept comma, space, tab and hash as terminator. */
+int64_t
+sg_get_llnum(const char * buf)
+{
+    int res, len, n;
+    int64_t num, ll;
+    uint64_t unum;
+    char * cp;
+    const char * b;
+    char c = 'c';
+    char c2, c3;
+    char lb[32];
+
+    if ((NULL == buf) || ('\0' == buf[0]))
+        return -1LL;
+    len = strlen(buf);
+    n = strspn(buf, " \t");
+    if (n > 0) {
+        if (n == len)
+            return -1LL;
+        buf += n;
+        len -=n;
+    }
+    /* following hack to keep C++ happy */
+    cp = strpbrk((char *)buf, " \t,#");
+    if (cp) {
+        len = cp - buf;
+        n = (int)sizeof(lb) - 1;
+        len = (len < n) ? len : n;
+        memcpy(lb, buf, len);
+        lb[len] = '\0';
+        b = lb;
+    } else
+        b = buf;
+    if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) {
+        res = sscanf(b + 2, "%" SCNx64 "", &unum);
+        num = unum;
+    } else if ('H' == toupper((int)b[len - 1])) {
+        res = sscanf(b, "%" SCNx64 "", &unum);
+        num = unum;
+    } else
+        res = sscanf(b, "%" SCNd64 "%c%c%c", &num, &c, &c2, &c3);
+    if (res < 1)
+        return -1LL;
+    else if (1 == res)
+        return num;
+    else {
+        if (res > 2)
+            c2 = toupper((int)c2);
+        if (res > 3)
+            c3 = toupper((int)c3);
+        switch (toupper((int)c)) {
+        case 'C':
+            return num;
+        case 'W':
+            return num * 2;
+        case 'B':
+            return num * 512;
+        case 'K':
+            if (2 == res)
+                return num * 1024;
+            if (('B' == c2) || ('D' == c2))
+                return num * 1000;
+            if (('I' == c2) && (4 == res) && ('B' == c3))
+                return num * 1024;
+            return -1LL;
+        case 'M':
+            if (2 == res)
+                return num * 1048576;
+            if (('B' == c2) || ('D' == c2))
+                return num * 1000000;
+            if (('I' == c2) && (4 == res) && ('B' == c3))
+                return num * 1048576;
+            return -1LL;
+        case 'G':
+            if (2 == res)
+                return num * 1073741824;
+            if (('B' == c2) || ('D' == c2))
+                return num * 1000000000;
+            if (('I' == c2) && (4 == res) && ('B' == c3))
+                return num * 1073741824;
+            return -1LL;
+        case 'T':
+            if (2 == res)
+                return num * 1099511627776LL;
+            if (('B' == c2) || ('D' == c2))
+                return num * 1000000000000LL;
+            if (('I' == c2) && (4 == res) && ('B' == c3))
+                return num * 1099511627776LL;
+            return -1LL;
+        case 'P':
+            if (2 == res)
+                return num * 1099511627776LL * 1024;
+            if (('B' == c2) || ('D' == c2))
+                return num * 1000000000000LL * 1000;
+            if (('I' == c2) && (4 == res) && ('B' == c3))
+                return num * 1099511627776LL * 1024;
+            return -1LL;
+        case 'X':
+            cp = (char *)strchr(b, 'x');
+            if (NULL == cp)
+                cp = (char *)strchr(b, 'X');
+            if (cp) {
+                ll = sg_get_llnum(cp + 1);
+                if (-1LL != ll)
+                    return num * ll;
+            }
+            return -1LL;
+        default:
+            pr2ws("unrecognized multiplier\n");
+            return -1LL;
+        }
+    }
+}
+
+/* Extract character sequence from ATA words as in the model string
+ * in a IDENTIFY DEVICE response. Returns number of characters
+ * written to 'ochars' before 0 character is found or 'num' words
+ * are processed. */
+int
+sg_ata_get_chars(const unsigned short * word_arr, int start_word,
+                 int num_words, int is_big_endian, char * ochars)
+{
+    int k;
+    unsigned short s;
+    char a, b;
+    char * op = ochars;
+
+    for (k = start_word; k < (start_word + num_words); ++k) {
+        s = word_arr[k];
+        if (is_big_endian) {
+            a = s & 0xff;
+            b = (s >> 8) & 0xff;
+        } else {
+            a = (s >> 8) & 0xff;
+            b = s & 0xff;
+        }
+        if (a == 0)
+            break;
+        *op++ = a;
+        if (b == 0)
+            break;
+        *op++ = b;
+    }
+    return op - ochars;
+}
+
+const char *
+sg_lib_version()
+{
+    return sg_lib_version_str;
+}
+
+
+#ifdef SG_LIB_MINGW
+/* Non Unix OSes distinguish between text and binary files.
+   Set text mode on fd. Does nothing in Unix. Returns negative number on
+   failure. */
+
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+sg_set_text_mode(int fd)
+{
+    return setmode(fd, O_TEXT);
+}
+
+/* Set binary mode on fd. Does nothing in Unix. Returns negative number on
+   failure. */
+int
+sg_set_binary_mode(int fd)
+{
+    return setmode(fd, O_BINARY);
+}
+
+#else
+/* For Unix the following functions are dummies. */
+int
+sg_set_text_mode(int fd)
+{
+    return fd;  /* fd should be >= 0 */
+}
+
+int
+sg_set_binary_mode(int fd)
+{
+    return fd;
+}
+
+#endif
+
+int
+pr2serr(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(stderr, fmt, args);
+    va_end(args);
+    return n;
+}
diff --git a/sg3_utils/lib/sg_lib_data.c b/sg3_utils/lib/sg_lib_data.c
new file mode 100644
index 0000000..5114c12
--- /dev/null
+++ b/sg3_utils/lib/sg_lib_data.c
@@ -0,0 +1,1452 @@
+/*
+ * Copyright (c) 2007-2016 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 <stdlib.h>
+
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+#define SG_SCSI_STRINGS 1
+#endif
+
+
+const char * sg_lib_version_str = "2.17 20160202";  /* spc5r08, sbc4r10 */
+
+
+/* indexed by pdt; those that map to own index do not decay */
+int sg_lib_pdt_decay_arr[32] = {
+    PDT_DISK, PDT_TAPE, PDT_TAPE /* printer */, PDT_PROCESSOR,
+    PDT_DISK /* WO */, PDT_MMC, PDT_SCANNER, PDT_DISK /* optical */,
+    PDT_MCHANGER, PDT_COMMS, 0xa, 0xb,
+    PDT_SAC, PDT_SES, PDT_DISK /* rbc */, PDT_OCRW,
+    PDT_BCC, PDT_OSD, PDT_TAPE /* adc */, PDT_SMD,
+    PDT_DISK /* zbc */, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, PDT_WLUN, PDT_UNKNOWN
+};
+
+#ifdef SG_SCSI_STRINGS
+struct sg_lib_value_name_t sg_lib_normal_opcodes[] = {
+    {0, 0, "Test Unit Ready"},
+    {0x1, 0, "Rezero Unit"},
+    {0x1, PDT_TAPE, "Rewind"},
+    {0x3, 0, "Request Sense"},
+    {0x4, 0, "Format Unit"},
+    {0x4, PDT_TAPE, "Format medium"},
+    {0x4, PDT_PRINTER, "Format"},
+    {0x5, 0, "Read Block Limits"},
+    {0x7, 0, "Reassign Blocks"},
+    {0x7, PDT_MCHANGER, "Initialize element status"},
+    {0x8, 0, "Read(6)"},        /* obsolete in sbc3r30 */
+    {0x8, PDT_PROCESSOR, "Receive"},
+    {0xa, 0, "Write(6)"},       /* obsolete in sbc3r30 */
+    {0xa, PDT_PRINTER, "Print"},
+    {0xa, PDT_PROCESSOR, "Send"},
+    {0xb, 0, "Seek(6)"},
+    {0xb, PDT_TAPE, "Set capacity"},
+    {0xb, PDT_PRINTER, "Slew and print"},
+    {0xf, 0, "Read reverse(6)"},
+    {0x10, 0, "Write filemarks(6)"},
+    {0x10, PDT_PRINTER, "Synchronize buffer"},
+    {0x11, 0, "Space(6)"},
+    {0x12, 0, "Inquiry"},
+    {0x13, 0, "Verify(6)"},  /* SSC */
+    {0x14, 0, "Recover buffered data"},
+    {0x15, 0, "Mode select(6)"}, /* SBC-3 r31 recommends Mode select(10) */
+    {0x16, 0, "Reserve(6)"},    /* obsolete in SPC-4 r11 */
+    {0x16, PDT_MCHANGER, "Reserve element(6)"},
+    {0x17, 0, "Release(6)"},    /* obsolete in SPC-4 r11 */
+    {0x17, PDT_MCHANGER, "Release element(6)"},
+    {0x18, 0, "Copy"},          /* obsolete in SPC-4 r11 */
+    {0x19, 0, "Erase(6)"},
+    {0x1a, 0, "Mode sense(6)"}, /* SBC-3 r31 recommends Mode sense(10) */
+    {0x1b, 0, "Start stop unit"},
+    {0x1b, PDT_TAPE, "Load unload"},
+    {0x1b, PDT_ADC, "Load unload"},
+    {0x1b, PDT_PRINTER, "Stop print"},
+    {0x1c, 0, "Receive diagnostic results"},
+    {0x1d, 0, "Send diagnostic"},
+    {0x1e, 0, "Prevent allow medium removal"},
+    {0x23, 0, "Read Format capacities"},
+    {0x24, 0, "Set window"},
+    {0x25, 0, "Read capacity(10)"},
+                        /* SBC-3 r31 recommends Read capacity(16) */
+    {0x25, PDT_OCRW, "Read card capacity"},
+    {0x28, 0, "Read(10)"},      /* SBC-3 r31 recommends Read(16) */
+    {0x29, 0, "Read generation"},
+    {0x2a, 0, "Write(10)"},     /* SBC-3 r31 recommends Write(16) */
+    {0x2b, 0, "Seek(10)"},
+    {0x2b, PDT_TAPE, "Locate(10)"},
+    {0x2b, PDT_MCHANGER, "Position to element"},
+    {0x2c, 0, "Erase(10)"},
+    {0x2d, 0, "Read updated block"},
+    {0x2e, 0, "Write and verify(10)"},
+                        /* SBC-3 r31 recommends Write and verify(16) */
+    {0x2f, 0, "Verify(10)"},    /* SBC-3 r31 recommends Verify(16) */
+    {0x30, 0, "Search data high(10)"},
+    {0x31, 0, "Search data equal(10)"},
+    {0x32, 0, "Search data low(10)"},
+    {0x33, 0, "Set limits(10)"},
+    {0x34, 0, "Pre-fetch(10)"}, /* SBC-3 r31 recommends Pre-fetch(16) */
+    {0x34, PDT_TAPE, "Read position"},
+    {0x35, 0, "Synchronize cache(10)"},
+                        /* SBC-3 r31 recommends Synchronize cache(16) */
+    {0x36, 0, "Lock unlock cache(10)"},
+    {0x37, 0, "Read defect data(10)"},
+                        /* SBC-3 r31 recommends Read defect data(12) */
+    {0x37, PDT_MCHANGER, "Initialize element status with range"},
+    {0x38, 0, "Medium scan"},
+    {0x39, 0, "Compare"},               /* obsolete in SPC-4 r11 */
+    {0x3a, 0, "Copy and verify"},       /* obsolete in SPC-4 r11 */
+    {0x3b, 0, "Write buffer"},
+    {0x3c, 0, "Read buffer(10)"},
+    {0x3d, 0, "Update block"},
+    {0x3e, 0, "Read long(10)"},         /* obsolete in SBC-4 r7 */
+    {0x3f, 0, "Write long(10)"}, /* SBC-3 r31 recommends Write long(16) */
+    {0x40, 0, "Change definition"},     /* obsolete in SPC-4 r11 */
+    {0x41, 0, "Write same(10)"}, /* SBC-3 r31 recommends Write same(16) */
+    {0x42, 0, "Unmap"},                 /* added SPC-4 rev 18 */
+    {0x42, PDT_MMC, "Read sub-channel"},
+    {0x43, PDT_MMC, "Read TOC/PMA/ATIP"},
+    {0x44, 0, "Report density support"},
+    {0x45, PDT_MMC, "Play audio(10)"},
+    {0x46, PDT_MMC, "Get configuration"},
+    {0x47, PDT_MMC, "Play audio msf"},
+    {0x48, 0, "Sanitize"},
+    {0x4a, PDT_MMC, "Get event status notification"},
+    {0x4b, PDT_MMC, "Pause/resume"},
+    {0x4c, 0, "Log select"},
+    {0x4d, 0, "Log sense"},
+    {0x4e, 0, "Stop play/scan"},
+    {0x50, 0, "Xdwrite(10)"},           /* obsolete in SBC-3 r31 */
+    {0x51, 0, "Xpwrite(10)"},
+    {0x51, PDT_MMC, "Read disk information"},
+    {0x52, 0, "Xdread(10)"},            /* obsolete in SBC-3 r31 */
+    {0x52, PDT_MMC, "Read track information"},
+    {0x53, 0, "Xdwriteread(10)"},
+    {0x54, 0, "Send OPC information"},
+    {0x55, 0, "Mode select(10)"},
+    {0x56, 0, "Reserve(10)"},           /* obsolete in SPC-4 r11 */
+    {0x56, PDT_MCHANGER, "Reserve element(10)"},
+    {0x57, 0, "Release(10)"},           /* obsolete in SPC-4 r11 */
+    {0x57, PDT_MCHANGER, "Release element(10)"},
+    {0x58, 0, "Repair track"},
+    {0x5a, 0, "Mode sense(10)"},
+    {0x5b, 0, "Close track/session"},
+    {0x5c, 0, "Read buffer capacity"},
+    {0x5d, 0, "Send cue sheet"},
+    {0x5e, 0, "Persistent reserve in"},
+    {0x5f, 0, "Persistent reserve out"},
+    {0x7e, 0, "Extended cdb (XCBD)"},           /* added in SPC-4 r12 */
+    {0x80, 0, "Xdwrite extended(16)"},
+    {0x80, PDT_TAPE, "Write filemarks(16)"},
+    {0x81, 0, "Rebuild(16)"},
+    {0x81, PDT_TAPE, "Read reverse(16)"},
+    {0x82, 0, "Regenerate(16)"},
+    {0x83, 0, "Third party copy out"},  /* Extended copy, before spc4r34 */
+        /* Following was "Receive copy results", before spc4r34 */
+    {0x84, 0, "Third party copy in"},
+    {0x85, 0, "ATA pass-through(16)"},  /* was 0x98 in spc3 rev21c */
+    {0x86, 0, "Access control in"},
+    {0x87, 0, "Access control out"},
+    {0x88, 0, "Read(16)"},
+    {0x89, 0, "Compare and write"},
+    {0x8a, 0, "Write(16)"},
+    {0x8b, 0, "Orwrite(16)"},
+    {0x8c, 0, "Read attribute"},
+    {0x8d, 0, "Write attribute"},
+    {0x8e, 0, "Write and verify(16)"},
+    {0x8f, 0, "Verify(16)"},
+    {0x90, 0, "Pre-fetch(16)"},
+    {0x91, 0, "Synchronize cache(16)"},
+    {0x91, PDT_TAPE, "Space(16)"},
+    {0x92, 0, "Lock unlock cache(16)"},
+    {0x92, PDT_TAPE, "Locate(16)"},
+    {0x93, 0, "Write same(16)"},
+    {0x93, PDT_TAPE, "Erase(16)"},
+    {0x94, PDT_ZBC, "ZBC out"},  /* new sbc4r04, has service actions */
+    {0x95, PDT_ZBC, "ZBC in"},   /* new sbc4r04, has service actions */
+    {0x9a, 0, "Write stream(16)"},      /* added sbc4r07 */
+    {0x9b, 0, "Read buffer(16)"},       /* added spc5r02 */
+    {0x9c, 0, "Write atomic(16)"},
+    {0x9d, 0, "Service action bidirectional"},  /* added spc4r35 */
+    {0x9e, 0, "Service action in(16)"},
+    {0x9f, 0, "Service action out(16)"},
+    {0xa0, 0, "Report luns"},
+    {0xa1, 0, "ATA pass-through(12)"},
+    {0xa1, PDT_MMC, "Blank"},
+    {0xa2, 0, "Security protocol in"},
+    {0xa3, 0, "Maintenance in"},
+    {0xa3, PDT_MMC, "Send key"},
+    {0xa4, 0, "Maintenance out"},
+    {0xa4, PDT_MMC, "Report key"},
+    {0xa5, 0, "Move medium"},
+    {0xa5, PDT_MMC, "Play audio(12)"},
+    {0xa6, 0, "Exchange medium"},
+    {0xa6, PDT_MMC, "Load/unload medium"},
+    {0xa7, 0, "Move medium attached"},
+    {0xa7, PDT_MMC, "Set read ahead"},
+    {0xa8, 0, "Read(12)"},      /* SBC-3 r31 recommends Read(16) */
+    {0xa9, 0, "Service action out(12)"},
+    {0xaa, 0, "Write(12)"},     /* SBC-3 r31 recommends Write(16) */
+    {0xab, 0, "Service action in(12)"},
+    {0xac, 0, "erase(12)"},
+    {0xac, PDT_MMC, "Get performance"},
+    {0xad, PDT_MMC, "Read DVD/BD structure"},
+    {0xae, 0, "Write and verify(12)"},
+                        /* SBC-3 r31 recommends Write and verify(16) */
+    {0xaf, 0, "Verify(12)"},    /* SBC-3 r31 recommends Verify(16) */
+    {0xb0, 0, "Search data high(12)"},
+    {0xb1, 0, "Search data equal(12)"},
+    {0xb1, PDT_MCHANGER, "Open/close import/export element"},
+    {0xb2, 0, "Search data low(12)"},
+    {0xb3, 0, "Set limits(12)"},
+    {0xb4, 0, "Read element status attached"},
+    {0xb5, 0, "Security protocol out"},
+    {0xb5, PDT_MCHANGER, "Request volume element address"},
+    {0xb6, 0, "Send volume tag"},
+    {0xb6, PDT_MMC, "Set streaming"},
+    {0xb7, 0, "Read defect data(12)"},
+    {0xb8, 0, "Read element status"},
+    {0xb9, 0, "Read CD msf"},
+    {0xba, 0, "Redundancy group in"},
+    {0xba, PDT_MMC, "Scan"},
+    {0xbb, 0, "Redundancy group out"},
+    {0xbb, PDT_MMC, "Set CD speed"},
+    {0xbc, 0, "Spare in"},
+    {0xbd, 0, "Spare out"},
+    {0xbd, PDT_MMC, "Mechanism status"},
+    {0xbe, 0, "Volume set in"},
+    {0xbe, PDT_MMC, "Read CD"},
+    {0xbf, 0, "Volume set out"},
+    {0xbf, PDT_MMC, "Send DVD/BD structure"},
+    {0xffff, 0, NULL},
+};
+
+/* Read buffer [0x3c] service actions, need prefix */
+struct sg_lib_value_name_t sg_lib_read_buff_arr[] = {
+    {0x0, 0, "combined header and data [or multiple modes]"},
+    {0x2, 0, "data"},
+    {0x3, 0, "descriptor"},
+    {0xa, 0, "read data from echo buffer"},
+    {0xb, 0, "echo buffer descriptor"},
+    {0x1a, 0, "enable expander comms protocol and echo buffer"},
+    {0x1c, 0, "error history"},
+    {0xffff, 0, NULL},
+};
+
+/* Write buffer [0x3b] service actions, need prefix */
+struct sg_lib_value_name_t sg_lib_write_buff_arr[] = {
+    {0x0, 0, "combined header and data [or multiple modes]"},
+    {0x2, 0, "data"},
+    {0x4, 0, "download microcode and activate"},
+    {0x5, 0, "download microcode, save, and activate"},
+    {0x6, 0, "download microcode with offsets and activate"},
+    {0x7, 0, "download microcode with offsets, save, and activate"},
+    {0xa, 0, "write data to echo buffer"},
+    {0xd, 0, "download microcode with offsets, select activation events, "
+             "save and defer activate"},
+    {0xe, 0, "download microcode with offsets, save and defer activate"},
+    {0xf, 0, "activate deferred microcode"},
+    {0x1a, 0, "enable expander comms protocol and echo buffer"},
+    {0x1b, 0, "disable expander comms protocol"},
+    {0x1c, 0, "download application client error history"},
+    {0xffff, 0, NULL},
+};
+
+/* Read position (SSC) [0x34] service actions, need prefix */
+struct sg_lib_value_name_t sg_lib_read_pos_arr[] = {
+    {0x0, PDT_TAPE, "short form - block id"},
+    {0x1, PDT_TAPE, "short form - vendor specific"},
+    {0x6, PDT_TAPE, "long form"},
+    {0x8, PDT_TAPE, "extended form"},
+    {0xffff, 0, NULL},
+};
+
+/* Maintenance in [0xa3] service actions */
+struct sg_lib_value_name_t sg_lib_maint_in_arr[] = {
+    {0x5, 0, "Report identifying information"},
+                /* was "Report device identifier" prior to spc4r07 */
+    {0xa, 0, "Report target port groups"},
+    {0xb, 0, "Report aliases"},
+    {0xc, 0, "Report supported operation codes"},
+    {0xd, 0, "Report supported task management functions"},
+    {0xe, 0, "Report priority"},
+    {0xf, 0, "Report timestamp"},
+    {0x10, 0, "Management protocol in"},
+    {0x1d, 0, "Report provisioning initialization pattern"},
+    {0x1f, 0, "Maintenance in vendor specific"},
+    {0xffff, 0, NULL},
+};
+
+/* Maintenance out [0xa4] service actions */
+struct sg_lib_value_name_t sg_lib_maint_out_arr[] = {
+    {0x6, 0, "Set identifying information"},
+                /* was "Set device identifier" prior to spc4r07 */
+    {0xa, 0, "Set target port groups"},
+    {0xb, 0, "Change aliases"},
+    {0xc, 0, "Remove I_T nexus"},
+    {0xe, 0, "Set priority"},
+    {0xf, 0, "Set timestamp"},
+    {0x10, 0, "Management protocol out"},
+    {0x1f, 0, "Maintenance out vendor specific"},
+    {0xffff, 0, NULL},
+};
+
+/* Sanitize [0x48] service actions, need prefix */
+struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[] = {
+    {0x1, 0, "overwrite"},
+    {0x2, 0, "block erase"},
+    {0x3, 0, "cryptographic erase"},
+    {0x1f, 0, "exit failure mode"},
+    {0xffff, 0, NULL},
+};
+
+/* Service action in(12) [0xab] service actions */
+struct sg_lib_value_name_t sg_lib_serv_in12_arr[] = {
+    {0x1, 0, "Read media serial number"},
+    {0xffff, 0, NULL},
+};
+
+/* Service action out(12) [0xa9] service actions */
+struct sg_lib_value_name_t sg_lib_serv_out12_arr[] = {
+    {0xff, 0, "Impossible command name"},
+    {0xffff, 0, NULL},
+};
+
+/* Service action in(16) [0x9e] service actions */
+struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = {
+    {0x10, 0, "Read capacity(16)"},
+    {0x11, 0, "Read long(16)"},         /* obsolete in SBC-4 r7 */
+    {0x12, 0, "Get LBA status"},
+    {0x13, 0, "Report referrals"},
+    {0x14, 0, "Stream control"},
+    {0x15, 0, "Background control"},
+    {0x16, 0, "Get stream status"},
+    {0xffff, 0, NULL},
+};
+
+/* Service action out(16) [0x9f] service actions */
+struct sg_lib_value_name_t sg_lib_serv_out16_arr[] = {
+    {0x11, 0, "Write long(16)"},
+    {0x14, PDT_ZBC, "Reset write pointer"},
+    {0x1f, PDT_ADC, "Notify data transfer device(16)"},
+    {0xffff, 0, NULL},
+};
+
+/* Service action bidirectional [0x9d] service actions */
+struct sg_lib_value_name_t sg_lib_serv_bidi_arr[] = {
+    {0xffff, 0, NULL},
+};
+
+/* Persistent reserve in [0x5e] service actions, need prefix */
+struct sg_lib_value_name_t sg_lib_pr_in_arr[] = {
+    {0x0, 0, "read keys"},
+    {0x1, 0, "read reservation"},
+    {0x2, 0, "report capabilities"},
+    {0x3, 0, "read full status"},
+    {0xffff, 0, NULL},
+};
+
+/* Persistent reserve out [0x5f] service actions, need prefix */
+struct sg_lib_value_name_t sg_lib_pr_out_arr[] = {
+    {0x0, 0, "register"},
+    {0x1, 0, "reserve"},
+    {0x2, 0, "release"},
+    {0x3, 0, "clear"},
+    {0x4, 0, "preempt"},
+    {0x5, 0, "preempt and abort"},
+    {0x6, 0, "register and ignore existing key"},
+    {0x7, 0, "register and move"},
+    {0x8, 0, "replace lost reservation"},
+    {0xffff, 0, NULL},
+};
+
+/* Third party copy in [0x83] service actions
+ * Opcode 'Receive copy results' was renamed 'Third party copy in' in spc4r34
+ * LID1 is an abbreviation of List Identifier length of 1 byte */
+struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = {
+    {0x0, 0, "Extended copy(LID1)"},
+    {0x1, 0, "Extended copy(LID4)"},
+    {0x10, 0, "Populate token"},
+    {0x11, 0, "Write using token"},
+    {0x1c, 0, "Copy operation abort"},
+    {0xffff, 0, NULL},
+};
+
+/* Third party copy out [0x84] service actions
+ * Opcode 'Extended copy' was renamed 'Third party copy out' in spc4r34
+ * LID4 is an abbreviation of List Identifier length of 4 bytes */
+struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = {
+    {0x0, 0, "Receive copy status(LID1)"},
+    {0x1, 0, "Receive copy data(LID1)"},
+    {0x3, 0, "Receive copy operating parameters"},
+    {0x4, 0, "Receive copy failure details(LID1)"},
+    {0x5, 0, "Receive copy status(LID4)"},
+    {0x6, 0, "Receive copy data(LID4)"},
+    {0x7, 0, "Receive ROD token information"},
+    {0x8, 0, "Report all ROD tokens"},
+    {0xffff, 0, NULL},
+};
+
+/* Variable length cdb [0x7f] service actions (more than 16 bytes long) */
+struct sg_lib_value_name_t sg_lib_variable_length_arr[] = {
+    {0x1, 0, "Rebuild(32)"},
+    {0x2, 0, "Regenerate(32)"},
+    {0x3, 0, "Xdread(32)"},     /* obsolete in SBC-3 r31 */
+    {0x4, 0, "Xdwrite(32)"},    /* obsolete in SBC-3 r31 */
+    {0x5, 0, "Xdwrite extended(32)"},
+    {0x6, 0, "Xpwrite(32)"},
+    {0x7, 0, "Xdwriteread(32)"},
+    {0x8, 0, "Xdwrite extended(64)"},
+    {0x9, 0, "Read(32)"},
+    {0xa, 0, "Verify(32)"},
+    {0xb, 0, "Write(32)"},
+    {0xc, 0, "Write and verify(32)"},
+    {0xd, 0, "Write same(32)"},
+    {0xe, 0, "Orwrite(32)"},         /* added sbc3r25 */
+    {0xf, 0, "Atomic write(32)"},    /* added sbc4r02 */
+    {0x10, 0, "Write stream(32)"},   /* added sbc4r07 */
+    {0x1800, 0, "Receive credential"},
+    {0x8801, 0, "Format OSD (osd)"},
+    {0x8802, 0, "Create (osd)"},
+    {0x8803, 0, "List (osd)"},
+    {0x8805, 0, "Read (osd)"},
+    {0x8806, 0, "Write (osd)"},
+    {0x8807, 0, "Append (osd)"},
+    {0x8808, 0, "Flush (osd)"},
+    {0x880a, 0, "Remove (osd)"},
+    {0x880b, 0, "Create partition (osd)"},
+    {0x880c, 0, "Remove partition (osd)"},
+    {0x880e, 0, "Get attributes (osd)"},
+    {0x880f, 0, "Set attributes (osd)"},
+    {0x8812, 0, "Create and write (osd)"},
+    {0x8815, 0, "Create collection (osd)"},
+    {0x8816, 0, "Remove collection (osd)"},
+    {0x8817, 0, "List collection (osd)"},
+    {0x8818, 0, "Set key (osd)"},
+    {0x8819, 0, "Set master key (osd)"},
+    {0x881a, 0, "Flush collection (osd)"},
+    {0x881b, 0, "Flush partition (osd)"},
+    {0x881c, 0, "Flush OSD (osd)"},
+    {0x8880, 0, "Object structure check (osd-2)"},
+    {0x8881, 0, "Format OSD (osd-2)"},
+    {0x8882, 0, "Create (osd-2)"},
+    {0x8883, 0, "List (osd-2)"},
+    {0x8884, 0, "Punch (osd-2)"},
+    {0x8885, 0, "Read (osd-2)"},
+    {0x8886, 0, "Write (osd-2)"},
+    {0x8887, 0, "Append (osd-2)"},
+    {0x8888, 0, "Flush (osd-2)"},
+    {0x8889, 0, "Clear (osd-2)"},
+    {0x888a, 0, "Remove (osd-2)"},
+    {0x888b, 0, "Create partition (osd-2)"},
+    {0x888c, 0, "Remove partition (osd-2)"},
+    {0x888e, 0, "Get attributes (osd-2)"},
+    {0x888f, 0, "Set attributes (osd-2)"},
+    {0x8892, 0, "Create and write (osd-2)"},
+    {0x8895, 0, "Create collection (osd-2)"},
+    {0x8896, 0, "Remove collection (osd-2)"},
+    {0x8897, 0, "List collection (osd-2)"},
+    {0x8898, 0, "Set key (osd-2)"},
+    {0x8899, 0, "Set master key (osd-2)"},
+    {0x889a, 0, "Flush collection (osd-2)"},
+    {0x889b, 0, "Flush partition (osd-2)"},
+    {0x889c, 0, "Flush OSD (osd-2)"},
+    {0x88a0, 0, "Query (osd-2)"},
+    {0x88a1, 0, "Remove member objects (osd-2)"},
+    {0x88a2, 0, "Get member attributes (osd-2)"},
+    {0x88a3, 0, "Set member attributes (osd-2)"},
+    {0x88b1, 0, "Read map (osd-2)"},
+    {0x8f7c, 0, "Perform SCSI command (osd-2)"},
+    {0x8f7d, 0, "Perform task management function (osd-2)"},
+    {0x8f7e, 0, "Perform SCSI command (osd)"},
+    {0x8f7f, 0, "Perform task management function (osd)"},
+    {0xffff, 0, NULL},
+};
+
+/* Zoning out [0x94] service actions */
+struct sg_lib_value_name_t sg_lib_zoning_out_arr[] = {
+    {0x1, PDT_ZBC, "Close zone"},
+    {0x2, PDT_ZBC, "Finish zone"},
+    {0x3, PDT_ZBC, "Open zone"},
+    {0x4, PDT_ZBC, "Reset write pointer"},
+    {0xffff, 0, NULL},
+};
+
+/* Zoning in [0x95] service actions */
+struct sg_lib_value_name_t sg_lib_zoning_in_arr[] = {
+    {0x0, PDT_ZBC, "Report zones"},
+    {0xffff, 0, NULL},
+};
+
+/* Read attribute [0x8c] service actions */
+struct sg_lib_value_name_t sg_lib_read_attr_arr[] = {
+    {0x0, 0, "attribute values"},
+    {0x1, 0, "attribute list"},
+    {0x2, 0, "logical volume list"},
+    {0x3, 0, "partition list"},
+    {0x5, 0, "supported attributes"},
+    {0xffff, 0, NULL},
+};
+
+#else   /* SG_SCSI_STRINGS */
+
+struct sg_lib_value_name_t sg_lib_normal_opcodes[] = {
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_read_buff_arr[] = {  /* opcode 0x3c */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_write_buff_arr[] = {  /* opcode 0x3b */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_maint_in_arr[] = {  /* opcode 0xa3 */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_maint_out_arr[] = {  /* opcode 0xa4 */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_sanitize_sa_arr[] = {  /* opcode 0x94 */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_serv_in12_arr[] = { /* opcode 0xab */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_serv_out12_arr[] = { /* opcode 0xa9 */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_serv_in16_arr[] = { /* opcode 0x9e */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_serv_out16_arr[] = { /* opcode 0x9f */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_serv_bidi_arr[] = { /* opcode 0x9d */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_pr_in_arr[] = { /* opcode 0x5e */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_pr_out_arr[] = { /* opcode 0x5f */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_xcopy_sa_arr[] = { /* opcode 0x83 */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_rec_copy_sa_arr[] = { /* opcode 0x84 */
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_variable_length_arr[] = {
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_zoning_out_arr[] = {
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_zoning_in_arr[] = {
+    {0xffff, 0, NULL},
+};
+
+struct sg_lib_value_name_t sg_lib_read_attr_arr[] = {
+    {0xffff, 0, NULL},
+};
+
+#endif  /* SG_SCSI_STRINGS */
+
+/* A conveniently formatted list of SCSI ASC/ASCQ codes and their
+ * corresponding text can be found at: www.t10.org/lists/asc-num.txt
+ * The following should match asc-num.txt dated 20150423 */
+
+#ifdef SG_SCSI_STRINGS
+struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[] =
+{
+    {0x40,0x01,0x7f,"Ram failure [0x%x]"},
+    {0x40,0x80,0xff,"Diagnostic failure on component [0x%x]"},
+    {0x41,0x01,0xff,"Data path failure [0x%x]"},
+    {0x42,0x01,0xff,"Power-on or self-test failure [0x%x]"},
+    {0x4d,0x00,0xff,"Tagged overlapped commands [0x%x]"},
+    {0x70,0x00,0xff,"Decompression exception short algorithm id of 0x%x"},
+    {0, 0, 0, NULL}
+};
+
+struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] =
+{
+    {0x00,0x00,"No additional sense information"},
+    {0x00,0x01,"Filemark detected"},
+    {0x00,0x02,"End-of-partition/medium detected"},
+    {0x00,0x03,"Setmark detected"},
+    {0x00,0x04,"Beginning-of-partition/medium detected"},
+    {0x00,0x05,"End-of-data detected"},
+    {0x00,0x06,"I/O process terminated"},
+    {0x00,0x07,"Programmable early warning detected"},
+    {0x00,0x11,"Audio play operation in progress"},
+    {0x00,0x12,"Audio play operation paused"},
+    {0x00,0x13,"Audio play operation successfully completed"},
+    {0x00,0x14,"Audio play operation stopped due to error"},
+    {0x00,0x15,"No current audio status to return"},
+    {0x00,0x16,"operation in progress"},
+    {0x00,0x17,"Cleaning requested"},
+    {0x00,0x18,"Erase operation in progress"},
+    {0x00,0x19,"Locate operation in progress"},
+    {0x00,0x1a,"Rewind operation in progress"},
+    {0x00,0x1b,"Set capacity operation in progress"},
+    {0x00,0x1c,"Verify operation in progress"},
+    {0x00,0x1d,"ATA pass through information available"},
+    {0x00,0x1e,"Conflicting SA creation request"},
+    {0x00,0x1f,"Logical unit transitioning to another power condition"},
+    {0x00,0x20,"Extended copy information available"},
+    {0x00,0x21,"Atomic command aborted due to ACA"},
+    {0x01,0x00,"No index/sector signal"},
+    {0x02,0x00,"No seek complete"},
+    {0x03,0x00,"Peripheral device write fault"},
+    {0x03,0x01,"No write current"},
+    {0x03,0x02,"Excessive write errors"},
+    {0x04,0x00,"Logical unit not ready, cause not reportable"},
+    {0x04,0x01,"Logical unit is in process of becoming ready"},
+    {0x04,0x02,"Logical unit not ready, "
+                "initializing command required"},
+    {0x04,0x03,"Logical unit not ready, "
+                "manual intervention required"},
+    {0x04,0x04,"Logical unit not ready, format in progress"},
+    {0x04,0x05,"Logical unit not ready, rebuild in progress"},
+    {0x04,0x06,"Logical unit not ready, recalculation in progress"},
+    {0x04,0x07,"Logical unit not ready, operation in progress"},
+    {0x04,0x08,"Logical unit not ready, long write in progress"},
+    {0x04,0x09,"Logical unit not ready, self-test in progress"},
+    {0x04,0x0a,"Logical unit "
+                "not accessible, asymmetric access state transition"},
+    {0x04,0x0b,"Logical unit "
+                "not accessible, target port in standby state"},
+    {0x04,0x0c,"Logical unit "
+                "not accessible, target port in unavailable state"},
+    {0x04,0x0d,"Logical unit not ready, structure check required"},
+    {0x04,0x0e,"Logical unit not ready, security session in progress"},
+    {0x04,0x10,"Logical unit not ready, "
+                "auxiliary memory not accessible"},
+    {0x04,0x11,"Logical unit not ready, "
+                "notify (enable spinup) required"},
+    {0x04,0x12,"Logical unit not ready, offline"},
+    {0x04,0x13,"Logical unit not ready, SA creation in progress"},
+    {0x04,0x14,"Logical unit not ready, space allocation in progress"},
+    {0x04,0x15,"Logical unit not ready, robotics disabled"},
+    {0x04,0x16,"Logical unit not ready, configuration required"},
+    {0x04,0x17,"Logical unit not ready, calibration required"},
+    {0x04,0x18,"Logical unit not ready, a door is open"},
+    {0x04,0x19,"Logical unit not ready, operating in sequential mode"},
+    {0x04,0x1a,"Logical unit not ready, start stop unit command in progress"},
+    {0x04,0x1b,"Logical unit not ready, sanitize in progress"},
+    {0x04,0x1c,"Logical unit not ready, additional power use not yet "
+                "granted"},
+    {0x04,0x1d,"Logical unit not ready, configuration in progress"},
+    {0x04,0x1e,"Logical unit not ready, microcode activation required"},
+    {0x04,0x1f,"Logical unit not ready, microcode download required"},
+    {0x04,0x20,"Logical unit not ready, logical unit reset required"},
+    {0x04,0x21,"Logical unit not ready, hard reset required"},
+    {0x04,0x22,"Logical unit not ready, power cycle required"},
+    {0x05,0x00,"Logical unit does not respond to selection"},
+    {0x06,0x00,"No reference position found"},
+    {0x07,0x00,"Multiple peripheral devices selected"},
+    {0x08,0x00,"Logical unit communication failure"},
+    {0x08,0x01,"Logical unit communication time-out"},
+    {0x08,0x02,"Logical unit communication parity error"},
+    {0x08,0x03,"Logical unit communication CRC error (Ultra-DMA/32)"},
+    {0x08,0x04,"Unreachable copy target"},
+    {0x09,0x00,"Track following error"},
+    {0x09,0x01,"Tracking servo failure"},
+    {0x09,0x02,"Focus servo failure"},
+    {0x09,0x03,"Spindle servo failure"},
+    {0x09,0x04,"Head select fault"},
+    {0x09,0x05,"Vibration induced tracking error"},
+    {0x0A,0x00,"Error log overflow"},
+    {0x0B,0x00,"Warning"},
+    {0x0B,0x01,"Warning - specified temperature exceeded"},
+    {0x0B,0x02,"Warning - enclosure degraded"},
+    {0x0B,0x03,"Warning - background self-test failed"},
+    {0x0B,0x04,"Warning - background pre-scan detected medium error"},
+    {0x0B,0x05,"Warning - background medium scan detected medium error"},
+    {0x0B,0x06,"Warning - non-volatile cache now volatile"},
+    {0x0B,0x07,"Warning - degraded power to non-volatile cache"},
+    {0x0B,0x08,"Warning - power loss expected"},
+    {0x0B,0x09,"Warning - device statistics notification active"},
+    {0x0B,0x0A,"Warning - high critical temperature limit exceeded"},
+    {0x0B,0x0B,"Warning - low critical temperature limit exceeded"},
+    {0x0B,0x0C,"Warning - high operating temperature limit exceeded"},
+    {0x0B,0x0D,"Warning - low operating temperature limit exceeded"},
+    {0x0B,0x0E,"Warning - high critical humidity limit exceeded"},
+    {0x0B,0x0F,"Warning - low critical humidity limit exceeded"},
+    {0x0B,0x10,"Warning - high operating humidity limit exceeded"},
+    {0x0B,0x11,"Warning - low operating humidity limit exceeded"},
+    {0x0C,0x00,"Write error"},
+    {0x0C,0x01,"Write error - recovered with auto reallocation"},
+    {0x0C,0x02,"Write error - auto reallocation failed"},
+    {0x0C,0x03,"Write error - recommend reassignment"},
+    {0x0C,0x04,"Compression check miscompare error"},
+    {0x0C,0x05,"Data expansion occurred during compression"},
+    {0x0C,0x06,"Block not compressible"},
+    {0x0C,0x07,"Write error - recovery needed"},
+    {0x0C,0x08,"Write error - recovery failed"},
+    {0x0C,0x09,"Write error - loss of streaming"},
+    {0x0C,0x0A,"Write error - padding blocks added"},
+    {0x0C,0x0B,"Auxiliary memory write error"},
+    {0x0C,0x0C,"Write error - unexpected unsolicited data"},
+    {0x0C,0x0D,"Write error - not enough unsolicited data"},
+    {0x0C,0x0E,"Multiple write errors"},
+    {0x0C,0x0F,"Defects in error window"},
+    {0x0C,0x10,"Incomplete multiple atomic write operations"},
+    {0x0C,0x11,"Write error - recovery scan needed"},
+    {0x0C,0x12,"Write error - insufficient zone resources"},
+    {0x0D,0x00,"Error detected by third party temporary initiator"},
+    {0x0D,0x01,"Third party device failure"},
+    {0x0D,0x02,"Copy target device not reachable"},
+    {0x0D,0x03,"Incorrect copy target device type"},
+    {0x0D,0x04,"Copy target device data underrun"},
+    {0x0D,0x05,"Copy target device data overrun"},
+    {0x0E,0x00,"Invalid information unit"},
+    {0x0E,0x01,"Information unit too short"},
+    {0x0E,0x02,"Information unit too long"},
+    {0x0E,0x03,"Invalid field in command information unit"},
+    {0x10,0x00,"Id CRC or ECC error"},
+    {0x10,0x01,"Logical block guard check failed"},
+    {0x10,0x02,"Logical block application tag check failed"},
+    {0x10,0x03,"Logical block reference tag check failed"},
+    {0x10,0x04,"Logical block protection error on recover buffered data"},
+    {0x10,0x05,"Logical block protection method error"},
+    {0x11,0x00,"Unrecovered read error"},
+    {0x11,0x01,"Read retries exhausted"},
+    {0x11,0x02,"Error too long to correct"},
+    {0x11,0x03,"Multiple read errors"},
+    {0x11,0x04,"Unrecovered read error - auto reallocate failed"},
+    {0x11,0x05,"L-EC uncorrectable error"},
+    {0x11,0x06,"CIRC unrecovered error"},
+    {0x11,0x07,"Data re-synchronization error"},
+    {0x11,0x08,"Incomplete block read"},
+    {0x11,0x09,"No gap found"},
+    {0x11,0x0A,"Miscorrected error"},
+    {0x11,0x0B,"Unrecovered read error - recommend reassignment"},
+    {0x11,0x0C,"Unrecovered read error - recommend rewrite the data"},
+    {0x11,0x0D,"De-compression CRC error"},
+    {0x11,0x0E,"Cannot decompress using declared algorithm"},
+    {0x11,0x0F,"Error reading UPC/EAN number"},
+    {0x11,0x10,"Error reading ISRC number"},
+    {0x11,0x11,"Read error - loss of streaming"},
+    {0x11,0x12,"Auxiliary memory read error"},
+    {0x11,0x13,"Read error - failed retransmission request"},
+    {0x11,0x14,"Read error - LBA marked bad by application client"},
+    {0x11,0x15,"Write after sanitize required"},
+    {0x12,0x00,"Address mark not found for id field"},
+    {0x13,0x00,"Address mark not found for data field"},
+    {0x14,0x00,"Recorded entity not found"},
+    {0x14,0x01,"Record not found"},
+    {0x14,0x02,"Filemark or setmark not found"},
+    {0x14,0x03,"End-of-data not found"},
+    {0x14,0x04,"Block sequence error"},
+    {0x14,0x05,"Record not found - recommend reassignment"},
+    {0x14,0x06,"Record not found - data auto-reallocated"},
+    {0x14,0x07,"Locate operation failure"},
+    {0x15,0x00,"Random positioning error"},
+    {0x15,0x01,"Mechanical positioning error"},
+    {0x15,0x02,"Positioning error detected by read of medium"},
+    {0x16,0x00,"Data synchronization mark error"},
+    {0x16,0x01,"Data sync error - data rewritten"},
+    {0x16,0x02,"Data sync error - recommend rewrite"},
+    {0x16,0x03,"Data sync error - data auto-reallocated"},
+    {0x16,0x04,"Data sync error - recommend reassignment"},
+    {0x17,0x00,"Recovered data with no error correction applied"},
+    {0x17,0x01,"Recovered data with retries"},
+    {0x17,0x02,"Recovered data with positive head offset"},
+    {0x17,0x03,"Recovered data with negative head offset"},
+    {0x17,0x04,"Recovered data with retries and/or circ applied"},
+    {0x17,0x05,"Recovered data using previous sector id"},
+    {0x17,0x06,"Recovered data without ECC - data auto-reallocated"},
+    {0x17,0x07,"Recovered data without ECC - recommend reassignment"},
+    {0x17,0x08,"Recovered data without ECC - recommend rewrite"},
+    {0x17,0x09,"Recovered data without ECC - data rewritten"},
+    {0x18,0x00,"Recovered data with error correction applied"},
+    {0x18,0x01,"Recovered data with error corr. & retries applied"},
+    {0x18,0x02,"Recovered data - data auto-reallocated"},
+    {0x18,0x03,"Recovered data with CIRC"},
+    {0x18,0x04,"Recovered data with L-EC"},
+    {0x18,0x05,"Recovered data - recommend reassignment"},
+    {0x18,0x06,"Recovered data - recommend rewrite"},
+    {0x18,0x07,"Recovered data with ECC - data rewritten"},
+    {0x18,0x08,"Recovered data with linking"},
+    {0x19,0x00,"Defect list error"},
+    {0x19,0x01,"Defect list not available"},
+    {0x19,0x02,"Defect list error in primary list"},
+    {0x19,0x03,"Defect list error in grown list"},
+    {0x1A,0x00,"Parameter list length error"},
+    {0x1B,0x00,"Synchronous data transfer error"},
+    {0x1C,0x00,"Defect list not found"},
+    {0x1C,0x01,"Primary defect list not found"},
+    {0x1C,0x02,"Grown defect list not found"},
+    {0x1D,0x00,"Miscompare during verify operation"},
+    {0x1D,0x01,"Miscompare verify of unmapped lba"},
+    {0x1E,0x00,"Recovered id with ECC correction"},
+    {0x1F,0x00,"Partial defect list transfer"},
+    {0x20,0x00,"Invalid command operation code"},
+    {0x20,0x01,"Access denied - initiator pending-enrolled"},
+    {0x20,0x02,"Access denied - no access rights"},
+    {0x20,0x03,"Access denied - invalid mgmt id key"},
+    {0x20,0x04,"Illegal command while in write capable state"},
+    {0x20,0x05,"Write type operation while in read capable state (obs)"},
+    {0x20,0x06,"Illegal command while in explicit address mode"},
+    {0x20,0x07,"Illegal command while in implicit address mode"},
+    {0x20,0x08,"Access denied - enrollment conflict"},
+    {0x20,0x09,"Access denied - invalid LU identifier"},
+    {0x20,0x0A,"Access denied - invalid proxy token"},
+    {0x20,0x0B,"Access denied - ACL LUN conflict"},
+    {0x20,0x0C,"Illegal command when not in append-only mode"},
+    {0x20,0x0D,"Not an administrative logical unit"},
+    {0x20,0x0E,"Not a subsidiary logical unit"},
+    {0x20,0x0F,"Not a conglomerate logical unit"},
+    {0x21,0x00,"Logical block address out of range"},
+    {0x21,0x01,"Invalid element address"},
+    {0x21,0x02,"Invalid address for write"},
+    {0x21,0x03,"Invalid write crossing layer jump"},
+    {0x21,0x04,"Unaligned write command"},
+    {0x21,0x05,"Write boundary violation"},
+    {0x21,0x06,"Attempt to read invalid data"},
+    {0x21,0x07,"Read boundary violation"},
+    {0x21,0x08,"Misaligned write command"},
+    {0x22,0x00,"Illegal function (use 20 00, 24 00, or 26 00)"},
+    {0x23,0x00,"Invalid token operation, cause not reportable"},
+    {0x23,0x01,"Invalid token operation, unsupported token type"},
+    {0x23,0x02,"Invalid token operation, remote token usage not supported"},
+    {0x23,0x03,"invalid token operation, remote rod token creation not "
+               "supported"},
+    {0x23,0x04,"Invalid token operation, token unknown"},
+    {0x23,0x05,"Invalid token operation, token corrupt"},
+    {0x23,0x06,"Invalid token operation, token revoked"},
+    {0x23,0x07,"Invalid token operation, token expired"},
+    {0x23,0x08,"Invalid token operation, token cancelled"},
+    {0x23,0x09,"Invalid token operation, token deleted"},
+    {0x23,0x0a,"Invalid token operation, invalid token length"},
+    {0x24,0x00,"Invalid field in cdb"},
+    {0x24,0x01,"CDB decryption error"},
+    {0x24,0x02,"Invalid cdb field while in explicit block model (obs)"},
+    {0x24,0x03,"Invalid cdb field while in implicit block model (obs)"},
+    {0x24,0x04,"Security audit value frozen"},
+    {0x24,0x05,"Security working key frozen"},
+    {0x24,0x06,"Nonce not unique"},
+    {0x24,0x07,"Nonce timestamp out of range"},
+    {0x24,0x08,"Invalid xcdb"},
+    {0x25,0x00,"Logical unit not supported"},
+    {0x26,0x00,"Invalid field in parameter list"},
+    {0x26,0x01,"Parameter not supported"},
+    {0x26,0x02,"Parameter value invalid"},
+    {0x26,0x03,"Threshold parameters not supported"},
+    {0x26,0x04,"Invalid release of persistent reservation"},
+    {0x26,0x05,"Data decryption error"},
+    {0x26,0x06,"Too many target descriptors"},
+    {0x26,0x07,"Unsupported target descriptor type code"},
+    {0x26,0x08,"Too many segment descriptors"},
+    {0x26,0x09,"Unsupported segment descriptor type code"},
+    {0x26,0x0A,"Unexpected inexact segment"},
+    {0x26,0x0B,"Inline data length exceeded"},
+    {0x26,0x0C,"Invalid operation for copy source or destination"},
+    {0x26,0x0D,"Copy segment granularity violation"},
+    {0x26,0x0E,"Invalid parameter while port is enabled"},
+    {0x26,0x0F,"Invalid data-out buffer integrity check value"},
+    {0x26,0x10,"Data decryption key fail limit reached"},
+    {0x26,0x11,"Incomplete key-associated data set"},
+    {0x26,0x12,"Vendor specific key reference not found"},
+    {0x26,0x13,"Application tag mode page is invalid"},
+    {0x27,0x00,"Write protected"},
+    {0x27,0x01,"Hardware write protected"},
+    {0x27,0x02,"Logical unit software write protected"},
+    {0x27,0x03,"Associated write protect"},
+    {0x27,0x04,"Persistent write protect"},
+    {0x27,0x05,"Permanent write protect"},
+    {0x27,0x06,"Conditional write protect"},
+    {0x27,0x07,"Space allocation failed write protect"},
+    {0x27,0x08,"Zone is read only"},
+    {0x28,0x00,"Not ready to ready change, medium may have changed"},
+    {0x28,0x01,"Import or export element accessed"},
+    {0x28,0x02,"Format-layer may have changed"},
+    {0x28,0x03,"Import/export element accessed, medium changed"},
+    {0x29,0x00,"Power on, reset, or bus device reset occurred"},
+    {0x29,0x01,"Power on occurred"},
+    {0x29,0x02,"SCSI bus reset occurred"},
+    {0x29,0x03,"Bus device reset function occurred"},
+    {0x29,0x04,"Device internal reset"},
+    {0x29,0x05,"Transceiver mode changed to single-ended"},
+    {0x29,0x06,"Transceiver mode changed to lvd"},
+    {0x29,0x07,"I_T nexus loss occurred"},
+    {0x2A,0x00,"Parameters changed"},
+    {0x2A,0x01,"Mode parameters changed"},
+    {0x2A,0x02,"Log parameters changed"},
+    {0x2A,0x03,"Reservations preempted"},
+    {0x2A,0x04,"Reservations released"},
+    {0x2A,0x05,"Registrations preempted"},
+    {0x2A,0x06,"Asymmetric access state changed"},
+    {0x2A,0x07,"Implicit asymmetric access state transition failed"},
+    {0x2A,0x08,"Priority changed"},
+    {0x2A,0x09,"Capacity data has changed"},
+    {0x2A,0x0c, "Error recovery attributes have changed"},
+    {0x2A,0x0d, "Data encryption capabilities changed"},
+    {0x2A,0x10,"Timestamp changed"},
+    {0x2A,0x11,"Data encryption parameters changed by another i_t nexus"},
+    {0x2A,0x12,"Data encryption parameters changed by vendor specific event"},
+    {0x2A,0x13,"Data encryption key instance counter has changed"},
+    {0x2A,0x0a,"Error history i_t nexus cleared"},
+    {0x2A,0x0b,"Error history snapshot released"},
+    {0x2A,0x14,"SA creation capabilities data has changed"},
+    {0x2A,0x15,"Medium removal prevention preempted"},
+    {0x2A,0x16,"Zone reset write pointer recommended"},
+    {0x2B,0x00,"Copy cannot execute since host cannot disconnect"},
+    {0x2C,0x00,"Command sequence error"},
+    {0x2C,0x01,"Too many windows specified"},
+    {0x2C,0x02,"Invalid combination of windows specified"},
+    {0x2C,0x03,"Current program area is not empty"},
+    {0x2C,0x04,"Current program area is empty"},
+    {0x2C,0x05,"Illegal power condition request"},
+    {0x2C,0x06,"Persistent prevent conflict"},
+    {0x2C,0x07,"Previous busy status"},
+    {0x2C,0x08,"Previous task set full status"},
+    {0x2C,0x09,"Previous reservation conflict status"},
+    {0x2C,0x0A,"Partition or collection contains user objects"},
+    {0x2C,0x0B,"Not reserved"},
+    {0x2C,0x0C,"ORWRITE generation does not match"},
+    {0x2C,0x0D,"Reset write pointer not allowed"},
+    {0x2C,0x0E,"Zone is offline"},
+    {0x2C,0x0F,"Stream not open"},
+    {0x2C,0x10,"Unwritten data in zone"},
+    {0x2C,0x11,"Descriptor format sense data required"},
+    {0x2D,0x00,"Overwrite error on update in place"},
+    {0x2E,0x00,"Insufficient time for operation"},
+    {0x2E,0x01,"Command timeout before processing"},
+    {0x2E,0x02,"Command timeout during processing"},
+    {0x2E,0x03,"Command timeout during processing due to error recovery"},
+    {0x2F,0x00,"Commands cleared by another initiator"},
+    {0x2F,0x01,"Commands cleared by power loss notification"},
+    {0x2F,0x02,"Commands cleared by device server"},
+    {0x2F,0x03,"Some commands cleared by queuing layer event"},
+    {0x30,0x00,"Incompatible medium installed"},
+    {0x30,0x01,"Cannot read medium - unknown format"},
+    {0x30,0x02,"Cannot read medium - incompatible format"},
+    {0x30,0x03,"Cleaning cartridge installed"},
+    {0x30,0x04,"Cannot write medium - unknown format"},
+    {0x30,0x05,"Cannot write medium - incompatible format"},
+    {0x30,0x06,"Cannot format medium - incompatible medium"},
+    {0x30,0x07,"Cleaning failure"},
+    {0x30,0x08,"Cannot write - application code mismatch"},
+    {0x30,0x09,"Current session not fixated for append"},
+    {0x30,0x0A,"Cleaning request rejected"},
+    {0x30,0x0B,"Cleaning tape expired"},
+    {0x30,0x0C,"WORM medium - overwrite attempted"},
+    {0x30,0x0D,"WORM medium - integrity check"},
+    {0x30,0x10,"Medium not formatted"},
+    {0x30,0x11,"Incompatible volume type"},
+    {0x30,0x12,"Incompatible volume qualifier"},
+    {0x30,0x13,"Cleaning volume expired"},
+    {0x31,0x00,"Medium format corrupted"},
+    {0x31,0x01,"Format command failed"},
+    {0x31,0x02,"Zoned formatting failed due to spare linking"},
+    {0x31,0x03,"Sanitize command failed"},
+    {0x32,0x00,"No defect spare location available"},
+    {0x32,0x01,"Defect list update failure"},
+    {0x33,0x00,"Tape length error"},
+    {0x34,0x00,"Enclosure failure"},
+    {0x35,0x00,"Enclosure services failure"},
+    {0x35,0x01,"Unsupported enclosure function"},
+    {0x35,0x02,"Enclosure services unavailable"},
+    {0x35,0x03,"Enclosure services transfer failure"},
+    {0x35,0x04,"Enclosure services transfer refused"},
+    {0x35,0x05,"Enclosure services checksum error"},
+    {0x36,0x00,"Ribbon, ink, or toner failure"},
+    {0x37,0x00,"Rounded parameter"},
+    {0x38,0x00,"Event status notification"},
+    {0x38,0x02,"Esn - power management class event"},
+    {0x38,0x04,"Esn - media class event"},
+    {0x38,0x06,"Esn - device busy class event"},
+    {0x38,0x07,"Thin provisioning soft threshold reached"},
+    {0x39,0x00,"Saving parameters not supported"},
+    {0x3A,0x00,"Medium not present"},
+    {0x3A,0x01,"Medium not present - tray closed"},
+    {0x3A,0x02,"Medium not present - tray open"},
+    {0x3A,0x03,"Medium not present - loadable"},
+    {0x3A,0x04,"Medium not present - medium auxiliary memory accessible"},
+    {0x3B,0x00,"Sequential positioning error"},
+    {0x3B,0x01,"Tape position error at beginning-of-medium"},
+    {0x3B,0x02,"Tape position error at end-of-medium"},
+    {0x3B,0x03,"Tape or electronic vertical forms unit not ready"},
+    {0x3B,0x04,"Slew failure"},
+    {0x3B,0x05,"Paper jam"},
+    {0x3B,0x06,"Failed to sense top-of-form"},
+    {0x3B,0x07,"Failed to sense bottom-of-form"},
+    {0x3B,0x08,"Reposition error"},
+    {0x3B,0x09,"Read past end of medium"},
+    {0x3B,0x0A,"Read past beginning of medium"},
+    {0x3B,0x0B,"Position past end of medium"},
+    {0x3B,0x0C,"Position past beginning of medium"},
+    {0x3B,0x0D,"Medium destination element full"},
+    {0x3B,0x0E,"Medium source element empty"},
+    {0x3B,0x0F,"End of medium reached"},
+    {0x3B,0x11,"Medium magazine not accessible"},
+    {0x3B,0x12,"Medium magazine removed"},
+    {0x3B,0x13,"Medium magazine inserted"},
+    {0x3B,0x14,"Medium magazine locked"},
+    {0x3B,0x15,"Medium magazine unlocked"},
+    {0x3B,0x16,"Mechanical positioning or changer error"},
+    {0x3B,0x17,"Read past end of user object"},
+    {0x3B,0x18,"Element disabled"},
+    {0x3B,0x19,"Element enabled"},
+    {0x3B,0x1a,"Data transfer device removed"},
+    {0x3B,0x1b,"Data transfer device inserted"},
+    {0x3B,0x1c,"Too many logical objects on partition to support operation"},
+    {0x3D,0x00,"Invalid bits in identify message"},
+    {0x3E,0x00,"Logical unit has not self-configured yet"},
+    {0x3E,0x01,"Logical unit failure"},
+    {0x3E,0x02,"Timeout on logical unit"},
+    {0x3E,0x03,"Logical unit failed self-test"},
+    {0x3E,0x04,"Logical unit unable to update self-test log"},
+    {0x3F,0x00,"Target operating conditions have changed"},
+    {0x3F,0x01,"Microcode has been changed"},
+    {0x3F,0x02,"Changed operating definition"},
+    {0x3F,0x03,"Inquiry data has changed"},
+    {0x3F,0x04,"Component device attached"},
+    {0x3F,0x05,"Device identifier changed"},
+    {0x3F,0x06,"Redundancy group created or modified"},
+    {0x3F,0x07,"Redundancy group deleted"},
+    {0x3F,0x08,"Spare created or modified"},
+    {0x3F,0x09,"Spare deleted"},
+    {0x3F,0x0A,"Volume set created or modified"},
+    {0x3F,0x0B,"Volume set deleted"},
+    {0x3F,0x0C,"Volume set deassigned"},
+    {0x3F,0x0D,"Volume set reassigned"},
+    {0x3F,0x0E,"Reported luns data has changed"},
+    {0x3F,0x0F,"Echo buffer overwritten"},
+    {0x3F,0x10,"Medium loadable"},
+    {0x3F,0x11,"Medium auxiliary memory accessible"},
+    {0x3F,0x12,"iSCSI IP address added"},
+    {0x3F,0x13,"iSCSI IP address removed"},
+    {0x3F,0x14,"iSCSI IP address changed"},
+    {0x3F,0x15,"Inspect referrals sense descriptors"},
+    {0x3F,0x16,"Microcode has been changed without reset"},
+    {0x3F,0x17,"Zone transition to full"},
+    {0x3F,0x18,"Bind completed"},
+    {0x3F,0x19,"Bind redirected"},
+    {0x3F,0x1A,"Subsidiary binding changed"},
+
+    /*
+     * ASC 0x40, 0x41 and 0x42 overridden by "additional2" array entries
+     * for ascq > 1. Preferred error message for this group is
+     * "Diagnostic failure on component nn (80h-ffh)".
+     */
+    {0x40,0x00,"Ram failure (should use 40 nn)"},
+    {0x41,0x00,"Data path failure (should use 40 nn)"},
+    {0x42,0x00,"Power-on or self-test failure (should use 40 nn)"},
+
+    {0x43,0x00,"Message error"},
+    {0x44,0x00,"Internal target failure"},
+    {0x44,0x01,"Persistent reservation information lost"},
+    {0x44,0x71,"ATA device failed Set Features"},
+    {0x45,0x00,"Select or reselect failure"},
+    {0x46,0x00,"Unsuccessful soft reset"},
+    {0x47,0x00,"SCSI parity error"},
+    {0x47,0x01,"Data phase CRC error detected"},
+    {0x47,0x02,"SCSI parity error detected during st data phase"},
+    {0x47,0x03,"Information unit iuCRC error detected"},
+    {0x47,0x04,"Asynchronous information protection error detected"},
+    {0x47,0x05,"Protocol service CRC error"},
+    {0x47,0x06,"Phy test function in progress"},
+    {0x47,0x7F,"Some commands cleared by iSCSI protocol event"},
+    {0x48,0x00,"Initiator detected error message received"},
+    {0x49,0x00,"Invalid message error"},
+    {0x4A,0x00,"Command phase error"},
+    {0x4B,0x00,"Data phase error"},
+    {0x4B,0x01,"Invalid target port transfer tag received"},
+    {0x4B,0x02,"Too much write data"},
+    {0x4B,0x03,"Ack/nak timeout"},
+    {0x4B,0x04,"Nak received"},
+    {0x4B,0x05,"Data offset error"},
+    {0x4B,0x06,"Initiator response timeout"},
+    {0x4B,0x07,"Connection lost"},
+    {0x4B,0x08,"Data-in buffer overflow - data buffer size"},
+    {0x4B,0x09,"Data-in buffer overflow - data buffer descriptor area"},
+    {0x4B,0x0A,"Data-in buffer error"},
+    {0x4B,0x0B,"Data-out buffer overflow - data buffer size"},
+    {0x4B,0x0C,"Data-out buffer overflow - data buffer descriptor area"},
+    {0x4B,0x0D,"Data-out buffer error"},
+    {0x4B,0x0E,"PCIe fabric error"},
+    {0x4B,0x0f,"PCIe completion timeout"},
+    {0x4B,0x10,"PCIe completer abort"},
+    {0x4B,0x11,"PCIe poisoned tlp received"},
+    {0x4B,0x12,"PCIe ecrc check failed"},
+    {0x4B,0x13,"PCIe unsupported request"},
+    {0x4B,0x14,"PCIe acs violation"},
+    {0x4B,0x15,"PCIe tlp prefix blocked"},
+    {0x4C,0x00,"Logical unit failed self-configuration"},
+    /*
+     * ASC 0x4D overridden by an "additional2" array entry
+     * so there is no need to have them here.
+     */
+    /* {0x4D,0x00,"Tagged overlapped commands (nn = queue tag)"}, */
+
+    {0x4E,0x00,"Overlapped commands attempted"},
+    {0x50,0x00,"Write append error"},
+    {0x50,0x01,"Write append position error"},
+    {0x50,0x02,"Position error related to timing"},
+    {0x51,0x00,"Erase failure"},
+    {0x51,0x01,"Erase failure - incomplete erase operation detected"},
+    {0x52,0x00,"Cartridge fault"},
+    {0x53,0x00,"Media load or eject failed"},
+    {0x53,0x01,"Unload tape failure"},
+    {0x53,0x02,"Medium removal prevented"},
+    {0x53,0x03,"Medium removal prevented by data transfer element"},
+    {0x53,0x04,"Medium thread or unthread failure"},
+    {0x53,0x05,"Volume identifier invalid"},
+    {0x53,0x06,"Volume identifier missing"},
+    {0x53,0x07,"Duplicate volume identifier"},
+    {0x53,0x08,"Element status unknown"},
+    {0x53,0x09,"Data transfer device error - load failed"},
+    {0x53,0x0A,"Data transfer device error - unload failed"},
+    {0x53,0x0B,"Data transfer device error - unload missing"},
+    {0x53,0x0C,"Data transfer device error - eject failed"},
+    {0x53,0x0D,"Data transfer device error - library communication failed"},
+    {0x54,0x00,"SCSI to host system interface failure"},
+    {0x55,0x00,"System resource failure"},
+    {0x55,0x01,"System buffer full"},
+    {0x55,0x02,"Insufficient reservation resources"},
+    {0x55,0x03,"Insufficient resources"},
+    {0x55,0x04,"Insufficient registration resources"},
+    {0x55,0x05,"Insufficient access control resources"},
+    {0x55,0x06,"Auxiliary memory out of space"},
+    {0x55,0x07,"Quota error"},
+    {0x55,0x08,"Maximum number of supplemental decryption keys exceeded"},
+    {0x55,0x09,"Medium auxiliary memory not accessible"},
+    {0x55,0x0a,"Data currently unavailable"},
+    {0x55,0x0b,"Insufficient power for operation"},
+    {0x55,0x0c,"Insufficient resources to create rod"},
+    {0x55,0x0d,"Insufficient resources to create rod token"},
+    {0x55,0x0e,"Insufficient zone resources"},
+    {0x55,0x0f,"Insufficient zone resources to complete write"},
+    {0x55,0x10,"Maximum number of streams open"},
+    {0x55,0x11,"Insufficient resources to bind"},
+    {0x57,0x00,"Unable to recover table-of-contents"},
+    {0x58,0x00,"Generation does not exist"},
+    {0x59,0x00,"Updated block read"},
+    {0x5A,0x00,"Operator request or state change input"},
+    {0x5A,0x01,"Operator medium removal request"},
+    {0x5A,0x02,"Operator selected write protect"},
+    {0x5A,0x03,"Operator selected write permit"},
+    {0x5B,0x00,"Log exception"},
+    {0x5B,0x01,"Threshold condition met"},
+    {0x5B,0x02,"Log counter at maximum"},
+    {0x5B,0x03,"Log list codes exhausted"},
+    {0x5C,0x00,"Rpl status change"},
+    {0x5C,0x01,"Spindles synchronized"},
+    {0x5C,0x02,"Spindles not synchronized"},
+    {0x5D,0x00,"Failure prediction threshold exceeded"},
+    {0x5D,0x01,"Media failure prediction threshold exceeded"},
+    {0x5D,0x02,"Logical unit failure prediction threshold exceeded"},
+    {0x5D,0x03,"spare area exhaustion prediction threshold exceeded"},
+    {0x5D,0x10,"Hardware impending failure general hard drive failure"},
+    {0x5D,0x11,"Hardware impending failure drive error rate too high" },
+    {0x5D,0x12,"Hardware impending failure data error rate too high" },
+    {0x5D,0x13,"Hardware impending failure seek error rate too high" },
+    {0x5D,0x14,"Hardware impending failure too many block reassigns"},
+    {0x5D,0x15,"Hardware impending failure access times too high" },
+    {0x5D,0x16,"Hardware impending failure start unit times too high" },
+    {0x5D,0x17,"Hardware impending failure channel parametrics"},
+    {0x5D,0x18,"Hardware impending failure controller detected"},
+    {0x5D,0x19,"Hardware impending failure throughput performance"},
+    {0x5D,0x1A,"Hardware impending failure seek time performance"},
+    {0x5D,0x1B,"Hardware impending failure spin-up retry count"},
+    {0x5D,0x1C,"Hardware impending failure drive calibration retry count"},
+    {0x5D,0x20,"Controller impending failure general hard drive failure"},
+    {0x5D,0x21,"Controller impending failure drive error rate too high" },
+    {0x5D,0x22,"Controller impending failure data error rate too high" },
+    {0x5D,0x23,"Controller impending failure seek error rate too high" },
+    {0x5D,0x24,"Controller impending failure too many block reassigns"},
+    {0x5D,0x25,"Controller impending failure access times too high" },
+    {0x5D,0x26,"Controller impending failure start unit times too high" },
+    {0x5D,0x27,"Controller impending failure channel parametrics"},
+    {0x5D,0x28,"Controller impending failure controller detected"},
+    {0x5D,0x29,"Controller impending failure throughput performance"},
+    {0x5D,0x2A,"Controller impending failure seek time performance"},
+    {0x5D,0x2B,"Controller impending failure spin-up retry count"},
+    {0x5D,0x2C,"Controller impending failure drive calibration retry count"},
+    {0x5D,0x30,"Data channel impending failure general hard drive failure"},
+    {0x5D,0x31,"Data channel impending failure drive error rate too high" },
+    {0x5D,0x32,"Data channel impending failure data error rate too high" },
+    {0x5D,0x33,"Data channel impending failure seek error rate too high" },
+    {0x5D,0x34,"Data channel impending failure too many block reassigns"},
+    {0x5D,0x35,"Data channel impending failure access times too high" },
+    {0x5D,0x36,"Data channel impending failure start unit times too high" },
+    {0x5D,0x37,"Data channel impending failure channel parametrics"},
+    {0x5D,0x38,"Data channel impending failure controller detected"},
+    {0x5D,0x39,"Data channel impending failure throughput performance"},
+    {0x5D,0x3A,"Data channel impending failure seek time performance"},
+    {0x5D,0x3B,"Data channel impending failure spin-up retry count"},
+    {0x5D,0x3C,"Data channel impending failure drive calibration retry count"},
+    {0x5D,0x40,"Servo impending failure general hard drive failure"},
+    {0x5D,0x41,"Servo impending failure drive error rate too high" },
+    {0x5D,0x42,"Servo impending failure data error rate too high" },
+    {0x5D,0x43,"Servo impending failure seek error rate too high" },
+    {0x5D,0x44,"Servo impending failure too many block reassigns"},
+    {0x5D,0x45,"Servo impending failure access times too high" },
+    {0x5D,0x46,"Servo impending failure start unit times too high" },
+    {0x5D,0x47,"Servo impending failure channel parametrics"},
+    {0x5D,0x48,"Servo impending failure controller detected"},
+    {0x5D,0x49,"Servo impending failure throughput performance"},
+    {0x5D,0x4A,"Servo impending failure seek time performance"},
+    {0x5D,0x4B,"Servo impending failure spin-up retry count"},
+    {0x5D,0x4C,"Servo impending failure drive calibration retry count"},
+    {0x5D,0x50,"Spindle impending failure general hard drive failure"},
+    {0x5D,0x51,"Spindle impending failure drive error rate too high" },
+    {0x5D,0x52,"Spindle impending failure data error rate too high" },
+    {0x5D,0x53,"Spindle impending failure seek error rate too high" },
+    {0x5D,0x54,"Spindle impending failure too many block reassigns"},
+    {0x5D,0x55,"Spindle impending failure access times too high" },
+    {0x5D,0x56,"Spindle impending failure start unit times too high" },
+    {0x5D,0x57,"Spindle impending failure channel parametrics"},
+    {0x5D,0x58,"Spindle impending failure controller detected"},
+    {0x5D,0x59,"Spindle impending failure throughput performance"},
+    {0x5D,0x5A,"Spindle impending failure seek time performance"},
+    {0x5D,0x5B,"Spindle impending failure spin-up retry count"},
+    {0x5D,0x5C,"Spindle impending failure drive calibration retry count"},
+    {0x5D,0x60,"Firmware impending failure general hard drive failure"},
+    {0x5D,0x61,"Firmware impending failure drive error rate too high" },
+    {0x5D,0x62,"Firmware impending failure data error rate too high" },
+    {0x5D,0x63,"Firmware impending failure seek error rate too high" },
+    {0x5D,0x64,"Firmware impending failure too many block reassigns"},
+    {0x5D,0x65,"Firmware impending failure access times too high" },
+    {0x5D,0x66,"Firmware impending failure start unit times too high" },
+    {0x5D,0x67,"Firmware impending failure channel parametrics"},
+    {0x5D,0x68,"Firmware impending failure controller detected"},
+    {0x5D,0x69,"Firmware impending failure throughput performance"},
+    {0x5D,0x6A,"Firmware impending failure seek time performance"},
+    {0x5D,0x6B,"Firmware impending failure spin-up retry count"},
+    {0x5D,0x6C,"Firmware impending failure drive calibration retry count"},
+    {0x5D,0xFF,"Failure prediction threshold exceeded (false)"},
+    {0x5E,0x00,"Low power condition on"},
+    {0x5E,0x01,"Idle condition activated by timer"},
+    {0x5E,0x02,"Standby condition activated by timer"},
+    {0x5E,0x03,"Idle condition activated by command"},
+    {0x5E,0x04,"Standby condition activated by command"},
+    {0x5E,0x05,"Idle_b condition activated by timer"},
+    {0x5E,0x06,"Idle_b condition activated by command"},
+    {0x5E,0x07,"Idle_c condition activated by timer"},
+    {0x5E,0x08,"Idle_c condition activated by command"},
+    {0x5E,0x09,"Standby_y condition activated by timer"},
+    {0x5E,0x0a,"Standby_y condition activated by command"},
+    {0x5E,0x41,"Power state change to active"},
+    {0x5E,0x42,"Power state change to idle"},
+    {0x5E,0x43,"Power state change to standby"},
+    {0x5E,0x45,"Power state change to sleep"},
+    {0x5E,0x47,"Power state change to device control"},
+    {0x60,0x00,"Lamp failure"},
+    {0x61,0x00,"Video acquisition error"},
+    {0x61,0x01,"Unable to acquire video"},
+    {0x61,0x02,"Out of focus"},
+    {0x62,0x00,"Scan head positioning error"},
+    {0x63,0x00,"End of user area encountered on this track"},
+    {0x63,0x01,"Packet does not fit in available space"},
+    {0x64,0x00,"Illegal mode for this track"},
+    {0x64,0x01,"Invalid packet size"},
+    {0x65,0x00,"Voltage fault"},
+    {0x66,0x00,"Automatic document feeder cover up"},
+    {0x66,0x01,"Automatic document feeder lift up"},
+    {0x66,0x02,"Document jam in automatic document feeder"},
+    {0x66,0x03,"Document miss feed automatic in document feeder"},
+    {0x67,0x00,"Configuration failure"},
+    {0x67,0x01,"Configuration of incapable logical units failed"},
+    {0x67,0x02,"Add logical unit failed"},
+    {0x67,0x03,"Modification of logical unit failed"},
+    {0x67,0x04,"Exchange of logical unit failed"},
+    {0x67,0x05,"Remove of logical unit failed"},
+    {0x67,0x06,"Attachment of logical unit failed"},
+    {0x67,0x07,"Creation of logical unit failed"},
+    {0x67,0x08,"Assign failure occurred"},
+    {0x67,0x09,"Multiply assigned logical unit"},
+    {0x67,0x0A,"Set target port groups command failed"},
+    {0x67,0x0B,"ATA device feature not enabled"},
+    {0x67,0x0C,"Command rejected"},
+    {0x67,0x0D,"Explicit bind not allowed"},
+    {0x68,0x00,"Logical unit not configured"},
+    {0x68,0x01,"Subsidiary logical unit not configured"},
+    {0x69,0x00,"Data loss on logical unit"},
+    {0x69,0x01,"Multiple logical unit failures"},
+    {0x69,0x02,"Parity/data mismatch"},
+    {0x6A,0x00,"Informational, refer to log"},
+    {0x6B,0x00,"State change has occurred"},
+    {0x6B,0x01,"Redundancy level got better"},
+    {0x6B,0x02,"Redundancy level got worse"},
+    {0x6C,0x00,"Rebuild failure occurred"},
+    {0x6D,0x00,"Recalculate failure occurred"},
+    {0x6E,0x00,"Command to logical unit failed"},
+    {0x6F,0x00,"Copy protection key exchange failure - authentication "
+               "failure"},
+    {0x6F,0x01,"Copy protection key exchange failure - key not present"},
+    {0x6F,0x02,"Copy protection key exchange failure - key not established"},
+    {0x6F,0x03,"Read of scrambled sector without authentication"},
+    {0x6F,0x04,"Media region code is mismatched to logical unit region"},
+    {0x6F,0x05,"Drive region must be permanent/region reset count error"},
+    {0x6F,0x06,"Insufficient block count for binding nonce recording"},
+    {0x6F,0x07,"Conflict in binding nonce recording"},
+    {0x6F,0x08,"Insufficient permission"},
+    {0x6F,0x09,"Invalid drive-host pairing server"},
+    {0x6F,0x0A,"Drive-host pairing suspended"},
+    /*
+     * ASC 0x70 overridden by an "additional2" array entry
+     * so there is no need to have them here.
+     */
+    /* {0x70,0x00,"Decompression exception short algorithm id of nn"}, */
+
+    {0x71,0x00,"Decompression exception long algorithm id"},
+    {0x72,0x00,"Session fixation error"},
+    {0x72,0x01,"Session fixation error writing lead-in"},
+    {0x72,0x02,"Session fixation error writing lead-out"},
+    {0x72,0x03,"Session fixation error - incomplete track in session"},
+    {0x72,0x04,"Empty or partially written reserved track"},
+    {0x72,0x05,"No more track reservations allowed"},
+    {0x72,0x06,"RMZ extension is not allowed"},
+    {0x72,0x07,"No more test zone extensions are allowed"},
+    {0x73,0x00,"CD control error"},
+    {0x73,0x01,"Power calibration area almost full"},
+    {0x73,0x02,"Power calibration area is full"},
+    {0x73,0x03,"Power calibration area error"},
+    {0x73,0x04,"Program memory area update failure"},
+    {0x73,0x05,"Program memory area is full"},
+    {0x73,0x06,"RMA/PMA is almost full"},
+    {0x73,0x10,"Current power calibration area almost full"},
+    {0x73,0x11,"Current power calibration area is full"},
+    {0x73,0x17,"RDZ is full"},
+    {0x74,0x00,"Security error"},
+    {0x74,0x01,"Unable to decrypt data"},
+    {0x74,0x02,"Unencrypted data encountered while decrypting"},
+    {0x74,0x03,"Incorrect data encryption key"},
+    {0x74,0x04,"Cryptographic integrity validation failed"},
+    {0x74,0x05,"Error decrypting data"},
+    {0x74,0x06,"Unknown signature verification key"},
+    {0x74,0x07,"Encryption parameters not useable"},
+    {0x74,0x08,"Digital signature validation failure"},
+    {0x74,0x09,"Encryption mode mismatch on read"},
+    {0x74,0x0a,"Encrypted block not raw read enabled"},
+    {0x74,0x0b,"Incorrect Encryption parameters"},
+    {0x74,0x0c,"Unable to decrypt parameter list"},
+    {0x74,0x0d,"Encryption algorithm disabled"},
+    {0x74,0x10,"SA creation parameter value invalid"},
+    {0x74,0x11,"SA creation parameter value rejected"},
+    {0x74,0x12,"Invalid SA usage"},
+    {0x74,0x21,"Data encryption configuration prevented"},
+    {0x74,0x30,"SA creation parameter not supported"},
+    {0x74,0x40,"Authentication failed"},
+    {0x74,0x61,"External data encryption key manager access error"},
+    {0x74,0x62,"External data encryption key manager error"},
+    {0x74,0x63,"External data encryption key not found"},
+    {0x74,0x64,"External data encryption request not authorized"},
+    {0x74,0x6e,"External data encryption control timeout"},
+    {0x74,0x6f,"External data encryption control error"},
+    {0x74,0x71,"Logical unit access not authorized"},
+    {0x74,0x79,"Security conflict in translated device"},
+    {0, 0, NULL}
+};
+
+#else   /* SG_SCSI_STRINGS */
+
+struct sg_lib_asc_ascq_range_t sg_lib_asc_ascq_range[] =
+{
+    {0, 0, 0, NULL}
+};
+
+struct sg_lib_asc_ascq_t sg_lib_asc_ascq[] =
+{
+    {0, 0, NULL}
+};
+#endif /* SG_SCSI_STRINGS */
+
+const char * sg_lib_sense_key_desc[] = {
+    "No Sense",                 /* Filemark, ILI and/or EOM; progress
+                                   indication (during FORMAT); power
+                                   condition sensing (REQUEST SENSE) */
+    "Recovered Error",          /* The last command completed successfully
+                                   but used error correction */
+    "Not Ready",                /* The addressed target is not ready */
+    "Medium Error",             /* Data error detected on the medium */
+    "Hardware Error",           /* Controller or device failure */
+    "Illegal Request",
+    "Unit Attention",           /* Removable medium was changed, or
+                                   the target has been reset */
+    "Data Protect",             /* Access to the data is blocked */
+    "Blank Check",              /* Reached unexpected written or unwritten
+                                   region of the medium */
+    "Vendor specific(9)",       /* Vendor specific */
+    "Copy Aborted",             /* COPY or COMPARE was aborted */
+    "Aborted Command",          /* The target aborted the command */
+    "Equal",                    /* SEARCH DATA found data equal (obsolete) */
+    "Volume Overflow",          /* Medium full with data to be written */
+    "Miscompare",               /* Source data and data on the medium
+                                   do not agree */
+    "Completed"                 /* may occur for successful cmd (spc4r23) */
+};
+
+const char * sg_lib_pdt_strs[] = {
+    /* 0 */ "disk",
+    "tape",
+    "printer",                  /* obsolete, spc5r01 */
+    "processor",        /* often SAF-TE device, copy manager */
+    "write once optical disk",  /* obsolete, spc5r01 */
+    /* 5 */ "cd/dvd",
+    "scanner",                  /* obsolete */
+    "optical memory device",
+    "medium changer",
+    "communications",           /* obsolete */
+    /* 0xa */ "graphics [0xa]", /* obsolete */
+    "graphics [0xb]",           /* obsolete */
+    "storage array controller",
+    "enclosure services device",
+    "simplified direct access device",
+    "optical card reader/writer device",
+    /* 0x10 */ "bridge controller commands",
+    "object based storage",
+    "automation/driver interface",
+    "security manager device",  /* obsolete, spc5r01 */
+    "zoned block commands",
+    "0x15", "0x16", "0x17", "0x18",
+    "0x19", "0x1a", "0x1b", "0x1c", "0x1d",
+    "well known logical unit",
+    "no physical device on this lu",
+};
+
+const char * sg_lib_transport_proto_strs[] =
+{
+    "Fibre Channel Protocol for SCSI (FCP-4)",
+    "SCSI Parallel Interface (SPI-5)",  /* obsolete in spc5r01 */
+    "Serial Storage Architecture SCSI-3 Protocol (SSA-S3P)",
+    "Serial Bus Protocol for IEEE 1394 (SBP-3)",
+    "SCSI RDMA Protocol (SRP)",
+    "Internet SCSI (iSCSI)",
+    "Serial Attached SCSI Protocol (SPL-4)",
+    "Automation/Drive Interface Transport (ADT-2)",
+    "AT Attachment Interface (ACS-2)",          /* 0x8 */
+    "USB Attached SCSI (UAS-2)",
+    "SCSI over PCI Express (SOP)",
+    "PCIe",                             /* added in spc5r02 */
+    "Oxc", "Oxd", "Oxe",
+    "No specific protocol"
+};
diff --git a/sg3_utils/lib/sg_pt_common.c b/sg3_utils/lib/sg_pt_common.c
new file mode 100644
index 0000000..357816a
--- /dev/null
+++ b/sg3_utils/lib/sg_pt_common.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2009-2014 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 <stdlib.h>
+
+#include "sg_pt.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+static const char * scsi_pt_version_str = "2.12 20140606";
+
+const char *
+scsi_pt_version()
+{
+    return scsi_pt_version_str;
+}
diff --git a/sg3_utils/lib/sg_pt_freebsd.c b/sg3_utils/lib/sg_pt_freebsd.c
new file mode 100644
index 0000000..db2bcfd
--- /dev/null
+++ b/sg3_utils/lib/sg_pt_freebsd.c
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2005-2015 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.
+ */
+
+/* sg_pt_freebsd version 1.13 20150511 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+#include <camlib.h>
+#include <cam/scsi/scsi_message.h>
+// #include <sys/ata.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <fcntl.h>
+#include <stddef.h>
+
+#include "sg_pt.h"
+#include "sg_lib.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#define FREEBSD_MAXDEV 64
+#define FREEBSD_FDOFFSET 16;
+
+
+struct freebsd_dev_channel {
+  char* devname;                // the SCSI device name
+  int   unitnum;                // the SCSI unit number
+  struct cam_device* cam_dev;
+};
+
+// Private table of open devices: guaranteed zero on startup since
+// part of static data.
+static struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV];
+
+#define DEF_TIMEOUT 60000       /* 60,000 milliseconds (60 seconds) */
+
+struct sg_pt_freebsd_scsi {
+    struct cam_device* cam_dev; // copy held for error processing
+    union ccb *ccb;
+    unsigned char * cdb;
+    int cdb_len;
+    unsigned char * sense;
+    int sense_len;
+    unsigned char * dxferp;
+    int dxfer_len;
+    int dxfer_dir;
+    int scsi_status;
+    int resid;
+    int sense_resid;
+    int in_err;
+    int os_err;
+    int transport_err;
+};
+
+struct sg_pt_base {
+    struct sg_pt_freebsd_scsi impl;
+};
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+
+/* Returns >= 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_open_device(const char * device_name, int read_only, int verbose)
+{
+    int oflags = 0 /* O_NONBLOCK*/ ;
+
+    oflags |= (read_only ? O_RDONLY : O_RDWR);
+    return scsi_pt_open_flags(device_name, oflags, verbose);
+}
+
+/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
+ * together. The 'flags' argument is ignored in FreeBSD.
+ * Returns >= 0 if successful, otherwise returns negated errno. */
+int
+scsi_pt_open_flags(const char * device_name,
+                   int flags __attribute__ ((unused)), int verbose)
+{
+    struct freebsd_dev_channel *fdchan;
+    struct cam_device* cam_dev;
+    int k;
+
+    // Search table for a free entry
+    for (k = 0; k < FREEBSD_MAXDEV; k++)
+        if (! devicetable[k])
+            break;
+
+    // If no free entry found, return error.  We have max allowed number
+    // of "file descriptors" already allocated.
+    if (k == FREEBSD_MAXDEV) {
+        if (verbose)
+            pr2ws("too many open file descriptors (%d)\n", FREEBSD_MAXDEV);
+        errno = EMFILE;
+        return -1;
+    }
+
+    fdchan = (struct freebsd_dev_channel *)
+                calloc(1,sizeof(struct freebsd_dev_channel));
+    if (fdchan == NULL) {
+        // errno already set by call to calloc()
+        return -1;
+    }
+
+    if (! (fdchan->devname = (char *)calloc(1, DEV_IDLEN+1)))
+         return -1;
+
+    if (cam_get_device(device_name, fdchan->devname, DEV_IDLEN,
+                       &(fdchan->unitnum)) == -1) {
+        if (verbose)
+            pr2ws("bad device name structure\n");
+        errno = EINVAL;
+        return -1;
+    }
+
+    if (! (cam_dev = cam_open_spec_device(fdchan->devname,
+                                          fdchan->unitnum, O_RDWR, NULL))) {
+        if (verbose)
+            pr2ws("cam_open_spec_device: %s\n", cam_errbuf);
+        errno = EPERM;    /* permissions or no CAM */
+        return -1;
+    }
+    fdchan->cam_dev = cam_dev;
+    // return pointer to "file descriptor" table entry, properly offset.
+    devicetable[k] = fdchan;
+    return k + FREEBSD_FDOFFSET;
+}
+
+/* Returns 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_close_device(int device_fd)
+{
+    struct freebsd_dev_channel *fdchan;
+    int fd = device_fd - FREEBSD_FDOFFSET;
+
+    if ((fd < 0) || (fd >= FREEBSD_MAXDEV)) {
+        errno = ENODEV;
+        return -1;
+    }
+    fdchan = devicetable[fd];
+    if (NULL == fdchan) {
+        errno = ENODEV;
+        return -1;
+    }
+    if (fdchan->devname)
+        free(fdchan->devname);
+    if (fdchan->cam_dev)
+        cam_close_device(fdchan->cam_dev);
+    free(fdchan);
+    devicetable[fd] = NULL;
+    return 0;
+}
+
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+    struct sg_pt_freebsd_scsi * ptp;
+
+    /* The following 2 lines are temporary. It is to avoid a NULL pointer
+     * crash when an old utility is used with a newer library built after
+     * the sg_warnings_strm cleanup */
+    if (NULL == sg_warnings_strm)
+        sg_warnings_strm = stderr;
+
+    ptp = (struct sg_pt_freebsd_scsi *)
+                calloc(1, sizeof(struct sg_pt_freebsd_scsi));
+    if (ptp) {
+        memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi));
+        ptp->dxfer_dir = CAM_DIR_NONE;
+    }
+    return (struct sg_pt_base *)ptp;
+}
+
+void
+destruct_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (ptp) {
+        if (ptp->ccb)
+            cam_freeccb(ptp->ccb);
+        free(ptp);
+    }
+}
+
+void
+clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (ptp) {
+        if (ptp->ccb)
+            cam_freeccb(ptp->ccb);
+        memset(ptp, 0, sizeof(struct sg_pt_freebsd_scsi));
+        ptp->dxfer_dir = CAM_DIR_NONE;
+    }
+}
+
+void
+set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb, int cdb_len)
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (ptp->cdb)
+        ++ptp->in_err;
+    ptp->cdb = (unsigned char *)cdb;
+    ptp->cdb_len = cdb_len;
+}
+
+void
+set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
+                  int max_sense_len)
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (ptp->sense)
+        ++ptp->in_err;
+    memset(sense, 0, max_sense_len);
+    ptp->sense = sense;
+    ptp->sense_len = max_sense_len;
+}
+
+/* Setup for data transfer from device */
+void
+set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
+                    int dxfer_len)
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (ptp->dxferp)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->dxferp = dxferp;
+        ptp->dxfer_len = dxfer_len;
+        ptp->dxfer_dir = CAM_DIR_IN;
+    }
+}
+
+/* Setup for data transfer toward device */
+void
+set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
+                     int dxfer_len)
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (ptp->dxferp)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->dxferp = (unsigned char *)dxferp;
+        ptp->dxfer_len = dxfer_len;
+        ptp->dxfer_dir = CAM_DIR_OUT;
+    }
+}
+
+void
+set_scsi_pt_packet_id(struct sg_pt_base * vp __attribute__ ((unused)),
+                      int pack_id __attribute__ ((unused)))
+{
+}
+
+void
+set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag __attribute__ ((unused)))
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+}
+
+void
+set_scsi_pt_task_management(struct sg_pt_base * vp,
+                            int tmf_code __attribute__ ((unused)))
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+}
+
+void
+set_scsi_pt_task_attr(struct sg_pt_base * vp,
+                      int attrib __attribute__ ((unused)),
+                      int priority __attribute__ ((unused)))
+{
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+}
+
+void
+set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
+{
+    if (objp) { ; }     /* unused, suppress warning */
+    if (flags) { ; }     /* unused, suppress warning */
+}
+
+/* Executes SCSI command (or at least forwards it to lower layers).
+ * Clears os_err field prior to active call (whose result may set it
+ * again). */
+int
+do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose)
+{
+    int fd = device_fd - FREEBSD_FDOFFSET;
+    struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+    struct freebsd_dev_channel *fdchan;
+    union ccb *ccb;
+    int len, timout_ms;
+
+    ptp->os_err = 0;
+    if (ptp->in_err) {
+        if (verbose)
+            pr2ws("Replicated or unused set_scsi_pt...\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    if (NULL == ptp->cdb) {
+        if (verbose)
+            pr2ws("No command (cdb) given\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+
+    if ((fd < 0) || (fd >= FREEBSD_MAXDEV)) {
+        if (verbose)
+            pr2ws("Bad file descriptor\n");
+        ptp->os_err = ENODEV;
+        return -ptp->os_err;
+    }
+    fdchan = devicetable[fd];
+    if (NULL == fdchan) {
+        if (verbose)
+            pr2ws("File descriptor closed??\n");
+        ptp->os_err = ENODEV;
+        return -ptp->os_err;
+    }
+    if (NULL == fdchan->cam_dev) {
+        if (verbose)
+            pr2ws("No open CAM device\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+
+    if (NULL == ptp->ccb) {     /* re-use if we have one already */
+        if (! (ccb = cam_getccb(fdchan->cam_dev))) {
+            if (verbose)
+                pr2ws("cam_getccb: failed\n");
+            ptp->os_err = ENOMEM;
+            return -ptp->os_err;
+        }
+        ptp->ccb = ccb;
+    } else
+        ccb = ptp->ccb;
+
+    // clear out structure, except for header that was filled in for us
+    bzero(&(&ccb->ccb_h)[1],
+            sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+    timout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT;
+    cam_fill_csio(&ccb->csio,
+                  /* retries */ 1,
+                  /* cbfcnp */ NULL,
+                  /* flags */ ptp->dxfer_dir,
+                  /* tagaction */ MSG_SIMPLE_Q_TAG,
+                  /* dataptr */ ptp->dxferp,
+                  /* datalen */ ptp->dxfer_len,
+                  /* senselen */ ptp->sense_len,
+                  /* cdblen */ ptp->cdb_len,
+                  /* timeout (millisecs) */ timout_ms);
+    memcpy(ccb->csio.cdb_io.cdb_bytes, ptp->cdb, ptp->cdb_len);
+
+    if (cam_send_ccb(fdchan->cam_dev, ccb) < 0) {
+        if (verbose) {
+            warn("error sending SCSI ccb");
+ #if __FreeBSD_version > 500000
+            cam_error_print(fdchan->cam_dev, ccb, CAM_ESF_ALL,
+                            CAM_EPF_ALL, stderr);
+ #endif
+        }
+        cam_freeccb(ptp->ccb);
+        ptp->ccb = NULL;
+        ptp->os_err = EIO;
+        return -ptp->os_err;
+    }
+
+    if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) ||
+        ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)) {
+        ptp->scsi_status = ccb->csio.scsi_status;
+        ptp->resid = ccb->csio.resid;
+        ptp->sense_resid = ccb->csio.sense_resid;
+
+        if ((SAM_STAT_CHECK_CONDITION == ptp->scsi_status) ||
+            (SAM_STAT_COMMAND_TERMINATED == ptp->scsi_status)) {
+            if (ptp->sense_resid > ptp->sense_len)
+                len = ptp->sense_len;   /* crazy; ignore sense_resid */
+            else
+                len = ptp->sense_len - ptp->sense_resid;
+            if (len > 0)
+                memcpy(ptp->sense, &(ccb->csio.sense_data), len);
+        }
+    } else
+        ptp->transport_err = 1;
+
+    ptp->cam_dev = fdchan->cam_dev;     // for error processing
+    return 0;
+}
+
+int
+get_scsi_pt_result_category(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (ptp->os_err)
+        return SCSI_PT_RESULT_OS_ERR;
+    else if (ptp->transport_err)
+        return SCSI_PT_RESULT_TRANSPORT_ERR;
+    else if ((SAM_STAT_CHECK_CONDITION == ptp->scsi_status) ||
+             (SAM_STAT_COMMAND_TERMINATED == ptp->scsi_status))
+        return SCSI_PT_RESULT_SENSE;
+    else if (ptp->scsi_status)
+        return SCSI_PT_RESULT_STATUS;
+    else
+        return SCSI_PT_RESULT_GOOD;
+}
+
+int
+get_scsi_pt_resid(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    return ptp->resid;
+}
+
+int
+get_scsi_pt_status_response(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    return ptp->scsi_status;
+}
+
+int
+get_scsi_pt_sense_len(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (ptp->sense_resid > ptp->sense_len)
+        return ptp->sense_len;  /* strange; ignore ptp->sense_resid */
+    else
+        return ptp->sense_len - ptp->sense_resid;
+}
+
+int
+get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused)))
+{
+    // const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    return -1;
+}
+
+int
+get_scsi_pt_transport_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    return ptp->transport_err;
+}
+
+int
+get_scsi_pt_os_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    return ptp->os_err;
+}
+
+char *
+get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
+                              char * b)
+{
+    const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+
+    if (0 == ptp->transport_err) {
+        strncpy(b, "no transport error available", max_b_len);
+        b[max_b_len - 1] = '\0';
+        return b;
+    }
+#if __FreeBSD_version > 500000
+    if (ptp->cam_dev)
+        cam_error_string(ptp->cam_dev, ptp->ccb, b, max_b_len, CAM_ESF_ALL,
+                         CAM_EPF_ALL);
+    else {
+        strncpy(b, "no transport error available", max_b_len);
+        b[max_b_len - 1] = '\0';
+   }
+#else
+    strncpy(b, "no transport error available", max_b_len);
+    b[max_b_len - 1] = '\0';
+#endif
+    return b;
+}
+
+char *
+get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
+{
+    const struct sg_pt_freebsd_scsi * ptp = &vp->impl;
+    const char * cp;
+
+    cp = safe_strerror(ptp->os_err);
+    strncpy(b, cp, max_b_len);
+    if ((int)strlen(cp) >= max_b_len)
+        b[max_b_len - 1] = '\0';
+    return b;
+}
diff --git a/sg3_utils/lib/sg_pt_linux.c b/sg3_utils/lib/sg_pt_linux.c
new file mode 100644
index 0000000..a4fd712
--- /dev/null
+++ b/sg3_utils/lib/sg_pt_linux.c
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 2005-2015 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.
+ */
+
+/* sg_pt_linux version 1.25 20151217 */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sg_pt.h"
+#include "sg_lib.h"
+#include "sg_linux_inc.h"
+
+#define DEF_TIMEOUT 60000       /* 60,000 millisecs (60 seconds) */
+
+static const char * linux_host_bytes[] = {
+    "DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT",
+    "DID_BAD_TARGET", "DID_ABORT", "DID_PARITY", "DID_ERROR",
+    "DID_RESET", "DID_BAD_INTR", "DID_PASSTHROUGH", "DID_SOFT_ERROR",
+    "DID_IMM_RETRY", "DID_REQUEUE" /* 0xd */,
+    "DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST",
+    "DID_TARGET_FAILURE" /* 0x10 */,
+    "DID_NEXUS_FAILURE (reservation conflict)",
+    "DID_ALLOC_FAILURE",
+    "DID_MEDIUM_ERROR",
+};
+
+#define LINUX_HOST_BYTES_SZ \
+        (int)(sizeof(linux_host_bytes) / sizeof(linux_host_bytes[0]))
+
+static const char * linux_driver_bytes[] = {
+    "DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA",
+    "DRIVER_ERROR", "DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",
+    "DRIVER_SENSE"
+};
+
+#define LINUX_DRIVER_BYTES_SZ \
+    (int)(sizeof(linux_driver_bytes) / sizeof(linux_driver_bytes[0]))
+
+#if 0
+static const char * linux_driver_suggests[] = {
+    "SUGGEST_OK", "SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP",
+    "SUGGEST_DIE", "UNKNOWN","UNKNOWN","UNKNOWN",
+    "SUGGEST_SENSE"
+};
+
+#define LINUX_DRIVER_SUGGESTS_SZ \
+    (int)(sizeof(linux_driver_suggests) / sizeof(linux_driver_suggests[0]))
+#endif
+
+/*
+ * These defines are for constants that should be visible in the
+ * /usr/include/scsi directory (brought in by sg_linux_inc.h).
+ * Redefined and aliased here to decouple this code from
+ * sg_io_linux.h  N.B. the SUGGEST_* constants are no longer used.
+ */
+#ifndef DRIVER_MASK
+#define DRIVER_MASK 0x0f
+#endif
+#ifndef SUGGEST_MASK
+#define SUGGEST_MASK 0xf0
+#endif
+#ifndef DRIVER_SENSE
+#define DRIVER_SENSE 0x08
+#endif
+#define SG_LIB_DRIVER_MASK      DRIVER_MASK
+#define SG_LIB_SUGGEST_MASK     SUGGEST_MASK
+#define SG_LIB_DRIVER_SENSE    DRIVER_SENSE
+
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+#if defined(IGNORE_LINUX_BSG) || ! defined(HAVE_LINUX_BSG_H)
+/*
+ * sg(v3) via SG_IO ioctl on a sg node or other node that accepts that ioctl.
+ * Decision has been made at compile time because either:
+ *   a) no /usr/include/linux/bsg.h header file was found, or
+ *   b) the builder gave the '--enable-no-linux-bsg' option to ./configure
+ */
+
+
+struct sg_pt_linux_scsi {
+    struct sg_io_hdr io_hdr;
+    int in_err;
+    int os_err;
+};
+
+struct sg_pt_base {
+    struct sg_pt_linux_scsi impl;
+};
+
+
+/* Returns >= 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_open_device(const char * device_name, int read_only, int verbose)
+{
+    int oflags = O_NONBLOCK;
+
+    oflags |= (read_only ? O_RDONLY : O_RDWR);
+    return scsi_pt_open_flags(device_name, oflags, verbose);
+}
+
+/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */
+/* together. The 'flags' argument is advisory and may be ignored. */
+/* Returns >= 0 if successful, otherwise returns negated errno. */
+int
+scsi_pt_open_flags(const char * device_name, int flags, int verbose)
+{
+    int fd;
+
+    if (verbose > 1) {
+        pr2ws("open %s with flags=0x%x\n", device_name, flags);
+    }
+    fd = open(device_name, flags);
+    if (fd < 0)
+        fd = -errno;
+    return fd;
+}
+
+/* Returns 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_close_device(int device_fd)
+{
+    int res;
+
+    res = close(device_fd);
+    if (res < 0)
+        res = -errno;
+    return res;
+}
+
+
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+    struct sg_pt_linux_scsi * ptp;
+
+    /* The following 2 lines are temporary. It is to avoid a NULL pointer
+     * crash when an old utility is used with a newer library built after
+     * the sg_warnings_strm cleanup */
+    if (NULL == sg_warnings_strm)
+        sg_warnings_strm = stderr;
+
+    ptp = (struct sg_pt_linux_scsi *)
+          calloc(1, sizeof(struct sg_pt_linux_scsi));
+    if (ptp) {
+        ptp->io_hdr.interface_id = 'S';
+        ptp->io_hdr.dxfer_direction = SG_DXFER_NONE;
+    }
+    return (struct sg_pt_base *)ptp;
+}
+
+void
+destruct_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp)
+        free(ptp);
+}
+
+void
+clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp) {
+        memset(ptp, 0, sizeof(struct sg_pt_linux_scsi));
+        ptp->io_hdr.interface_id = 'S';
+        ptp->io_hdr.dxfer_direction = SG_DXFER_NONE;
+    }
+}
+
+void
+set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
+                int cdb_len)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp->io_hdr.cmdp)
+        ++ptp->in_err;
+    ptp->io_hdr.cmdp = (unsigned char *)cdb;
+    ptp->io_hdr.cmd_len = cdb_len;
+}
+
+void
+set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
+                  int max_sense_len)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp->io_hdr.sbp)
+        ++ptp->in_err;
+    memset(sense, 0, max_sense_len);
+    ptp->io_hdr.sbp = sense;
+    ptp->io_hdr.mx_sb_len = max_sense_len;
+}
+
+/* Setup for data transfer from device */
+void
+set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
+                    int dxfer_len)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp->io_hdr.dxferp)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->io_hdr.dxferp = dxferp;
+        ptp->io_hdr.dxfer_len = dxfer_len;
+        ptp->io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    }
+}
+
+/* Setup for data transfer toward device */
+void
+set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
+                     int dxfer_len)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp->io_hdr.dxferp)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->io_hdr.dxferp = (unsigned char *)dxferp;
+        ptp->io_hdr.dxfer_len = dxfer_len;
+        ptp->io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+    }
+}
+
+void
+set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ptp->io_hdr.pack_id = pack_id;
+}
+
+void
+set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+    if (tag) { ; }     /* unused, suppress warning */
+}
+
+/* Note that task management function codes are transport specific */
+void
+set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+    if (tmf_code) { ; }     /* unused, suppress warning */
+}
+
+void
+set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+    if (attribute) { ; }     /* unused, suppress warning */
+    if (priority) { ; }      /* unused, suppress warning */
+}
+
+#ifndef SG_FLAG_Q_AT_TAIL
+#define SG_FLAG_Q_AT_TAIL 0x10
+#endif
+#ifndef SG_FLAG_Q_AT_HEAD
+#define SG_FLAG_Q_AT_HEAD 0x20
+#endif
+
+void
+set_scsi_pt_flags(struct sg_pt_base * vp, int flags)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    /* default action of sg driver [sg v3 interface] is QUEUE_AT_HEAD */
+    /* default action of block layer SG_IO ioctl is QUEUE_AT_TAIL */
+    if (SCSI_PT_FLAGS_QUEUE_AT_HEAD & flags) {  /* favour AT_HEAD */
+        ptp->io_hdr.flags |= SG_FLAG_Q_AT_HEAD;
+        ptp->io_hdr.flags &= ~SG_FLAG_Q_AT_TAIL;
+    } else if (SCSI_PT_FLAGS_QUEUE_AT_TAIL & flags) {
+        ptp->io_hdr.flags |= SG_FLAG_Q_AT_TAIL;
+        ptp->io_hdr.flags &= ~SG_FLAG_Q_AT_HEAD;
+    }
+}
+
+/* Executes SCSI command (or at least forwards it to lower layers).
+ * Clears os_err field prior to active call (whose result may set it
+ * again). */
+int
+do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ptp->os_err = 0;
+    if (ptp->in_err) {
+        if (verbose)
+            pr2ws("Replicated or unused set_scsi_pt... functions\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    if (NULL == ptp->io_hdr.cmdp) {
+        if (verbose)
+            pr2ws("No SCSI command (cdb) given\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    /* io_hdr.timeout is in milliseconds */
+    ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) :
+                                             DEF_TIMEOUT);
+    if (ptp->io_hdr.sbp && (ptp->io_hdr.mx_sb_len > 0))
+        memset(ptp->io_hdr.sbp, 0, ptp->io_hdr.mx_sb_len);
+    if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) {
+        ptp->os_err = errno;
+        if (verbose > 1)
+            pr2ws("ioctl(SG_IO) failed: %s (errno=%d)\n",
+                  strerror(ptp->os_err), ptp->os_err);
+        return -ptp->os_err;
+    }
+    return 0;
+}
+
+int
+get_scsi_pt_result_category(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+    int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK;
+    int scsi_st = ptp->io_hdr.status & 0x7e;
+
+    if (ptp->os_err)
+        return SCSI_PT_RESULT_OS_ERR;
+    else if (ptp->io_hdr.host_status)
+        return SCSI_PT_RESULT_TRANSPORT_ERR;
+    else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st))
+        return SCSI_PT_RESULT_TRANSPORT_ERR;
+    else if ((SG_LIB_DRIVER_SENSE == dr_st) ||
+             (SAM_STAT_CHECK_CONDITION == scsi_st) ||
+             (SAM_STAT_COMMAND_TERMINATED == scsi_st))
+        return SCSI_PT_RESULT_SENSE;
+    else if (scsi_st)
+        return SCSI_PT_RESULT_STATUS;
+    else
+        return SCSI_PT_RESULT_GOOD;
+}
+
+int
+get_scsi_pt_resid(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.resid;
+}
+
+int
+get_scsi_pt_status_response(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.status;
+}
+
+int
+get_scsi_pt_sense_len(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.sb_len_wr;
+}
+
+int
+get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.duration;
+}
+
+int
+get_scsi_pt_transport_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return (ptp->io_hdr.host_status << 8) + ptp->io_hdr.driver_status;
+}
+
+int
+get_scsi_pt_os_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->os_err;
+}
+
+/* Returns b which will contain a null char terminated string (if
+ * max_b_len > 0). That string should decode Linux driver and host
+ * status values. */
+char *
+get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
+                              char * b)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+    int ds = ptp->io_hdr.driver_status;
+    int hs = ptp->io_hdr.host_status;
+    int n, m;
+    char * cp = b;
+    int driv;
+    const char * driv_cp = "unknown";
+
+    if (max_b_len < 1)
+        return b;
+    m = max_b_len;
+    n = 0;
+    if (hs) {
+        if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ))
+            n = snprintf(cp, m, "Host_status=0x%02x is unknown\n", hs);
+        else
+            n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs,
+                         linux_host_bytes[hs]);
+    }
+    m -= n;
+    if (m < 1) {
+        b[max_b_len - 1] = '\0';
+        return b;
+    }
+    cp += n;
+    driv = ds & SG_LIB_DRIVER_MASK;
+    if (driv < LINUX_DRIVER_BYTES_SZ)
+        driv_cp = linux_driver_bytes[driv];
+#if 0
+    sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4;
+    if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
+        sugg_cp = linux_driver_suggests[sugg];
+#endif
+    n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp);
+    m -= n;
+    if (m < 1)
+        b[max_b_len - 1] = '\0';
+    return b;
+}
+
+char *
+get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+    const char * cp;
+
+    cp = safe_strerror(ptp->os_err);
+    strncpy(b, cp, max_b_len);
+    if ((int)strlen(cp) >= max_b_len)
+        b[max_b_len - 1] = '\0';
+    return b;
+}
+
+
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+#else /* allow for runtime selection of sg v3 or v4 (via bsg) */
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+/*
+ * So bsg is an option. Thus we make a runtime decision. If all the following
+ * are true we use sg v4 which is only currently supported on bsg device
+ * nodes:
+ *   a) there is a bsg entry in the /proc/devices file
+ *   b) the device node given to scsi_pt_open() is a char device
+ *   c) the char major number of the device node given to scsi_pt_open()
+ *      matches the char major number of the bsg entry in /proc/devices
+ * Otherwise the sg v3 interface is used.
+ *
+ * Note that in either case we prepare the data in a sg v4 structure. If
+ * the runtime tests indicate that the v3 interface is needed then
+ * do_scsi_pt_v3() transfers the input data into a v3 structure and
+ * then the output data is transferred back into a sg v4 structure.
+ * That implementation detail could change in the future.
+ *
+ * [20120806] Only use MAJOR() macro in kdev_t.h if that header file is
+ * available and major() macro [N.B. lower case] is not available.
+ */
+
+
+#include <linux/types.h>
+#include <linux/bsg.h>
+
+#ifdef major
+#define SG_DEV_MAJOR major
+#else
+#ifdef HAVE_LINUX_KDEV_T_H
+#include <linux/kdev_t.h>
+#endif
+#define SG_DEV_MAJOR MAJOR  /* MAJOR() macro faulty if > 255 minors */
+#endif
+
+
+struct sg_pt_linux_scsi {
+    struct sg_io_v4 io_hdr;     /* use v4 header as it is more general */
+    int in_err;
+    int os_err;
+    unsigned char tmf_request[4];
+};
+
+struct sg_pt_base {
+    struct sg_pt_linux_scsi impl;
+};
+
+static int bsg_major_checked = 0;
+static int bsg_major = 0;
+
+
+
+static void
+find_bsg_major(int verbose)
+{
+    const char * proc_devices = "/proc/devices";
+    FILE *fp;
+    char a[128];
+    char b[128];
+    char * cp;
+    int n;
+
+    if (NULL == (fp = fopen(proc_devices, "r"))) {
+        if (verbose)
+            pr2ws("fopen %s failed: %s\n", proc_devices, strerror(errno));
+        return;
+    }
+    while ((cp = fgets(b, sizeof(b), fp))) {
+        if ((1 == sscanf(b, "%126s", a)) &&
+            (0 == memcmp(a, "Character", 9)))
+            break;
+    }
+    while (cp && (cp = fgets(b, sizeof(b), fp))) {
+        if (2 == sscanf(b, "%d %126s", &n, a)) {
+            if (0 == strcmp("bsg", a)) {
+                bsg_major = n;
+                break;
+            }
+        } else
+            break;
+    }
+    if (verbose > 3) {
+        if (cp)
+            pr2ws("found bsg_major=%d\n", bsg_major);
+        else
+            pr2ws("found no bsg char device in %s\n", proc_devices);
+    }
+    fclose(fp);
+}
+
+
+/* Returns >= 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_open_device(const char * device_name, int read_only, int verbose)
+{
+    int oflags = O_NONBLOCK;
+
+    oflags |= (read_only ? O_RDONLY : O_RDWR);
+    return scsi_pt_open_flags(device_name, oflags, verbose);
+}
+
+/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed */
+/* together. The 'flags' argument is advisory and may be ignored. */
+/* Returns >= 0 if successful, otherwise returns negated errno. */
+int
+scsi_pt_open_flags(const char * device_name, int flags, int verbose)
+{
+    int fd;
+
+    if (! bsg_major_checked) {
+        bsg_major_checked = 1;
+        find_bsg_major(verbose);
+    }
+    if (verbose > 1)
+        pr2ws("open %s with flags=0x%x\n", device_name, flags);
+    fd = open(device_name, flags);
+    if (fd < 0)
+        fd = -errno;
+    return fd;
+}
+
+/* Returns 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_close_device(int device_fd)
+{
+    int res;
+
+    res = close(device_fd);
+    if (res < 0)
+        res = -errno;
+    return res;
+}
+
+
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+    struct sg_pt_linux_scsi * ptp;
+
+    ptp = (struct sg_pt_linux_scsi *)
+          calloc(1, sizeof(struct sg_pt_linux_scsi));
+    if (ptp) {
+        ptp->io_hdr.guard = 'Q';
+#ifdef BSG_PROTOCOL_SCSI
+        ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
+#endif
+#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
+        ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
+#endif
+    }
+    return (struct sg_pt_base *)ptp;
+}
+
+void
+destruct_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp)
+        free(ptp);
+}
+
+void
+clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp) {
+        memset(ptp, 0, sizeof(struct sg_pt_linux_scsi));
+        ptp->io_hdr.guard = 'Q';
+#ifdef BSG_PROTOCOL_SCSI
+        ptp->io_hdr.protocol = BSG_PROTOCOL_SCSI;
+#endif
+#ifdef BSG_SUB_PROTOCOL_SCSI_CMD
+        ptp->io_hdr.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
+#endif
+    }
+}
+
+void
+set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
+                int cdb_len)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp->io_hdr.request)
+        ++ptp->in_err;
+    /* C99 has intptr_t instead of long */
+    ptp->io_hdr.request = (__u64)(long)cdb;
+    ptp->io_hdr.request_len = cdb_len;
+}
+
+void
+set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
+                  int max_sense_len)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp->io_hdr.response)
+        ++ptp->in_err;
+    memset(sense, 0, max_sense_len);
+    ptp->io_hdr.response = (__u64)(long)sense;
+    ptp->io_hdr.max_response_len = max_sense_len;
+}
+
+/* Setup for data transfer from device */
+void
+set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
+                    int dxfer_len)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp->io_hdr.din_xferp)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->io_hdr.din_xferp = (__u64)(long)dxferp;
+        ptp->io_hdr.din_xfer_len = dxfer_len;
+    }
+}
+
+/* Setup for data transfer toward device */
+void
+set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
+                     int dxfer_len)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (ptp->io_hdr.dout_xferp)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->io_hdr.dout_xferp = (__u64)(long)dxferp;
+        ptp->io_hdr.dout_xfer_len = dxfer_len;
+    }
+}
+
+void
+set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ptp->io_hdr.spare_in = pack_id;
+}
+
+void
+set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ptp->io_hdr.request_tag = tag;
+}
+
+/* Note that task management function codes are transport specific */
+void
+set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ptp->io_hdr.subprotocol = 1;        /* SCSI task management function */
+    ptp->tmf_request[0] = (unsigned char)tmf_code;      /* assume it fits */
+    ptp->io_hdr.request = (__u64)(long)(&(ptp->tmf_request[0]));
+    ptp->io_hdr.request_len = 1;
+}
+
+void
+set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    ptp->io_hdr.request_attr = attribute;
+    ptp->io_hdr.request_priority = priority;
+}
+
+#ifndef BSG_FLAG_Q_AT_TAIL
+#define BSG_FLAG_Q_AT_TAIL 0x10
+#endif
+#ifndef BSG_FLAG_Q_AT_HEAD
+#define BSG_FLAG_Q_AT_HEAD 0x20
+#endif
+
+/* Need this later if translated to v3 interface */
+#ifndef SG_FLAG_Q_AT_TAIL
+#define SG_FLAG_Q_AT_TAIL 0x10
+#endif
+#ifndef SG_FLAG_Q_AT_HEAD
+#define SG_FLAG_Q_AT_HEAD 0x20
+#endif
+
+void
+set_scsi_pt_flags(struct sg_pt_base * vp, int flags)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    /* default action of bsg driver (sg v4) is QUEUE_AT_HEAD */
+    /* default action of block layer SG_IO ioctl is QUEUE_AT_TAIL */
+    if (SCSI_PT_FLAGS_QUEUE_AT_HEAD & flags) {  /* favour AT_HEAD */
+        ptp->io_hdr.flags |= BSG_FLAG_Q_AT_HEAD;
+        ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_TAIL;
+    } else if (SCSI_PT_FLAGS_QUEUE_AT_TAIL & flags) {
+        ptp->io_hdr.flags |= BSG_FLAG_Q_AT_TAIL;
+        ptp->io_hdr.flags &= ~BSG_FLAG_Q_AT_HEAD;
+    }
+}
+
+/* N.B. Returns din_resid and ignores dout_resid */
+int
+get_scsi_pt_resid(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.din_resid;
+}
+
+int
+get_scsi_pt_status_response(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.device_status;
+}
+
+int
+get_scsi_pt_sense_len(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.response_len;
+}
+
+int
+get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.duration;
+}
+
+int
+get_scsi_pt_transport_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->io_hdr.transport_status;
+}
+
+/* Returns b which will contain a null char terminated string (if
+ * max_b_len > 0). Combined driver and transport (called "host" in Linux
+ * kernel) statuses */
+char *
+get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
+                              char * b)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+    int ds = ptp->io_hdr.driver_status;
+    int hs = ptp->io_hdr.transport_status;
+    int n, m;
+    char * cp = b;
+    int driv;
+    const char * driv_cp = "invalid";
+
+    if (max_b_len < 1)
+        return b;
+    m = max_b_len;
+    n = 0;
+    if (hs) {
+        if ((hs < 0) || (hs >= LINUX_HOST_BYTES_SZ))
+            n = snprintf(cp, m, "Host_status=0x%02x is invalid\n", hs);
+        else
+            n = snprintf(cp, m, "Host_status=0x%02x [%s]\n", hs,
+                         linux_host_bytes[hs]);
+    }
+    m -= n;
+    if (m < 1) {
+        b[max_b_len - 1] = '\0';
+        return b;
+    }
+    cp += n;
+    driv = ds & SG_LIB_DRIVER_MASK;
+    if (driv < LINUX_DRIVER_BYTES_SZ)
+        driv_cp = linux_driver_bytes[driv];
+#if 0
+    sugg = (ds & SG_LIB_SUGGEST_MASK) >> 4;
+    if (sugg < LINUX_DRIVER_SUGGESTS_SZ)
+        sugg_cp = linux_driver_suggests[sugg];
+#endif
+    n = snprintf(cp, m, "Driver_status=0x%02x [%s]\n", ds, driv_cp);
+    m -= n;
+    if (m < 1)
+        b[max_b_len - 1] = '\0';
+    return b;
+}
+
+int
+get_scsi_pt_result_category(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+    int dr_st = ptp->io_hdr.driver_status & SG_LIB_DRIVER_MASK;
+    int scsi_st = ptp->io_hdr.device_status & 0x7e;
+
+    if (ptp->os_err)
+        return SCSI_PT_RESULT_OS_ERR;
+    else if (ptp->io_hdr.transport_status)
+        return SCSI_PT_RESULT_TRANSPORT_ERR;
+    else if (dr_st && (SG_LIB_DRIVER_SENSE != dr_st))
+        return SCSI_PT_RESULT_TRANSPORT_ERR;
+    else if ((SG_LIB_DRIVER_SENSE == dr_st) ||
+             (SAM_STAT_CHECK_CONDITION == scsi_st) ||
+             (SAM_STAT_COMMAND_TERMINATED == scsi_st))
+        return SCSI_PT_RESULT_SENSE;
+    else if (scsi_st)
+        return SCSI_PT_RESULT_STATUS;
+    else
+        return SCSI_PT_RESULT_GOOD;
+}
+
+int
+get_scsi_pt_os_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    return ptp->os_err;
+}
+
+char *
+get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
+{
+    const struct sg_pt_linux_scsi * ptp = &vp->impl;
+    const char * cp;
+
+    cp = safe_strerror(ptp->os_err);
+    strncpy(b, cp, max_b_len);
+    if ((int)strlen(cp) >= max_b_len)
+        b[max_b_len - 1] = '\0';
+    return b;
+}
+
+/* Executes SCSI command using sg v3 interface */
+static int
+do_scsi_pt_v3(struct sg_pt_linux_scsi * ptp, int fd, int time_secs,
+              int verbose)
+{
+    struct sg_io_hdr v3_hdr;
+
+    memset(&v3_hdr, 0, sizeof(v3_hdr));
+    /* convert v4 to v3 header */
+    v3_hdr.interface_id = 'S';
+    v3_hdr.dxfer_direction = SG_DXFER_NONE;
+    v3_hdr.cmdp = (unsigned char *)(long)ptp->io_hdr.request;
+    v3_hdr.cmd_len = (unsigned char)ptp->io_hdr.request_len;
+    if (ptp->io_hdr.din_xfer_len > 0) {
+        if (ptp->io_hdr.dout_xfer_len > 0) {
+            if (verbose)
+                pr2ws("sgv3 doesn't support bidi\n");
+            return SCSI_PT_DO_BAD_PARAMS;
+        }
+        v3_hdr.dxferp = (void *)(long)ptp->io_hdr.din_xferp;
+        v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.din_xfer_len;
+        v3_hdr.dxfer_direction =  SG_DXFER_FROM_DEV;
+    } else if (ptp->io_hdr.dout_xfer_len > 0) {
+        v3_hdr.dxferp = (void *)(long)ptp->io_hdr.dout_xferp;
+        v3_hdr.dxfer_len = (unsigned int)ptp->io_hdr.dout_xfer_len;
+        v3_hdr.dxfer_direction =  SG_DXFER_TO_DEV;
+    }
+    if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) {
+        v3_hdr.sbp = (unsigned char *)(long)ptp->io_hdr.response;
+        v3_hdr.mx_sb_len = (unsigned char)ptp->io_hdr.max_response_len;
+    }
+    v3_hdr.pack_id = (int)ptp->io_hdr.spare_in;
+    if (BSG_FLAG_Q_AT_HEAD & ptp->io_hdr.flags)
+        v3_hdr.flags |= SG_FLAG_Q_AT_HEAD;      /* favour AT_HEAD */
+    else if (BSG_FLAG_Q_AT_TAIL & ptp->io_hdr.flags)
+        v3_hdr.flags |= SG_FLAG_Q_AT_TAIL;
+
+    if (NULL == v3_hdr.cmdp) {
+        if (verbose)
+            pr2ws("No SCSI command (cdb) given\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    /* io_hdr.timeout is in milliseconds, if greater than zero */
+    v3_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT);
+    /* Finally do the v3 SG_IO ioctl */
+    if (ioctl(fd, SG_IO, &v3_hdr) < 0) {
+        ptp->os_err = errno;
+        if (verbose > 1)
+            pr2ws("ioctl(SG_IO v3) failed: %s (errno=%d)\n",
+                  strerror(ptp->os_err), ptp->os_err);
+        return -ptp->os_err;
+    }
+    ptp->io_hdr.device_status = (__u32)v3_hdr.status;
+    ptp->io_hdr.driver_status = (__u32)v3_hdr.driver_status;
+    ptp->io_hdr.transport_status = (__u32)v3_hdr.host_status;
+    ptp->io_hdr.response_len = (__u32)v3_hdr.sb_len_wr;
+    ptp->io_hdr.duration = (__u32)v3_hdr.duration;
+    ptp->io_hdr.din_resid = (__s32)v3_hdr.resid;
+    /* v3_hdr.info not passed back since no mapping defined (yet) */
+    return 0;
+}
+
+/* Executes SCSI command (or at least forwards it to lower layers).
+ * Clears os_err field prior to active call (whose result may set it
+ * again). */
+int
+do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
+{
+    struct sg_pt_linux_scsi * ptp = &vp->impl;
+
+    if (! bsg_major_checked) {
+        bsg_major_checked = 1;
+        find_bsg_major(verbose);
+    }
+    ptp->os_err = 0;
+    if (ptp->in_err) {
+        if (verbose)
+            pr2ws("Replicated or unused set_scsi_pt... functions\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    if (bsg_major <= 0)
+        return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
+    else {
+        struct stat a_stat;
+
+        if (fstat(fd, &a_stat) < 0) {
+            ptp->os_err = errno;
+            if (verbose > 1)
+                pr2ws("fstat() failed: %s (errno=%d)\n",
+                      strerror(ptp->os_err), ptp->os_err);
+            return -ptp->os_err;
+        }
+        if (! S_ISCHR(a_stat.st_mode) ||
+            (bsg_major != (int)SG_DEV_MAJOR(a_stat.st_rdev)))
+            return do_scsi_pt_v3(ptp, fd, time_secs, verbose);
+    }
+
+    if (! ptp->io_hdr.request) {
+        if (verbose)
+            pr2ws("No SCSI command (cdb) given (v4)\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    /* io_hdr.timeout is in milliseconds */
+    ptp->io_hdr.timeout = ((time_secs > 0) ? (time_secs * 1000) :
+                                             DEF_TIMEOUT);
+#if 0
+    /* sense buffer already zeroed */
+    if (ptp->io_hdr.response && (ptp->io_hdr.max_response_len > 0)) {
+        void * p;
+
+        p = (void *)(long)ptp->io_hdr.response;
+        memset(p, 0, ptp->io_hdr.max_response_len);
+    }
+#endif
+    if (ioctl(fd, SG_IO, &ptp->io_hdr) < 0) {
+        ptp->os_err = errno;
+        if (verbose > 1)
+            pr2ws("ioctl(SG_IO v4) failed: %s (errno=%d)\n",
+                  strerror(ptp->os_err), ptp->os_err);
+        return -ptp->os_err;
+    }
+    return 0;
+}
+
+#endif
+// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
diff --git a/sg3_utils/lib/sg_pt_osf1.c b/sg3_utils/lib/sg_pt_osf1.c
new file mode 100644
index 0000000..fced077
--- /dev/null
+++ b/sg3_utils/lib/sg_pt_osf1.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2005-2015 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 <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <io/common/devgetinfo.h>
+#include <io/common/iotypes.h>
+#include <io/cam/cam.h>
+#include <io/cam/uagt.h>
+#include <io/cam/rzdisk.h>
+#include <io/cam/scsi_opcodes.h>
+#include <io/cam/scsi_all.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "sg_pt.h"
+#include "sg_lib.h"
+
+
+#define OSF1_MAXDEV 64
+
+struct osf1_dev_channel {
+    int bus;
+    int tgt;
+    int lun;
+};
+
+// Private table of open devices: guaranteed zero on startup since
+// part of static data.
+static struct osf1_dev_channel *devicetable[OSF1_MAXDEV] = {0};
+static char *cam_dev = "/dev/cam";
+static int camfd;
+static int camopened = 0;
+
+struct sg_pt_osf1_scsi {
+    unsigned char * cdb;
+    int cdb_len;
+    unsigned char * sense;
+    int sense_len;
+    unsigned char * dxferp;
+    int dxfer_len;
+    int dxfer_dir;
+    int scsi_status;
+    int resid;
+    int sense_resid;
+    int in_err;
+    int os_err;
+    int transport_err;
+};
+
+struct sg_pt_base {
+    struct sg_pt_osf1_scsi impl;
+};
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+
+/* Returns >= 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_open_device(const char * device_name, int read_only, int verbose)
+{
+    int oflags = 0 /* O_NONBLOCK*/ ;
+
+    oflags |= (read_only ? O_RDONLY : O_RDWR);
+    return scsi_pt_open_flags(device_name, oflags, verbose);
+}
+
+/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
+ * together. The 'flags' argument is ignored in OSF-1.
+ * Returns >= 0 if successful, otherwise returns negated errno. */
+int
+scsi_pt_open_flags(const char * device_name, int flags, int verbose)
+{
+    struct osf1_dev_channel *fdchan;
+    int fd, k;
+
+    if (!camopened) {
+        camfd = open(cam_dev, O_RDWR, 0);
+        if (camfd < 0)
+            return -1;
+        camopened++;
+    }
+
+    // Search table for a free entry
+    for (k = 0; k < OSF1_MAXDEV; k++)
+        if (! devicetable[k])
+            break;
+
+    if (k == OSF1_MAXDEV) {
+        if (verbose)
+            pr2ws("too many open devices (%d)\n", OSF1_MAXDEV);
+        errno=EMFILE;
+        return -1;
+    }
+
+    fdchan = (struct osf1_dev_channel *)calloc(1,
+                                sizeof(struct osf1_dev_channel));
+    if (fdchan == NULL) {
+        // errno already set by call to malloc()
+        return -1;
+    }
+
+    fd = open(device_name, O_RDONLY|O_NONBLOCK);
+    if (fd > 0) {
+        device_info_t devinfo;
+        bzero(&devinfo, sizeof(devinfo));
+        if (ioctl(fd, DEVGETINFO, &devinfo) == 0) {
+            fdchan->bus = devinfo.v1.businfo.bus.scsi.bus_num;
+            fdchan->tgt = devinfo.v1.businfo.bus.scsi.tgt_id;
+            fdchan->lun = devinfo.v1.businfo.bus.scsi.lun;
+        }
+        close (fd);
+    } else {
+        free(fdchan);
+        return -1;
+    }
+
+    devicetable[k] = fdchan;
+    return k;
+}
+
+/* Returns 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_close_device(int device_fd)
+{
+    struct osf1_dev_channel *fdchan;
+    int i;
+
+    if ((device_fd < 0) || (device_fd >= OSF1_MAXDEV)) {
+        errno = ENODEV;
+        return -1;
+    }
+
+    fdchan = devicetable[device_fd];
+    if (NULL == fdchan) {
+        errno = ENODEV;
+        return -1;
+    }
+
+    free(fdchan);
+    devicetable[device_fd] = NULL;
+
+    for (i = 0; i < OSF1_MAXDEV; i++) {
+        if (devicetable[i])
+            break;
+    }
+    if (i == OSF1_MAXDEV) {
+        close(camfd);
+        camopened = 0;
+    }
+    return 0;
+}
+
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+    struct sg_pt_osf1_scsi * ptp;
+
+    ptp = (struct sg_pt_osf1_scsi *)malloc(sizeof(struct sg_pt_osf1_scsi));
+    if (ptp) {
+        bzero(ptp, sizeof(struct sg_pt_osf1_scsi));
+        ptp->dxfer_dir = CAM_DIR_NONE;
+    }
+    return (struct sg_pt_base *)ptp;
+}
+
+void
+destruct_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    if (ptp)
+        free(ptp);
+}
+
+void
+clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    if (ptp) {
+        bzero(ptp, sizeof(struct sg_pt_osf1_scsi));
+        ptp->dxfer_dir = CAM_DIR_NONE;
+    }
+}
+
+void
+set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
+                int cdb_len)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    if (ptp->cdb)
+        ++ptp->in_err;
+    ptp->cdb = (unsigned char *)cdb;
+    ptp->cdb_len = cdb_len;
+}
+
+void
+set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
+                  int max_sense_len)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    if (ptp->sense)
+        ++ptp->in_err;
+    bzero(sense, max_sense_len);
+    ptp->sense = sense;
+    ptp->sense_len = max_sense_len;
+}
+
+/* from device */
+void
+set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
+                    int dxfer_len)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    if (ptp->dxferp)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->dxferp = dxferp;
+        ptp->dxfer_len = dxfer_len;
+        ptp->dxfer_dir = CAM_DIR_IN;
+    }
+}
+
+/* to device */
+void
+set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
+                     int dxfer_len)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    if (ptp->dxferp)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->dxferp = (unsigned char *)dxferp;
+        ptp->dxfer_len = dxfer_len;
+        ptp->dxfer_dir = CAM_DIR_OUT;
+    }
+}
+
+void
+set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
+{
+}
+
+void
+set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+}
+
+void
+set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+}
+
+void
+set_scsi_pt_task_attr(struct sg_pt_base * vp, int attrib, int priority)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+}
+
+void
+set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
+{
+    /* do nothing, suppress warnings */
+    objp = objp;
+    flags = flags;
+}
+
+static int
+release_sim(struct sg_pt_base *vp, int device_fd, int verbose) {
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+    struct osf1_dev_channel *fdchan = devicetable[device_fd];
+    UAGT_CAM_CCB uagt;
+    CCB_RELSIM relsim;
+    int retval;
+
+    bzero(&uagt, sizeof(uagt));
+    bzero(&relsim, sizeof(relsim));
+
+    uagt.uagt_ccb = (CCB_HEADER *) &relsim;
+    uagt.uagt_ccblen = sizeof(relsim);
+
+    relsim.cam_ch.cam_ccb_len = sizeof(relsim);
+    relsim.cam_ch.cam_func_code = XPT_REL_SIMQ;
+    relsim.cam_ch.cam_flags = CAM_DIR_IN | CAM_DIS_CALLBACK;
+    relsim.cam_ch.cam_path_id = fdchan->bus;
+    relsim.cam_ch.cam_target_id = fdchan->tgt;
+    relsim.cam_ch.cam_target_lun = fdchan->lun;
+
+    retval = ioctl(camfd, UAGT_CAM_IO, &uagt);
+    if (retval < 0) {
+        if (verbose)
+            pr2ws("CAM ioctl error (Release SIM Queue)\n");
+    }
+    return retval;
+}
+
+int
+do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose)
+{
+    struct sg_pt_osf1_scsi * ptp = &vp->impl;
+    struct osf1_dev_channel *fdchan;
+    int len, retval;
+    CCB_SCSIIO ccb;
+    UAGT_CAM_CCB uagt;
+    unsigned char sensep[ADDL_SENSE_LENGTH];
+
+
+    ptp->os_err = 0;
+    if (ptp->in_err) {
+        if (verbose)
+            pr2ws("Replicated or unused set_scsi_pt...\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    if (NULL == ptp->cdb) {
+        if (verbose)
+            pr2ws("No command (cdb) given\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+
+    if ((device_fd < 0) || (device_fd >= OSF1_MAXDEV)) {
+        if (verbose)
+            pr2ws("Bad file descriptor\n");
+        ptp->os_err = ENODEV;
+        return -ptp->os_err;
+    }
+    fdchan = devicetable[device_fd];
+    if (NULL == fdchan) {
+        if (verbose)
+            pr2ws("File descriptor closed??\n");
+        ptp->os_err = ENODEV;
+        return -ptp->os_err;
+    }
+    if (0 == camopened) {
+        if (verbose)
+            pr2ws("No open CAM device\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+
+    bzero(&uagt, sizeof(uagt));
+    bzero(&ccb, sizeof(ccb));
+
+    uagt.uagt_ccb = (CCB_HEADER *) &ccb;
+    uagt.uagt_ccblen = sizeof(ccb);
+    uagt.uagt_snsbuf = ccb.cam_sense_ptr = ptp->sense ? ptp->sense : sensep;
+    uagt.uagt_snslen = ccb.cam_sense_len = ptp->sense ? ptp->sense_len :
+                                                        sizeof sensep;
+    uagt.uagt_buffer = ccb.cam_data_ptr =  ptp->dxferp;
+    uagt.uagt_buflen = ccb.cam_dxfer_len = ptp->dxfer_len;
+
+    ccb.cam_timeout = time_secs;
+    ccb.cam_ch.my_addr = (CCB_HEADER *) &ccb;
+    ccb.cam_ch.cam_ccb_len = sizeof(ccb);
+    ccb.cam_ch.cam_func_code = XPT_SCSI_IO;
+    ccb.cam_ch.cam_flags = ptp->dxfer_dir;
+    ccb.cam_cdb_len = ptp->cdb_len;
+    memcpy(ccb.cam_cdb_io.cam_cdb_bytes, ptp->cdb, ptp->cdb_len);
+    ccb.cam_ch.cam_path_id = fdchan->bus;
+    ccb.cam_ch.cam_target_id = fdchan->tgt;
+    ccb.cam_ch.cam_target_lun = fdchan->lun;
+
+    if (ioctl(camfd, UAGT_CAM_IO, &uagt) < 0) {
+        if (verbose)
+            pr2ws("CAN I/O Error\n");
+        ptp->os_err = EIO;
+        return -ptp->os_err;
+    }
+
+    if (((ccb.cam_ch.cam_status & CAM_STATUS_MASK) == CAM_REQ_CMP) ||
+            ((ccb.cam_ch.cam_status & CAM_STATUS_MASK) == CAM_REQ_CMP_ERR)) {
+        ptp->scsi_status = ccb.cam_scsi_status;
+        ptp->resid = ccb.cam_resid;
+        if (ptp->sense)
+            ptp->sense_resid = ccb.cam_sense_resid;
+    } else {
+        ptp->transport_err = 1;
+    }
+
+    /* If the SIM queue is frozen, release SIM queue. */
+    if (ccb.cam_ch.cam_status & CAM_SIM_QFRZN)
+        release_sim(vp, device_fd, verbose);
+
+    return 0;
+}
+
+int
+get_scsi_pt_result_category(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    if (ptp->os_err)
+        return SCSI_PT_RESULT_OS_ERR;
+    else if (ptp->transport_err)
+        return SCSI_PT_RESULT_TRANSPORT_ERR;
+    else if ((SAM_STAT_CHECK_CONDITION == ptp->scsi_status) ||
+             (SAM_STAT_COMMAND_TERMINATED == ptp->scsi_status))
+        return SCSI_PT_RESULT_SENSE;
+    else if (ptp->scsi_status)
+        return SCSI_PT_RESULT_STATUS;
+    else
+        return SCSI_PT_RESULT_GOOD;
+}
+
+int
+get_scsi_pt_resid(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    return ptp->resid;
+}
+
+int
+get_scsi_pt_status_response(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    return ptp->scsi_status;
+}
+
+int
+get_scsi_pt_sense_len(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+    int len;
+
+    len = ptp->sense_len - ptp->sense_resid;
+    return (len > 0) ? len : 0;
+}
+
+int
+get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
+{
+    // const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    return -1;
+}
+
+int
+get_scsi_pt_transport_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    return ptp->transport_err;
+}
+
+int
+get_scsi_pt_os_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    return ptp->os_err;
+}
+
+char *
+get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
+                              char * b)
+{
+    const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+
+    if (0 == ptp->transport_err) {
+        strncpy(b, "no transport error available", max_b_len);
+        b[max_b_len - 1] = '\0';
+        return b;
+    }
+    strncpy(b, "no transport error available", max_b_len);
+    b[max_b_len - 1] = '\0';
+    return b;
+}
+
+char *
+get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
+{
+    const struct sg_pt_osf1_scsi * ptp = &vp->impl;
+    const char * cp;
+
+    cp = safe_strerror(ptp->os_err);
+    strncpy(b, cp, max_b_len);
+    if ((int)strlen(cp) >= max_b_len)
+        b[max_b_len - 1] = '\0';
+    return b;
+}
diff --git a/sg3_utils/lib/sg_pt_solaris.c b/sg3_utils/lib/sg_pt_solaris.c
new file mode 100644
index 0000000..497acf9
--- /dev/null
+++ b/sg3_utils/lib/sg_pt_solaris.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2007-2015 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.
+ */
+
+/* sg_pt_solaris version 1.04 20151220 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+
+/* Solaris headers */
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/impl/types.h>
+#include <sys/scsi/impl/uscsi.h>
+
+#include "sg_pt.h"
+#include "sg_lib.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#define DEF_TIMEOUT 60       /* 60 seconds */
+
+struct sg_pt_solaris_scsi {
+    struct uscsi_cmd uscsi;
+    int max_sense_len;
+    int in_err;
+    int os_err;
+};
+
+struct sg_pt_base {
+    struct sg_pt_solaris_scsi impl;
+};
+
+
+/* Returns >= 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_open_device(const char * device_name, int read_only, int verbose)
+{
+    int oflags = 0 /* O_NONBLOCK*/ ;
+
+    oflags |= (read_only ? O_RDONLY : O_RDWR);
+    return scsi_pt_open_flags(device_name, oflags, verbose);
+}
+
+/* Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
+ * together. The 'flags' argument is ignored in Solaris.
+ * Returns >= 0 if successful, otherwise returns negated errno. */
+int
+scsi_pt_open_flags(const char * device_name, int flags_arg, int verbose)
+{
+    int oflags = O_NONBLOCK | O_RDWR;
+    int fd;
+
+    flags_arg = flags_arg;  /* ignore flags argument, suppress warning */
+    if (verbose > 1) {
+        fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
+                "open %s with flags=0x%x\n", device_name, oflags);
+    }
+    fd = open(device_name, oflags);
+    if (fd < 0)
+        fd = -errno;
+    return fd;
+}
+
+/* Returns 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_close_device(int device_fd)
+{
+    int res;
+
+    res = close(device_fd);
+    if (res < 0)
+        res = -errno;
+    return res;
+}
+
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+    struct sg_pt_solaris_scsi * ptp;
+
+    ptp = (struct sg_pt_solaris_scsi *)
+          calloc(1, sizeof(struct sg_pt_solaris_scsi));
+    if (ptp) {
+        ptp->uscsi.uscsi_timeout = DEF_TIMEOUT;
+        ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
+        ptp->uscsi.uscsi_timeout = DEF_TIMEOUT;
+    }
+    return (struct sg_pt_base *)ptp;
+}
+
+void
+destruct_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    if (ptp)
+        free(ptp);
+}
+
+void
+clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    if (ptp) {
+        memset(ptp, 0, sizeof(struct sg_pt_solaris_scsi));
+        ptp->uscsi.uscsi_timeout = DEF_TIMEOUT;
+        ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
+        ptp->uscsi.uscsi_timeout = DEF_TIMEOUT;
+    }
+}
+
+void
+set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
+                int cdb_len)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    if (ptp->uscsi.uscsi_cdb)
+        ++ptp->in_err;
+    ptp->uscsi.uscsi_cdb = (char *)cdb;
+    ptp->uscsi.uscsi_cdblen = cdb_len;
+}
+
+void
+set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
+                  int max_sense_len)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    if (ptp->uscsi.uscsi_rqbuf)
+        ++ptp->in_err;
+    memset(sense, 0, max_sense_len);
+    ptp->uscsi.uscsi_rqbuf = (char *)sense;
+    ptp->uscsi.uscsi_rqlen = max_sense_len;
+    ptp->max_sense_len = max_sense_len;
+}
+
+/* from device */
+void
+set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
+                    int dxfer_len)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    if (ptp->uscsi.uscsi_bufaddr)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->uscsi.uscsi_bufaddr = (char *)dxferp;
+        ptp->uscsi.uscsi_buflen = dxfer_len;
+        ptp->uscsi.uscsi_flags = USCSI_READ | USCSI_ISOLATE | USCSI_RQENABLE;
+    }
+}
+
+/* to device */
+void
+set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
+                     int dxfer_len)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    if (ptp->uscsi.uscsi_bufaddr)
+        ++ptp->in_err;
+    if (dxfer_len > 0) {
+        ptp->uscsi.uscsi_bufaddr = (char *)dxferp;
+        ptp->uscsi.uscsi_buflen = dxfer_len;
+        ptp->uscsi.uscsi_flags = USCSI_WRITE | USCSI_ISOLATE | USCSI_RQENABLE;
+    }
+}
+
+void
+set_scsi_pt_packet_id(struct sg_pt_base * vp, int pack_id)
+{
+    // struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    vp = vp;                    /* ignore and suppress warning */
+    pack_id = pack_id;          /* ignore and suppress warning */
+}
+
+void
+set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag)
+{
+    // struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    vp = vp;                    /* ignore and suppress warning */
+    tag = tag;                  /* ignore and suppress warning */
+}
+
+/* Note that task management function codes are transport specific */
+void
+set_scsi_pt_task_management(struct sg_pt_base * vp, int tmf_code)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+    tmf_code = tmf_code;        /* dummy to silence compiler */
+}
+
+void
+set_scsi_pt_task_attr(struct sg_pt_base * vp, int attribute, int priority)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    ++ptp->in_err;
+    attribute = attribute;      /* dummy to silence compiler */
+    priority = priority;        /* dummy to silence compiler */
+}
+
+void
+set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
+{
+    /* do nothing, suppress warnings */
+    objp = objp;
+    flags = flags;
+}
+
+/* Executes SCSI command (or at least forwards it to lower layers).
+ * Clears os_err field prior to active call (whose result may set it
+ * again). */
+int
+do_scsi_pt(struct sg_pt_base * vp, int fd, int time_secs, int verbose)
+{
+    struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    ptp->os_err = 0;
+    if (ptp->in_err) {
+        if (verbose)
+            fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
+                    "Replicated or unused set_scsi_pt... functions\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    if (NULL == ptp->uscsi.uscsi_cdb) {
+        if (verbose)
+            fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
+                    "No SCSI command (cdb) given\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    if (time_secs > 0)
+        ptp->uscsi.uscsi_timeout = time_secs;
+
+    if (ioctl(fd, USCSICMD, &ptp->uscsi)) {
+        ptp->os_err = errno;
+        if ((EIO == ptp->os_err) && ptp->uscsi.uscsi_status) {
+            ptp->os_err = 0;
+            return 0;
+        }
+        if (verbose)
+            fprintf(sg_warnings_strm ? sg_warnings_strm : stderr,
+                    "ioctl(USCSICMD) failed with os_err (errno) = %d\n",
+                    ptp->os_err);
+        return -ptp->os_err;
+    }
+    return 0;
+}
+
+int
+get_scsi_pt_result_category(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+    int scsi_st = ptp->uscsi.uscsi_status;
+
+    if (ptp->os_err)
+        return SCSI_PT_RESULT_OS_ERR;
+    else if ((SAM_STAT_CHECK_CONDITION == scsi_st) ||
+             (SAM_STAT_COMMAND_TERMINATED == scsi_st))
+        return SCSI_PT_RESULT_SENSE;
+    else if (scsi_st)
+        return SCSI_PT_RESULT_STATUS;
+    else
+        return SCSI_PT_RESULT_GOOD;
+}
+
+int
+get_scsi_pt_resid(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    return ptp->uscsi.uscsi_resid;
+}
+
+int
+get_scsi_pt_status_response(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    return ptp->uscsi.uscsi_status;
+}
+
+int
+get_scsi_pt_sense_len(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+    int res;
+
+    if (ptp->max_sense_len > 0) {
+        res = ptp->max_sense_len - ptp->uscsi.uscsi_rqresid;
+        return (res > 0) ? res : 0;
+    }
+    return 0;
+}
+
+int
+get_scsi_pt_duration_ms(const struct sg_pt_base * vp)
+{
+    // const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    vp = vp;            /* ignore and suppress warning */
+    return -1;          /* not available */
+}
+
+int
+get_scsi_pt_transport_err(const struct sg_pt_base * vp)
+{
+    // const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    vp = vp;            /* ignore and suppress warning */
+    return 0;
+}
+
+int
+get_scsi_pt_os_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    return ptp->os_err;
+}
+
+char *
+get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
+                              char * b)
+{
+    // const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+
+    vp = vp;            /* ignore and suppress warning */
+    if (max_b_len > 0)
+        b[0] = '\0';
+
+    return b;
+}
+
+char *
+get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
+{
+    const struct sg_pt_solaris_scsi * ptp = &vp->impl;
+    const char * cp;
+
+    cp = safe_strerror(ptp->os_err);
+    strncpy(b, cp, max_b_len);
+    if ((int)strlen(cp) >= max_b_len)
+        b[max_b_len - 1] = '\0';
+    return b;
+}
diff --git a/sg3_utils/lib/sg_pt_win32.c b/sg3_utils/lib/sg_pt_win32.c
new file mode 100644
index 0000000..d750378
--- /dev/null
+++ b/sg3_utils/lib/sg_pt_win32.c
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2006-2015 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.
+ */
+
+/* sg_pt_win32 version 1.16 20150511 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "sg_pt.h"
+#include "sg_lib.h"
+#include "sg_pt_win32.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef O_EXCL
+// #define O_EXCL 0x80  // cygwin ??
+// #define O_EXCL 0x80  // Linux
+#define O_EXCL 0x400    // mingw
+#warning "O_EXCL not defined"
+#endif
+
+/* Use the Microsoft SCSI Pass Through (SPT) interface. It has two
+ * variants: "SPT" where data is double buffered; and "SPTD" where data
+ * pointers to the user space are passed to the OS. Only Windows
+ * 2000 and later (i.e. not 95,98 or ME).
+ * There is no ASPI interface which relies on a dll from adaptec.
+ * This code uses cygwin facilities and is built in a cygwin
+ * shell. It can be run in a normal DOS shell if the cygwin1.dll
+ * file is put in an appropriate place.
+ * This code can build in a MinGW environment.
+ *
+ * N.B. MSDN says that the "SPT" interface (i.e. double buffered)
+ * should be used for small amounts of data (it says "< 16 KB").
+ * The direct variant (i.e. IOCTL_SCSI_PASS_THROUGH_DIRECT) should
+ * be used for larger amounts of data but the buffer needs to be
+ * "cache aligned". Is that 16 byte alignment or greater?
+ *
+ * This code will default to indirect (i.e. double buffered) access
+ * unless the WIN32_SPT_DIRECT preprocessor constant is defined in
+ * config.h . In version 1.12 runtime selection of direct and indirect
+ * access was added; the default is still determined by the
+ * WIN32_SPT_DIRECT preprocessor constant.
+ */
+
+#define DEF_TIMEOUT 60       /* 60 seconds */
+#define MAX_OPEN_SIMULT 8
+#define WIN32_FDOFFSET 32
+
+struct sg_pt_handle {
+    int in_use;
+    HANDLE fh;
+    char adapter[32];
+    int bus;
+    int target;
+    int lun;
+    int verbose;        /* tunnel verbose through to scsi_pt_close_device */
+};
+
+static struct sg_pt_handle handle_arr[MAX_OPEN_SIMULT];
+
+struct sg_pt_win32_scsi {
+    unsigned char * dxferp;
+    int dxfer_len;
+    unsigned char * sensep;
+    int sense_len;
+    int scsi_status;
+    int resid;
+    int sense_resid;
+    int in_err;
+    int os_err;                 /* pseudo unix error */
+    int transport_err;          /* windows error number */
+    union {
+        SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb_d;
+        /* Last entry in structure so data buffer can be extended */
+        SCSI_PASS_THROUGH_WITH_BUFFERS swb_i;
+    };
+};
+
+/* embed pointer so can change on fly if (non-direct) data buffer
+ * is not big enough */
+struct sg_pt_base {
+    struct sg_pt_win32_scsi * implp;
+};
+
+#ifdef WIN32_SPT_DIRECT
+static int spt_direct = 1;
+#else
+static int spt_direct = 0;
+#endif
+
+#ifdef __GNUC__
+static int pr2ws(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2ws(const char * fmt, ...);
+#endif
+
+
+static int
+pr2ws(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(sg_warnings_strm ? sg_warnings_strm : stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+
+/* Request SPT direct interface when state_direct is 1, state_direct set
+ * to 0 for the SPT indirect interface. */
+void
+scsi_pt_win32_direct(int state_direct)
+{
+    spt_direct = state_direct;
+}
+
+/* Returns current SPT interface state, 1 for direct, 0 for indirect */
+int
+scsi_pt_win32_spt_state(void)
+{
+    return spt_direct;
+}
+
+
+/* Returns >= 0 if successful. If error in Unix returns negated errno. */
+int
+scsi_pt_open_device(const char * device_name, int read_only, int verbose)
+{
+    int oflags = 0 /* O_NONBLOCK*/ ;
+
+    oflags |= (read_only ? 0 : 0);      /* was ... ? O_RDONLY : O_RDWR) */
+    return scsi_pt_open_flags(device_name, oflags, verbose);
+}
+
+/*
+ * Similar to scsi_pt_open_device() but takes Unix style open flags OR-ed
+ * together. The 'flags' argument is ignored in Windows.
+ * Returns >= 0 if successful, otherwise returns negated errno.
+ * Optionally accept leading "\\.\". If given something of the form
+ * "SCSI<num>:<bus>,<target>,<lun>" where the values in angle brackets
+ * are integers, then will attempt to open "\\.\SCSI<num>:" and save the
+ * other three values for the DeviceIoControl call. The trailing ".<lun>"
+ * is optionally and if not given 0 is assumed. Since "PhysicalDrive"
+ * is a lot of keystrokes, "PD" is accepted and converted to the longer
+ * form.
+ */
+int
+scsi_pt_open_flags(const char * device_name, int flags, int verbose)
+{
+    int len, k, adapter_num, bus, target, lun, off, got_scsi_name;
+    int index, num, got_pd_name, pd_num, share_mode;
+    struct sg_pt_handle * shp;
+    char buff[8];
+
+    share_mode = (O_EXCL & flags) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE);
+    /* lock */
+    for (k = 0; k < MAX_OPEN_SIMULT; k++)
+        if (0 == handle_arr[k].in_use)
+            break;
+    if (k == MAX_OPEN_SIMULT) {
+        if (verbose)
+            pr2ws("too many open handles (%d)\n", MAX_OPEN_SIMULT);
+        return -EMFILE;
+    } else
+        handle_arr[k].in_use = 1;
+    /* unlock */
+    index = k;
+    shp = handle_arr + index;
+    adapter_num = 0;
+    bus = 0;    /* also known as 'PathId' in MS docs */
+    target = 0;
+    lun = 0;
+    got_pd_name = 0;
+    got_scsi_name = 0;
+    len = strlen(device_name);
+    if ((len > 4) && (0 == strncmp("\\\\.\\", device_name, 4)))
+        off = 4;
+    else
+        off = 0;
+    if (len > (off + 2)) {
+        buff[0] = toupper((int)device_name[off + 0]);
+        buff[1] = toupper((int)device_name[off + 1]);
+        if (0 == strncmp("PD", buff, 2)) {
+            num = sscanf(device_name + off + 2, "%d", &pd_num);
+            if (1 == num)
+                got_pd_name = 1;
+        }
+        if (0 == got_pd_name) {
+            buff[2] = toupper((int)device_name[off + 2]);
+            buff[3] = toupper((int)device_name[off + 3]);
+            if (0 == strncmp("SCSI", buff, 4)) {
+                num = sscanf(device_name + off + 4, "%d:%d,%d,%d",
+                             &adapter_num, &bus, &target, &lun);
+                if (num < 3) {
+                    if (verbose)
+                        pr2ws("expected format like: "
+                              "'SCSI<port>:<bus>.<target>[.<lun>]'\n");
+                    shp->in_use = 0;
+                    return -EINVAL;
+                }
+                got_scsi_name = 1;
+            }
+        }
+    }
+    shp->bus = bus;
+    shp->target = target;
+    shp->lun = lun;
+    shp->verbose = verbose;
+    memset(shp->adapter, 0, sizeof(shp->adapter));
+    strncpy(shp->adapter, "\\\\.\\", 4);
+    if (got_pd_name)
+        snprintf(shp->adapter + 4, sizeof(shp->adapter) - 5,
+                 "PhysicalDrive%d", pd_num);
+    else if (got_scsi_name)
+        snprintf(shp->adapter + 4, sizeof(shp->adapter) - 5, "SCSI%d:",
+                 adapter_num);
+    else
+        snprintf(shp->adapter + 4, sizeof(shp->adapter) - 5, "%s",
+                 device_name + off);
+    shp->fh = CreateFile(shp->adapter, GENERIC_READ | GENERIC_WRITE,
+                         share_mode, NULL, OPEN_EXISTING, 0, NULL);
+    if (shp->fh == INVALID_HANDLE_VALUE) {
+        if (verbose)
+            pr2ws("Windows CreateFile error=%u\n",
+                  (unsigned int)GetLastError());
+        shp->in_use = 0;
+        return -ENODEV;
+    }
+    return index + WIN32_FDOFFSET;
+}
+
+
+/* Returns 0 if successful. If device_id seems wild returns -ENODEV,
+ * other errors return 0. If CloseHandle() fails and verbose > 0 then
+ * outputs warning with value from GetLastError(). The verbose value
+ * defaults to zero and is potentially set from the most recent call
+ * to scsi_pt_open_device() or do_scsi_pt(). */
+int
+scsi_pt_close_device(int device_fd)
+{
+    struct sg_pt_handle * shp;
+    int index;
+
+    index = device_fd - WIN32_FDOFFSET;
+
+    if ((index < 0) || (index >= WIN32_FDOFFSET))
+        return -ENODEV;
+    shp = handle_arr + index;
+    if ((! CloseHandle(shp->fh)) && shp->verbose)
+        pr2ws("Windows CloseHandle error=%u\n", (unsigned int)GetLastError());
+    shp->bus = 0;
+    shp->target = 0;
+    shp->lun = 0;
+    memset(shp->adapter, 0, sizeof(shp->adapter));
+    shp->in_use = 0;
+    shp->verbose = 0;
+    return 0;
+}
+
+struct sg_pt_base *
+construct_scsi_pt_obj()
+{
+    struct sg_pt_win32_scsi * psp;
+    struct sg_pt_base * vp = NULL;
+
+    /* The following 2 lines are temporary. It is to avoid a NULL pointer
+     * crash when an old utility is used with a newer library built after
+     * the sg_warnings_strm cleanup */
+    if (NULL == sg_warnings_strm)
+        sg_warnings_strm = stderr;
+
+    psp = (struct sg_pt_win32_scsi *)calloc(sizeof(struct sg_pt_win32_scsi),
+                                            1);
+    if (psp) {
+        if (spt_direct) {
+            psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+            psp->swb_d.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN;
+            psp->swb_d.spt.SenseInfoOffset =
+                offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+            psp->swb_d.spt.TimeOutValue = DEF_TIMEOUT;
+        } else {
+            psp->swb_i.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+            psp->swb_i.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN;
+            psp->swb_i.spt.SenseInfoOffset =
+                offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+            psp->swb_i.spt.TimeOutValue = DEF_TIMEOUT;
+        }
+        vp = malloc(sizeof(struct sg_pt_win32_scsi *)); // yes a pointer
+        if (vp)
+            vp->implp = psp;
+        else
+            free(psp);
+    }
+    return vp;
+}
+
+void
+destruct_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    if (vp) {
+        struct sg_pt_win32_scsi * psp = vp->implp;
+
+        if (psp) {
+            free(psp);
+        }
+        free(vp);
+    }
+}
+
+void
+clear_scsi_pt_obj(struct sg_pt_base * vp)
+{
+    struct sg_pt_win32_scsi * psp = vp->implp;
+
+    if (psp) {
+        memset(psp, 0, sizeof(struct sg_pt_win32_scsi));
+        if (spt_direct) {
+            psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+            psp->swb_d.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN;
+            psp->swb_d.spt.SenseInfoOffset =
+                offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+            psp->swb_d.spt.TimeOutValue = DEF_TIMEOUT;
+        } else {
+            psp->swb_i.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+            psp->swb_i.spt.SenseInfoLength = SCSI_MAX_SENSE_LEN;
+            psp->swb_i.spt.SenseInfoOffset =
+                offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+            psp->swb_i.spt.TimeOutValue = DEF_TIMEOUT;
+        }
+    }
+}
+
+void
+set_scsi_pt_cdb(struct sg_pt_base * vp, const unsigned char * cdb,
+                int cdb_len)
+{
+    struct sg_pt_win32_scsi * psp = vp->implp;
+
+    if (spt_direct) {
+        if (psp->swb_d.spt.CdbLength > 0)
+            ++psp->in_err;
+        if (cdb_len > (int)sizeof(psp->swb_d.spt.Cdb)) {
+            ++psp->in_err;
+            return;
+        }
+        memcpy(psp->swb_d.spt.Cdb, cdb, cdb_len);
+        psp->swb_d.spt.CdbLength = cdb_len;
+    } else {
+        if (psp->swb_i.spt.CdbLength > 0)
+            ++psp->in_err;
+        if (cdb_len > (int)sizeof(psp->swb_i.spt.Cdb)) {
+            ++psp->in_err;
+            return;
+        }
+        memcpy(psp->swb_i.spt.Cdb, cdb, cdb_len);
+        psp->swb_i.spt.CdbLength = cdb_len;
+    }
+}
+
+void
+set_scsi_pt_sense(struct sg_pt_base * vp, unsigned char * sense,
+                  int sense_len)
+{
+    struct sg_pt_win32_scsi * psp = vp->implp;
+
+    if (psp->sensep)
+        ++psp->in_err;
+    memset(sense, 0, sense_len);
+    psp->sensep = sense;
+    psp->sense_len = sense_len;
+}
+
+/* from device */
+void
+set_scsi_pt_data_in(struct sg_pt_base * vp, unsigned char * dxferp,
+                    int dxfer_len)
+{
+    struct sg_pt_win32_scsi * psp = vp->implp;
+
+    if (psp->dxferp)
+        ++psp->in_err;
+    if (dxfer_len > 0) {
+        psp->dxferp = dxferp;
+        psp->dxfer_len = dxfer_len;
+        if (spt_direct)
+            psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_IN;
+        else
+            psp->swb_i.spt.DataIn = SCSI_IOCTL_DATA_IN;
+    }
+}
+
+/* to device */
+void
+set_scsi_pt_data_out(struct sg_pt_base * vp, const unsigned char * dxferp,
+                     int dxfer_len)
+{
+    struct sg_pt_win32_scsi * psp = vp->implp;
+
+    if (psp->dxferp)
+        ++psp->in_err;
+    if (dxfer_len > 0) {
+        psp->dxferp = (unsigned char *)dxferp;
+        psp->dxfer_len = dxfer_len;
+        if (spt_direct)
+            psp->swb_d.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+        else
+            psp->swb_i.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+    }
+}
+
+void
+set_scsi_pt_packet_id(struct sg_pt_base * vp __attribute__ ((unused)),
+                      int pack_id __attribute__ ((unused)))
+{
+}
+
+void
+set_scsi_pt_tag(struct sg_pt_base * vp, uint64_t tag __attribute__ ((unused)))
+{
+    struct sg_pt_win32_scsi * psp = vp->implp;
+
+    ++psp->in_err;
+}
+
+void
+set_scsi_pt_task_management(struct sg_pt_base * vp,
+                            int tmf_code __attribute__ ((unused)))
+{
+    struct sg_pt_win32_scsi * psp = vp->implp;
+
+    ++psp->in_err;
+}
+
+void
+set_scsi_pt_task_attr(struct sg_pt_base * vp,
+                      int attrib __attribute__ ((unused)),
+                      int priority __attribute__ ((unused)))
+{
+    struct sg_pt_win32_scsi * psp = vp->implp;
+
+    ++psp->in_err;
+}
+
+void
+set_scsi_pt_flags(struct sg_pt_base * objp, int flags)
+{
+    /* do nothing, suppress warnings */
+    objp = objp;
+    flags = flags;
+}
+
+/* Executes SCSI command (or at least forwards it to lower layers)
+ * using direct interface. Clears os_err field prior to active call (whose
+ * result may set it again). */
+static int
+do_scsi_pt_direct(struct sg_pt_base * vp, int device_fd, int time_secs,
+                  int verbose)
+{
+    int index = device_fd - WIN32_FDOFFSET;
+    struct sg_pt_win32_scsi * psp = vp->implp;
+    struct sg_pt_handle * shp;
+    BOOL status;
+    DWORD returned;
+
+    psp->os_err = 0;
+    if (psp->in_err) {
+        if (verbose)
+            pr2ws("Replicated or unused set_scsi_pt...\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    if (0 == psp->swb_d.spt.CdbLength) {
+        if (verbose)
+            pr2ws("No command (cdb) given\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+
+    index = device_fd - WIN32_FDOFFSET;
+    if ((index < 0) || (index >= WIN32_FDOFFSET)) {
+        if (verbose)
+            pr2ws("Bad file descriptor\n");
+        psp->os_err = ENODEV;
+        return -psp->os_err;
+    }
+    shp = handle_arr + index;
+    if (0 == shp->in_use) {
+        if (verbose)
+            pr2ws("File descriptor closed??\n");
+        psp->os_err = ENODEV;
+        return -psp->os_err;
+    }
+    shp->verbose = verbose;
+    psp->swb_d.spt.Length = sizeof (SCSI_PASS_THROUGH_DIRECT);
+    psp->swb_d.spt.PathId = shp->bus;
+    psp->swb_d.spt.TargetId = shp->target;
+    psp->swb_d.spt.Lun = shp->lun;
+    psp->swb_d.spt.TimeOutValue = time_secs;
+    psp->swb_d.spt.DataTransferLength = psp->dxfer_len;
+    if (verbose > 4) {
+        pr2ws(" spt_direct, adapter: %s  Length=%d ScsiStatus=%d PathId=%d "
+              "TargetId=%d Lun=%d\n", shp->adapter,
+              (int)psp->swb_d.spt.Length, (int)psp->swb_d.spt.ScsiStatus,
+              (int)psp->swb_d.spt.PathId, (int)psp->swb_d.spt.TargetId,
+              (int)psp->swb_d.spt.Lun);
+        pr2ws("    CdbLength=%d SenseInfoLength=%d DataIn=%d "
+              "DataTransferLength=%u\n",
+              (int)psp->swb_d.spt.CdbLength,
+              (int)psp->swb_d.spt.SenseInfoLength,
+              (int)psp->swb_d.spt.DataIn,
+              (unsigned int)psp->swb_d.spt.DataTransferLength);
+        pr2ws("    TimeOutValue=%u SenseInfoOffset=%u\n",
+              (unsigned int)psp->swb_d.spt.TimeOutValue,
+              (unsigned int)psp->swb_d.spt.SenseInfoOffset);
+    }
+    psp->swb_d.spt.DataBuffer = psp->dxferp;
+    status = DeviceIoControl(shp->fh, IOCTL_SCSI_PASS_THROUGH_DIRECT,
+                            &psp->swb_d,
+                            sizeof(psp->swb_d),
+                            &psp->swb_d,
+                            sizeof(psp->swb_d),
+                            &returned,
+                            NULL);
+    if (! status) {
+        unsigned int u;
+
+        u = (unsigned int)GetLastError();
+        if (verbose)
+            pr2ws("Windows DeviceIoControl error=%u\n", u);
+        psp->transport_err = (int)u;
+        psp->os_err = EIO;
+        return 0;       /* let app find transport error */
+    }
+
+    psp->scsi_status = psp->swb_d.spt.ScsiStatus;
+    if ((SAM_STAT_CHECK_CONDITION == psp->scsi_status) ||
+        (SAM_STAT_COMMAND_TERMINATED == psp->scsi_status))
+        memcpy(psp->sensep, psp->swb_d.ucSenseBuf, psp->sense_len);
+    else
+        psp->sense_len = 0;
+    psp->sense_resid = 0;
+    if ((psp->dxfer_len > 0) && (psp->swb_d.spt.DataTransferLength > 0))
+        psp->resid = psp->dxfer_len - psp->swb_d.spt.DataTransferLength;
+    else
+        psp->resid = 0;
+
+    return 0;
+}
+
+/* Executes SCSI command (or at least forwards it to lower layers) using
+ * indirect interface. Clears os_err field prior to active call (whose
+ * result may set it again). */
+static int
+do_scsi_pt_indirect(struct sg_pt_base * vp, int device_fd, int time_secs,
+                    int verbose)
+{
+    int index = device_fd - WIN32_FDOFFSET;
+    struct sg_pt_win32_scsi * psp = vp->implp;
+    struct sg_pt_handle * shp;
+    BOOL status;
+    DWORD returned;
+
+    psp->os_err = 0;
+    if (psp->in_err) {
+        if (verbose)
+            pr2ws("Replicated or unused set_scsi_pt...\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+    if (0 == psp->swb_i.spt.CdbLength) {
+        if (verbose)
+            pr2ws("No command (cdb) given\n");
+        return SCSI_PT_DO_BAD_PARAMS;
+    }
+
+    index = device_fd - WIN32_FDOFFSET;
+    if ((index < 0) || (index >= WIN32_FDOFFSET)) {
+        if (verbose)
+            pr2ws("Bad file descriptor\n");
+        psp->os_err = ENODEV;
+        return -psp->os_err;
+    }
+    shp = handle_arr + index;
+    if (0 == shp->in_use) {
+        if (verbose)
+            pr2ws("File descriptor closed??\n");
+        psp->os_err = ENODEV;
+        return -psp->os_err;
+    }
+    shp->verbose = verbose;
+    if (psp->dxfer_len > (int)sizeof(psp->swb_i.ucDataBuf)) {
+        int extra = psp->dxfer_len - (int)sizeof(psp->swb_i.ucDataBuf);
+        struct sg_pt_win32_scsi * epsp;
+
+        if (verbose > 4)
+            pr2ws("spt_indirect: dxfer_len (%d) too large for initial data\n"
+                  "  buffer (%d bytes), try enlarging\n", psp->dxfer_len,
+                  (int)sizeof(psp->swb_i.ucDataBuf));
+        epsp = (struct sg_pt_win32_scsi *)
+               calloc(sizeof(struct sg_pt_win32_scsi) + extra, 1);
+        if (NULL == epsp) {
+            pr2ws("do_scsi_pt: failed to enlarge data buffer to %d bytes\n",
+                  psp->dxfer_len);
+            psp->os_err = ENOMEM;
+            return -psp->os_err;
+        }
+        memcpy(epsp, psp, sizeof(struct sg_pt_win32_scsi));
+        free(psp);
+        vp->implp = epsp;
+        psp = epsp;
+    }
+    psp->swb_i.spt.Length = sizeof (SCSI_PASS_THROUGH);
+    psp->swb_i.spt.DataBufferOffset =
+                offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
+    psp->swb_i.spt.PathId = shp->bus;
+    psp->swb_i.spt.TargetId = shp->target;
+    psp->swb_i.spt.Lun = shp->lun;
+    psp->swb_i.spt.TimeOutValue = time_secs;
+    psp->swb_i.spt.DataTransferLength = psp->dxfer_len;
+    if (verbose > 4) {
+        pr2ws(" spt_indirect, adapter: %s  Length=%d ScsiStatus=%d PathId=%d "
+              "TargetId=%d Lun=%d\n", shp->adapter,
+              (int)psp->swb_i.spt.Length, (int)psp->swb_i.spt.ScsiStatus,
+              (int)psp->swb_i.spt.PathId, (int)psp->swb_i.spt.TargetId,
+              (int)psp->swb_i.spt.Lun);
+        pr2ws("    CdbLength=%d SenseInfoLength=%d DataIn=%d "
+              "DataTransferLength=%u\n",
+              (int)psp->swb_i.spt.CdbLength,
+              (int)psp->swb_i.spt.SenseInfoLength,
+              (int)psp->swb_i.spt.DataIn,
+              (unsigned int)psp->swb_i.spt.DataTransferLength);
+        pr2ws("    TimeOutValue=%u DataBufferOffset=%u "
+              "SenseInfoOffset=%u\n",
+              (unsigned int)psp->swb_i.spt.TimeOutValue,
+              (unsigned int)psp->swb_i.spt.DataBufferOffset,
+              (unsigned int)psp->swb_i.spt.SenseInfoOffset);
+    }
+    if ((psp->dxfer_len > 0) &&
+        (SCSI_IOCTL_DATA_OUT == psp->swb_i.spt.DataIn))
+        memcpy(psp->swb_i.ucDataBuf, psp->dxferp, psp->dxfer_len);
+    status = DeviceIoControl(shp->fh, IOCTL_SCSI_PASS_THROUGH,
+                            &psp->swb_i,
+                            sizeof(psp->swb_i),
+                            &psp->swb_i,
+                            sizeof(psp->swb_i),
+                            &returned,
+                            NULL);
+    if (! status) {
+        unsigned int u;
+
+        u = (unsigned int)GetLastError();
+        if (verbose)
+            pr2ws("Windows DeviceIoControl error=%u\n", u);
+        psp->transport_err = (int)u;
+        psp->os_err = EIO;
+        return 0;       /* let app find transport error */
+    }
+    if ((psp->dxfer_len > 0) && (SCSI_IOCTL_DATA_IN == psp->swb_i.spt.DataIn))
+        memcpy(psp->dxferp, psp->swb_i.ucDataBuf, psp->dxfer_len);
+
+    psp->scsi_status = psp->swb_i.spt.ScsiStatus;
+    if ((SAM_STAT_CHECK_CONDITION == psp->scsi_status) ||
+        (SAM_STAT_COMMAND_TERMINATED == psp->scsi_status))
+        memcpy(psp->sensep, psp->swb_i.ucSenseBuf, psp->sense_len);
+    else
+        psp->sense_len = 0;
+    psp->sense_resid = 0;
+    if ((psp->dxfer_len > 0) && (psp->swb_i.spt.DataTransferLength > 0))
+        psp->resid = psp->dxfer_len - psp->swb_i.spt.DataTransferLength;
+    else
+        psp->resid = 0;
+
+    return 0;
+}
+
+/* Executes SCSI command (or at least forwards it to lower layers).
+ * Clears os_err field prior to active call (whose result may set it
+ * again). */
+int
+do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose)
+{
+    if (spt_direct)
+        return do_scsi_pt_direct(vp, device_fd, time_secs, verbose);
+    else
+        return do_scsi_pt_indirect(vp, device_fd, time_secs, verbose);
+}
+
+int
+get_scsi_pt_result_category(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_win32_scsi * psp = vp->implp;
+
+    if (psp->transport_err)     /* give transport error highest priority */
+        return SCSI_PT_RESULT_TRANSPORT_ERR;
+    else if (psp->os_err)
+        return SCSI_PT_RESULT_OS_ERR;
+    else if ((SAM_STAT_CHECK_CONDITION == psp->scsi_status) ||
+             (SAM_STAT_COMMAND_TERMINATED == psp->scsi_status))
+        return SCSI_PT_RESULT_SENSE;
+    else if (psp->scsi_status)
+        return SCSI_PT_RESULT_STATUS;
+    else
+        return SCSI_PT_RESULT_GOOD;
+}
+
+int
+get_scsi_pt_resid(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_win32_scsi * psp = vp->implp;
+
+    return psp->resid;
+}
+
+int
+get_scsi_pt_status_response(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_win32_scsi * psp = vp->implp;
+
+    return psp->scsi_status;
+}
+
+int
+get_scsi_pt_sense_len(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_win32_scsi * psp = vp->implp;
+    int len;
+
+    len = psp->sense_len - psp->sense_resid;
+    return (len > 0) ? len : 0;
+}
+
+int
+get_scsi_pt_duration_ms(const struct sg_pt_base * vp __attribute__ ((unused)))
+{
+    // const struct sg_pt_freebsd_scsi * psp = vp->implp;
+
+    return -1;
+}
+
+int
+get_scsi_pt_transport_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_win32_scsi * psp = vp->implp;
+
+    return psp->transport_err;
+}
+
+int
+get_scsi_pt_os_err(const struct sg_pt_base * vp)
+{
+    const struct sg_pt_win32_scsi * psp = vp->implp;
+
+    return psp->os_err;
+}
+
+
+char *
+get_scsi_pt_transport_err_str(const struct sg_pt_base * vp, int max_b_len,
+                              char * b)
+{
+    struct sg_pt_win32_scsi * psp = (struct sg_pt_win32_scsi *)vp->implp;
+    LPVOID lpMsgBuf;
+    int k, num, ch;
+
+    if (max_b_len < 2) {
+        if (1 == max_b_len)
+            b[0] = '\0';
+        return b;
+    }
+    memset(b, 0, max_b_len);
+    FormatMessage(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER |
+        FORMAT_MESSAGE_FROM_SYSTEM,
+        NULL,
+        psp->transport_err,
+        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        (LPTSTR) &lpMsgBuf,
+        0, NULL );
+    num = lstrlen((LPCTSTR)lpMsgBuf);
+    if (num < 1)
+        return b;
+    num = (num < max_b_len) ? num : (max_b_len - 1);
+    for (k = 0; k < num; ++k) {
+        ch = *((LPCTSTR)lpMsgBuf + k);
+        if ((ch >= 0x0) && (ch < 0x7f))
+            b[k] = ch & 0x7f;
+        else
+            b[k] = '?';
+    }
+    return b;
+}
+
+char *
+get_scsi_pt_os_err_str(const struct sg_pt_base * vp, int max_b_len, char * b)
+{
+    const struct sg_pt_win32_scsi * psp = vp->implp;
+    const char * cp;
+
+    cp = safe_strerror(psp->os_err);
+    strncpy(b, cp, max_b_len);
+    if ((int)strlen(cp) >= max_b_len)
+        b[max_b_len - 1] = '\0';
+    return b;
+}
diff --git a/sg3_utils/ltmain.sh b/sg3_utils/ltmain.sh
new file mode 100755
index 0000000..bffda54
--- /dev/null
+++ b/sg3_utils/ltmain.sh
@@ -0,0 +1,9661 @@
+
+# libtool (GNU libtool) 2.4.2
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
+# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions.  There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html,
+# or obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Usage: $progname [OPTION]... [MODE-ARG]...
+#
+# Provide generalized library-building support services.
+#
+#       --config             show all configuration variables
+#       --debug              enable verbose shell tracing
+#   -n, --dry-run            display commands without modifying any files
+#       --features           display basic configuration information and exit
+#       --mode=MODE          use operation mode MODE
+#       --preserve-dup-deps  don't remove duplicate dependency libraries
+#       --quiet, --silent    don't print informational messages
+#       --no-quiet, --no-silent
+#                            print informational messages (default)
+#       --no-warn            don't display warning messages
+#       --tag=TAG            use configuration variables from tag TAG
+#   -v, --verbose            print more informational messages than default
+#       --no-verbose         don't print the extra informational messages
+#       --version            print version information
+#   -h, --help, --help-all   print short, long, or detailed help message
+#
+# MODE must be one of the following:
+#
+#         clean              remove files from the build directory
+#         compile            compile a source file into a libtool object
+#         execute            automatically set library path, then run a program
+#         finish             complete the installation of libtool libraries
+#         install            install libraries or executables
+#         link               create a library or an executable
+#         uninstall          remove libraries from an installed directory
+#
+# MODE-ARGS vary depending on the MODE.  When passed as first option,
+# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that.
+# Try `$progname --help --mode=MODE' for a more detailed description of MODE.
+#
+# When reporting a bug, please describe a test case to reproduce it and
+# include the following information:
+#
+#         host-triplet:	$host
+#         shell:		$SHELL
+#         compiler:		$LTCC
+#         compiler flags:		$LTCFLAGS
+#         linker:		$LD (gnu? $with_gnu_ld)
+#         $progname:	(GNU libtool) 2.4.2 Debian-2.4.2-1.11
+#         automake:	$automake_version
+#         autoconf:	$autoconf_version
+#
+# Report bugs to <bug-libtool@gnu.org>.
+# GNU libtool home page: <http://www.gnu.org/software/libtool/>.
+# General help using GNU software: <http://www.gnu.org/gethelp/>.
+
+PROGRAM=libtool
+PACKAGE=libtool
+VERSION="2.4.2 Debian-2.4.2-1.11"
+TIMESTAMP=""
+package_revision=1.3337
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# NLS nuisances: We save the old values to restore during execute mode.
+lt_user_locale=
+lt_safe_locale=
+for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+  eval "if test \"\${$lt_var+set}\" = set; then
+          save_$lt_var=\$$lt_var
+          $lt_var=C
+	  export $lt_var
+	  lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\"
+	  lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\"
+	fi"
+done
+LC_ALL=C
+LANGUAGE=C
+export LANGUAGE LC_ALL
+
+$lt_unset CDPATH
+
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath="$0"
+
+
+
+: ${CP="cp -f"}
+test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+: ${Xsed="$SED -e 1s/^X//"}
+
+# Global variables:
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63  # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77	  # $? = 77 is used to indicate a skipped test to automake.
+
+exit_status=$EXIT_SUCCESS
+
+# Make sure IFS has a sensible default
+lt_nl='
+'
+IFS=" 	$lt_nl"
+
+dirname="s,/[^/]*$,,"
+basename="s,^.*/,,"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+    func_dirname_result=`$ECHO "${1}" | $SED "$dirname"`
+    if test "X$func_dirname_result" = "X${1}"; then
+      func_dirname_result="${3}"
+    else
+      func_dirname_result="$func_dirname_result${2}"
+    fi
+} # func_dirname may be replaced by extended shell implementation
+
+
+# func_basename file
+func_basename ()
+{
+    func_basename_result=`$ECHO "${1}" | $SED "$basename"`
+} # func_basename may be replaced by extended shell implementation
+
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+#   dirname:  Compute the dirname of FILE.  If nonempty,
+#             add APPEND to the result, otherwise set result
+#             to NONDIR_REPLACEMENT.
+#             value returned in "$func_dirname_result"
+#   basename: Compute filename of FILE.
+#             value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+    # Extract subdirectory from the argument.
+    func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"`
+    if test "X$func_dirname_result" = "X${1}"; then
+      func_dirname_result="${3}"
+    else
+      func_dirname_result="$func_dirname_result${2}"
+    fi
+    func_basename_result=`$ECHO "${1}" | $SED -e "$basename"`
+} # func_dirname_and_basename may be replaced by extended shell implementation
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+    case ${2} in
+      .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+      *)  func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+    esac
+} # func_stripname may be replaced by extended shell implementation
+
+
+# These SED scripts presuppose an absolute path with a trailing slash.
+pathcar='s,^/\([^/]*\).*$,\1,'
+pathcdr='s,^/[^/]*,,'
+removedotparts=':dotsl
+		s@/\./@/@g
+		t dotsl
+		s,/\.$,/,'
+collapseslashes='s@/\{1,\}@/@g'
+finalslash='s,/*$,/,'
+
+# func_normal_abspath PATH
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+#             value returned in "$func_normal_abspath_result"
+func_normal_abspath ()
+{
+  # Start from root dir and reassemble the path.
+  func_normal_abspath_result=
+  func_normal_abspath_tpath=$1
+  func_normal_abspath_altnamespace=
+  case $func_normal_abspath_tpath in
+    "")
+      # Empty path, that just means $cwd.
+      func_stripname '' '/' "`pwd`"
+      func_normal_abspath_result=$func_stripname_result
+      return
+    ;;
+    # The next three entries are used to spot a run of precisely
+    # two leading slashes without using negated character classes;
+    # we take advantage of case's first-match behaviour.
+    ///*)
+      # Unusual form of absolute path, do nothing.
+    ;;
+    //*)
+      # Not necessarily an ordinary path; POSIX reserves leading '//'
+      # and for example Cygwin uses it to access remote file shares
+      # over CIFS/SMB, so we conserve a leading double slash if found.
+      func_normal_abspath_altnamespace=/
+    ;;
+    /*)
+      # Absolute path, do nothing.
+    ;;
+    *)
+      # Relative path, prepend $cwd.
+      func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+    ;;
+  esac
+  # Cancel out all the simple stuff to save iterations.  We also want
+  # the path to end with a slash for ease of parsing, so make sure
+  # there is one (and only one) here.
+  func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"`
+  while :; do
+    # Processed it all yet?
+    if test "$func_normal_abspath_tpath" = / ; then
+      # If we ascended to the root using ".." the result may be empty now.
+      if test -z "$func_normal_abspath_result" ; then
+        func_normal_abspath_result=/
+      fi
+      break
+    fi
+    func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$pathcar"`
+    func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$pathcdr"`
+    # Figure out what to do with it
+    case $func_normal_abspath_tcomponent in
+      "")
+        # Trailing empty path component, ignore it.
+      ;;
+      ..)
+        # Parent dir; strip last assembled component from result.
+        func_dirname "$func_normal_abspath_result"
+        func_normal_abspath_result=$func_dirname_result
+      ;;
+      *)
+        # Actual path component, append it.
+        func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent
+      ;;
+    esac
+  done
+  # Restore leading double-slash if one was found on entry.
+  func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+# func_relative_path SRCDIR DSTDIR
+# generates a relative path from SRCDIR to DSTDIR, with a trailing
+# slash if non-empty, suitable for immediately appending a filename
+# without needing to append a separator.
+#             value returned in "$func_relative_path_result"
+func_relative_path ()
+{
+  func_relative_path_result=
+  func_normal_abspath "$1"
+  func_relative_path_tlibdir=$func_normal_abspath_result
+  func_normal_abspath "$2"
+  func_relative_path_tbindir=$func_normal_abspath_result
+
+  # Ascend the tree starting from libdir
+  while :; do
+    # check if we have found a prefix of bindir
+    case $func_relative_path_tbindir in
+      $func_relative_path_tlibdir)
+        # found an exact match
+        func_relative_path_tcancelled=
+        break
+        ;;
+      $func_relative_path_tlibdir*)
+        # found a matching prefix
+        func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+        func_relative_path_tcancelled=$func_stripname_result
+        if test -z "$func_relative_path_result"; then
+          func_relative_path_result=.
+        fi
+        break
+        ;;
+      *)
+        func_dirname $func_relative_path_tlibdir
+        func_relative_path_tlibdir=${func_dirname_result}
+        if test "x$func_relative_path_tlibdir" = x ; then
+          # Have to descend all the way to the root!
+          func_relative_path_result=../$func_relative_path_result
+          func_relative_path_tcancelled=$func_relative_path_tbindir
+          break
+        fi
+        func_relative_path_result=../$func_relative_path_result
+        ;;
+    esac
+  done
+
+  # Now calculate path; take care to avoid doubling-up slashes.
+  func_stripname '' '/' "$func_relative_path_result"
+  func_relative_path_result=$func_stripname_result
+  func_stripname '/' '/' "$func_relative_path_tcancelled"
+  if test "x$func_stripname_result" != x ; then
+    func_relative_path_result=${func_relative_path_result}/${func_stripname_result}
+  fi
+
+  # Normalisation. If bindir is libdir, return empty string,
+  # else relative path ending with a slash; either way, target
+  # file name can be directly appended.
+  if test ! -z "$func_relative_path_result"; then
+    func_stripname './' '' "$func_relative_path_result/"
+    func_relative_path_result=$func_stripname_result
+  fi
+}
+
+# The name of this program:
+func_dirname_and_basename "$progpath"
+progname=$func_basename_result
+
+# Make sure we have an absolute path for reexecution:
+case $progpath in
+  [\\/]*|[A-Za-z]:\\*) ;;
+  *[\\/]*)
+     progdir=$func_dirname_result
+     progdir=`cd "$progdir" && pwd`
+     progpath="$progdir/$progname"
+     ;;
+  *)
+     save_IFS="$IFS"
+     IFS=${PATH_SEPARATOR-:}
+     for progdir in $PATH; do
+       IFS="$save_IFS"
+       test -x "$progdir/$progname" && break
+     done
+     IFS="$save_IFS"
+     test -n "$progdir" || progdir=`pwd`
+     progpath="$progdir/$progname"
+     ;;
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed="${SED}"' -e 1s/^X//'
+sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s,[].[^$\\*\/],\\&,g'
+
+# Sed substitution that converts a w32 file name or path
+# which contains forward slashes, into one that contains
+# (escaped) backslashes.  A very naive implementation.
+lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+
+# Re-`\' parameter expansions in output of double_quote_subst that were
+# `\'-ed in input to the same.  If an odd number of `\' preceded a '$'
+# in input to double_quote_subst, that '$' was protected from expansion.
+# Since each input `\' is now two `\'s, look for any number of runs of
+# four `\'s followed by two `\'s and then a '$'.  `\' that '$'.
+bs='\\'
+bs2='\\\\'
+bs4='\\\\\\\\'
+dollar='\$'
+sed_double_backslash="\
+  s/$bs4/&\\
+/g
+  s/^$bs2$dollar/$bs&/
+  s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g
+  s/\n//g"
+
+# Standard options:
+opt_dry_run=false
+opt_help=false
+opt_quiet=false
+opt_verbose=false
+opt_warning=:
+
+# func_echo arg...
+# Echo program name prefixed message, along with the current mode
+# name if it has been set yet.
+func_echo ()
+{
+    $ECHO "$progname: ${opt_mode+$opt_mode: }$*"
+}
+
+# func_verbose arg...
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+    $opt_verbose && func_echo ${1+"$@"}
+
+    # A bug in bash halts the script if the last line of a function
+    # fails when set -e is in force, so we need another command to
+    # work around that:
+    :
+}
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO "$*"
+}
+
+# func_error arg...
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+    $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2
+}
+
+# func_warning arg...
+# Echo program name prefixed warning message to standard error.
+func_warning ()
+{
+    $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2
+
+    # bash bug again:
+    :
+}
+
+# func_fatal_error arg...
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+    func_error ${1+"$@"}
+    exit $EXIT_FAILURE
+}
+
+# func_fatal_help arg...
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+    func_error ${1+"$@"}
+    func_fatal_error "$help"
+}
+help="Try \`$progname --help' for more information."  ## default
+
+
+# func_grep expression filename
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+    $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_mkdir_p directory-path
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+    my_directory_path="$1"
+    my_dir_list=
+
+    if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then
+
+      # Protect directory names starting with `-'
+      case $my_directory_path in
+        -*) my_directory_path="./$my_directory_path" ;;
+      esac
+
+      # While some portion of DIR does not yet exist...
+      while test ! -d "$my_directory_path"; do
+        # ...make a list in topmost first order.  Use a colon delimited
+	# list incase some portion of path contains whitespace.
+        my_dir_list="$my_directory_path:$my_dir_list"
+
+        # If the last portion added has no slash in it, the list is done
+        case $my_directory_path in */*) ;; *) break ;; esac
+
+        # ...otherwise throw away the child directory and loop
+        my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"`
+      done
+      my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'`
+
+      save_mkdir_p_IFS="$IFS"; IFS=':'
+      for my_dir in $my_dir_list; do
+	IFS="$save_mkdir_p_IFS"
+        # mkdir can fail with a `File exist' error if two processes
+        # try to create one of the directories concurrently.  Don't
+        # stop in that case!
+        $MKDIR "$my_dir" 2>/dev/null || :
+      done
+      IFS="$save_mkdir_p_IFS"
+
+      # Bail out if we (or some other process) failed to create a directory.
+      test -d "$my_directory_path" || \
+        func_fatal_error "Failed to create \`$1'"
+    fi
+}
+
+
+# func_mktempdir [string]
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible.  If
+# given, STRING is the basename for that directory.
+func_mktempdir ()
+{
+    my_template="${TMPDIR-/tmp}/${1-$progname}"
+
+    if test "$opt_dry_run" = ":"; then
+      # Return a directory name, but don't create it in dry-run mode
+      my_tmpdir="${my_template}-$$"
+    else
+
+      # If mktemp works, use that first and foremost
+      my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null`
+
+      if test ! -d "$my_tmpdir"; then
+        # Failing that, at least try and use $RANDOM to avoid a race
+        my_tmpdir="${my_template}-${RANDOM-0}$$"
+
+        save_mktempdir_umask=`umask`
+        umask 0077
+        $MKDIR "$my_tmpdir"
+        umask $save_mktempdir_umask
+      fi
+
+      # If we're not in dry-run mode, bomb out on failure
+      test -d "$my_tmpdir" || \
+        func_fatal_error "cannot create temporary directory \`$my_tmpdir'"
+    fi
+
+    $ECHO "$my_tmpdir"
+}
+
+
+# func_quote_for_eval arg
+# Aesthetically quote ARG to be evaled later.
+# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT
+# is double-quoted, suitable for a subsequent eval, whereas
+# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters
+# which are still active within double quotes backslashified.
+func_quote_for_eval ()
+{
+    case $1 in
+      *[\\\`\"\$]*)
+	func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;;
+      *)
+        func_quote_for_eval_unquoted_result="$1" ;;
+    esac
+
+    case $func_quote_for_eval_unquoted_result in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting, command substitution and and variable
+      # expansion for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+        func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\""
+        ;;
+      *)
+        func_quote_for_eval_result="$func_quote_for_eval_unquoted_result"
+    esac
+}
+
+
+# func_quote_for_expand arg
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+    case $1 in
+      *[\\\`\"]*)
+	my_arg=`$ECHO "$1" | $SED \
+	    -e "$double_quote_subst" -e "$sed_double_backslash"` ;;
+      *)
+        my_arg="$1" ;;
+    esac
+
+    case $my_arg in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting and command substitution for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+        my_arg="\"$my_arg\""
+        ;;
+    esac
+
+    func_quote_for_expand_result="$my_arg"
+}
+
+
+# func_show_eval cmd [fail_exp]
+# Unless opt_silent is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+    my_cmd="$1"
+    my_fail_exp="${2-:}"
+
+    ${opt_silent-false} || {
+      func_quote_for_expand "$my_cmd"
+      eval "func_echo $func_quote_for_expand_result"
+    }
+
+    if ${opt_dry_run-false}; then :; else
+      eval "$my_cmd"
+      my_status=$?
+      if test "$my_status" -eq 0; then :; else
+	eval "(exit $my_status); $my_fail_exp"
+      fi
+    fi
+}
+
+
+# func_show_eval_locale cmd [fail_exp]
+# Unless opt_silent is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.  Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+    my_cmd="$1"
+    my_fail_exp="${2-:}"
+
+    ${opt_silent-false} || {
+      func_quote_for_expand "$my_cmd"
+      eval "func_echo $func_quote_for_expand_result"
+    }
+
+    if ${opt_dry_run-false}; then :; else
+      eval "$lt_user_locale
+	    $my_cmd"
+      my_status=$?
+      eval "$lt_safe_locale"
+      if test "$my_status" -eq 0; then :; else
+	eval "(exit $my_status); $my_fail_exp"
+      fi
+    fi
+}
+
+# func_tr_sh
+# Turn $1 into a string suitable for a shell variable name.
+# Result is stored in $func_tr_sh_result.  All characters
+# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
+# if $1 begins with a digit, a '_' is prepended as well.
+func_tr_sh ()
+{
+  case $1 in
+  [0-9]* | *[!a-zA-Z0-9_]*)
+    func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'`
+    ;;
+  * )
+    func_tr_sh_result=$1
+    ;;
+  esac
+}
+
+
+# func_version
+# Echo version message to standard output and exit.
+func_version ()
+{
+    $opt_debug
+
+    $SED -n '/(C)/!b go
+	:more
+	/\./!{
+	  N
+	  s/\n# / /
+	  b more
+	}
+	:go
+	/^# '$PROGRAM' (GNU /,/# warranty; / {
+        s/^# //
+	s/^# *$//
+        s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/
+        p
+     }' < "$progpath"
+     exit $?
+}
+
+# func_usage
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+    $opt_debug
+
+    $SED -n '/^# Usage:/,/^#  *.*--help/ {
+        s/^# //
+	s/^# *$//
+	s/\$progname/'$progname'/
+	p
+    }' < "$progpath"
+    echo
+    $ECHO "run \`$progname --help | more' for full usage"
+    exit $?
+}
+
+# func_help [NOEXIT]
+# Echo long help message to standard output and exit,
+# unless 'noexit' is passed as argument.
+func_help ()
+{
+    $opt_debug
+
+    $SED -n '/^# Usage:/,/# Report bugs to/ {
+	:print
+        s/^# //
+	s/^# *$//
+	s*\$progname*'$progname'*
+	s*\$host*'"$host"'*
+	s*\$SHELL*'"$SHELL"'*
+	s*\$LTCC*'"$LTCC"'*
+	s*\$LTCFLAGS*'"$LTCFLAGS"'*
+	s*\$LD*'"$LD"'*
+	s/\$with_gnu_ld/'"$with_gnu_ld"'/
+	s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/
+	s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/
+	p
+	d
+     }
+     /^# .* home page:/b print
+     /^# General help using/b print
+     ' < "$progpath"
+    ret=$?
+    if test -z "$1"; then
+      exit $ret
+    fi
+}
+
+# func_missing_arg argname
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+    $opt_debug
+
+    func_error "missing argument for $1."
+    exit_cmd=exit
+}
+
+
+# func_split_short_opt shortopt
+# Set func_split_short_opt_name and func_split_short_opt_arg shell
+# variables after splitting SHORTOPT after the 2nd character.
+func_split_short_opt ()
+{
+    my_sed_short_opt='1s/^\(..\).*$/\1/;q'
+    my_sed_short_rest='1s/^..\(.*\)$/\1/;q'
+
+    func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"`
+    func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"`
+} # func_split_short_opt may be replaced by extended shell implementation
+
+
+# func_split_long_opt longopt
+# Set func_split_long_opt_name and func_split_long_opt_arg shell
+# variables after splitting LONGOPT at the `=' sign.
+func_split_long_opt ()
+{
+    my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q'
+    my_sed_long_arg='1s/^--[^=]*=//'
+
+    func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"`
+    func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"`
+} # func_split_long_opt may be replaced by extended shell implementation
+
+exit_cmd=:
+
+
+
+
+
+magic="%%%MAGIC variable%%%"
+magic_exe="%%%MAGIC EXE variable%%%"
+
+# Global variables.
+nonopt=
+preserve_args=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+extracted_archives=
+extracted_serial=0
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end.  This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+    eval "${1}=\$${1}\${2}"
+} # func_append may be replaced by extended shell implementation
+
+# func_append_quoted var value
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+func_append_quoted ()
+{
+    func_quote_for_eval "${2}"
+    eval "${1}=\$${1}\\ \$func_quote_for_eval_result"
+} # func_append_quoted may be replaced by extended shell implementation
+
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+    func_arith_result=`expr "${@}"`
+} # func_arith may be replaced by extended shell implementation
+
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+    func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len`
+} # func_len may be replaced by extended shell implementation
+
+
+# func_lo2o object
+func_lo2o ()
+{
+    func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"`
+} # func_lo2o may be replaced by extended shell implementation
+
+
+# func_xform libobj-or-source
+func_xform ()
+{
+    func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'`
+} # func_xform may be replaced by extended shell implementation
+
+
+# func_fatal_configuration arg...
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+    func_error ${1+"$@"}
+    func_error "See the $PACKAGE documentation for more information."
+    func_fatal_error "Fatal configuration error."
+}
+
+
+# func_config
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+    re_begincf='^# ### BEGIN LIBTOOL'
+    re_endcf='^# ### END LIBTOOL'
+
+    # Default configuration.
+    $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+    # Now print the configurations for the tags.
+    for tagname in $taglist; do
+      $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+    done
+
+    exit $?
+}
+
+# func_features
+# Display the features supported by this script.
+func_features ()
+{
+    echo "host: $host"
+    if test "$build_libtool_libs" = yes; then
+      echo "enable shared libraries"
+    else
+      echo "disable shared libraries"
+    fi
+    if test "$build_old_libs" = yes; then
+      echo "enable static libraries"
+    else
+      echo "disable static libraries"
+    fi
+
+    exit $?
+}
+
+# func_enable_tag tagname
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag.  We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+  # Global variable:
+  tagname="$1"
+
+  re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+  re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+  sed_extractcf="/$re_begincf/,/$re_endcf/p"
+
+  # Validate tagname.
+  case $tagname in
+    *[!-_A-Za-z0-9,/]*)
+      func_fatal_error "invalid tag name: $tagname"
+      ;;
+  esac
+
+  # Don't test for the "default" C tag, as we know it's
+  # there but not specially marked.
+  case $tagname in
+    CC) ;;
+    *)
+      if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+	taglist="$taglist $tagname"
+
+	# Evaluate the configuration.  Be careful to quote the path
+	# and the sed script, to avoid splitting on whitespace, but
+	# also don't use non-portable quotes within backquotes within
+	# quotes we have to do it in 2 steps:
+	extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+	eval "$extractedcf"
+      else
+	func_error "ignoring unknown tag $tagname"
+      fi
+      ;;
+  esac
+}
+
+# func_check_version_match
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+  if test "$package_revision" != "$macro_revision"; then
+    if test "$VERSION" != "$macro_version"; then
+      if test -z "$macro_version"; then
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+      else
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+      fi
+    else
+      cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+    fi
+
+    exit $EXIT_MISMATCH
+  fi
+}
+
+
+# Shorthand for --mode=foo, only valid as the first argument
+case $1 in
+clean|clea|cle|cl)
+  shift; set dummy --mode clean ${1+"$@"}; shift
+  ;;
+compile|compil|compi|comp|com|co|c)
+  shift; set dummy --mode compile ${1+"$@"}; shift
+  ;;
+execute|execut|execu|exec|exe|ex|e)
+  shift; set dummy --mode execute ${1+"$@"}; shift
+  ;;
+finish|finis|fini|fin|fi|f)
+  shift; set dummy --mode finish ${1+"$@"}; shift
+  ;;
+install|instal|insta|inst|ins|in|i)
+  shift; set dummy --mode install ${1+"$@"}; shift
+  ;;
+link|lin|li|l)
+  shift; set dummy --mode link ${1+"$@"}; shift
+  ;;
+uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+  shift; set dummy --mode uninstall ${1+"$@"}; shift
+  ;;
+esac
+
+
+
+# Option defaults:
+opt_debug=:
+opt_dry_run=false
+opt_config=false
+opt_preserve_dup_deps=false
+opt_features=false
+opt_finish=false
+opt_help=false
+opt_help_all=false
+opt_silent=:
+opt_warning=:
+opt_verbose=:
+opt_silent=false
+opt_verbose=false
+
+
+# Parse options once, thoroughly.  This comes as soon as possible in the
+# script to make things like `--version' happen as quickly as we can.
+{
+  # this just eases exit handling
+  while test $# -gt 0; do
+    opt="$1"
+    shift
+    case $opt in
+      --debug|-x)	opt_debug='set -x'
+			func_echo "enabling shell trace mode"
+			$opt_debug
+			;;
+      --dry-run|--dryrun|-n)
+			opt_dry_run=:
+			;;
+      --config)
+			opt_config=:
+func_config
+			;;
+      --dlopen|-dlopen)
+			optarg="$1"
+			opt_dlopen="${opt_dlopen+$opt_dlopen
+}$optarg"
+			shift
+			;;
+      --preserve-dup-deps)
+			opt_preserve_dup_deps=:
+			;;
+      --features)
+			opt_features=:
+func_features
+			;;
+      --finish)
+			opt_finish=:
+set dummy --mode finish ${1+"$@"}; shift
+			;;
+      --help)
+			opt_help=:
+			;;
+      --help-all)
+			opt_help_all=:
+opt_help=': help-all'
+			;;
+      --mode)
+			test $# = 0 && func_missing_arg $opt && break
+			optarg="$1"
+			opt_mode="$optarg"
+case $optarg in
+  # Valid mode arguments:
+  clean|compile|execute|finish|install|link|relink|uninstall) ;;
+
+  # Catch anything else as an error
+  *) func_error "invalid argument for $opt"
+     exit_cmd=exit
+     break
+     ;;
+esac
+			shift
+			;;
+      --no-silent|--no-quiet)
+			opt_silent=false
+func_append preserve_args " $opt"
+			;;
+      --no-warning|--no-warn)
+			opt_warning=false
+func_append preserve_args " $opt"
+			;;
+      --no-verbose)
+			opt_verbose=false
+func_append preserve_args " $opt"
+			;;
+      --silent|--quiet)
+			opt_silent=:
+func_append preserve_args " $opt"
+        opt_verbose=false
+			;;
+      --verbose|-v)
+			opt_verbose=:
+func_append preserve_args " $opt"
+opt_silent=false
+			;;
+      --tag)
+			test $# = 0 && func_missing_arg $opt && break
+			optarg="$1"
+			opt_tag="$optarg"
+func_append preserve_args " $opt $optarg"
+func_enable_tag "$optarg"
+			shift
+			;;
+
+      -\?|-h)		func_usage				;;
+      --help)		func_help				;;
+      --version)	func_version				;;
+
+      # Separate optargs to long options:
+      --*=*)
+			func_split_long_opt "$opt"
+			set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"}
+			shift
+			;;
+
+      # Separate non-argument short options:
+      -\?*|-h*|-n*|-v*)
+			func_split_short_opt "$opt"
+			set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"}
+			shift
+			;;
+
+      --)		break					;;
+      -*)		func_fatal_help "unrecognized option \`$opt'" ;;
+      *)		set dummy "$opt" ${1+"$@"};	shift; break  ;;
+    esac
+  done
+
+  # Validate options:
+
+  # save first non-option argument
+  if test "$#" -gt 0; then
+    nonopt="$opt"
+    shift
+  fi
+
+  # preserve --debug
+  test "$opt_debug" = : || func_append preserve_args " --debug"
+
+  case $host in
+    *cygwin* | *mingw* | *pw32* | *cegcc*)
+      # don't eliminate duplications in $postdeps and $predeps
+      opt_duplicate_compiler_generated_deps=:
+      ;;
+    *)
+      opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
+      ;;
+  esac
+
+  $opt_help || {
+    # Sanity checks first:
+    func_check_version_match
+
+    if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+      func_fatal_configuration "not configured to build any kind of library"
+    fi
+
+    # Darwin sucks
+    eval std_shrext=\"$shrext_cmds\"
+
+    # Only execute mode is allowed to have -dlopen flags.
+    if test -n "$opt_dlopen" && test "$opt_mode" != execute; then
+      func_error "unrecognized option \`-dlopen'"
+      $ECHO "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    # Change the help message to a mode-specific one.
+    generic_help="$help"
+    help="Try \`$progname --help --mode=$opt_mode' for more information."
+  }
+
+
+  # Bail if the options were screwed
+  $exit_cmd $EXIT_FAILURE
+}
+
+
+
+
+## ----------- ##
+##    Main.    ##
+## ----------- ##
+
+# func_lalib_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+    test -f "$1" &&
+      $SED -e 4q "$1" 2>/dev/null \
+        | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs.  To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway.  Works if `file' does not exist.
+func_lalib_unsafe_p ()
+{
+    lalib_p=no
+    if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+	for lalib_p_l in 1 2 3 4
+	do
+	    read lalib_p_line
+	    case "$lalib_p_line" in
+		\#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+	    esac
+	done
+	exec 0<&5 5<&-
+    fi
+    test "$lalib_p" = yes
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+    func_lalib_p "$1"
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+    func_ltwrapper_exec_suffix=
+    case $1 in
+    *.exe) ;;
+    *) func_ltwrapper_exec_suffix=.exe ;;
+    esac
+    $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+    func_dirname_and_basename "$1" "" "."
+    func_stripname '' '.exe' "$func_basename_result"
+    func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+    func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+    $opt_debug
+    save_ifs=$IFS; IFS='~'
+    for cmd in $1; do
+      IFS=$save_ifs
+      eval cmd=\"$cmd\"
+      func_show_eval "$cmd" "${2-:}"
+    done
+    IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)!  Also, sourcing
+# `FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+    $opt_debug
+    case $1 in
+    */* | *\\*)	. "$1" ;;
+    *)		. "./$1" ;;
+    esac
+}
+
+
+# func_resolve_sysroot PATH
+# Replace a leading = in PATH with a sysroot.  Store the result into
+# func_resolve_sysroot_result
+func_resolve_sysroot ()
+{
+  func_resolve_sysroot_result=$1
+  case $func_resolve_sysroot_result in
+  =*)
+    func_stripname '=' '' "$func_resolve_sysroot_result"
+    func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
+    ;;
+  esac
+}
+
+# func_replace_sysroot PATH
+# If PATH begins with the sysroot, replace it with = and
+# store the result into func_replace_sysroot_result.
+func_replace_sysroot ()
+{
+  case "$lt_sysroot:$1" in
+  ?*:"$lt_sysroot"*)
+    func_stripname "$lt_sysroot" '' "$1"
+    func_replace_sysroot_result="=$func_stripname_result"
+    ;;
+  *)
+    # Including no sysroot.
+    func_replace_sysroot_result=$1
+    ;;
+  esac
+}
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+    $opt_debug
+    if test -n "$available_tags" && test -z "$tagname"; then
+      CC_quoted=
+      for arg in $CC; do
+	func_append_quoted CC_quoted "$arg"
+      done
+      CC_expanded=`func_echo_all $CC`
+      CC_quoted_expanded=`func_echo_all $CC_quoted`
+      case $@ in
+      # Blanks in the command may have been stripped by the calling shell,
+      # but not from the CC environment variable when configure was run.
+      " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+      " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
+      # Blanks at the start of $base_compile will cause this to fail
+      # if we don't check for them as well.
+      *)
+	for z in $available_tags; do
+	  if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+	    # Evaluate the configuration.
+	    eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+	    CC_quoted=
+	    for arg in $CC; do
+	      # Double-quote args containing other shell metacharacters.
+	      func_append_quoted CC_quoted "$arg"
+	    done
+	    CC_expanded=`func_echo_all $CC`
+	    CC_quoted_expanded=`func_echo_all $CC_quoted`
+	    case "$@ " in
+	    " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+	    " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
+	      # The compiler in the base compile command matches
+	      # the one in the tagged configuration.
+	      # Assume this is the tagged configuration we want.
+	      tagname=$z
+	      break
+	      ;;
+	    esac
+	  fi
+	done
+	# If $tagname still isn't set, then no tagged configuration
+	# was found and let the user know that the "--tag" command
+	# line option must be used.
+	if test -z "$tagname"; then
+	  func_echo "unable to infer tagged configuration"
+	  func_fatal_error "specify a tag with \`--tag'"
+#	else
+#	  func_verbose "using $tagname tagged configuration"
+	fi
+	;;
+      esac
+    fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+    write_libobj=${1}
+    if test "$build_libtool_libs" = yes; then
+      write_lobj=\'${2}\'
+    else
+      write_lobj=none
+    fi
+
+    if test "$build_old_libs" = yes; then
+      write_oldobj=\'${3}\'
+    else
+      write_oldobj=none
+    fi
+
+    $opt_dry_run || {
+      cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+      $MV "${write_libobj}T" "${write_libobj}"
+    }
+}
+
+
+##################################################
+# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
+##################################################
+
+# func_convert_core_file_wine_to_w32 ARG
+# Helper function used by file name conversion functions when $build is *nix,
+# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH.
+#
+# ARG is the $build file name to be converted to w32 format.
+# Result is available in $func_convert_core_file_wine_to_w32_result, and will
+# be empty on error (or when ARG is empty)
+func_convert_core_file_wine_to_w32 ()
+{
+  $opt_debug
+  func_convert_core_file_wine_to_w32_result="$1"
+  if test -n "$1"; then
+    # Unfortunately, winepath does not exit with a non-zero error code, so we
+    # are forced to check the contents of stdout. On the other hand, if the
+    # command is not found, the shell will set an exit code of 127 and print
+    # *an error message* to stdout. So we must check for both error code of
+    # zero AND non-empty stdout, which explains the odd construction:
+    func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
+    if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then
+      func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
+        $SED -e "$lt_sed_naive_backslashify"`
+    else
+      func_convert_core_file_wine_to_w32_result=
+    fi
+  fi
+}
+# end: func_convert_core_file_wine_to_w32
+
+
+# func_convert_core_path_wine_to_w32 ARG
+# Helper function used by path conversion functions when $build is *nix, and
+# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
+# configured wine environment available, with the winepath program in $build's
+# $PATH. Assumes ARG has no leading or trailing path separator characters.
+#
+# ARG is path to be converted from $build format to win32.
+# Result is available in $func_convert_core_path_wine_to_w32_result.
+# Unconvertible file (directory) names in ARG are skipped; if no directory names
+# are convertible, then the result may be empty.
+func_convert_core_path_wine_to_w32 ()
+{
+  $opt_debug
+  # unfortunately, winepath doesn't convert paths, only file names
+  func_convert_core_path_wine_to_w32_result=""
+  if test -n "$1"; then
+    oldIFS=$IFS
+    IFS=:
+    for func_convert_core_path_wine_to_w32_f in $1; do
+      IFS=$oldIFS
+      func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
+      if test -n "$func_convert_core_file_wine_to_w32_result" ; then
+        if test -z "$func_convert_core_path_wine_to_w32_result"; then
+          func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result"
+        else
+          func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
+        fi
+      fi
+    done
+    IFS=$oldIFS
+  fi
+}
+# end: func_convert_core_path_wine_to_w32
+
+
+# func_cygpath ARGS...
+# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
+# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
+# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
+# (2), returns the Cygwin file name or path in func_cygpath_result (input
+# file name or path is assumed to be in w32 format, as previously converted
+# from $build's *nix or MSYS format). In case (3), returns the w32 file name
+# or path in func_cygpath_result (input file name or path is assumed to be in
+# Cygwin format). Returns an empty string on error.
+#
+# ARGS are passed to cygpath, with the last one being the file name or path to
+# be converted.
+#
+# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
+# environment variable; do not put it in $PATH.
+func_cygpath ()
+{
+  $opt_debug
+  if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
+    func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
+    if test "$?" -ne 0; then
+      # on failure, ensure result is empty
+      func_cygpath_result=
+    fi
+  else
+    func_cygpath_result=
+    func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'"
+  fi
+}
+#end: func_cygpath
+
+
+# func_convert_core_msys_to_w32 ARG
+# Convert file name or path ARG from MSYS format to w32 format.  Return
+# result in func_convert_core_msys_to_w32_result.
+func_convert_core_msys_to_w32 ()
+{
+  $opt_debug
+  # awkward: cmd appends spaces to result
+  func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
+    $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"`
+}
+#end: func_convert_core_msys_to_w32
+
+
+# func_convert_file_check ARG1 ARG2
+# Verify that ARG1 (a file name in $build format) was converted to $host
+# format in ARG2. Otherwise, emit an error message, but continue (resetting
+# func_to_host_file_result to ARG1).
+func_convert_file_check ()
+{
+  $opt_debug
+  if test -z "$2" && test -n "$1" ; then
+    func_error "Could not determine host file name corresponding to"
+    func_error "  \`$1'"
+    func_error "Continuing, but uninstalled executables may not work."
+    # Fallback:
+    func_to_host_file_result="$1"
+  fi
+}
+# end func_convert_file_check
+
+
+# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
+# Verify that FROM_PATH (a path in $build format) was converted to $host
+# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
+# func_to_host_file_result to a simplistic fallback value (see below).
+func_convert_path_check ()
+{
+  $opt_debug
+  if test -z "$4" && test -n "$3"; then
+    func_error "Could not determine the host path corresponding to"
+    func_error "  \`$3'"
+    func_error "Continuing, but uninstalled executables may not work."
+    # Fallback.  This is a deliberately simplistic "conversion" and
+    # should not be "improved".  See libtool.info.
+    if test "x$1" != "x$2"; then
+      lt_replace_pathsep_chars="s|$1|$2|g"
+      func_to_host_path_result=`echo "$3" |
+        $SED -e "$lt_replace_pathsep_chars"`
+    else
+      func_to_host_path_result="$3"
+    fi
+  fi
+}
+# end func_convert_path_check
+
+
+# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
+# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
+# and appending REPL if ORIG matches BACKPAT.
+func_convert_path_front_back_pathsep ()
+{
+  $opt_debug
+  case $4 in
+  $1 ) func_to_host_path_result="$3$func_to_host_path_result"
+    ;;
+  esac
+  case $4 in
+  $2 ) func_append func_to_host_path_result "$3"
+    ;;
+  esac
+}
+# end func_convert_path_front_back_pathsep
+
+
+##################################################
+# $build to $host FILE NAME CONVERSION FUNCTIONS #
+##################################################
+# invoked via `$to_host_file_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# Result will be available in $func_to_host_file_result.
+
+
+# func_to_host_file ARG
+# Converts the file name ARG from $build format to $host format. Return result
+# in func_to_host_file_result.
+func_to_host_file ()
+{
+  $opt_debug
+  $to_host_file_cmd "$1"
+}
+# end func_to_host_file
+
+
+# func_to_tool_file ARG LAZY
+# converts the file name ARG from $build format to toolchain format. Return
+# result in func_to_tool_file_result.  If the conversion in use is listed
+# in (the comma separated) LAZY, no conversion takes place.
+func_to_tool_file ()
+{
+  $opt_debug
+  case ,$2, in
+    *,"$to_tool_file_cmd",*)
+      func_to_tool_file_result=$1
+      ;;
+    *)
+      $to_tool_file_cmd "$1"
+      func_to_tool_file_result=$func_to_host_file_result
+      ;;
+  esac
+}
+# end func_to_tool_file
+
+
+# func_convert_file_noop ARG
+# Copy ARG to func_to_host_file_result.
+func_convert_file_noop ()
+{
+  func_to_host_file_result="$1"
+}
+# end func_convert_file_noop
+
+
+# func_convert_file_msys_to_w32 ARG
+# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper.  Returns result in
+# func_to_host_file_result.
+func_convert_file_msys_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_msys_to_w32 "$1"
+    func_to_host_file_result="$func_convert_core_msys_to_w32_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_w32
+
+
+# func_convert_file_cygwin_to_w32 ARG
+# Convert file name ARG from Cygwin to w32 format.  Returns result in
+# func_to_host_file_result.
+func_convert_file_cygwin_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
+    # LT_CYGPATH in this case.
+    func_to_host_file_result=`cygpath -m "$1"`
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_cygwin_to_w32
+
+
+# func_convert_file_nix_to_w32 ARG
+# Convert file name ARG from *nix to w32 format.  Requires a wine environment
+# and a working winepath. Returns result in func_to_host_file_result.
+func_convert_file_nix_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_file_wine_to_w32 "$1"
+    func_to_host_file_result="$func_convert_core_file_wine_to_w32_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_w32
+
+
+# func_convert_file_msys_to_cygwin ARG
+# Convert file name ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_file_msys_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_msys_to_w32 "$1"
+    func_cygpath -u "$func_convert_core_msys_to_w32_result"
+    func_to_host_file_result="$func_cygpath_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_cygwin
+
+
+# func_convert_file_nix_to_cygwin ARG
+# Convert file name ARG from *nix to Cygwin format.  Requires Cygwin installed
+# in a wine environment, working winepath, and LT_CYGPATH set.  Returns result
+# in func_to_host_file_result.
+func_convert_file_nix_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
+    func_convert_core_file_wine_to_w32 "$1"
+    func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
+    func_to_host_file_result="$func_cygpath_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_cygwin
+
+
+#############################################
+# $build to $host PATH CONVERSION FUNCTIONS #
+#############################################
+# invoked via `$to_host_path_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# The result will be available in $func_to_host_path_result.
+#
+# Path separators are also converted from $build format to $host format.  If
+# ARG begins or ends with a path separator character, it is preserved (but
+# converted to $host format) on output.
+#
+# All path conversion functions are named using the following convention:
+#   file name conversion function    : func_convert_file_X_to_Y ()
+#   path conversion function         : func_convert_path_X_to_Y ()
+# where, for any given $build/$host combination the 'X_to_Y' value is the
+# same.  If conversion functions are added for new $build/$host combinations,
+# the two new functions must follow this pattern, or func_init_to_host_path_cmd
+# will break.
+
+
+# func_init_to_host_path_cmd
+# Ensures that function "pointer" variable $to_host_path_cmd is set to the
+# appropriate value, based on the value of $to_host_file_cmd.
+to_host_path_cmd=
+func_init_to_host_path_cmd ()
+{
+  $opt_debug
+  if test -z "$to_host_path_cmd"; then
+    func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
+    to_host_path_cmd="func_convert_path_${func_stripname_result}"
+  fi
+}
+
+
+# func_to_host_path ARG
+# Converts the path ARG from $build format to $host format. Return result
+# in func_to_host_path_result.
+func_to_host_path ()
+{
+  $opt_debug
+  func_init_to_host_path_cmd
+  $to_host_path_cmd "$1"
+}
+# end func_to_host_path
+
+
+# func_convert_path_noop ARG
+# Copy ARG to func_to_host_path_result.
+func_convert_path_noop ()
+{
+  func_to_host_path_result="$1"
+}
+# end func_convert_path_noop
+
+
+# func_convert_path_msys_to_w32 ARG
+# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper.  Returns result in
+# func_to_host_path_result.
+func_convert_path_msys_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # Remove leading and trailing path separator characters from ARG.  MSYS
+    # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
+    # and winepath ignores them completely.
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+    func_to_host_path_result="$func_convert_core_msys_to_w32_result"
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_msys_to_w32
+
+
+# func_convert_path_cygwin_to_w32 ARG
+# Convert path ARG from Cygwin to w32 format.  Returns result in
+# func_to_host_file_result.
+func_convert_path_cygwin_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_cygwin_to_w32
+
+
+# func_convert_path_nix_to_w32 ARG
+# Convert path ARG from *nix to w32 format.  Requires a wine environment and
+# a working winepath.  Returns result in func_to_host_file_result.
+func_convert_path_nix_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+    func_to_host_path_result="$func_convert_core_path_wine_to_w32_result"
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_nix_to_w32
+
+
+# func_convert_path_msys_to_cygwin ARG
+# Convert path ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_path_msys_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+    func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
+    func_to_host_path_result="$func_cygpath_result"
+    func_convert_path_check : : \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+  fi
+}
+# end func_convert_path_msys_to_cygwin
+
+
+# func_convert_path_nix_to_cygwin ARG
+# Convert path ARG from *nix to Cygwin format.  Requires Cygwin installed in a
+# a wine environment, working winepath, and LT_CYGPATH set.  Returns result in
+# func_to_host_file_result.
+func_convert_path_nix_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # Remove leading and trailing path separator characters from
+    # ARG. msys behavior is inconsistent here, cygpath turns them
+    # into '.;' and ';.', and winepath ignores them completely.
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+    func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
+    func_to_host_path_result="$func_cygpath_result"
+    func_convert_path_check : : \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+  fi
+}
+# end func_convert_path_nix_to_cygwin
+
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+    $opt_debug
+    # Get the compilation command and the source file.
+    base_compile=
+    srcfile="$nonopt"  #  always keep a non-empty value in "srcfile"
+    suppress_opt=yes
+    suppress_output=
+    arg_mode=normal
+    libobj=
+    later=
+    pie_flag=
+
+    for arg
+    do
+      case $arg_mode in
+      arg  )
+	# do not "continue".  Instead, add this to base_compile
+	lastarg="$arg"
+	arg_mode=normal
+	;;
+
+      target )
+	libobj="$arg"
+	arg_mode=normal
+	continue
+	;;
+
+      normal )
+	# Accept any command-line options.
+	case $arg in
+	-o)
+	  test -n "$libobj" && \
+	    func_fatal_error "you cannot specify \`-o' more than once"
+	  arg_mode=target
+	  continue
+	  ;;
+
+	-pie | -fpie | -fPIE)
+          func_append pie_flag " $arg"
+	  continue
+	  ;;
+
+	-shared | -static | -prefer-pic | -prefer-non-pic)
+	  func_append later " $arg"
+	  continue
+	  ;;
+
+	-no-suppress)
+	  suppress_opt=no
+	  continue
+	  ;;
+
+	-Xcompiler)
+	  arg_mode=arg  #  the next one goes into the "base_compile" arg list
+	  continue      #  The current "srcfile" will either be retained or
+	  ;;            #  replaced later.  I would guess that would be a bug.
+
+	-Wc,*)
+	  func_stripname '-Wc,' '' "$arg"
+	  args=$func_stripname_result
+	  lastarg=
+	  save_ifs="$IFS"; IFS=','
+	  for arg in $args; do
+	    IFS="$save_ifs"
+	    func_append_quoted lastarg "$arg"
+	  done
+	  IFS="$save_ifs"
+	  func_stripname ' ' '' "$lastarg"
+	  lastarg=$func_stripname_result
+
+	  # Add the arguments to base_compile.
+	  func_append base_compile " $lastarg"
+	  continue
+	  ;;
+
+	*)
+	  # Accept the current argument as the source file.
+	  # The previous "srcfile" becomes the current argument.
+	  #
+	  lastarg="$srcfile"
+	  srcfile="$arg"
+	  ;;
+	esac  #  case $arg
+	;;
+      esac    #  case $arg_mode
+
+      # Aesthetically quote the previous argument.
+      func_append_quoted base_compile "$lastarg"
+    done # for arg
+
+    case $arg_mode in
+    arg)
+      func_fatal_error "you must specify an argument for -Xcompile"
+      ;;
+    target)
+      func_fatal_error "you must specify a target with \`-o'"
+      ;;
+    *)
+      # Get the name of the library object.
+      test -z "$libobj" && {
+	func_basename "$srcfile"
+	libobj="$func_basename_result"
+      }
+      ;;
+    esac
+
+    # Recognize several different file suffixes.
+    # If the user specifies -o file.o, it is replaced with file.lo
+    case $libobj in
+    *.[cCFSifmso] | \
+    *.ada | *.adb | *.ads | *.asm | \
+    *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+    *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
+      func_xform "$libobj"
+      libobj=$func_xform_result
+      ;;
+    esac
+
+    case $libobj in
+    *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+    *)
+      func_fatal_error "cannot determine name of library object from \`$libobj'"
+      ;;
+    esac
+
+    func_infer_tag $base_compile
+
+    for arg in $later; do
+      case $arg in
+      -shared)
+	test "$build_libtool_libs" != yes && \
+	  func_fatal_configuration "can not build a shared library"
+	build_old_libs=no
+	continue
+	;;
+
+      -static)
+	build_libtool_libs=no
+	build_old_libs=yes
+	continue
+	;;
+
+      -prefer-pic)
+	pic_mode=yes
+	continue
+	;;
+
+      -prefer-non-pic)
+	pic_mode=no
+	continue
+	;;
+      esac
+    done
+
+    func_quote_for_eval "$libobj"
+    test "X$libobj" != "X$func_quote_for_eval_result" \
+      && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"'	 &()|`$[]' \
+      && func_warning "libobj name \`$libobj' may not contain shell special characters."
+    func_dirname_and_basename "$obj" "/" ""
+    objname="$func_basename_result"
+    xdir="$func_dirname_result"
+    lobj=${xdir}$objdir/$objname
+
+    test -z "$base_compile" && \
+      func_fatal_help "you must specify a compilation command"
+
+    # Delete any leftover library objects.
+    if test "$build_old_libs" = yes; then
+      removelist="$obj $lobj $libobj ${libobj}T"
+    else
+      removelist="$lobj $libobj ${libobj}T"
+    fi
+
+    # On Cygwin there's no "real" PIC flag so we must build both object types
+    case $host_os in
+    cygwin* | mingw* | pw32* | os2* | cegcc*)
+      pic_mode=default
+      ;;
+    esac
+    if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+      # non-PIC code in shared libraries is not supported
+      pic_mode=default
+    fi
+
+    # Calculate the filename of the output object if compiler does
+    # not support -o with -c
+    if test "$compiler_c_o" = no; then
+      output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext}
+      lockfile="$output_obj.lock"
+    else
+      output_obj=
+      need_locks=no
+      lockfile=
+    fi
+
+    # Lock this critical section if it is needed
+    # We use this script file to make the link, it avoids creating a new file
+    if test "$need_locks" = yes; then
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+	func_echo "Waiting for $lockfile to be removed"
+	sleep 2
+      done
+    elif test "$need_locks" = warn; then
+      if test -f "$lockfile"; then
+	$ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+      func_append removelist " $output_obj"
+      $ECHO "$srcfile" > "$lockfile"
+    fi
+
+    $opt_dry_run || $RM $removelist
+    func_append removelist " $lockfile"
+    trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+    func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
+    srcfile=$func_to_tool_file_result
+    func_quote_for_eval "$srcfile"
+    qsrcfile=$func_quote_for_eval_result
+
+    # Only build a PIC object if we are building libtool libraries.
+    if test "$build_libtool_libs" = yes; then
+      # Without this assignment, base_compile gets emptied.
+      fbsd_hideous_sh_bug=$base_compile
+
+      if test "$pic_mode" != no; then
+	command="$base_compile $qsrcfile $pic_flag"
+      else
+	# Don't build PIC code
+	command="$base_compile $qsrcfile"
+      fi
+
+      func_mkdir_p "$xdir$objdir"
+
+      if test -z "$output_obj"; then
+	# Place PIC objects in $objdir
+	func_append command " -o $lobj"
+      fi
+
+      func_show_eval_locale "$command"	\
+          'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+      if test "$need_locks" = warn &&
+	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+	$ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed, then go on to compile the next one
+      if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+	func_show_eval '$MV "$output_obj" "$lobj"' \
+	  'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+
+      # Allow error messages only from the first compilation.
+      if test "$suppress_opt" = yes; then
+	suppress_output=' >/dev/null 2>&1'
+      fi
+    fi
+
+    # Only build a position-dependent object if we build old libraries.
+    if test "$build_old_libs" = yes; then
+      if test "$pic_mode" != yes; then
+	# Don't build PIC code
+	command="$base_compile $qsrcfile$pie_flag"
+      else
+	command="$base_compile $qsrcfile $pic_flag"
+      fi
+      if test "$compiler_c_o" = yes; then
+	func_append command " -o $obj"
+      fi
+
+      # Suppress compiler output if we already did a PIC compilation.
+      func_append command "$suppress_output"
+      func_show_eval_locale "$command" \
+        '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+      if test "$need_locks" = warn &&
+	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+	$ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed
+      if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+	func_show_eval '$MV "$output_obj" "$obj"' \
+	  'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+    fi
+
+    $opt_dry_run || {
+      func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+      # Unlock the critical section if it was locked
+      if test "$need_locks" != no; then
+	removelist=$lockfile
+        $RM "$lockfile"
+      fi
+    }
+
+    exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+  test "$opt_mode" = compile && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+    # We need to display help for each of the modes.
+    case $opt_mode in
+      "")
+        # Generic help is extracted from the usage comments
+        # at the start of this file.
+        func_help
+        ;;
+
+      clean)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      compile)
+      $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
+  -no-suppress      do not suppress compiler output for multiple passes
+  -prefer-pic       try to build PIC objects only
+  -prefer-non-pic   try to build non-PIC objects only
+  -shared           do not build a \`.o' file suitable for static linking
+  -static           only build a \`.o' file suitable for static linking
+  -Wc,FLAG          pass FLAG directly to the compiler
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+        ;;
+
+      execute)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+  -dlopen FILE      add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+        ;;
+
+      finish)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges.  Use
+the \`--dry-run' option if you just want to see what would be executed."
+        ;;
+
+      install)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command.  The first component should be
+either the \`install' or \`cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+  -inst-prefix-dir PREFIX-DIR  Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+        ;;
+
+      link)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+  -all-static       do not do any dynamic linking at all
+  -avoid-version    do not add a version suffix if possible
+  -bindir BINDIR    specify path to binaries directory (for systems where
+                    libraries must be found in the PATH setting at runtime)
+  -dlopen FILE      \`-dlpreopen' FILE if it cannot be dlopened at runtime
+  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
+  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+  -export-symbols SYMFILE
+                    try to export only the symbols listed in SYMFILE
+  -export-symbols-regex REGEX
+                    try to export only the symbols matching REGEX
+  -LLIBDIR          search LIBDIR for required installed libraries
+  -lNAME            OUTPUT-FILE requires the installed library libNAME
+  -module           build a library that can dlopened
+  -no-fast-install  disable the fast-install mode
+  -no-install       link a not-installable executable
+  -no-undefined     declare that a library does not refer to external symbols
+  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
+  -objectlist FILE  Use a list of object files found in FILE to specify objects
+  -precious-files-regex REGEX
+                    don't remove output files matching REGEX
+  -release RELEASE  specify package release information
+  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
+  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
+  -shared           only do dynamic linking of libtool libraries
+  -shrext SUFFIX    override the standard shared library file extension
+  -static           do not do any dynamic linking of uninstalled libtool libraries
+  -static-libtool-libs
+                    do not do any dynamic linking of libtool libraries
+  -version-info CURRENT[:REVISION[:AGE]]
+                    specify library version info [each variable defaults to 0]
+  -weak LIBNAME     declare that the target provides the LIBNAME interface
+  -Wc,FLAG
+  -Xcompiler FLAG   pass linker-specific FLAG directly to the compiler
+  -Wl,FLAG
+  -Xlinker FLAG     pass linker-specific FLAG directly to the linker
+  -XCClinker FLAG   pass link-specific FLAG to the compiler driver (CC)
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename.  Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+        ;;
+
+      uninstall)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      *)
+        func_fatal_help "invalid operation mode \`$opt_mode'"
+        ;;
+    esac
+
+    echo
+    $ECHO "Try \`$progname --help' for more information about other modes."
+}
+
+# Now that we've collected a possible --mode arg, show help if necessary
+if $opt_help; then
+  if test "$opt_help" = :; then
+    func_mode_help
+  else
+    {
+      func_help noexit
+      for opt_mode in compile link execute install finish uninstall clean; do
+	func_mode_help
+      done
+    } | sed -n '1p; 2,$s/^Usage:/  or: /p'
+    {
+      func_help noexit
+      for opt_mode in compile link execute install finish uninstall clean; do
+	echo
+	func_mode_help
+      done
+    } |
+    sed '1d
+      /^When reporting/,/^Report/{
+	H
+	d
+      }
+      $x
+      /information about other modes/d
+      /more detailed .*MODE/d
+      s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
+  fi
+  exit $?
+fi
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+    $opt_debug
+    # The first argument is the command name.
+    cmd="$nonopt"
+    test -z "$cmd" && \
+      func_fatal_help "you must specify a COMMAND"
+
+    # Handle -dlopen flags immediately.
+    for file in $opt_dlopen; do
+      test -f "$file" \
+	|| func_fatal_help "\`$file' is not a file"
+
+      dir=
+      case $file in
+      *.la)
+	func_resolve_sysroot "$file"
+	file=$func_resolve_sysroot_result
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$file" \
+	  || func_fatal_help "\`$lib' is not a valid libtool archive"
+
+	# Read the libtool library.
+	dlname=
+	library_names=
+	func_source "$file"
+
+	# Skip this library if it cannot be dlopened.
+	if test -z "$dlname"; then
+	  # Warn if it was a shared library.
+	  test -n "$library_names" && \
+	    func_warning "\`$file' was not linked with \`-export-dynamic'"
+	  continue
+	fi
+
+	func_dirname "$file" "" "."
+	dir="$func_dirname_result"
+
+	if test -f "$dir/$objdir/$dlname"; then
+	  func_append dir "/$objdir"
+	else
+	  if test ! -f "$dir/$dlname"; then
+	    func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'"
+	  fi
+	fi
+	;;
+
+      *.lo)
+	# Just add the directory containing the .lo file.
+	func_dirname "$file" "" "."
+	dir="$func_dirname_result"
+	;;
+
+      *)
+	func_warning "\`-dlopen' is ignored for non-libtool libraries and objects"
+	continue
+	;;
+      esac
+
+      # Get the absolute pathname.
+      absdir=`cd "$dir" && pwd`
+      test -n "$absdir" && dir="$absdir"
+
+      # Now add the directory to shlibpath_var.
+      if eval "test -z \"\$$shlibpath_var\""; then
+	eval "$shlibpath_var=\"\$dir\""
+      else
+	eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+      fi
+    done
+
+    # This variable tells wrapper scripts just to set shlibpath_var
+    # rather than running their programs.
+    libtool_execute_magic="$magic"
+
+    # Check if any of the arguments is a wrapper script.
+    args=
+    for file
+    do
+      case $file in
+      -* | *.la | *.lo ) ;;
+      *)
+	# Do a test to see if this is really a libtool program.
+	if func_ltwrapper_script_p "$file"; then
+	  func_source "$file"
+	  # Transform arg to wrapped name.
+	  file="$progdir/$program"
+	elif func_ltwrapper_executable_p "$file"; then
+	  func_ltwrapper_scriptname "$file"
+	  func_source "$func_ltwrapper_scriptname_result"
+	  # Transform arg to wrapped name.
+	  file="$progdir/$program"
+	fi
+	;;
+      esac
+      # Quote arguments (to preserve shell metacharacters).
+      func_append_quoted args "$file"
+    done
+
+    if test "X$opt_dry_run" = Xfalse; then
+      if test -n "$shlibpath_var"; then
+	# Export the shlibpath_var.
+	eval "export $shlibpath_var"
+      fi
+
+      # Restore saved environment variables
+      for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+      do
+	eval "if test \"\${save_$lt_var+set}\" = set; then
+                $lt_var=\$save_$lt_var; export $lt_var
+	      else
+		$lt_unset $lt_var
+	      fi"
+      done
+
+      # Now prepare to actually exec the command.
+      exec_cmd="\$cmd$args"
+    else
+      # Display what would be done.
+      if test -n "$shlibpath_var"; then
+	eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+	echo "export $shlibpath_var"
+      fi
+      $ECHO "$cmd$args"
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test "$opt_mode" = execute && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+    $opt_debug
+    libs=
+    libdirs=
+    admincmds=
+
+    for opt in "$nonopt" ${1+"$@"}
+    do
+      if test -d "$opt"; then
+	func_append libdirs " $opt"
+
+      elif test -f "$opt"; then
+	if func_lalib_unsafe_p "$opt"; then
+	  func_append libs " $opt"
+	else
+	  func_warning "\`$opt' is not a valid libtool archive"
+	fi
+
+      else
+	func_fatal_error "invalid argument \`$opt'"
+      fi
+    done
+
+    if test -n "$libs"; then
+      if test -n "$lt_sysroot"; then
+        sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
+        sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
+      else
+        sysroot_cmd=
+      fi
+
+      # Remove sysroot references
+      if $opt_dry_run; then
+        for lib in $libs; do
+          echo "removing references to $lt_sysroot and \`=' prefixes from $lib"
+        done
+      else
+        tmpdir=`func_mktempdir`
+        for lib in $libs; do
+	  sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+	    > $tmpdir/tmp-la
+	  mv -f $tmpdir/tmp-la $lib
+	done
+        ${RM}r "$tmpdir"
+      fi
+    fi
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      for libdir in $libdirs; do
+	if test -n "$finish_cmds"; then
+	  # Do each command in the finish commands.
+	  func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+	fi
+	if test -n "$finish_eval"; then
+	  # Do the single finish_eval.
+	  eval cmds=\"$finish_eval\"
+	  $opt_dry_run || eval "$cmds" || func_append admincmds "
+       $cmds"
+	fi
+      done
+    fi
+
+    # Exit here if they wanted silent mode.
+    $opt_silent && exit $EXIT_SUCCESS
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      echo "----------------------------------------------------------------------"
+      echo "Libraries have been installed in:"
+      for libdir in $libdirs; do
+	$ECHO "   $libdir"
+      done
+      echo
+      echo "If you ever happen to want to link against installed libraries"
+      echo "in a given directory, LIBDIR, you must either use libtool, and"
+      echo "specify the full pathname of the library, or use the \`-LLIBDIR'"
+      echo "flag during linking and do at least one of the following:"
+      if test -n "$shlibpath_var"; then
+	echo "   - add LIBDIR to the \`$shlibpath_var' environment variable"
+	echo "     during execution"
+      fi
+      if test -n "$runpath_var"; then
+	echo "   - add LIBDIR to the \`$runpath_var' environment variable"
+	echo "     during linking"
+      fi
+      if test -n "$hardcode_libdir_flag_spec"; then
+	libdir=LIBDIR
+	eval flag=\"$hardcode_libdir_flag_spec\"
+
+	$ECHO "   - use the \`$flag' linker flag"
+      fi
+      if test -n "$admincmds"; then
+	$ECHO "   - have your system administrator run these commands:$admincmds"
+      fi
+      if test -f /etc/ld.so.conf; then
+	echo "   - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+      fi
+      echo
+
+      echo "See any operating system documentation about shared libraries for"
+      case $host in
+	solaris2.[6789]|solaris2.1[0-9])
+	  echo "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+	  echo "pages."
+	  ;;
+	*)
+	  echo "more information, such as the ld(1) and ld.so(8) manual pages."
+	  ;;
+      esac
+      echo "----------------------------------------------------------------------"
+    fi
+    exit $EXIT_SUCCESS
+}
+
+test "$opt_mode" = finish && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+    $opt_debug
+    # There may be an optional sh(1) argument at the beginning of
+    # install_prog (especially on Windows NT).
+    if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+       # Allow the use of GNU shtool's install command.
+       case $nonopt in *shtool*) :;; *) false;; esac; then
+      # Aesthetically quote it.
+      func_quote_for_eval "$nonopt"
+      install_prog="$func_quote_for_eval_result "
+      arg=$1
+      shift
+    else
+      install_prog=
+      arg=$nonopt
+    fi
+
+    # The real first argument should be the name of the installation program.
+    # Aesthetically quote it.
+    func_quote_for_eval "$arg"
+    func_append install_prog "$func_quote_for_eval_result"
+    install_shared_prog=$install_prog
+    case " $install_prog " in
+      *[\\\ /]cp\ *) install_cp=: ;;
+      *) install_cp=false ;;
+    esac
+
+    # We need to accept at least all the BSD install flags.
+    dest=
+    files=
+    opts=
+    prev=
+    install_type=
+    isdir=no
+    stripme=
+    no_mode=:
+    for arg
+    do
+      arg2=
+      if test -n "$dest"; then
+	func_append files " $dest"
+	dest=$arg
+	continue
+      fi
+
+      case $arg in
+      -d) isdir=yes ;;
+      -f)
+	if $install_cp; then :; else
+	  prev=$arg
+	fi
+	;;
+      -g | -m | -o)
+	prev=$arg
+	;;
+      -s)
+	stripme=" -s"
+	continue
+	;;
+      -*)
+	;;
+      *)
+	# If the previous option needed an argument, then skip it.
+	if test -n "$prev"; then
+	  if test "x$prev" = x-m && test -n "$install_override_mode"; then
+	    arg2=$install_override_mode
+	    no_mode=false
+	  fi
+	  prev=
+	else
+	  dest=$arg
+	  continue
+	fi
+	;;
+      esac
+
+      # Aesthetically quote the argument.
+      func_quote_for_eval "$arg"
+      func_append install_prog " $func_quote_for_eval_result"
+      if test -n "$arg2"; then
+	func_quote_for_eval "$arg2"
+      fi
+      func_append install_shared_prog " $func_quote_for_eval_result"
+    done
+
+    test -z "$install_prog" && \
+      func_fatal_help "you must specify an install program"
+
+    test -n "$prev" && \
+      func_fatal_help "the \`$prev' option requires an argument"
+
+    if test -n "$install_override_mode" && $no_mode; then
+      if $install_cp; then :; else
+	func_quote_for_eval "$install_override_mode"
+	func_append install_shared_prog " -m $func_quote_for_eval_result"
+      fi
+    fi
+
+    if test -z "$files"; then
+      if test -z "$dest"; then
+	func_fatal_help "no file or destination specified"
+      else
+	func_fatal_help "you must specify a destination"
+      fi
+    fi
+
+    # Strip any trailing slash from the destination.
+    func_stripname '' '/' "$dest"
+    dest=$func_stripname_result
+
+    # Check to see that the destination is a directory.
+    test -d "$dest" && isdir=yes
+    if test "$isdir" = yes; then
+      destdir="$dest"
+      destname=
+    else
+      func_dirname_and_basename "$dest" "" "."
+      destdir="$func_dirname_result"
+      destname="$func_basename_result"
+
+      # Not a directory, so check to see that there is only one file specified.
+      set dummy $files; shift
+      test "$#" -gt 1 && \
+	func_fatal_help "\`$dest' is not a directory"
+    fi
+    case $destdir in
+    [\\/]* | [A-Za-z]:[\\/]*) ;;
+    *)
+      for file in $files; do
+	case $file in
+	*.lo) ;;
+	*)
+	  func_fatal_help "\`$destdir' must be an absolute directory name"
+	  ;;
+	esac
+      done
+      ;;
+    esac
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    staticlibs=
+    future_libdirs=
+    current_libdirs=
+    for file in $files; do
+
+      # Do each installation.
+      case $file in
+      *.$libext)
+	# Do the static libraries later.
+	func_append staticlibs " $file"
+	;;
+
+      *.la)
+	func_resolve_sysroot "$file"
+	file=$func_resolve_sysroot_result
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$file" \
+	  || func_fatal_help "\`$file' is not a valid libtool archive"
+
+	library_names=
+	old_library=
+	relink_command=
+	func_source "$file"
+
+	# Add the libdir to current_libdirs if it is the destination.
+	if test "X$destdir" = "X$libdir"; then
+	  case "$current_libdirs " in
+	  *" $libdir "*) ;;
+	  *) func_append current_libdirs " $libdir" ;;
+	  esac
+	else
+	  # Note the libdir as a future libdir.
+	  case "$future_libdirs " in
+	  *" $libdir "*) ;;
+	  *) func_append future_libdirs " $libdir" ;;
+	  esac
+	fi
+
+	func_dirname "$file" "/" ""
+	dir="$func_dirname_result"
+	func_append dir "$objdir"
+
+	if test -n "$relink_command"; then
+	  # Determine the prefix the user has applied to our future dir.
+	  inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+
+	  # Don't allow the user to place us outside of our expected
+	  # location b/c this prevents finding dependent libraries that
+	  # are installed to the same prefix.
+	  # At present, this check doesn't affect windows .dll's that
+	  # are installed into $libdir/../bin (currently, that works fine)
+	  # but it's something to keep an eye on.
+	  test "$inst_prefix_dir" = "$destdir" && \
+	    func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir"
+
+	  if test -n "$inst_prefix_dir"; then
+	    # Stick the inst_prefix_dir data into the link command.
+	    relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+	  else
+	    relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
+	  fi
+
+	  func_warning "relinking \`$file'"
+	  func_show_eval "$relink_command" \
+	    'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"'
+	fi
+
+	# See the names of the shared library.
+	set dummy $library_names; shift
+	if test -n "$1"; then
+	  realname="$1"
+	  shift
+
+	  srcname="$realname"
+	  test -n "$relink_command" && srcname="$realname"T
+
+	  # Install the shared library and build the symlinks.
+	  func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
+	      'exit $?'
+	  tstripme="$stripme"
+	  case $host_os in
+	  cygwin* | mingw* | pw32* | cegcc*)
+	    case $realname in
+	    *.dll.a)
+	      tstripme=""
+	      ;;
+	    esac
+	    ;;
+	  esac
+	  if test -n "$tstripme" && test -n "$striplib"; then
+	    func_show_eval "$striplib $destdir/$realname" 'exit $?'
+	  fi
+
+	  if test "$#" -gt 0; then
+	    # Delete the old symlinks, and create new ones.
+	    # Try `ln -sf' first, because the `ln' binary might depend on
+	    # the symlink we replace!  Solaris /bin/ln does not understand -f,
+	    # so we also need to try rm && ln -s.
+	    for linkname
+	    do
+	      test "$linkname" != "$realname" \
+		&& func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+	    done
+	  fi
+
+	  # Do each command in the postinstall commands.
+	  lib="$destdir/$realname"
+	  func_execute_cmds "$postinstall_cmds" 'exit $?'
+	fi
+
+	# Install the pseudo-library for information purposes.
+	func_basename "$file"
+	name="$func_basename_result"
+	instname="$dir/$name"i
+	func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+	# Maybe install the static library, too.
+	test -n "$old_library" && func_append staticlibs " $dir/$old_library"
+	;;
+
+      *.lo)
+	# Install (i.e. copy) a libtool object.
+
+	# Figure out destination file name, if it wasn't already specified.
+	if test -n "$destname"; then
+	  destfile="$destdir/$destname"
+	else
+	  func_basename "$file"
+	  destfile="$func_basename_result"
+	  destfile="$destdir/$destfile"
+	fi
+
+	# Deduce the name of the destination old-style object file.
+	case $destfile in
+	*.lo)
+	  func_lo2o "$destfile"
+	  staticdest=$func_lo2o_result
+	  ;;
+	*.$objext)
+	  staticdest="$destfile"
+	  destfile=
+	  ;;
+	*)
+	  func_fatal_help "cannot copy a libtool object to \`$destfile'"
+	  ;;
+	esac
+
+	# Install the libtool object if requested.
+	test -n "$destfile" && \
+	  func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+	# Install the old object if enabled.
+	if test "$build_old_libs" = yes; then
+	  # Deduce the name of the old-style object file.
+	  func_lo2o "$file"
+	  staticobj=$func_lo2o_result
+	  func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+	fi
+	exit $EXIT_SUCCESS
+	;;
+
+      *)
+	# Figure out destination file name, if it wasn't already specified.
+	if test -n "$destname"; then
+	  destfile="$destdir/$destname"
+	else
+	  func_basename "$file"
+	  destfile="$func_basename_result"
+	  destfile="$destdir/$destfile"
+	fi
+
+	# If the file is missing, and there is a .exe on the end, strip it
+	# because it is most likely a libtool script we actually want to
+	# install
+	stripped_ext=""
+	case $file in
+	  *.exe)
+	    if test ! -f "$file"; then
+	      func_stripname '' '.exe' "$file"
+	      file=$func_stripname_result
+	      stripped_ext=".exe"
+	    fi
+	    ;;
+	esac
+
+	# Do a test to see if this is really a libtool program.
+	case $host in
+	*cygwin* | *mingw*)
+	    if func_ltwrapper_executable_p "$file"; then
+	      func_ltwrapper_scriptname "$file"
+	      wrapper=$func_ltwrapper_scriptname_result
+	    else
+	      func_stripname '' '.exe' "$file"
+	      wrapper=$func_stripname_result
+	    fi
+	    ;;
+	*)
+	    wrapper=$file
+	    ;;
+	esac
+	if func_ltwrapper_script_p "$wrapper"; then
+	  notinst_deplibs=
+	  relink_command=
+
+	  func_source "$wrapper"
+
+	  # Check the variables that should have been set.
+	  test -z "$generated_by_libtool_version" && \
+	    func_fatal_error "invalid libtool wrapper script \`$wrapper'"
+
+	  finalize=yes
+	  for lib in $notinst_deplibs; do
+	    # Check to see that each library is installed.
+	    libdir=
+	    if test -f "$lib"; then
+	      func_source "$lib"
+	    fi
+	    libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test
+	    if test -n "$libdir" && test ! -f "$libfile"; then
+	      func_warning "\`$lib' has not been installed in \`$libdir'"
+	      finalize=no
+	    fi
+	  done
+
+	  relink_command=
+	  func_source "$wrapper"
+
+	  outputname=
+	  if test "$fast_install" = no && test -n "$relink_command"; then
+	    $opt_dry_run || {
+	      if test "$finalize" = yes; then
+	        tmpdir=`func_mktempdir`
+		func_basename "$file$stripped_ext"
+		file="$func_basename_result"
+	        outputname="$tmpdir/$file"
+	        # Replace the output file specification.
+	        relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
+
+	        $opt_silent || {
+	          func_quote_for_expand "$relink_command"
+		  eval "func_echo $func_quote_for_expand_result"
+	        }
+	        if eval "$relink_command"; then :
+	          else
+		  func_error "error: relink \`$file' with the above command before installing it"
+		  $opt_dry_run || ${RM}r "$tmpdir"
+		  continue
+	        fi
+	        file="$outputname"
+	      else
+	        func_warning "cannot relink \`$file'"
+	      fi
+	    }
+	  else
+	    # Install the binary that we compiled earlier.
+	    file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"`
+	  fi
+	fi
+
+	# remove .exe since cygwin /usr/bin/install will append another
+	# one anyway
+	case $install_prog,$host in
+	*/usr/bin/install*,*cygwin*)
+	  case $file:$destfile in
+	  *.exe:*.exe)
+	    # this is ok
+	    ;;
+	  *.exe:*)
+	    destfile=$destfile.exe
+	    ;;
+	  *:*.exe)
+	    func_stripname '' '.exe' "$destfile"
+	    destfile=$func_stripname_result
+	    ;;
+	  esac
+	  ;;
+	esac
+	func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+	$opt_dry_run || if test -n "$outputname"; then
+	  ${RM}r "$tmpdir"
+	fi
+	;;
+      esac
+    done
+
+    for file in $staticlibs; do
+      func_basename "$file"
+      name="$func_basename_result"
+
+      # Set up the ranlib parameters.
+      oldlib="$destdir/$name"
+      func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+      tool_oldlib=$func_to_tool_file_result
+
+      func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+      if test -n "$stripme" && test -n "$old_striplib"; then
+	func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
+      fi
+
+      # Do each command in the postinstall commands.
+      func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+    done
+
+    test -n "$future_libdirs" && \
+      func_warning "remember to run \`$progname --finish$future_libdirs'"
+
+    if test -n "$current_libdirs"; then
+      # Maybe just do a dry run.
+      $opt_dry_run && current_libdirs=" -n$current_libdirs"
+      exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+    else
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test "$opt_mode" = install && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+    $opt_debug
+    my_outputname="$1"
+    my_originator="$2"
+    my_pic_p="${3-no}"
+    my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'`
+    my_dlsyms=
+
+    if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+      if test -n "$NM" && test -n "$global_symbol_pipe"; then
+	my_dlsyms="${my_outputname}S.c"
+      else
+	func_error "not configured to extract global symbols from dlpreopened files"
+      fi
+    fi
+
+    if test -n "$my_dlsyms"; then
+      case $my_dlsyms in
+      "") ;;
+      *.c)
+	# Discover the nlist of each of the dlfiles.
+	nlist="$output_objdir/${my_outputname}.nm"
+
+	func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+	# Parse the name list into a source file.
+	func_verbose "creating $output_objdir/$my_dlsyms"
+
+	$opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#endif
+
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+/* External symbol declarations for the compiler. */\
+"
+
+	if test "$dlself" = yes; then
+	  func_verbose "generating symbol list for \`$output'"
+
+	  $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+	  # Add our own program objects to the symbol list.
+	  progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	  for progfile in $progfiles; do
+	    func_to_tool_file "$progfile" func_convert_file_msys_to_w32
+	    func_verbose "extracting global C symbols from \`$func_to_tool_file_result'"
+	    $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
+	  done
+
+	  if test -n "$exclude_expsyms"; then
+	    $opt_dry_run || {
+	      eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	    }
+	  fi
+
+	  if test -n "$export_symbols_regex"; then
+	    $opt_dry_run || {
+	      eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	    }
+	  fi
+
+	  # Prepare the list of exported symbols
+	  if test -z "$export_symbols"; then
+	    export_symbols="$output_objdir/$outputname.exp"
+	    $opt_dry_run || {
+	      $RM $export_symbols
+	      eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+	      case $host in
+	      *cygwin* | *mingw* | *cegcc* )
+                eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+                eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+	        ;;
+	      esac
+	    }
+	  else
+	    $opt_dry_run || {
+	      eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+	      eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	      case $host in
+	        *cygwin* | *mingw* | *cegcc* )
+	          eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+	          eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+	          ;;
+	      esac
+	    }
+	  fi
+	fi
+
+	for dlprefile in $dlprefiles; do
+	  func_verbose "extracting global C symbols from \`$dlprefile'"
+	  func_basename "$dlprefile"
+	  name="$func_basename_result"
+          case $host in
+	    *cygwin* | *mingw* | *cegcc* )
+	      # if an import library, we need to obtain dlname
+	      if func_win32_import_lib_p "$dlprefile"; then
+	        func_tr_sh "$dlprefile"
+	        eval "curr_lafile=\$libfile_$func_tr_sh_result"
+	        dlprefile_dlbasename=""
+	        if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
+	          # Use subshell, to avoid clobbering current variable values
+	          dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
+	          if test -n "$dlprefile_dlname" ; then
+	            func_basename "$dlprefile_dlname"
+	            dlprefile_dlbasename="$func_basename_result"
+	          else
+	            # no lafile. user explicitly requested -dlpreopen <import library>.
+	            $sharedlib_from_linklib_cmd "$dlprefile"
+	            dlprefile_dlbasename=$sharedlib_from_linklib_result
+	          fi
+	        fi
+	        $opt_dry_run || {
+	          if test -n "$dlprefile_dlbasename" ; then
+	            eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
+	          else
+	            func_warning "Could not compute DLL name from $name"
+	            eval '$ECHO ": $name " >> "$nlist"'
+	          fi
+	          func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	          eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+	            $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+	        }
+	      else # not an import lib
+	        $opt_dry_run || {
+	          eval '$ECHO ": $name " >> "$nlist"'
+	          func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	          eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+	        }
+	      fi
+	    ;;
+	    *)
+	      $opt_dry_run || {
+	        eval '$ECHO ": $name " >> "$nlist"'
+	        func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	        eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+	      }
+	    ;;
+          esac
+	done
+
+	$opt_dry_run || {
+	  # Make sure we have at least an empty file.
+	  test -f "$nlist" || : > "$nlist"
+
+	  if test -n "$exclude_expsyms"; then
+	    $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+	    $MV "$nlist"T "$nlist"
+	  fi
+
+	  # Try sorting and uniquifying the output.
+	  if $GREP -v "^: " < "$nlist" |
+	      if sort -k 3 </dev/null >/dev/null 2>&1; then
+		sort -k 3
+	      else
+		sort +2
+	      fi |
+	      uniq > "$nlist"S; then
+	    :
+	  else
+	    $GREP -v "^: " < "$nlist" > "$nlist"S
+	  fi
+
+	  if test -f "$nlist"S; then
+	    eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+	  else
+	    echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
+	  fi
+
+	  echo >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols.  */
+typedef struct {
+  const char *name;
+  void *address;
+} lt_dlsymlist;
+extern LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];
+LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{\
+  { \"$my_originator\", (void *) 0 },"
+
+	  case $need_lib_prefix in
+	  no)
+	    eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+	    ;;
+	  *)
+	    eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+	    ;;
+	  esac
+	  echo >> "$output_objdir/$my_dlsyms" "\
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+	} # !$opt_dry_run
+
+	pic_flag_for_symtable=
+	case "$compile_command " in
+	*" -static "*) ;;
+	*)
+	  case $host in
+	  # compiling the symbol table file with pic_flag works around
+	  # a FreeBSD bug that causes programs to crash when -lm is
+	  # linked before any other PIC object.  But we must not use
+	  # pic_flag when linking with -static.  The problem exists in
+	  # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+	  *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+	    pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+	  *-*-hpux*)
+	    pic_flag_for_symtable=" $pic_flag"  ;;
+	  *)
+	    if test "X$my_pic_p" != Xno; then
+	      pic_flag_for_symtable=" $pic_flag"
+	    fi
+	    ;;
+	  esac
+	  ;;
+	esac
+	symtab_cflags=
+	for arg in $LTCFLAGS; do
+	  case $arg in
+	  -pie | -fpie | -fPIE) ;;
+	  *) func_append symtab_cflags " $arg" ;;
+	  esac
+	done
+
+	# Now compile the dynamic symbol file.
+	func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+	# Clean up the generated files.
+	func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"'
+
+	# Transform the symbol file into the correct name.
+	symfileobj="$output_objdir/${my_outputname}S.$objext"
+	case $host in
+	*cygwin* | *mingw* | *cegcc* )
+	  if test -f "$output_objdir/$my_outputname.def"; then
+	    compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+	    finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+	  else
+	    compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	    finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  fi
+	  ;;
+	*)
+	  compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  ;;
+	esac
+	;;
+      *)
+	func_fatal_error "unknown suffix for \`$my_dlsyms'"
+	;;
+      esac
+    else
+      # We keep going just in case the user didn't refer to
+      # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
+      # really was required.
+
+      # Nullify the symbol file.
+      compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"`
+      finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"`
+    fi
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+# Despite the name, also deal with 64 bit binaries.
+func_win32_libid ()
+{
+  $opt_debug
+  win32_libid_type="unknown"
+  win32_fileres=`file -L $1 2>/dev/null`
+  case $win32_fileres in
+  *ar\ archive\ import\ library*) # definitely import
+    win32_libid_type="x86 archive import"
+    ;;
+  *ar\ archive*) # could be an import, or static
+    # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+    if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+       $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+      func_to_tool_file "$1" func_convert_file_msys_to_w32
+      win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+	$SED -n -e '
+	    1,100{
+		/ I /{
+		    s,.*,import,
+		    p
+		    q
+		}
+	    }'`
+      case $win32_nmres in
+      import*)  win32_libid_type="x86 archive import";;
+      *)        win32_libid_type="x86 archive static";;
+      esac
+    fi
+    ;;
+  *DLL*)
+    win32_libid_type="x86 DLL"
+    ;;
+  *executable*) # but shell scripts are "executable" too...
+    case $win32_fileres in
+    *MS\ Windows\ PE\ Intel*)
+      win32_libid_type="x86 DLL"
+      ;;
+    esac
+    ;;
+  esac
+  $ECHO "$win32_libid_type"
+}
+
+# func_cygming_dll_for_implib ARG
+#
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+# Invoked by eval'ing the libtool variable
+#    $sharedlib_from_linklib_cmd
+# Result is available in the variable
+#    $sharedlib_from_linklib_result
+func_cygming_dll_for_implib ()
+{
+  $opt_debug
+  sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
+}
+
+# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
+#
+# The is the core of a fallback implementation of a
+# platform-specific function to extract the name of the
+# DLL associated with the specified import library LIBNAME.
+#
+# SECTION_NAME is either .idata$6 or .idata$7, depending
+# on the platform and compiler that created the implib.
+#
+# Echos the name of the DLL associated with the
+# specified import library.
+func_cygming_dll_for_implib_fallback_core ()
+{
+  $opt_debug
+  match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
+  $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
+    $SED '/^Contents of section '"$match_literal"':/{
+      # Place marker at beginning of archive member dllname section
+      s/.*/====MARK====/
+      p
+      d
+    }
+    # These lines can sometimes be longer than 43 characters, but
+    # are always uninteresting
+    /:[	 ]*file format pe[i]\{,1\}-/d
+    /^In archive [^:]*:/d
+    # Ensure marker is printed
+    /^====MARK====/p
+    # Remove all lines with less than 43 characters
+    /^.\{43\}/!d
+    # From remaining lines, remove first 43 characters
+    s/^.\{43\}//' |
+    $SED -n '
+      # Join marker and all lines until next marker into a single line
+      /^====MARK====/ b para
+      H
+      $ b para
+      b
+      :para
+      x
+      s/\n//g
+      # Remove the marker
+      s/^====MARK====//
+      # Remove trailing dots and whitespace
+      s/[\. \t]*$//
+      # Print
+      /./p' |
+    # we now have a list, one entry per line, of the stringified
+    # contents of the appropriate section of all members of the
+    # archive which possess that section. Heuristic: eliminate
+    # all those which have a first or second character that is
+    # a '.' (that is, objdump's representation of an unprintable
+    # character.) This should work for all archives with less than
+    # 0x302f exports -- but will fail for DLLs whose name actually
+    # begins with a literal '.' or a single character followed by
+    # a '.'.
+    #
+    # Of those that remain, print the first one.
+    $SED -e '/^\./d;/^.\./d;q'
+}
+
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+  $opt_debug
+  func_to_tool_file "$1" func_convert_file_msys_to_w32
+  func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+  test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+  $opt_debug
+  func_to_tool_file "$1" func_convert_file_msys_to_w32
+  func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+  test -n "$func_cygming_ms_implib_tmp"
+}
+
+# func_cygming_dll_for_implib_fallback ARG
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+#
+# This fallback implementation is for use when $DLLTOOL
+# does not support the --identify-strict option.
+# Invoked by eval'ing the libtool variable
+#    $sharedlib_from_linklib_cmd
+# Result is available in the variable
+#    $sharedlib_from_linklib_result
+func_cygming_dll_for_implib_fallback ()
+{
+  $opt_debug
+  if func_cygming_gnu_implib_p "$1" ; then
+    # binutils import library
+    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
+  elif func_cygming_ms_implib_p "$1" ; then
+    # ms-generated import library
+    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
+  else
+    # unknown
+    sharedlib_from_linklib_result=""
+  fi
+}
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+    $opt_debug
+    f_ex_an_ar_dir="$1"; shift
+    f_ex_an_ar_oldlib="$1"
+    if test "$lock_old_archive_extraction" = yes; then
+      lockfile=$f_ex_an_ar_oldlib.lock
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+	func_echo "Waiting for $lockfile to be removed"
+	sleep 2
+      done
+    fi
+    func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
+		   'stat=$?; rm -f "$lockfile"; exit $stat'
+    if test "$lock_old_archive_extraction" = yes; then
+      $opt_dry_run || rm -f "$lockfile"
+    fi
+    if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+     :
+    else
+      func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+    fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+    $opt_debug
+    my_gentop="$1"; shift
+    my_oldlibs=${1+"$@"}
+    my_oldobjs=""
+    my_xlib=""
+    my_xabs=""
+    my_xdir=""
+
+    for my_xlib in $my_oldlibs; do
+      # Extract the objects.
+      case $my_xlib in
+	[\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;;
+	*) my_xabs=`pwd`"/$my_xlib" ;;
+      esac
+      func_basename "$my_xlib"
+      my_xlib="$func_basename_result"
+      my_xlib_u=$my_xlib
+      while :; do
+        case " $extracted_archives " in
+	*" $my_xlib_u "*)
+	  func_arith $extracted_serial + 1
+	  extracted_serial=$func_arith_result
+	  my_xlib_u=lt$extracted_serial-$my_xlib ;;
+	*) break ;;
+	esac
+      done
+      extracted_archives="$extracted_archives $my_xlib_u"
+      my_xdir="$my_gentop/$my_xlib_u"
+
+      func_mkdir_p "$my_xdir"
+
+      case $host in
+      *-darwin*)
+	func_verbose "Extracting $my_xabs"
+	# Do not bother doing anything if just a dry run
+	$opt_dry_run || {
+	  darwin_orig_dir=`pwd`
+	  cd $my_xdir || exit $?
+	  darwin_archive=$my_xabs
+	  darwin_curdir=`pwd`
+	  darwin_base_archive=`basename "$darwin_archive"`
+	  darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+	  if test -n "$darwin_arches"; then
+	    darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+	    darwin_arch=
+	    func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+	    for darwin_arch in  $darwin_arches ; do
+	      func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+	      $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}"
+	      cd "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+	      func_extract_an_archive "`pwd`" "${darwin_base_archive}"
+	      cd "$darwin_curdir"
+	      $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}"
+	    done # $darwin_arches
+            ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+	    darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u`
+	    darwin_file=
+	    darwin_files=
+	    for darwin_file in $darwin_filelist; do
+	      darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+	      $LIPO -create -output "$darwin_file" $darwin_files
+	    done # $darwin_filelist
+	    $RM -rf unfat-$$
+	    cd "$darwin_orig_dir"
+	  else
+	    cd $darwin_orig_dir
+	    func_extract_an_archive "$my_xdir" "$my_xabs"
+	  fi # $darwin_arches
+	} # !$opt_dry_run
+	;;
+      *)
+        func_extract_an_archive "$my_xdir" "$my_xabs"
+	;;
+      esac
+      my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+    done
+
+    func_extract_archives_result="$my_oldobjs"
+}
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable.  Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take.  If 'yes', then the emitted script
+# will assume that the directory in which it is stored is
+# the $objdir directory.  This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+	func_emit_wrapper_arg1=${1-no}
+
+	$ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+  # install mode needs the following variables:
+  generated_by_libtool_version='$macro_version'
+  notinst_deplibs='$notinst_deplibs'
+else
+  # When we are sourced in execute mode, \$file and \$ECHO are already set.
+  if test \"\$libtool_execute_magic\" != \"$magic\"; then
+    file=\"\$0\""
+
+    qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+    $ECHO "\
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+    ECHO=\"$qECHO\"
+  fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ which is used only on
+# windows platforms, and (c) all begin with the string "--lt-"
+# (application programs are unlikely to have options which match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's $0 value, followed by "$@".
+lt_option_debug=
+func_parse_lt_options ()
+{
+  lt_script_arg0=\$0
+  shift
+  for lt_opt
+  do
+    case \"\$lt_opt\" in
+    --lt-debug) lt_option_debug=1 ;;
+    --lt-dump-script)
+        lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
+        test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
+        lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
+        cat \"\$lt_dump_D/\$lt_dump_F\"
+        exit 0
+      ;;
+    --lt-*)
+        \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
+        exit 1
+      ;;
+    esac
+  done
+
+  # Print the debug banner immediately:
+  if test -n \"\$lt_option_debug\"; then
+    echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2
+  fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+  lt_dump_args_N=1;
+  for lt_arg
+  do
+    \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\"
+    lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
+  done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+"
+  case $host in
+  # Backslashes separate directories on plain windows
+  *-*-mingw | *-*-os2* | *-cegcc*)
+    $ECHO "\
+      if test -n \"\$lt_option_debug\"; then
+        \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2
+        func_lt_dump_args \${1+\"\$@\"} 1>&2
+      fi
+      exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+    ;;
+
+  *)
+    $ECHO "\
+      if test -n \"\$lt_option_debug\"; then
+        \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2
+        func_lt_dump_args \${1+\"\$@\"} 1>&2
+      fi
+      exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+    ;;
+  esac
+  $ECHO "\
+      \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+      exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from \$@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+  case \" \$* \" in
+  *\\ --lt-*)
+    for lt_wr_arg
+    do
+      case \$lt_wr_arg in
+      --lt-*) ;;
+      *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+      esac
+      shift
+    done ;;
+  esac
+  func_exec_program_core \${1+\"\$@\"}
+}
+
+  # Parse options
+  func_parse_lt_options \"\$0\" \${1+\"\$@\"}
+
+  # Find the directory that this script lives in.
+  thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\`
+  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\`
+  while test -n \"\$file\"; do
+    destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\`
+
+    # If there was a directory component, then change thisdir.
+    if test \"x\$destdir\" != \"x\$file\"; then
+      case \"\$destdir\" in
+      [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+      *) thisdir=\"\$thisdir/\$destdir\" ;;
+      esac
+    fi
+
+    file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\`
+    file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
+  done
+
+  # Usually 'no', except on cygwin/mingw when embedded into
+  # the cwrapper.
+  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
+  if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+    # special case for '.'
+    if test \"\$thisdir\" = \".\"; then
+      thisdir=\`pwd\`
+    fi
+    # remove .libs from thisdir
+    case \"\$thisdir\" in
+    *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;;
+    $objdir )   thisdir=. ;;
+    esac
+  fi
+
+  # Try to get the absolute directory name.
+  absdir=\`cd \"\$thisdir\" && pwd\`
+  test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+	if test "$fast_install" = yes; then
+	  $ECHO "\
+  program=lt-'$outputname'$exeext
+  progdir=\"\$thisdir/$objdir\"
+
+  if test ! -f \"\$progdir/\$program\" ||
+     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+    file=\"\$\$-\$program\"
+
+    if test ! -d \"\$progdir\"; then
+      $MKDIR \"\$progdir\"
+    else
+      $RM \"\$progdir/\$file\"
+    fi"
+
+	  $ECHO "\
+
+    # relink executable if necessary
+    if test -n \"\$relink_command\"; then
+      if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+      else
+	$ECHO \"\$relink_command_output\" >&2
+	$RM \"\$progdir/\$file\"
+	exit 1
+      fi
+    fi
+
+    $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+    { $RM \"\$progdir/\$program\";
+      $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+    $RM \"\$progdir/\$file\"
+  fi"
+	else
+	  $ECHO "\
+  program='$outputname'
+  progdir=\"\$thisdir/$objdir\"
+"
+	fi
+
+	$ECHO "\
+
+  if test -f \"\$progdir/\$program\"; then"
+
+	# fixup the dll searchpath if we need to.
+	#
+	# Fix the DLL searchpath if we need to.  Do this before prepending
+	# to shlibpath, because on Windows, both are PATH and uninstalled
+	# libraries must come first.
+	if test -n "$dllsearchpath"; then
+	  $ECHO "\
+    # Add the dll search path components to the executable PATH
+    PATH=$dllsearchpath:\$PATH
+"
+	fi
+
+	# Export our shlibpath_var if we have one.
+	if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+	  $ECHO "\
+    # Add our own library path to $shlibpath_var
+    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+    # Some systems cannot cope with colon-terminated $shlibpath_var
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
+
+    export $shlibpath_var
+"
+	fi
+
+	$ECHO "\
+    if test \"\$libtool_execute_magic\" != \"$magic\"; then
+      # Run the actual program with our arguments.
+      func_exec_program \${1+\"\$@\"}
+    fi
+  else
+    # The program doesn't exist.
+    \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2
+    \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+    \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+    exit 1
+  fi
+fi\
+"
+}
+
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+	cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+   Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+
+   The $output program cannot be directly executed until all the libtool
+   libraries that it depends on are installed.
+
+   This wrapper executable should never be moved out of the build directory.
+   If it is, it will not operate correctly.
+*/
+EOF
+	    cat <<"EOF"
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+#  include <io.h>
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+/* declarations of non-ANSI functions */
+#if defined(__MINGW32__)
+# ifdef __STRICT_ANSI__
+int _putenv (const char *);
+# endif
+#elif defined(__CYGWIN__)
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+/* #elif defined (other platforms) ... */
+#endif
+
+/* portability defines, excluding path handling macros */
+#if defined(_MSC_VER)
+# define setmode _setmode
+# define stat    _stat
+# define chmod   _chmod
+# define getcwd  _getcwd
+# define putenv  _putenv
+# define S_IXUSR _S_IEXEC
+# ifndef _INTPTR_T_DEFINED
+#  define _INTPTR_T_DEFINED
+#  define intptr_t int
+# endif
+#elif defined(__MINGW32__)
+# define setmode _setmode
+# define stat    _stat
+# define chmod   _chmod
+# define getcwd  _getcwd
+# define putenv  _putenv
+#elif defined(__CYGWIN__)
+# define HAVE_SETENV
+# define FOPEN_WB "wb"
+/* #elif defined (other platforms) ... */
+#endif
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+/* path handling portability macros */
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+  defined (__OS2__)
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+#  define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+#  define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+  if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+#if defined(LT_DEBUGWRAPPER)
+static int lt_debug = 1;
+#else
+static int lt_debug = 0;
+#endif
+
+const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_debugprintf (const char *file, int line, const char *fmt, ...);
+void lt_fatal (const char *file, int line, const char *message, ...);
+static const char *nonnull (const char *s);
+static const char *nonempty (const char *s);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
+void lt_dump_script (FILE *f);
+EOF
+
+	    cat <<EOF
+volatile const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+	    if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+              func_to_host_path "$temp_rpath"
+	      cat <<EOF
+const char * LIB_PATH_VALUE   = "$func_to_host_path_result";
+EOF
+	    else
+	      cat <<"EOF"
+const char * LIB_PATH_VALUE   = "";
+EOF
+	    fi
+
+	    if test -n "$dllsearchpath"; then
+              func_to_host_path "$dllsearchpath:"
+	      cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE   = "$func_to_host_path_result";
+EOF
+	    else
+	      cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE   = "";
+EOF
+	    fi
+
+	    if test "$fast_install" = yes; then
+	      cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+	    else
+	      cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+	    fi
+
+
+	    cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX         "--lt-"
+
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+static const char *dumpscript_opt       = LTWRAPPER_OPTION_PREFIX "dump-script";
+static const char *debug_opt            = LTWRAPPER_OPTION_PREFIX "debug";
+
+int
+main (int argc, char *argv[])
+{
+  char **newargz;
+  int  newargc;
+  char *tmp_pathspec;
+  char *actual_cwrapper_path;
+  char *actual_cwrapper_name;
+  char *target_name;
+  char *lt_argv_zero;
+  intptr_t rval = 127;
+
+  int i;
+
+  program_name = (char *) xstrdup (base_name (argv[0]));
+  newargz = XMALLOC (char *, argc + 1);
+
+  /* very simple arg parsing; don't want to rely on getopt
+   * also, copy all non cwrapper options to newargz, except
+   * argz[0], which is handled differently
+   */
+  newargc=0;
+  for (i = 1; i < argc; i++)
+    {
+      if (strcmp (argv[i], dumpscript_opt) == 0)
+	{
+EOF
+	    case "$host" in
+	      *mingw* | *cygwin* )
+		# make stdout use "unix" line endings
+		echo "          setmode(1,_O_BINARY);"
+		;;
+	      esac
+
+	    cat <<"EOF"
+	  lt_dump_script (stdout);
+	  return 0;
+	}
+      if (strcmp (argv[i], debug_opt) == 0)
+	{
+          lt_debug = 1;
+          continue;
+	}
+      if (strcmp (argv[i], ltwrapper_option_prefix) == 0)
+        {
+          /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+             namespace, but it is not one of the ones we know about and
+             have already dealt with, above (inluding dump-script), then
+             report an error. Otherwise, targets might begin to believe
+             they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+             namespace. The first time any user complains about this, we'll
+             need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+             or a configure.ac-settable value.
+           */
+          lt_fatal (__FILE__, __LINE__,
+		    "unrecognized %s option: '%s'",
+                    ltwrapper_option_prefix, argv[i]);
+        }
+      /* otherwise ... */
+      newargz[++newargc] = xstrdup (argv[i]);
+    }
+  newargz[++newargc] = NULL;
+
+EOF
+	    cat <<EOF
+  /* The GNU banner must be the first non-error debug message */
+  lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\n");
+EOF
+	    cat <<"EOF"
+  lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
+  lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
+
+  tmp_pathspec = find_executable (argv[0]);
+  if (tmp_pathspec == NULL)
+    lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
+  lt_debugprintf (__FILE__, __LINE__,
+                  "(main) found exe (before symlink chase) at: %s\n",
+		  tmp_pathspec);
+
+  actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+  lt_debugprintf (__FILE__, __LINE__,
+                  "(main) found exe (after symlink chase) at: %s\n",
+		  actual_cwrapper_path);
+  XFREE (tmp_pathspec);
+
+  actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
+  strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+  /* wrapper name transforms */
+  strendzap (actual_cwrapper_name, ".exe");
+  tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+  XFREE (actual_cwrapper_name);
+  actual_cwrapper_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  /* target_name transforms -- use actual target program name; might have lt- prefix */
+  target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+  strendzap (target_name, ".exe");
+  tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+  XFREE (target_name);
+  target_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(main) libtool target name: %s\n",
+		  target_name);
+EOF
+
+	    cat <<EOF
+  newargz[0] =
+    XMALLOC (char, (strlen (actual_cwrapper_path) +
+		    strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+  strcpy (newargz[0], actual_cwrapper_path);
+  strcat (newargz[0], "$objdir");
+  strcat (newargz[0], "/");
+EOF
+
+	    cat <<"EOF"
+  /* stop here, and copy so we don't have to do this twice */
+  tmp_pathspec = xstrdup (newargz[0]);
+
+  /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+  strcat (newargz[0], actual_cwrapper_name);
+
+  /* DO want the lt- prefix here if it exists, so use target_name */
+  lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+  XFREE (tmp_pathspec);
+  tmp_pathspec = NULL;
+EOF
+
+	    case $host_os in
+	      mingw*)
+	    cat <<"EOF"
+  {
+    char* p;
+    while ((p = strchr (newargz[0], '\\')) != NULL)
+      {
+	*p = '/';
+      }
+    while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+      {
+	*p = '/';
+      }
+  }
+EOF
+	    ;;
+	    esac
+
+	    cat <<"EOF"
+  XFREE (target_name);
+  XFREE (actual_cwrapper_path);
+  XFREE (actual_cwrapper_name);
+
+  lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+  lt_setenv ("DUALCASE", "1");  /* for MSK sh */
+  /* Update the DLL searchpath.  EXE_PATH_VALUE ($dllsearchpath) must
+     be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
+     because on Windows, both *_VARNAMEs are PATH but uninstalled
+     libraries must come first. */
+  lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+  lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+
+  lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
+		  nonnull (lt_argv_zero));
+  for (i = 0; i < newargc; i++)
+    {
+      lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
+		      i, nonnull (newargz[i]));
+    }
+
+EOF
+
+	    case $host_os in
+	      mingw*)
+		cat <<"EOF"
+  /* execv doesn't actually work on mingw as expected on unix */
+  newargz = prepare_spawn (newargz);
+  rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+  if (rval == -1)
+    {
+      /* failed to start process */
+      lt_debugprintf (__FILE__, __LINE__,
+		      "(main) failed to launch target \"%s\": %s\n",
+		      lt_argv_zero, nonnull (strerror (errno)));
+      return 127;
+    }
+  return rval;
+EOF
+		;;
+	      *)
+		cat <<"EOF"
+  execv (lt_argv_zero, newargz);
+  return rval; /* =127, but avoids unused variable warning */
+EOF
+		;;
+	    esac
+
+	    cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+  void *p = (void *) malloc (num);
+  if (!p)
+    lt_fatal (__FILE__, __LINE__, "memory exhausted");
+
+  return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+  return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+			  string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+  const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  /* Skip over the disk name in MSDOS pathnames. */
+  if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+    name += 2;
+#endif
+
+  for (base = name; *name; name++)
+    if (IS_DIR_SEPARATOR (*name))
+      base = name + 1;
+  return base;
+}
+
+int
+check_executable (const char *path)
+{
+  struct stat st;
+
+  lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
+                  nonempty (path));
+  if ((!path) || (!*path))
+    return 0;
+
+  if ((stat (path, &st) >= 0)
+      && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+    return 1;
+  else
+    return 0;
+}
+
+int
+make_executable (const char *path)
+{
+  int rval = 0;
+  struct stat st;
+
+  lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
+                  nonempty (path));
+  if ((!path) || (!*path))
+    return 0;
+
+  if (stat (path, &st) >= 0)
+    {
+      rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+    }
+  return rval;
+}
+
+/* Searches for the full path of the wrapper.  Returns
+   newly allocated full path name if found, NULL otherwise
+   Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+  int has_slash = 0;
+  const char *p;
+  const char *p_next;
+  /* static buffer for getcwd */
+  char tmp[LT_PATHMAX + 1];
+  int tmp_len;
+  char *concat_name;
+
+  lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
+                  nonempty (wrapper));
+
+  if ((wrapper == NULL) || (*wrapper == '\0'))
+    return NULL;
+
+  /* Absolute path? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+    {
+      concat_name = xstrdup (wrapper);
+      if (check_executable (concat_name))
+	return concat_name;
+      XFREE (concat_name);
+    }
+  else
+    {
+#endif
+      if (IS_DIR_SEPARATOR (wrapper[0]))
+	{
+	  concat_name = xstrdup (wrapper);
+	  if (check_executable (concat_name))
+	    return concat_name;
+	  XFREE (concat_name);
+	}
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+    }
+#endif
+
+  for (p = wrapper; *p; p++)
+    if (*p == '/')
+      {
+	has_slash = 1;
+	break;
+      }
+  if (!has_slash)
+    {
+      /* no slashes; search PATH */
+      const char *path = getenv ("PATH");
+      if (path != NULL)
+	{
+	  for (p = path; *p; p = p_next)
+	    {
+	      const char *q;
+	      size_t p_len;
+	      for (q = p; *q; q++)
+		if (IS_PATH_SEPARATOR (*q))
+		  break;
+	      p_len = q - p;
+	      p_next = (*q == '\0' ? q : q + 1);
+	      if (p_len == 0)
+		{
+		  /* empty path: current directory */
+		  if (getcwd (tmp, LT_PATHMAX) == NULL)
+		    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+                              nonnull (strerror (errno)));
+		  tmp_len = strlen (tmp);
+		  concat_name =
+		    XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+		  memcpy (concat_name, tmp, tmp_len);
+		  concat_name[tmp_len] = '/';
+		  strcpy (concat_name + tmp_len + 1, wrapper);
+		}
+	      else
+		{
+		  concat_name =
+		    XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+		  memcpy (concat_name, p, p_len);
+		  concat_name[p_len] = '/';
+		  strcpy (concat_name + p_len + 1, wrapper);
+		}
+	      if (check_executable (concat_name))
+		return concat_name;
+	      XFREE (concat_name);
+	    }
+	}
+      /* not found in PATH; assume curdir */
+    }
+  /* Relative path | not found in path: prepend cwd */
+  if (getcwd (tmp, LT_PATHMAX) == NULL)
+    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+              nonnull (strerror (errno)));
+  tmp_len = strlen (tmp);
+  concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+  memcpy (concat_name, tmp, tmp_len);
+  concat_name[tmp_len] = '/';
+  strcpy (concat_name + tmp_len + 1, wrapper);
+
+  if (check_executable (concat_name))
+    return concat_name;
+  XFREE (concat_name);
+  return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+  return xstrdup (pathspec);
+#else
+  char buf[LT_PATHMAX];
+  struct stat s;
+  char *tmp_pathspec = xstrdup (pathspec);
+  char *p;
+  int has_symlinks = 0;
+  while (strlen (tmp_pathspec) && !has_symlinks)
+    {
+      lt_debugprintf (__FILE__, __LINE__,
+		      "checking path component for symlinks: %s\n",
+		      tmp_pathspec);
+      if (lstat (tmp_pathspec, &s) == 0)
+	{
+	  if (S_ISLNK (s.st_mode) != 0)
+	    {
+	      has_symlinks = 1;
+	      break;
+	    }
+
+	  /* search backwards for last DIR_SEPARATOR */
+	  p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+	  while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+	    p--;
+	  if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+	    {
+	      /* no more DIR_SEPARATORS left */
+	      break;
+	    }
+	  *p = '\0';
+	}
+      else
+	{
+	  lt_fatal (__FILE__, __LINE__,
+		    "error accessing file \"%s\": %s",
+		    tmp_pathspec, nonnull (strerror (errno)));
+	}
+    }
+  XFREE (tmp_pathspec);
+
+  if (!has_symlinks)
+    {
+      return xstrdup (pathspec);
+    }
+
+  tmp_pathspec = realpath (pathspec, buf);
+  if (tmp_pathspec == 0)
+    {
+      lt_fatal (__FILE__, __LINE__,
+		"could not follow symlinks for %s", pathspec);
+    }
+  return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+  size_t len, patlen;
+
+  assert (str != NULL);
+  assert (pat != NULL);
+
+  len = strlen (str);
+  patlen = strlen (pat);
+
+  if (patlen <= len)
+    {
+      str += len - patlen;
+      if (strcmp (str, pat) == 0)
+	*str = '\0';
+    }
+  return str;
+}
+
+void
+lt_debugprintf (const char *file, int line, const char *fmt, ...)
+{
+  va_list args;
+  if (lt_debug)
+    {
+      (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
+      va_start (args, fmt);
+      (void) vfprintf (stderr, fmt, args);
+      va_end (args);
+    }
+}
+
+static void
+lt_error_core (int exit_status, const char *file,
+	       int line, const char *mode,
+	       const char *message, va_list ap)
+{
+  fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
+  vfprintf (stderr, message, ap);
+  fprintf (stderr, ".\n");
+
+  if (exit_status >= 0)
+    exit (exit_status);
+}
+
+void
+lt_fatal (const char *file, int line, const char *message, ...)
+{
+  va_list ap;
+  va_start (ap, message);
+  lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
+  va_end (ap);
+}
+
+static const char *
+nonnull (const char *s)
+{
+  return s ? s : "(null)";
+}
+
+static const char *
+nonempty (const char *s)
+{
+  return (s && !*s) ? "(empty)" : nonnull (s);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_setenv) setting '%s' to '%s'\n",
+                  nonnull (name), nonnull (value));
+  {
+#ifdef HAVE_SETENV
+    /* always make a copy, for consistency with !HAVE_SETENV */
+    char *str = xstrdup (value);
+    setenv (name, str, 1);
+#else
+    int len = strlen (name) + 1 + strlen (value) + 1;
+    char *str = XMALLOC (char, len);
+    sprintf (str, "%s=%s", name, value);
+    if (putenv (str) != EXIT_SUCCESS)
+      {
+        XFREE (str);
+      }
+#endif
+  }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+  char *new_value;
+  if (orig_value && *orig_value)
+    {
+      int orig_value_len = strlen (orig_value);
+      int add_len = strlen (add);
+      new_value = XMALLOC (char, add_len + orig_value_len + 1);
+      if (to_end)
+        {
+          strcpy (new_value, orig_value);
+          strcpy (new_value + orig_value_len, add);
+        }
+      else
+        {
+          strcpy (new_value, add);
+          strcpy (new_value + add_len, orig_value);
+        }
+    }
+  else
+    {
+      new_value = xstrdup (add);
+    }
+  return new_value;
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+                  nonnull (name), nonnull (value));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      /* some systems can't cope with a ':'-terminated path #' */
+      int len = strlen (new_value);
+      while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+        {
+          new_value[len-1] = '\0';
+        }
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+                  nonnull (name), nonnull (value));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+EOF
+	    case $host_os in
+	      mingw*)
+		cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+   Note that spawn() does not by itself call the command interpreter
+     (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+         GetVersionEx(&v);
+         v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+      }) ? "cmd.exe" : "command.com").
+   Instead it simply concatenates the arguments, separated by ' ', and calls
+   CreateProcess().  We must quote the arguments since Win32 CreateProcess()
+   interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+   special way:
+   - Space and tab are interpreted as delimiters. They are not treated as
+     delimiters if they are surrounded by double quotes: "...".
+   - Unescaped double quotes are removed from the input. Their only effect is
+     that within double quotes, space and tab are treated like normal
+     characters.
+   - Backslashes not followed by double quotes are not special.
+   - But 2*n+1 backslashes followed by a double quote become
+     n backslashes followed by a double quote (n >= 0):
+       \" -> "
+       \\\" -> \"
+       \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+  size_t argc;
+  char **new_argv;
+  size_t i;
+
+  /* Count number of arguments.  */
+  for (argc = 0; argv[argc] != NULL; argc++)
+    ;
+
+  /* Allocate new argument vector.  */
+  new_argv = XMALLOC (char *, argc + 1);
+
+  /* Put quoted arguments into the new argument vector.  */
+  for (i = 0; i < argc; i++)
+    {
+      const char *string = argv[i];
+
+      if (string[0] == '\0')
+	new_argv[i] = xstrdup ("\"\"");
+      else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+	{
+	  int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+	  size_t length;
+	  unsigned int backslashes;
+	  const char *s;
+	  char *quoted_string;
+	  char *p;
+
+	  length = 0;
+	  backslashes = 0;
+	  if (quote_around)
+	    length++;
+	  for (s = string; *s != '\0'; s++)
+	    {
+	      char c = *s;
+	      if (c == '"')
+		length += backslashes + 1;
+	      length++;
+	      if (c == '\\')
+		backslashes++;
+	      else
+		backslashes = 0;
+	    }
+	  if (quote_around)
+	    length += backslashes + 1;
+
+	  quoted_string = XMALLOC (char, length + 1);
+
+	  p = quoted_string;
+	  backslashes = 0;
+	  if (quote_around)
+	    *p++ = '"';
+	  for (s = string; *s != '\0'; s++)
+	    {
+	      char c = *s;
+	      if (c == '"')
+		{
+		  unsigned int j;
+		  for (j = backslashes + 1; j > 0; j--)
+		    *p++ = '\\';
+		}
+	      *p++ = c;
+	      if (c == '\\')
+		backslashes++;
+	      else
+		backslashes = 0;
+	    }
+	  if (quote_around)
+	    {
+	      unsigned int j;
+	      for (j = backslashes; j > 0; j--)
+		*p++ = '\\';
+	      *p++ = '"';
+	    }
+	  *p = '\0';
+
+	  new_argv[i] = quoted_string;
+	}
+      else
+	new_argv[i] = (char *) string;
+    }
+  new_argv[argc] = NULL;
+
+  return new_argv;
+}
+EOF
+		;;
+	    esac
+
+            cat <<"EOF"
+void lt_dump_script (FILE* f)
+{
+EOF
+	    func_emit_wrapper yes |
+	      $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/  fputs ("\1", f);/p
+g
+D'
+            cat <<"EOF"
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_win32_import_lib_p ARG
+# True if ARG is an import lib, as indicated by $file_magic_cmd
+func_win32_import_lib_p ()
+{
+    $opt_debug
+    case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
+    *import*) : ;;
+    *) false ;;
+    esac
+}
+
+# func_mode_link arg...
+func_mode_link ()
+{
+    $opt_debug
+    case $host in
+    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+      # It is impossible to link a dll without this setting, and
+      # we shouldn't force the makefile maintainer to figure out
+      # which system we are compiling for in order to pass an extra
+      # flag for every libtool invocation.
+      # allow_undefined=no
+
+      # FIXME: Unfortunately, there are problems with the above when trying
+      # to make a dll which has undefined symbols, in which case not
+      # even a static library is built.  For now, we need to specify
+      # -no-undefined on the libtool link line when we can be certain
+      # that all symbols are satisfied, otherwise we get a static library.
+      allow_undefined=yes
+      ;;
+    *)
+      allow_undefined=yes
+      ;;
+    esac
+    libtool_args=$nonopt
+    base_compile="$nonopt $@"
+    compile_command=$nonopt
+    finalize_command=$nonopt
+
+    compile_rpath=
+    finalize_rpath=
+    compile_shlibpath=
+    finalize_shlibpath=
+    convenience=
+    old_convenience=
+    deplibs=
+    old_deplibs=
+    compiler_flags=
+    linker_flags=
+    dllsearchpath=
+    lib_search_path=`pwd`
+    inst_prefix_dir=
+    new_inherited_linker_flags=
+
+    avoid_version=no
+    bindir=
+    dlfiles=
+    dlprefiles=
+    dlself=no
+    export_dynamic=no
+    export_symbols=
+    export_symbols_regex=
+    generated=
+    libobjs=
+    ltlibs=
+    module=no
+    no_install=no
+    objs=
+    non_pic_objects=
+    precious_files_regex=
+    prefer_static_libs=no
+    preload=no
+    prev=
+    prevarg=
+    release=
+    rpath=
+    xrpath=
+    perm_rpath=
+    temp_rpath=
+    thread_safe=no
+    vinfo=
+    vinfo_number=no
+    weak_libs=
+    single_module="${wl}-single_module"
+    func_infer_tag $base_compile
+
+    # We need to know -static, to get the right output filenames.
+    for arg
+    do
+      case $arg in
+      -shared)
+	test "$build_libtool_libs" != yes && \
+	  func_fatal_configuration "can not build a shared library"
+	build_old_libs=no
+	break
+	;;
+      -all-static | -static | -static-libtool-libs)
+	case $arg in
+	-all-static)
+	  if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+	    func_warning "complete static linking is impossible in this configuration"
+	  fi
+	  if test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=yes
+	  ;;
+	-static)
+	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=built
+	  ;;
+	-static-libtool-libs)
+	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=yes
+	  ;;
+	esac
+	build_libtool_libs=no
+	build_old_libs=yes
+	break
+	;;
+      esac
+    done
+
+    # See if our shared archives depend on static archives.
+    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+    # Go through the arguments, transforming them on the way.
+    while test "$#" -gt 0; do
+      arg="$1"
+      shift
+      func_quote_for_eval "$arg"
+      qarg=$func_quote_for_eval_unquoted_result
+      func_append libtool_args " $func_quote_for_eval_result"
+
+      # If the previous option needs an argument, assign it.
+      if test -n "$prev"; then
+	case $prev in
+	output)
+	  func_append compile_command " @OUTPUT@"
+	  func_append finalize_command " @OUTPUT@"
+	  ;;
+	esac
+
+	case $prev in
+	bindir)
+	  bindir="$arg"
+	  prev=
+	  continue
+	  ;;
+	dlfiles|dlprefiles)
+	  if test "$preload" = no; then
+	    # Add the symbol object into the linking commands.
+	    func_append compile_command " @SYMFILE@"
+	    func_append finalize_command " @SYMFILE@"
+	    preload=yes
+	  fi
+	  case $arg in
+	  *.la | *.lo) ;;  # We handle these cases below.
+	  force)
+	    if test "$dlself" = no; then
+	      dlself=needless
+	      export_dynamic=yes
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  self)
+	    if test "$prev" = dlprefiles; then
+	      dlself=yes
+	    elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+	      dlself=yes
+	    else
+	      dlself=needless
+	      export_dynamic=yes
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  *)
+	    if test "$prev" = dlfiles; then
+	      func_append dlfiles " $arg"
+	    else
+	      func_append dlprefiles " $arg"
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  esac
+	  ;;
+	expsyms)
+	  export_symbols="$arg"
+	  test -f "$arg" \
+	    || func_fatal_error "symbol file \`$arg' does not exist"
+	  prev=
+	  continue
+	  ;;
+	expsyms_regex)
+	  export_symbols_regex="$arg"
+	  prev=
+	  continue
+	  ;;
+	framework)
+	  case $host in
+	    *-*-darwin*)
+	      case "$deplibs " in
+		*" $qarg.ltframework "*) ;;
+		*) func_append deplibs " $qarg.ltframework" # this is fixed later
+		   ;;
+	      esac
+	      ;;
+	  esac
+	  prev=
+	  continue
+	  ;;
+	inst_prefix)
+	  inst_prefix_dir="$arg"
+	  prev=
+	  continue
+	  ;;
+	objectlist)
+	  if test -f "$arg"; then
+	    save_arg=$arg
+	    moreargs=
+	    for fil in `cat "$save_arg"`
+	    do
+#	      func_append moreargs " $fil"
+	      arg=$fil
+	      # A libtool-controlled object.
+
+	      # Check to see that this really is a libtool object.
+	      if func_lalib_unsafe_p "$arg"; then
+		pic_object=
+		non_pic_object=
+
+		# Read the .lo file
+		func_source "$arg"
+
+		if test -z "$pic_object" ||
+		   test -z "$non_pic_object" ||
+		   test "$pic_object" = none &&
+		   test "$non_pic_object" = none; then
+		  func_fatal_error "cannot find name of object for \`$arg'"
+		fi
+
+		# Extract subdirectory from the argument.
+		func_dirname "$arg" "/" ""
+		xdir="$func_dirname_result"
+
+		if test "$pic_object" != none; then
+		  # Prepend the subdirectory the object is found in.
+		  pic_object="$xdir$pic_object"
+
+		  if test "$prev" = dlfiles; then
+		    if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+		      func_append dlfiles " $pic_object"
+		      prev=
+		      continue
+		    else
+		      # If libtool objects are unsupported, then we need to preload.
+		      prev=dlprefiles
+		    fi
+		  fi
+
+		  # CHECK ME:  I think I busted this.  -Ossama
+		  if test "$prev" = dlprefiles; then
+		    # Preload the old-style object.
+		    func_append dlprefiles " $pic_object"
+		    prev=
+		  fi
+
+		  # A PIC object.
+		  func_append libobjs " $pic_object"
+		  arg="$pic_object"
+		fi
+
+		# Non-PIC object.
+		if test "$non_pic_object" != none; then
+		  # Prepend the subdirectory the object is found in.
+		  non_pic_object="$xdir$non_pic_object"
+
+		  # A standard non-PIC object
+		  func_append non_pic_objects " $non_pic_object"
+		  if test -z "$pic_object" || test "$pic_object" = none ; then
+		    arg="$non_pic_object"
+		  fi
+		else
+		  # If the PIC object exists, use it instead.
+		  # $xdir was prepended to $pic_object above.
+		  non_pic_object="$pic_object"
+		  func_append non_pic_objects " $non_pic_object"
+		fi
+	      else
+		# Only an error if not doing a dry-run.
+		if $opt_dry_run; then
+		  # Extract subdirectory from the argument.
+		  func_dirname "$arg" "/" ""
+		  xdir="$func_dirname_result"
+
+		  func_lo2o "$arg"
+		  pic_object=$xdir$objdir/$func_lo2o_result
+		  non_pic_object=$xdir$func_lo2o_result
+		  func_append libobjs " $pic_object"
+		  func_append non_pic_objects " $non_pic_object"
+	        else
+		  func_fatal_error "\`$arg' is not a valid libtool object"
+		fi
+	      fi
+	    done
+	  else
+	    func_fatal_error "link input file \`$arg' does not exist"
+	  fi
+	  arg=$save_arg
+	  prev=
+	  continue
+	  ;;
+	precious_regex)
+	  precious_files_regex="$arg"
+	  prev=
+	  continue
+	  ;;
+	release)
+	  release="-$arg"
+	  prev=
+	  continue
+	  ;;
+	rpath | xrpath)
+	  # We need an absolute path.
+	  case $arg in
+	  [\\/]* | [A-Za-z]:[\\/]*) ;;
+	  *)
+	    func_fatal_error "only absolute run-paths are allowed"
+	    ;;
+	  esac
+	  if test "$prev" = rpath; then
+	    case "$rpath " in
+	    *" $arg "*) ;;
+	    *) func_append rpath " $arg" ;;
+	    esac
+	  else
+	    case "$xrpath " in
+	    *" $arg "*) ;;
+	    *) func_append xrpath " $arg" ;;
+	    esac
+	  fi
+	  prev=
+	  continue
+	  ;;
+	shrext)
+	  shrext_cmds="$arg"
+	  prev=
+	  continue
+	  ;;
+	weak)
+	  func_append weak_libs " $arg"
+	  prev=
+	  continue
+	  ;;
+	xcclinker)
+	  func_append linker_flags " $qarg"
+	  func_append compiler_flags " $qarg"
+	  prev=
+	  func_append compile_command " $qarg"
+	  func_append finalize_command " $qarg"
+	  continue
+	  ;;
+	xcompiler)
+	  func_append compiler_flags " $qarg"
+	  prev=
+	  func_append compile_command " $qarg"
+	  func_append finalize_command " $qarg"
+	  continue
+	  ;;
+	xlinker)
+	  func_append linker_flags " $qarg"
+	  func_append compiler_flags " $wl$qarg"
+	  prev=
+	  func_append compile_command " $wl$qarg"
+	  func_append finalize_command " $wl$qarg"
+	  continue
+	  ;;
+	*)
+	  eval "$prev=\"\$arg\""
+	  prev=
+	  continue
+	  ;;
+	esac
+      fi # test -n "$prev"
+
+      prevarg="$arg"
+
+      case $arg in
+      -all-static)
+	if test -n "$link_static_flag"; then
+	  # See comment for -static flag below, for more details.
+	  func_append compile_command " $link_static_flag"
+	  func_append finalize_command " $link_static_flag"
+	fi
+	continue
+	;;
+
+      -allow-undefined)
+	# FIXME: remove this flag sometime in the future.
+	func_fatal_error "\`-allow-undefined' must not be used because it is the default"
+	;;
+
+      -avoid-version)
+	avoid_version=yes
+	continue
+	;;
+
+      -bindir)
+	prev=bindir
+	continue
+	;;
+
+      -dlopen)
+	prev=dlfiles
+	continue
+	;;
+
+      -dlpreopen)
+	prev=dlprefiles
+	continue
+	;;
+
+      -export-dynamic)
+	export_dynamic=yes
+	continue
+	;;
+
+      -export-symbols | -export-symbols-regex)
+	if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+	  func_fatal_error "more than one -exported-symbols argument is not allowed"
+	fi
+	if test "X$arg" = "X-export-symbols"; then
+	  prev=expsyms
+	else
+	  prev=expsyms_regex
+	fi
+	continue
+	;;
+
+      -framework)
+	prev=framework
+	continue
+	;;
+
+      -inst-prefix-dir)
+	prev=inst_prefix
+	continue
+	;;
+
+      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+      # so, if we see these flags be careful not to treat them like -L
+      -L[A-Z][A-Z]*:*)
+	case $with_gcc/$host in
+	no/*-*-irix* | /*-*-irix*)
+	  func_append compile_command " $arg"
+	  func_append finalize_command " $arg"
+	  ;;
+	esac
+	continue
+	;;
+
+      -L*)
+	func_stripname "-L" '' "$arg"
+	if test -z "$func_stripname_result"; then
+	  if test "$#" -gt 0; then
+	    func_fatal_error "require no space between \`-L' and \`$1'"
+	  else
+	    func_fatal_error "need path for \`-L' option"
+	  fi
+	fi
+	func_resolve_sysroot "$func_stripname_result"
+	dir=$func_resolve_sysroot_result
+	# We need an absolute path.
+	case $dir in
+	[\\/]* | [A-Za-z]:[\\/]*) ;;
+	*)
+	  absdir=`cd "$dir" && pwd`
+	  test -z "$absdir" && \
+	    func_fatal_error "cannot determine absolute directory name of \`$dir'"
+	  dir="$absdir"
+	  ;;
+	esac
+	case "$deplibs " in
+	*" -L$dir "* | *" $arg "*)
+	  # Will only happen for absolute or sysroot arguments
+	  ;;
+	*)
+	  # Preserve sysroot, but never include relative directories
+	  case $dir in
+	    [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
+	    *) func_append deplibs " -L$dir" ;;
+	  esac
+	  func_append lib_search_path " $dir"
+	  ;;
+	esac
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+	  testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+	  case :$dllsearchpath: in
+	  *":$dir:"*) ;;
+	  ::) dllsearchpath=$dir;;
+	  *) func_append dllsearchpath ":$dir";;
+	  esac
+	  case :$dllsearchpath: in
+	  *":$testbindir:"*) ;;
+	  ::) dllsearchpath=$testbindir;;
+	  *) func_append dllsearchpath ":$testbindir";;
+	  esac
+	  ;;
+	esac
+	continue
+	;;
+
+      -l*)
+	if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+	  case $host in
+	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+	    # These systems don't actually have a C or math library (as such)
+	    continue
+	    ;;
+	  *-*-os2*)
+	    # These systems don't actually have a C library (as such)
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+	    # Do not include libc due to us having libc/libc_r.
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  *-*-rhapsody* | *-*-darwin1.[012])
+	    # Rhapsody C and math libraries are in the System framework
+	    func_append deplibs " System.ltframework"
+	    continue
+	    ;;
+	  *-*-sco3.2v5* | *-*-sco5v6*)
+	    # Causes problems with __ctype
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+	    # Compiler inserts libc in the correct place for threads to work
+	    test "X$arg" = "X-lc" && continue
+	    ;;
+	  esac
+	elif test "X$arg" = "X-lc_r"; then
+	 case $host in
+	 *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+	   # Do not include libc_r directly, use -pthread flag.
+	   continue
+	   ;;
+	 esac
+	fi
+	func_append deplibs " $arg"
+	continue
+	;;
+
+      -module)
+	module=yes
+	continue
+	;;
+
+      # Tru64 UNIX uses -model [arg] to determine the layout of C++
+      # classes, name mangling, and exception handling.
+      # Darwin uses the -arch flag to determine output architecture.
+      -model|-arch|-isysroot|--sysroot)
+	func_append compiler_flags " $arg"
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+	prev=xcompiler
+	continue
+	;;
+
+      -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+      |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+	func_append compiler_flags " $arg"
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+	case "$new_inherited_linker_flags " in
+	    *" $arg "*) ;;
+	    * ) func_append new_inherited_linker_flags " $arg" ;;
+	esac
+	continue
+	;;
+
+      -multi_module)
+	single_module="${wl}-multi_module"
+	continue
+	;;
+
+      -no-fast-install)
+	fast_install=no
+	continue
+	;;
+
+      -no-install)
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+	  # The PATH hackery in wrapper scripts is required on Windows
+	  # and Darwin in order for the loader to find any dlls it needs.
+	  func_warning "\`-no-install' is ignored for $host"
+	  func_warning "assuming \`-no-fast-install' instead"
+	  fast_install=no
+	  ;;
+	*) no_install=yes ;;
+	esac
+	continue
+	;;
+
+      -no-undefined)
+	allow_undefined=no
+	continue
+	;;
+
+      -objectlist)
+	prev=objectlist
+	continue
+	;;
+
+      -o) prev=output ;;
+
+      -precious-files-regex)
+	prev=precious_regex
+	continue
+	;;
+
+      -release)
+	prev=release
+	continue
+	;;
+
+      -rpath)
+	prev=rpath
+	continue
+	;;
+
+      -R)
+	prev=xrpath
+	continue
+	;;
+
+      -R*)
+	func_stripname '-R' '' "$arg"
+	dir=$func_stripname_result
+	# We need an absolute path.
+	case $dir in
+	[\\/]* | [A-Za-z]:[\\/]*) ;;
+	=*)
+	  func_stripname '=' '' "$dir"
+	  dir=$lt_sysroot$func_stripname_result
+	  ;;
+	*)
+	  func_fatal_error "only absolute run-paths are allowed"
+	  ;;
+	esac
+	case "$xrpath " in
+	*" $dir "*) ;;
+	*) func_append xrpath " $dir" ;;
+	esac
+	continue
+	;;
+
+      -shared)
+	# The effects of -shared are defined in a previous loop.
+	continue
+	;;
+
+      -shrext)
+	prev=shrext
+	continue
+	;;
+
+      -static | -static-libtool-libs)
+	# The effects of -static are defined in a previous loop.
+	# We used to do the same as -all-static on platforms that
+	# didn't have a PIC flag, but the assumption that the effects
+	# would be equivalent was wrong.  It would break on at least
+	# Digital Unix and AIX.
+	continue
+	;;
+
+      -thread-safe)
+	thread_safe=yes
+	continue
+	;;
+
+      -version-info)
+	prev=vinfo
+	continue
+	;;
+
+      -version-number)
+	prev=vinfo
+	vinfo_number=yes
+	continue
+	;;
+
+      -weak)
+        prev=weak
+	continue
+	;;
+
+      -Wc,*)
+	func_stripname '-Wc,' '' "$arg"
+	args=$func_stripname_result
+	arg=
+	save_ifs="$IFS"; IFS=','
+	for flag in $args; do
+	  IFS="$save_ifs"
+          func_quote_for_eval "$flag"
+	  func_append arg " $func_quote_for_eval_result"
+	  func_append compiler_flags " $func_quote_for_eval_result"
+	done
+	IFS="$save_ifs"
+	func_stripname ' ' '' "$arg"
+	arg=$func_stripname_result
+	;;
+
+      -Wl,*)
+	func_stripname '-Wl,' '' "$arg"
+	args=$func_stripname_result
+	arg=
+	save_ifs="$IFS"; IFS=','
+	for flag in $args; do
+	  IFS="$save_ifs"
+          func_quote_for_eval "$flag"
+	  func_append arg " $wl$func_quote_for_eval_result"
+	  func_append compiler_flags " $wl$func_quote_for_eval_result"
+	  func_append linker_flags " $func_quote_for_eval_result"
+	done
+	IFS="$save_ifs"
+	func_stripname ' ' '' "$arg"
+	arg=$func_stripname_result
+	;;
+
+      -Xcompiler)
+	prev=xcompiler
+	continue
+	;;
+
+      -Xlinker)
+	prev=xlinker
+	continue
+	;;
+
+      -XCClinker)
+	prev=xcclinker
+	continue
+	;;
+
+      # -msg_* for osf cc
+      -msg_*)
+	func_quote_for_eval "$arg"
+	arg="$func_quote_for_eval_result"
+	;;
+
+      # Flags to be passed through unchanged, with rationale:
+      # -64, -mips[0-9]      enable 64-bit mode for the SGI compiler
+      # -r[0-9][0-9]*        specify processor for the SGI compiler
+      # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
+      # +DA*, +DD*           enable 64-bit mode for the HP compiler
+      # -q*                  compiler args for the IBM compiler
+      # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
+      # -F/path              path to uninstalled frameworks, gcc on darwin
+      # -p, -pg, --coverage, -fprofile-*  profiling flags for GCC
+      # @file                GCC response files
+      # -tp=*                Portland pgcc target processor selection
+      # --sysroot=*          for sysroot support
+      # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+      -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+      -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+      -O*|-flto*|-fwhopr*|-fuse-linker-plugin)
+        func_quote_for_eval "$arg"
+	arg="$func_quote_for_eval_result"
+        func_append compile_command " $arg"
+        func_append finalize_command " $arg"
+        func_append compiler_flags " $arg"
+        continue
+        ;;
+
+      # Some other compiler flag.
+      -* | +*)
+        func_quote_for_eval "$arg"
+	arg="$func_quote_for_eval_result"
+	;;
+
+      *.$objext)
+	# A standard object.
+	func_append objs " $arg"
+	;;
+
+      *.lo)
+	# A libtool-controlled object.
+
+	# Check to see that this really is a libtool object.
+	if func_lalib_unsafe_p "$arg"; then
+	  pic_object=
+	  non_pic_object=
+
+	  # Read the .lo file
+	  func_source "$arg"
+
+	  if test -z "$pic_object" ||
+	     test -z "$non_pic_object" ||
+	     test "$pic_object" = none &&
+	     test "$non_pic_object" = none; then
+	    func_fatal_error "cannot find name of object for \`$arg'"
+	  fi
+
+	  # Extract subdirectory from the argument.
+	  func_dirname "$arg" "/" ""
+	  xdir="$func_dirname_result"
+
+	  if test "$pic_object" != none; then
+	    # Prepend the subdirectory the object is found in.
+	    pic_object="$xdir$pic_object"
+
+	    if test "$prev" = dlfiles; then
+	      if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+		func_append dlfiles " $pic_object"
+		prev=
+		continue
+	      else
+		# If libtool objects are unsupported, then we need to preload.
+		prev=dlprefiles
+	      fi
+	    fi
+
+	    # CHECK ME:  I think I busted this.  -Ossama
+	    if test "$prev" = dlprefiles; then
+	      # Preload the old-style object.
+	      func_append dlprefiles " $pic_object"
+	      prev=
+	    fi
+
+	    # A PIC object.
+	    func_append libobjs " $pic_object"
+	    arg="$pic_object"
+	  fi
+
+	  # Non-PIC object.
+	  if test "$non_pic_object" != none; then
+	    # Prepend the subdirectory the object is found in.
+	    non_pic_object="$xdir$non_pic_object"
+
+	    # A standard non-PIC object
+	    func_append non_pic_objects " $non_pic_object"
+	    if test -z "$pic_object" || test "$pic_object" = none ; then
+	      arg="$non_pic_object"
+	    fi
+	  else
+	    # If the PIC object exists, use it instead.
+	    # $xdir was prepended to $pic_object above.
+	    non_pic_object="$pic_object"
+	    func_append non_pic_objects " $non_pic_object"
+	  fi
+	else
+	  # Only an error if not doing a dry-run.
+	  if $opt_dry_run; then
+	    # Extract subdirectory from the argument.
+	    func_dirname "$arg" "/" ""
+	    xdir="$func_dirname_result"
+
+	    func_lo2o "$arg"
+	    pic_object=$xdir$objdir/$func_lo2o_result
+	    non_pic_object=$xdir$func_lo2o_result
+	    func_append libobjs " $pic_object"
+	    func_append non_pic_objects " $non_pic_object"
+	  else
+	    func_fatal_error "\`$arg' is not a valid libtool object"
+	  fi
+	fi
+	;;
+
+      *.$libext)
+	# An archive.
+	func_append deplibs " $arg"
+	func_append old_deplibs " $arg"
+	continue
+	;;
+
+      *.la)
+	# A libtool-controlled library.
+
+	func_resolve_sysroot "$arg"
+	if test "$prev" = dlfiles; then
+	  # This library was specified with -dlopen.
+	  func_append dlfiles " $func_resolve_sysroot_result"
+	  prev=
+	elif test "$prev" = dlprefiles; then
+	  # The library was specified with -dlpreopen.
+	  func_append dlprefiles " $func_resolve_sysroot_result"
+	  prev=
+	else
+	  func_append deplibs " $func_resolve_sysroot_result"
+	fi
+	continue
+	;;
+
+      # Some other compiler argument.
+      *)
+	# Unknown arguments in both finalize_command and compile_command need
+	# to be aesthetically quoted because they are evaled later.
+	func_quote_for_eval "$arg"
+	arg="$func_quote_for_eval_result"
+	;;
+      esac # arg
+
+      # Now actually substitute the argument into the commands.
+      if test -n "$arg"; then
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+      fi
+    done # argument parsing loop
+
+    test -n "$prev" && \
+      func_fatal_help "the \`$prevarg' option requires an argument"
+
+    if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+      eval arg=\"$export_dynamic_flag_spec\"
+      func_append compile_command " $arg"
+      func_append finalize_command " $arg"
+    fi
+
+    oldlibs=
+    # calculate the name of the file, without its directory
+    func_basename "$output"
+    outputname="$func_basename_result"
+    libobjs_save="$libobjs"
+
+    if test -n "$shlibpath_var"; then
+      # get the directories listed in $shlibpath_var
+      eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\`
+    else
+      shlib_search_path=
+    fi
+    eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+    func_dirname "$output" "/" ""
+    output_objdir="$func_dirname_result$objdir"
+    func_to_tool_file "$output_objdir/"
+    tool_output_objdir=$func_to_tool_file_result
+    # Create the object directory.
+    func_mkdir_p "$output_objdir"
+
+    # Determine the type of output
+    case $output in
+    "")
+      func_fatal_help "you must specify an output file"
+      ;;
+    *.$libext) linkmode=oldlib ;;
+    *.lo | *.$objext) linkmode=obj ;;
+    *.la) linkmode=lib ;;
+    *) linkmode=prog ;; # Anything else should be a program.
+    esac
+
+    specialdeplibs=
+
+    libs=
+    # Find all interdependent deplibs by searching for libraries
+    # that are linked more than once (e.g. -la -lb -la)
+    for deplib in $deplibs; do
+      if $opt_preserve_dup_deps ; then
+	case "$libs " in
+	*" $deplib "*) func_append specialdeplibs " $deplib" ;;
+	esac
+      fi
+      func_append libs " $deplib"
+    done
+
+    if test "$linkmode" = lib; then
+      libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+      # Compute libraries that are listed more than once in $predeps
+      # $postdeps and mark them as special (i.e., whose duplicates are
+      # not to be eliminated).
+      pre_post_deps=
+      if $opt_duplicate_compiler_generated_deps; then
+	for pre_post_dep in $predeps $postdeps; do
+	  case "$pre_post_deps " in
+	  *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;;
+	  esac
+	  func_append pre_post_deps " $pre_post_dep"
+	done
+      fi
+      pre_post_deps=
+    fi
+
+    deplibs=
+    newdependency_libs=
+    newlib_search_path=
+    need_relink=no # whether we're linking any uninstalled libtool libraries
+    notinst_deplibs= # not-installed libtool libraries
+    notinst_path= # paths that contain not-installed libtool libraries
+
+    case $linkmode in
+    lib)
+	passes="conv dlpreopen link"
+	for file in $dlfiles $dlprefiles; do
+	  case $file in
+	  *.la) ;;
+	  *)
+	    func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file"
+	    ;;
+	  esac
+	done
+	;;
+    prog)
+	compile_deplibs=
+	finalize_deplibs=
+	alldeplibs=no
+	newdlfiles=
+	newdlprefiles=
+	passes="conv scan dlopen dlpreopen link"
+	;;
+    *)  passes="conv"
+	;;
+    esac
+
+    for pass in $passes; do
+      # The preopen pass in lib mode reverses $deplibs; put it back here
+      # so that -L comes before libs that need it for instance...
+      if test "$linkmode,$pass" = "lib,link"; then
+	## FIXME: Find the place where the list is rebuilt in the wrong
+	##        order, and fix it there properly
+        tmp_deplibs=
+	for deplib in $deplibs; do
+	  tmp_deplibs="$deplib $tmp_deplibs"
+	done
+	deplibs="$tmp_deplibs"
+      fi
+
+      if test "$linkmode,$pass" = "lib,link" ||
+	 test "$linkmode,$pass" = "prog,scan"; then
+	libs="$deplibs"
+	deplibs=
+      fi
+      if test "$linkmode" = prog; then
+	case $pass in
+	dlopen) libs="$dlfiles" ;;
+	dlpreopen) libs="$dlprefiles" ;;
+	link)
+	  libs="$deplibs %DEPLIBS%"
+	  test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs"
+	  ;;
+	esac
+      fi
+      if test "$linkmode,$pass" = "lib,dlpreopen"; then
+	# Collect and forward deplibs of preopened libtool libs
+	for lib in $dlprefiles; do
+	  # Ignore non-libtool-libs
+	  dependency_libs=
+	  func_resolve_sysroot "$lib"
+	  case $lib in
+	  *.la)	func_source "$func_resolve_sysroot_result" ;;
+	  esac
+
+	  # Collect preopened libtool deplibs, except any this library
+	  # has declared as weak libs
+	  for deplib in $dependency_libs; do
+	    func_basename "$deplib"
+            deplib_base=$func_basename_result
+	    case " $weak_libs " in
+	    *" $deplib_base "*) ;;
+	    *) func_append deplibs " $deplib" ;;
+	    esac
+	  done
+	done
+	libs="$dlprefiles"
+      fi
+      if test "$pass" = dlopen; then
+	# Collect dlpreopened libraries
+	save_deplibs="$deplibs"
+	deplibs=
+      fi
+
+      for deplib in $libs; do
+	lib=
+	found=no
+	case $deplib in
+	-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+        |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+	  if test "$linkmode,$pass" = "prog,link"; then
+	    compile_deplibs="$deplib $compile_deplibs"
+	    finalize_deplibs="$deplib $finalize_deplibs"
+	  else
+	    func_append compiler_flags " $deplib"
+	    if test "$linkmode" = lib ; then
+		case "$new_inherited_linker_flags " in
+		    *" $deplib "*) ;;
+		    * ) func_append new_inherited_linker_flags " $deplib" ;;
+		esac
+	    fi
+	  fi
+	  continue
+	  ;;
+	-l*)
+	  if test "$linkmode" != lib && test "$linkmode" != prog; then
+	    func_warning "\`-l' is ignored for archives/objects"
+	    continue
+	  fi
+	  func_stripname '-l' '' "$deplib"
+	  name=$func_stripname_result
+	  if test "$linkmode" = lib; then
+	    searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+	  else
+	    searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+	  fi
+	  for searchdir in $searchdirs; do
+	    for search_ext in .la $std_shrext .so .a; do
+	      # Search the libtool library
+	      lib="$searchdir/lib${name}${search_ext}"
+	      if test -f "$lib"; then
+		if test "$search_ext" = ".la"; then
+		  found=yes
+		else
+		  found=no
+		fi
+		break 2
+	      fi
+	    done
+	  done
+	  if test "$found" != yes; then
+	    # deplib doesn't seem to be a libtool library
+	    if test "$linkmode,$pass" = "prog,link"; then
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    else
+	      deplibs="$deplib $deplibs"
+	      test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+	    fi
+	    continue
+	  else # deplib is a libtool library
+	    # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+	    # We need to do some special things here, and not later.
+	    if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+	      case " $predeps $postdeps " in
+	      *" $deplib "*)
+		if func_lalib_p "$lib"; then
+		  library_names=
+		  old_library=
+		  func_source "$lib"
+		  for l in $old_library $library_names; do
+		    ll="$l"
+		  done
+		  if test "X$ll" = "X$old_library" ; then # only static version available
+		    found=no
+		    func_dirname "$lib" "" "."
+		    ladir="$func_dirname_result"
+		    lib=$ladir/$old_library
+		    if test "$linkmode,$pass" = "prog,link"; then
+		      compile_deplibs="$deplib $compile_deplibs"
+		      finalize_deplibs="$deplib $finalize_deplibs"
+		    else
+		      deplibs="$deplib $deplibs"
+		      test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+		    fi
+		    continue
+		  fi
+		fi
+		;;
+	      *) ;;
+	      esac
+	    fi
+	  fi
+	  ;; # -l
+	*.ltframework)
+	  if test "$linkmode,$pass" = "prog,link"; then
+	    compile_deplibs="$deplib $compile_deplibs"
+	    finalize_deplibs="$deplib $finalize_deplibs"
+	  else
+	    deplibs="$deplib $deplibs"
+	    if test "$linkmode" = lib ; then
+		case "$new_inherited_linker_flags " in
+		    *" $deplib "*) ;;
+		    * ) func_append new_inherited_linker_flags " $deplib" ;;
+		esac
+	    fi
+	  fi
+	  continue
+	  ;;
+	-L*)
+	  case $linkmode in
+	  lib)
+	    deplibs="$deplib $deplibs"
+	    test "$pass" = conv && continue
+	    newdependency_libs="$deplib $newdependency_libs"
+	    func_stripname '-L' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    func_append newlib_search_path " $func_resolve_sysroot_result"
+	    ;;
+	  prog)
+	    if test "$pass" = conv; then
+	      deplibs="$deplib $deplibs"
+	      continue
+	    fi
+	    if test "$pass" = scan; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    fi
+	    func_stripname '-L' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    func_append newlib_search_path " $func_resolve_sysroot_result"
+	    ;;
+	  *)
+	    func_warning "\`-L' is ignored for archives/objects"
+	    ;;
+	  esac # linkmode
+	  continue
+	  ;; # -L
+	-R*)
+	  if test "$pass" = link; then
+	    func_stripname '-R' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    dir=$func_resolve_sysroot_result
+	    # Make sure the xrpath contains only unique directories.
+	    case "$xrpath " in
+	    *" $dir "*) ;;
+	    *) func_append xrpath " $dir" ;;
+	    esac
+	  fi
+	  deplibs="$deplib $deplibs"
+	  continue
+	  ;;
+	*.la)
+	  func_resolve_sysroot "$deplib"
+	  lib=$func_resolve_sysroot_result
+	  ;;
+	*.$libext)
+	  if test "$pass" = conv; then
+	    deplibs="$deplib $deplibs"
+	    continue
+	  fi
+	  case $linkmode in
+	  lib)
+	    # Linking convenience modules into shared libraries is allowed,
+	    # but linking other static libraries is non-portable.
+	    case " $dlpreconveniencelibs " in
+	    *" $deplib "*) ;;
+	    *)
+	      valid_a_lib=no
+	      case $deplibs_check_method in
+		match_pattern*)
+		  set dummy $deplibs_check_method; shift
+		  match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+		  if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
+		    | $EGREP "$match_pattern_regex" > /dev/null; then
+		    valid_a_lib=yes
+		  fi
+		;;
+		pass_all)
+		  valid_a_lib=yes
+		;;
+	      esac
+	      if test "$valid_a_lib" != yes; then
+		echo
+		$ECHO "*** Warning: Trying to link with static lib archive $deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because the file extensions .$libext of this argument makes me believe"
+		echo "*** that it is just a static archive that I should not use here."
+	      else
+		echo
+		$ECHO "*** Warning: Linking the shared library $output against the"
+		$ECHO "*** static library $deplib is not portable!"
+		deplibs="$deplib $deplibs"
+	      fi
+	      ;;
+	    esac
+	    continue
+	    ;;
+	  prog)
+	    if test "$pass" != link; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    fi
+	    continue
+	    ;;
+	  esac # linkmode
+	  ;; # *.$libext
+	*.lo | *.$objext)
+	  if test "$pass" = conv; then
+	    deplibs="$deplib $deplibs"
+	  elif test "$linkmode" = prog; then
+	    if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+	      # If there is no dlopen support or we're linking statically,
+	      # we need to preload.
+	      func_append newdlprefiles " $deplib"
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    else
+	      func_append newdlfiles " $deplib"
+	    fi
+	  fi
+	  continue
+	  ;;
+	%DEPLIBS%)
+	  alldeplibs=yes
+	  continue
+	  ;;
+	esac # case $deplib
+
+	if test "$found" = yes || test -f "$lib"; then :
+	else
+	  func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'"
+	fi
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$lib" \
+	  || func_fatal_error "\`$lib' is not a valid libtool archive"
+
+	func_dirname "$lib" "" "."
+	ladir="$func_dirname_result"
+
+	dlname=
+	dlopen=
+	dlpreopen=
+	libdir=
+	library_names=
+	old_library=
+	inherited_linker_flags=
+	# If the library was installed with an old release of libtool,
+	# it will not redefine variables installed, or shouldnotlink
+	installed=yes
+	shouldnotlink=no
+	avoidtemprpath=
+
+
+	# Read the .la file
+	func_source "$lib"
+
+	# Convert "-framework foo" to "foo.ltframework"
+	if test -n "$inherited_linker_flags"; then
+	  tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'`
+	  for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+	    case " $new_inherited_linker_flags " in
+	      *" $tmp_inherited_linker_flag "*) ;;
+	      *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
+	    esac
+	  done
+	fi
+	dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	if test "$linkmode,$pass" = "lib,link" ||
+	   test "$linkmode,$pass" = "prog,scan" ||
+	   { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+	  test -n "$dlopen" && func_append dlfiles " $dlopen"
+	  test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
+	fi
+
+	if test "$pass" = conv; then
+	  # Only check for convenience libraries
+	  deplibs="$lib $deplibs"
+	  if test -z "$libdir"; then
+	    if test -z "$old_library"; then
+	      func_fatal_error "cannot find name of link library for \`$lib'"
+	    fi
+	    # It is a libtool convenience library, so add in its objects.
+	    func_append convenience " $ladir/$objdir/$old_library"
+	    func_append old_convenience " $ladir/$objdir/$old_library"
+	    tmp_libs=
+	    for deplib in $dependency_libs; do
+	      deplibs="$deplib $deplibs"
+	      if $opt_preserve_dup_deps ; then
+		case "$tmp_libs " in
+		*" $deplib "*) func_append specialdeplibs " $deplib" ;;
+		esac
+	      fi
+	      func_append tmp_libs " $deplib"
+	    done
+	  elif test "$linkmode" != prog && test "$linkmode" != lib; then
+	    func_fatal_error "\`$lib' is not a convenience library"
+	  fi
+	  continue
+	fi # $pass = conv
+
+
+	# Get the name of the library we link against.
+	linklib=
+	if test -n "$old_library" &&
+	   { test "$prefer_static_libs" = yes ||
+	     test "$prefer_static_libs,$installed" = "built,no"; }; then
+	  linklib=$old_library
+	else
+	  for l in $old_library $library_names; do
+	    linklib="$l"
+	  done
+	fi
+	if test -z "$linklib"; then
+	  func_fatal_error "cannot find name of link library for \`$lib'"
+	fi
+
+	# This library was specified with -dlopen.
+	if test "$pass" = dlopen; then
+	  if test -z "$libdir"; then
+	    func_fatal_error "cannot -dlopen a convenience library: \`$lib'"
+	  fi
+	  if test -z "$dlname" ||
+	     test "$dlopen_support" != yes ||
+	     test "$build_libtool_libs" = no; then
+	    # If there is no dlname, no dlopen support or we're linking
+	    # statically, we need to preload.  We also need to preload any
+	    # dependent libraries so libltdl's deplib preloader doesn't
+	    # bomb out in the load deplibs phase.
+	    func_append dlprefiles " $lib $dependency_libs"
+	  else
+	    func_append newdlfiles " $lib"
+	  fi
+	  continue
+	fi # $pass = dlopen
+
+	# We need an absolute path.
+	case $ladir in
+	[\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+	*)
+	  abs_ladir=`cd "$ladir" && pwd`
+	  if test -z "$abs_ladir"; then
+	    func_warning "cannot determine absolute directory name of \`$ladir'"
+	    func_warning "passing it literally to the linker, although it might fail"
+	    abs_ladir="$ladir"
+	  fi
+	  ;;
+	esac
+	func_basename "$lib"
+	laname="$func_basename_result"
+
+	# Find the relevant object directory and library name.
+	if test "X$installed" = Xyes; then
+	  if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+	    func_warning "library \`$lib' was moved."
+	    dir="$ladir"
+	    absdir="$abs_ladir"
+	    libdir="$abs_ladir"
+	  else
+	    dir="$lt_sysroot$libdir"
+	    absdir="$lt_sysroot$libdir"
+	  fi
+	  test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes
+	else
+	  if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+	    dir="$ladir"
+	    absdir="$abs_ladir"
+	    # Remove this search path later
+	    func_append notinst_path " $abs_ladir"
+	  else
+	    dir="$ladir/$objdir"
+	    absdir="$abs_ladir/$objdir"
+	    # Remove this search path later
+	    func_append notinst_path " $abs_ladir"
+	  fi
+	fi # $installed = yes
+	func_stripname 'lib' '.la' "$laname"
+	name=$func_stripname_result
+
+	# This library was specified with -dlpreopen.
+	if test "$pass" = dlpreopen; then
+	  if test -z "$libdir" && test "$linkmode" = prog; then
+	    func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'"
+	  fi
+	  case "$host" in
+	    # special handling for platforms with PE-DLLs.
+	    *cygwin* | *mingw* | *cegcc* )
+	      # Linker will automatically link against shared library if both
+	      # static and shared are present.  Therefore, ensure we extract
+	      # symbols from the import library if a shared library is present
+	      # (otherwise, the dlopen module name will be incorrect).  We do
+	      # this by putting the import library name into $newdlprefiles.
+	      # We recover the dlopen module name by 'saving' the la file
+	      # name in a special purpose variable, and (later) extracting the
+	      # dlname from the la file.
+	      if test -n "$dlname"; then
+	        func_tr_sh "$dir/$linklib"
+	        eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
+	        func_append newdlprefiles " $dir/$linklib"
+	      else
+	        func_append newdlprefiles " $dir/$old_library"
+	        # Keep a list of preopened convenience libraries to check
+	        # that they are being used correctly in the link pass.
+	        test -z "$libdir" && \
+	          func_append dlpreconveniencelibs " $dir/$old_library"
+	      fi
+	    ;;
+	    * )
+	      # Prefer using a static library (so that no silly _DYNAMIC symbols
+	      # are required to link).
+	      if test -n "$old_library"; then
+	        func_append newdlprefiles " $dir/$old_library"
+	        # Keep a list of preopened convenience libraries to check
+	        # that they are being used correctly in the link pass.
+	        test -z "$libdir" && \
+	          func_append dlpreconveniencelibs " $dir/$old_library"
+	      # Otherwise, use the dlname, so that lt_dlopen finds it.
+	      elif test -n "$dlname"; then
+	        func_append newdlprefiles " $dir/$dlname"
+	      else
+	        func_append newdlprefiles " $dir/$linklib"
+	      fi
+	    ;;
+	  esac
+	fi # $pass = dlpreopen
+
+	if test -z "$libdir"; then
+	  # Link the convenience library
+	  if test "$linkmode" = lib; then
+	    deplibs="$dir/$old_library $deplibs"
+	  elif test "$linkmode,$pass" = "prog,link"; then
+	    compile_deplibs="$dir/$old_library $compile_deplibs"
+	    finalize_deplibs="$dir/$old_library $finalize_deplibs"
+	  else
+	    deplibs="$lib $deplibs" # used for prog,scan pass
+	  fi
+	  continue
+	fi
+
+
+	if test "$linkmode" = prog && test "$pass" != link; then
+	  func_append newlib_search_path " $ladir"
+	  deplibs="$lib $deplibs"
+
+	  linkalldeplibs=no
+	  if test "$link_all_deplibs" != no || test -z "$library_names" ||
+	     test "$build_libtool_libs" = no; then
+	    linkalldeplibs=yes
+	  fi
+
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    case $deplib in
+	    -L*) func_stripname '-L' '' "$deplib"
+	         func_resolve_sysroot "$func_stripname_result"
+	         func_append newlib_search_path " $func_resolve_sysroot_result"
+		 ;;
+	    esac
+	    # Need to link against all dependency_libs?
+	    if test "$linkalldeplibs" = yes; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      # Need to hardcode shared library paths
+	      # or/and link against static libraries
+	      newdependency_libs="$deplib $newdependency_libs"
+	    fi
+	    if $opt_preserve_dup_deps ; then
+	      case "$tmp_libs " in
+	      *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+	      esac
+	    fi
+	    func_append tmp_libs " $deplib"
+	  done # for deplib
+	  continue
+	fi # $linkmode = prog...
+
+	if test "$linkmode,$pass" = "prog,link"; then
+	  if test -n "$library_names" &&
+	     { { test "$prefer_static_libs" = no ||
+	         test "$prefer_static_libs,$installed" = "built,yes"; } ||
+	       test -z "$old_library"; }; then
+	    # We need to hardcode the library path
+	    if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
+	      # Make sure the rpath contains only unique directories.
+	      case "$temp_rpath:" in
+	      *"$absdir:"*) ;;
+	      *) func_append temp_rpath "$absdir:" ;;
+	      esac
+	    fi
+
+	    # Hardcode the library path.
+	    # Skip directories that are in the system default run-time
+	    # search path.
+	    case " $sys_lib_dlsearch_path " in
+	    *" $absdir "*) ;;
+	    *)
+	      case "$compile_rpath " in
+	      *" $absdir "*) ;;
+	      *) func_append compile_rpath " $absdir" ;;
+	      esac
+	      ;;
+	    esac
+	    case " $sys_lib_dlsearch_path " in
+	    *" $libdir "*) ;;
+	    *)
+	      case "$finalize_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append finalize_rpath " $libdir" ;;
+	      esac
+	      ;;
+	    esac
+	  fi # $linkmode,$pass = prog,link...
+
+	  if test "$alldeplibs" = yes &&
+	     { test "$deplibs_check_method" = pass_all ||
+	       { test "$build_libtool_libs" = yes &&
+		 test -n "$library_names"; }; }; then
+	    # We only need to search for static libraries
+	    continue
+	  fi
+	fi
+
+	link_static=no # Whether the deplib will be linked statically
+	use_static_libs=$prefer_static_libs
+	if test "$use_static_libs" = built && test "$installed" = yes; then
+	  use_static_libs=no
+	fi
+	if test -n "$library_names" &&
+	   { test "$use_static_libs" = no || test -z "$old_library"; }; then
+	  case $host in
+	  *cygwin* | *mingw* | *cegcc*)
+	      # No point in relinking DLLs because paths are not encoded
+	      func_append notinst_deplibs " $lib"
+	      need_relink=no
+	    ;;
+	  *)
+	    if test "$installed" = no; then
+	      func_append notinst_deplibs " $lib"
+	      need_relink=yes
+	    fi
+	    ;;
+	  esac
+	  # This is a shared library
+
+	  # Warn about portability, can't link against -module's on some
+	  # systems (darwin).  Don't bleat about dlopened modules though!
+	  dlopenmodule=""
+	  for dlpremoduletest in $dlprefiles; do
+	    if test "X$dlpremoduletest" = "X$lib"; then
+	      dlopenmodule="$dlpremoduletest"
+	      break
+	    fi
+	  done
+	  if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then
+	    echo
+	    if test "$linkmode" = prog; then
+	      $ECHO "*** Warning: Linking the executable $output against the loadable module"
+	    else
+	      $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+	    fi
+	    $ECHO "*** $linklib is not portable!"
+	  fi
+	  if test "$linkmode" = lib &&
+	     test "$hardcode_into_libs" = yes; then
+	    # Hardcode the library path.
+	    # Skip directories that are in the system default run-time
+	    # search path.
+	    case " $sys_lib_dlsearch_path " in
+	    *" $absdir "*) ;;
+	    *)
+	      case "$compile_rpath " in
+	      *" $absdir "*) ;;
+	      *) func_append compile_rpath " $absdir" ;;
+	      esac
+	      ;;
+	    esac
+	    case " $sys_lib_dlsearch_path " in
+	    *" $libdir "*) ;;
+	    *)
+	      case "$finalize_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append finalize_rpath " $libdir" ;;
+	      esac
+	      ;;
+	    esac
+	  fi
+
+	  if test -n "$old_archive_from_expsyms_cmds"; then
+	    # figure out the soname
+	    set dummy $library_names
+	    shift
+	    realname="$1"
+	    shift
+	    libname=`eval "\\$ECHO \"$libname_spec\""`
+	    # use dlname if we got it. it's perfectly good, no?
+	    if test -n "$dlname"; then
+	      soname="$dlname"
+	    elif test -n "$soname_spec"; then
+	      # bleh windows
+	      case $host in
+	      *cygwin* | mingw* | *cegcc*)
+	        func_arith $current - $age
+		major=$func_arith_result
+		versuffix="-$major"
+		;;
+	      esac
+	      eval soname=\"$soname_spec\"
+	    else
+	      soname="$realname"
+	    fi
+
+	    # Make a new name for the extract_expsyms_cmds to use
+	    soroot="$soname"
+	    func_basename "$soroot"
+	    soname="$func_basename_result"
+	    func_stripname 'lib' '.dll' "$soname"
+	    newlib=libimp-$func_stripname_result.a
+
+	    # If the library has no export list, then create one now
+	    if test -f "$output_objdir/$soname-def"; then :
+	    else
+	      func_verbose "extracting exported symbol list from \`$soname'"
+	      func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+	    fi
+
+	    # Create $newlib
+	    if test -f "$output_objdir/$newlib"; then :; else
+	      func_verbose "generating import library for \`$soname'"
+	      func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+	    fi
+	    # make sure the library variables are pointing to the new library
+	    dir=$output_objdir
+	    linklib=$newlib
+	  fi # test -n "$old_archive_from_expsyms_cmds"
+
+	  if test "$linkmode" = prog || test "$opt_mode" != relink; then
+	    add_shlibpath=
+	    add_dir=
+	    add=
+	    lib_linked=yes
+	    case $hardcode_action in
+	    immediate | unsupported)
+	      if test "$hardcode_direct" = no; then
+		add="$dir/$linklib"
+		case $host in
+		  *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;;
+		  *-*-sysv4*uw2*) add_dir="-L$dir" ;;
+		  *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+		    *-*-unixware7*) add_dir="-L$dir" ;;
+		  *-*-darwin* )
+		    # if the lib is a (non-dlopened) module then we can not
+		    # link against it, someone is ignoring the earlier warnings
+		    if /usr/bin/file -L $add 2> /dev/null |
+			 $GREP ": [^:]* bundle" >/dev/null ; then
+		      if test "X$dlopenmodule" != "X$lib"; then
+			$ECHO "*** Warning: lib $linklib is a module, not a shared library"
+			if test -z "$old_library" ; then
+			  echo
+			  echo "*** And there doesn't seem to be a static archive available"
+			  echo "*** The link will probably fail, sorry"
+			else
+			  add="$dir/$old_library"
+			fi
+		      elif test -n "$old_library"; then
+			add="$dir/$old_library"
+		      fi
+		    fi
+		esac
+	      elif test "$hardcode_minus_L" = no; then
+		case $host in
+		*-*-sunos*) add_shlibpath="$dir" ;;
+		esac
+		add_dir="-L$dir"
+		add="-l$name"
+	      elif test "$hardcode_shlibpath_var" = no; then
+		add_shlibpath="$dir"
+		add="-l$name"
+	      else
+		lib_linked=no
+	      fi
+	      ;;
+	    relink)
+	      if test "$hardcode_direct" = yes &&
+	         test "$hardcode_direct_absolute" = no; then
+		add="$dir/$linklib"
+	      elif test "$hardcode_minus_L" = yes; then
+		add_dir="-L$absdir"
+		# Try looking first in the location we're being installed to.
+		if test -n "$inst_prefix_dir"; then
+		  case $libdir in
+		    [\\/]*)
+		      func_append add_dir " -L$inst_prefix_dir$libdir"
+		      ;;
+		  esac
+		fi
+		add="-l$name"
+	      elif test "$hardcode_shlibpath_var" = yes; then
+		add_shlibpath="$dir"
+		add="-l$name"
+	      else
+		lib_linked=no
+	      fi
+	      ;;
+	    *) lib_linked=no ;;
+	    esac
+
+	    if test "$lib_linked" != yes; then
+	      func_fatal_configuration "unsupported hardcode properties"
+	    fi
+
+	    if test -n "$add_shlibpath"; then
+	      case :$compile_shlibpath: in
+	      *":$add_shlibpath:"*) ;;
+	      *) func_append compile_shlibpath "$add_shlibpath:" ;;
+	      esac
+	    fi
+	    if test "$linkmode" = prog; then
+	      test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+	      test -n "$add" && compile_deplibs="$add $compile_deplibs"
+	    else
+	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
+	      test -n "$add" && deplibs="$add $deplibs"
+	      if test "$hardcode_direct" != yes &&
+		 test "$hardcode_minus_L" != yes &&
+		 test "$hardcode_shlibpath_var" = yes; then
+		case :$finalize_shlibpath: in
+		*":$libdir:"*) ;;
+		*) func_append finalize_shlibpath "$libdir:" ;;
+		esac
+	      fi
+	    fi
+	  fi
+
+	  if test "$linkmode" = prog || test "$opt_mode" = relink; then
+	    add_shlibpath=
+	    add_dir=
+	    add=
+	    # Finalize command for both is simple: just hardcode it.
+	    if test "$hardcode_direct" = yes &&
+	       test "$hardcode_direct_absolute" = no; then
+	      add="$libdir/$linklib"
+	    elif test "$hardcode_minus_L" = yes; then
+	      add_dir="-L$libdir"
+	      add="-l$name"
+	    elif test "$hardcode_shlibpath_var" = yes; then
+	      case :$finalize_shlibpath: in
+	      *":$libdir:"*) ;;
+	      *) func_append finalize_shlibpath "$libdir:" ;;
+	      esac
+	      add="-l$name"
+	    elif test "$hardcode_automatic" = yes; then
+	      if test -n "$inst_prefix_dir" &&
+		 test -f "$inst_prefix_dir$libdir/$linklib" ; then
+		add="$inst_prefix_dir$libdir/$linklib"
+	      else
+		add="$libdir/$linklib"
+	      fi
+	    else
+	      # We cannot seem to hardcode it, guess we'll fake it.
+	      add_dir="-L$libdir"
+	      # Try looking first in the location we're being installed to.
+	      if test -n "$inst_prefix_dir"; then
+		case $libdir in
+		  [\\/]*)
+		    func_append add_dir " -L$inst_prefix_dir$libdir"
+		    ;;
+		esac
+	      fi
+	      add="-l$name"
+	    fi
+
+	    if test "$linkmode" = prog; then
+	      test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+	      test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+	    else
+	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
+	      test -n "$add" && deplibs="$add $deplibs"
+	    fi
+	  fi
+	elif test "$linkmode" = prog; then
+	  # Here we assume that one of hardcode_direct or hardcode_minus_L
+	  # is not unsupported.  This is valid on all known static and
+	  # shared platforms.
+	  if test "$hardcode_direct" != unsupported; then
+	    test -n "$old_library" && linklib="$old_library"
+	    compile_deplibs="$dir/$linklib $compile_deplibs"
+	    finalize_deplibs="$dir/$linklib $finalize_deplibs"
+	  else
+	    compile_deplibs="-l$name -L$dir $compile_deplibs"
+	    finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+	  fi
+	elif test "$build_libtool_libs" = yes; then
+	  # Not a shared library
+	  if test "$deplibs_check_method" != pass_all; then
+	    # We're trying link a shared library against a static one
+	    # but the system doesn't support it.
+
+	    # Just print a warning and add the library to dependency_libs so
+	    # that the program can be linked against the static library.
+	    echo
+	    $ECHO "*** Warning: This system can not link to static lib archive $lib."
+	    echo "*** I have the capability to make that library automatically link in when"
+	    echo "*** you link to this library.  But I can only do this if you have a"
+	    echo "*** shared version of the library, which you do not appear to have."
+	    if test "$module" = yes; then
+	      echo "*** But as you try to build a module library, libtool will still create "
+	      echo "*** a static module, that should work as long as the dlopening application"
+	      echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+	      if test -z "$global_symbol_pipe"; then
+		echo
+		echo "*** However, this would only work if libtool was able to extract symbol"
+		echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+		echo "*** not find such a program.  So, this module is probably useless."
+		echo "*** \`nm' from GNU binutils and a full rebuild may help."
+	      fi
+	      if test "$build_old_libs" = no; then
+		build_libtool_libs=module
+		build_old_libs=yes
+	      else
+		build_libtool_libs=no
+	      fi
+	    fi
+	  else
+	    deplibs="$dir/$old_library $deplibs"
+	    link_static=yes
+	  fi
+	fi # link shared/static library?
+
+	if test "$linkmode" = lib; then
+	  if test -n "$dependency_libs" &&
+	     { test "$hardcode_into_libs" != yes ||
+	       test "$build_old_libs" = yes ||
+	       test "$link_static" = yes; }; then
+	    # Extract -R from dependency_libs
+	    temp_deplibs=
+	    for libdir in $dependency_libs; do
+	      case $libdir in
+	      -R*) func_stripname '-R' '' "$libdir"
+	           temp_xrpath=$func_stripname_result
+		   case " $xrpath " in
+		   *" $temp_xrpath "*) ;;
+		   *) func_append xrpath " $temp_xrpath";;
+		   esac;;
+	      *) func_append temp_deplibs " $libdir";;
+	      esac
+	    done
+	    dependency_libs="$temp_deplibs"
+	  fi
+
+	  func_append newlib_search_path " $absdir"
+	  # Link against this library
+	  test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+	  # ... and its dependency_libs
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    newdependency_libs="$deplib $newdependency_libs"
+	    case $deplib in
+              -L*) func_stripname '-L' '' "$deplib"
+                   func_resolve_sysroot "$func_stripname_result";;
+              *) func_resolve_sysroot "$deplib" ;;
+            esac
+	    if $opt_preserve_dup_deps ; then
+	      case "$tmp_libs " in
+	      *" $func_resolve_sysroot_result "*)
+                func_append specialdeplibs " $func_resolve_sysroot_result" ;;
+	      esac
+	    fi
+	    func_append tmp_libs " $func_resolve_sysroot_result"
+	  done
+
+	  if test "$link_all_deplibs" != no; then
+	    # Add the search paths of all dependency libraries
+	    for deplib in $dependency_libs; do
+	      path=
+	      case $deplib in
+	      -L*) path="$deplib" ;;
+	      *.la)
+	        func_resolve_sysroot "$deplib"
+	        deplib=$func_resolve_sysroot_result
+	        func_dirname "$deplib" "" "."
+		dir=$func_dirname_result
+		# We need an absolute path.
+		case $dir in
+		[\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+		*)
+		  absdir=`cd "$dir" && pwd`
+		  if test -z "$absdir"; then
+		    func_warning "cannot determine absolute directory name of \`$dir'"
+		    absdir="$dir"
+		  fi
+		  ;;
+		esac
+		if $GREP "^installed=no" $deplib > /dev/null; then
+		case $host in
+		*-*-darwin*)
+		  depdepl=
+		  eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+		  if test -n "$deplibrary_names" ; then
+		    for tmp in $deplibrary_names ; do
+		      depdepl=$tmp
+		    done
+		    if test -f "$absdir/$objdir/$depdepl" ; then
+		      depdepl="$absdir/$objdir/$depdepl"
+		      darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+                      if test -z "$darwin_install_name"; then
+                          darwin_install_name=`${OTOOL64} -L $depdepl  | awk '{if (NR == 2) {print $1;exit}}'`
+                      fi
+		      func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
+		      func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}"
+		      path=
+		    fi
+		  fi
+		  ;;
+		*)
+		  path="-L$absdir/$objdir"
+		  ;;
+		esac
+		else
+		  eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+		  test -z "$libdir" && \
+		    func_fatal_error "\`$deplib' is not a valid libtool archive"
+		  test "$absdir" != "$libdir" && \
+		    func_warning "\`$deplib' seems to be moved"
+
+		  path="-L$absdir"
+		fi
+		;;
+	      esac
+	      case " $deplibs " in
+	      *" $path "*) ;;
+	      *) deplibs="$path $deplibs" ;;
+	      esac
+	    done
+	  fi # link_all_deplibs != no
+	fi # linkmode = lib
+      done # for deplib in $libs
+      if test "$pass" = link; then
+	if test "$linkmode" = "prog"; then
+	  compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+	  finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+	else
+	  compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	fi
+      fi
+      dependency_libs="$newdependency_libs"
+      if test "$pass" = dlpreopen; then
+	# Link the dlpreopened libraries before other libraries
+	for deplib in $save_deplibs; do
+	  deplibs="$deplib $deplibs"
+	done
+      fi
+      if test "$pass" != dlopen; then
+	if test "$pass" != conv; then
+	  # Make sure lib_search_path contains only unique directories.
+	  lib_search_path=
+	  for dir in $newlib_search_path; do
+	    case "$lib_search_path " in
+	    *" $dir "*) ;;
+	    *) func_append lib_search_path " $dir" ;;
+	    esac
+	  done
+	  newlib_search_path=
+	fi
+
+	if test "$linkmode,$pass" != "prog,link"; then
+	  vars="deplibs"
+	else
+	  vars="compile_deplibs finalize_deplibs"
+	fi
+	for var in $vars dependency_libs; do
+	  # Add libraries to $var in reverse order
+	  eval tmp_libs=\"\$$var\"
+	  new_libs=
+	  for deplib in $tmp_libs; do
+	    # FIXME: Pedantically, this is the right thing to do, so
+	    #        that some nasty dependency loop isn't accidentally
+	    #        broken:
+	    #new_libs="$deplib $new_libs"
+	    # Pragmatically, this seems to cause very few problems in
+	    # practice:
+	    case $deplib in
+	    -L*) new_libs="$deplib $new_libs" ;;
+	    -R*) ;;
+	    *)
+	      # And here is the reason: when a library appears more
+	      # than once as an explicit dependence of a library, or
+	      # is implicitly linked in more than once by the
+	      # compiler, it is considered special, and multiple
+	      # occurrences thereof are not removed.  Compare this
+	      # with having the same library being listed as a
+	      # dependency of multiple other libraries: in this case,
+	      # we know (pedantically, we assume) the library does not
+	      # need to be listed more than once, so we keep only the
+	      # last copy.  This is not always right, but it is rare
+	      # enough that we require users that really mean to play
+	      # such unportable linking tricks to link the library
+	      # using -Wl,-lname, so that libtool does not consider it
+	      # for duplicate removal.
+	      case " $specialdeplibs " in
+	      *" $deplib "*) new_libs="$deplib $new_libs" ;;
+	      *)
+		case " $new_libs " in
+		*" $deplib "*) ;;
+		*) new_libs="$deplib $new_libs" ;;
+		esac
+		;;
+	      esac
+	      ;;
+	    esac
+	  done
+	  tmp_libs=
+	  for deplib in $new_libs; do
+	    case $deplib in
+	    -L*)
+	      case " $tmp_libs " in
+	      *" $deplib "*) ;;
+	      *) func_append tmp_libs " $deplib" ;;
+	      esac
+	      ;;
+	    *) func_append tmp_libs " $deplib" ;;
+	    esac
+	  done
+	  eval $var=\"$tmp_libs\"
+	done # for var
+      fi
+      # Last step: remove runtime libs from dependency_libs
+      # (they stay in deplibs)
+      tmp_libs=
+      for i in $dependency_libs ; do
+	case " $predeps $postdeps $compiler_lib_search_path " in
+	*" $i "*)
+	  i=""
+	  ;;
+	esac
+	if test -n "$i" ; then
+	  func_append tmp_libs " $i"
+	fi
+      done
+      dependency_libs=$tmp_libs
+    done # for pass
+    if test "$linkmode" = prog; then
+      dlfiles="$newdlfiles"
+    fi
+    if test "$linkmode" = prog || test "$linkmode" = lib; then
+      dlprefiles="$newdlprefiles"
+    fi
+
+    case $linkmode in
+    oldlib)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+	func_warning "\`-dlopen' is ignored for archives"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+	func_warning "\`-l' and \`-L' are ignored for archives" ;;
+      esac
+
+      test -n "$rpath" && \
+	func_warning "\`-rpath' is ignored for archives"
+
+      test -n "$xrpath" && \
+	func_warning "\`-R' is ignored for archives"
+
+      test -n "$vinfo" && \
+	func_warning "\`-version-info/-version-number' is ignored for archives"
+
+      test -n "$release" && \
+	func_warning "\`-release' is ignored for archives"
+
+      test -n "$export_symbols$export_symbols_regex" && \
+	func_warning "\`-export-symbols' is ignored for archives"
+
+      # Now set the variables for building old libraries.
+      build_libtool_libs=no
+      oldlibs="$output"
+      func_append objs "$old_deplibs"
+      ;;
+
+    lib)
+      # Make sure we only generate libraries of the form `libNAME.la'.
+      case $outputname in
+      lib*)
+	func_stripname 'lib' '.la' "$outputname"
+	name=$func_stripname_result
+	eval shared_ext=\"$shrext_cmds\"
+	eval libname=\"$libname_spec\"
+	;;
+      *)
+	test "$module" = no && \
+	  func_fatal_help "libtool library \`$output' must begin with \`lib'"
+
+	if test "$need_lib_prefix" != no; then
+	  # Add the "lib" prefix for modules if required
+	  func_stripname '' '.la' "$outputname"
+	  name=$func_stripname_result
+	  eval shared_ext=\"$shrext_cmds\"
+	  eval libname=\"$libname_spec\"
+	else
+	  func_stripname '' '.la' "$outputname"
+	  libname=$func_stripname_result
+	fi
+	;;
+      esac
+
+      if test -n "$objs"; then
+	if test "$deplibs_check_method" != pass_all; then
+	  func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs"
+	else
+	  echo
+	  $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+	  $ECHO "*** objects $objs is not portable!"
+	  func_append libobjs " $objs"
+	fi
+      fi
+
+      test "$dlself" != no && \
+	func_warning "\`-dlopen self' is ignored for libtool libraries"
+
+      set dummy $rpath
+      shift
+      test "$#" -gt 1 && \
+	func_warning "ignoring multiple \`-rpath's for a libtool library"
+
+      install_libdir="$1"
+
+      oldlibs=
+      if test -z "$rpath"; then
+	if test "$build_libtool_libs" = yes; then
+	  # Building a libtool convenience library.
+	  # Some compilers have problems with a `.al' extension so
+	  # convenience libraries should have the same extension an
+	  # archive normally would.
+	  oldlibs="$output_objdir/$libname.$libext $oldlibs"
+	  build_libtool_libs=convenience
+	  build_old_libs=yes
+	fi
+
+	test -n "$vinfo" && \
+	  func_warning "\`-version-info/-version-number' is ignored for convenience libraries"
+
+	test -n "$release" && \
+	  func_warning "\`-release' is ignored for convenience libraries"
+      else
+
+	# Parse the version information argument.
+	save_ifs="$IFS"; IFS=':'
+	set dummy $vinfo 0 0 0
+	shift
+	IFS="$save_ifs"
+
+	test -n "$7" && \
+	  func_fatal_help "too many parameters to \`-version-info'"
+
+	# convert absolute version numbers to libtool ages
+	# this retains compatibility with .la files and attempts
+	# to make the code below a bit more comprehensible
+
+	case $vinfo_number in
+	yes)
+	  number_major="$1"
+	  number_minor="$2"
+	  number_revision="$3"
+	  #
+	  # There are really only two kinds -- those that
+	  # use the current revision as the major version
+	  # and those that subtract age and use age as
+	  # a minor version.  But, then there is irix
+	  # which has an extra 1 added just for fun
+	  #
+	  case $version_type in
+	  # correct linux to gnu/linux during the next big refactor
+	  darwin|linux|osf|windows|none)
+	    func_arith $number_major + $number_minor
+	    current=$func_arith_result
+	    age="$number_minor"
+	    revision="$number_revision"
+	    ;;
+	  freebsd-aout|freebsd-elf|qnx|sunos)
+	    current="$number_major"
+	    revision="$number_minor"
+	    age="0"
+	    ;;
+	  irix|nonstopux)
+	    func_arith $number_major + $number_minor
+	    current=$func_arith_result
+	    age="$number_minor"
+	    revision="$number_minor"
+	    lt_irix_increment=no
+	    ;;
+	  *)
+	    func_fatal_configuration "$modename: unknown library version type \`$version_type'"
+	    ;;
+	  esac
+	  ;;
+	no)
+	  current="$1"
+	  revision="$2"
+	  age="$3"
+	  ;;
+	esac
+
+	# Check that each of the things are valid numbers.
+	case $current in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "CURRENT \`$current' must be a nonnegative integer"
+	  func_fatal_error "\`$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	case $revision in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "REVISION \`$revision' must be a nonnegative integer"
+	  func_fatal_error "\`$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	case $age in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "AGE \`$age' must be a nonnegative integer"
+	  func_fatal_error "\`$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	if test "$age" -gt "$current"; then
+	  func_error "AGE \`$age' is greater than the current interface number \`$current'"
+	  func_fatal_error "\`$vinfo' is not valid version information"
+	fi
+
+	# Calculate the version variables.
+	major=
+	versuffix=
+	verstring=
+	case $version_type in
+	none) ;;
+
+	darwin)
+	  # Like Linux, but with the current version available in
+	  # verstring for coding it into the library header
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix="$major.$age.$revision"
+	  # Darwin ld doesn't like 0 for these options...
+	  func_arith $current + 1
+	  minor_current=$func_arith_result
+	  xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+	  verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+	  ;;
+
+	freebsd-aout)
+	  major=".$current"
+	  versuffix=".$current.$revision";
+	  ;;
+
+	freebsd-elf)
+	  major=".$current"
+	  versuffix=".$current"
+	  ;;
+
+	irix | nonstopux)
+	  if test "X$lt_irix_increment" = "Xno"; then
+	    func_arith $current - $age
+	  else
+	    func_arith $current - $age + 1
+	  fi
+	  major=$func_arith_result
+
+	  case $version_type in
+	    nonstopux) verstring_prefix=nonstopux ;;
+	    *)         verstring_prefix=sgi ;;
+	  esac
+	  verstring="$verstring_prefix$major.$revision"
+
+	  # Add in all the interfaces that we are compatible with.
+	  loop=$revision
+	  while test "$loop" -ne 0; do
+	    func_arith $revision - $loop
+	    iface=$func_arith_result
+	    func_arith $loop - 1
+	    loop=$func_arith_result
+	    verstring="$verstring_prefix$major.$iface:$verstring"
+	  done
+
+	  # Before this point, $major must not contain `.'.
+	  major=.$major
+	  versuffix="$major.$revision"
+	  ;;
+
+	linux) # correct to gnu/linux during the next big refactor
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix="$major.$age.$revision"
+	  ;;
+
+	osf)
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix=".$current.$age.$revision"
+	  verstring="$current.$age.$revision"
+
+	  # Add in all the interfaces that we are compatible with.
+	  loop=$age
+	  while test "$loop" -ne 0; do
+	    func_arith $current - $loop
+	    iface=$func_arith_result
+	    func_arith $loop - 1
+	    loop=$func_arith_result
+	    verstring="$verstring:${iface}.0"
+	  done
+
+	  # Make executables depend on our current version.
+	  func_append verstring ":${current}.0"
+	  ;;
+
+	qnx)
+	  major=".$current"
+	  versuffix=".$current"
+	  ;;
+
+	sunos)
+	  major=".$current"
+	  versuffix=".$current.$revision"
+	  ;;
+
+	windows)
+	  # Use '-' rather than '.', since we only want one
+	  # extension on DOS 8.3 filesystems.
+	  func_arith $current - $age
+	  major=$func_arith_result
+	  versuffix="-$major"
+	  ;;
+
+	*)
+	  func_fatal_configuration "unknown library version type \`$version_type'"
+	  ;;
+	esac
+
+	# Clear the version info if we defaulted, and they specified a release.
+	if test -z "$vinfo" && test -n "$release"; then
+	  major=
+	  case $version_type in
+	  darwin)
+	    # we can't check for "0.0" in archive_cmds due to quoting
+	    # problems, so we reset it completely
+	    verstring=
+	    ;;
+	  *)
+	    verstring="0.0"
+	    ;;
+	  esac
+	  if test "$need_version" = no; then
+	    versuffix=
+	  else
+	    versuffix=".0.0"
+	  fi
+	fi
+
+	# Remove version info from name if versioning should be avoided
+	if test "$avoid_version" = yes && test "$need_version" = no; then
+	  major=
+	  versuffix=
+	  verstring=""
+	fi
+
+	# Check to see if the archive will have undefined symbols.
+	if test "$allow_undefined" = yes; then
+	  if test "$allow_undefined_flag" = unsupported; then
+	    func_warning "undefined symbols not allowed in $host shared libraries"
+	    build_libtool_libs=no
+	    build_old_libs=yes
+	  fi
+	else
+	  # Don't allow undefined symbols.
+	  allow_undefined_flag="$no_undefined_flag"
+	fi
+
+      fi
+
+      func_generate_dlsyms "$libname" "$libname" "yes"
+      func_append libobjs " $symfileobj"
+      test "X$libobjs" = "X " && libobjs=
+
+      if test "$opt_mode" != relink; then
+	# Remove our outputs, but don't remove object files since they
+	# may have been created when compiling PIC objects.
+	removelist=
+	tempremovelist=`$ECHO "$output_objdir/*"`
+	for p in $tempremovelist; do
+	  case $p in
+	    *.$objext | *.gcno)
+	       ;;
+	    $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
+	       if test "X$precious_files_regex" != "X"; then
+		 if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+		 then
+		   continue
+		 fi
+	       fi
+	       func_append removelist " $p"
+	       ;;
+	    *) ;;
+	  esac
+	done
+	test -n "$removelist" && \
+	  func_show_eval "${RM}r \$removelist"
+      fi
+
+      # Now set the variables for building old libraries.
+      if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+	func_append oldlibs " $output_objdir/$libname.$libext"
+
+	# Transform .lo files to .o files.
+	oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP`
+      fi
+
+      # Eliminate all temporary directories.
+      #for path in $notinst_path; do
+      #	lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
+      #	deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
+      #	dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"`
+      #done
+
+      if test -n "$xrpath"; then
+	# If the user specified any rpath flags, then add them.
+	temp_xrpath=
+	for libdir in $xrpath; do
+	  func_replace_sysroot "$libdir"
+	  func_append temp_xrpath " -R$func_replace_sysroot_result"
+	  case "$finalize_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_rpath " $libdir" ;;
+	  esac
+	done
+	if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+	  dependency_libs="$temp_xrpath $dependency_libs"
+	fi
+      fi
+
+      # Make sure dlfiles contains only unique files that won't be dlpreopened
+      old_dlfiles="$dlfiles"
+      dlfiles=
+      for lib in $old_dlfiles; do
+	case " $dlprefiles $dlfiles " in
+	*" $lib "*) ;;
+	*) func_append dlfiles " $lib" ;;
+	esac
+      done
+
+      # Make sure dlprefiles contains only unique files
+      old_dlprefiles="$dlprefiles"
+      dlprefiles=
+      for lib in $old_dlprefiles; do
+	case "$dlprefiles " in
+	*" $lib "*) ;;
+	*) func_append dlprefiles " $lib" ;;
+	esac
+      done
+
+      if test "$build_libtool_libs" = yes; then
+	if test -n "$rpath"; then
+	  case $host in
+	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+	    # these systems don't actually have a c library (as such)!
+	    ;;
+	  *-*-rhapsody* | *-*-darwin1.[012])
+	    # Rhapsody C library is in the System framework
+	    func_append deplibs " System.ltframework"
+	    ;;
+	  *-*-netbsd*)
+	    # Don't link with libc until the a.out ld.so is fixed.
+	    ;;
+	  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+	    # Do not include libc due to us having libc/libc_r.
+	    ;;
+	  *-*-sco3.2v5* | *-*-sco5v6*)
+	    # Causes problems with __ctype
+	    ;;
+	  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+	    # Compiler inserts libc in the correct place for threads to work
+	    ;;
+	  *)
+	    # Add libc to deplibs on all other systems if necessary.
+	    if test "$build_libtool_need_lc" = "yes"; then
+	      func_append deplibs " -lc"
+	    fi
+	    ;;
+	  esac
+	fi
+
+	# Transform deplibs into only deplibs that can be linked in shared.
+	name_save=$name
+	libname_save=$libname
+	release_save=$release
+	versuffix_save=$versuffix
+	major_save=$major
+	# I'm not sure if I'm treating the release correctly.  I think
+	# release should show up in the -l (ie -lgmp5) so we don't want to
+	# add it in twice.  Is that correct?
+	release=""
+	versuffix=""
+	major=""
+	newdeplibs=
+	droppeddeps=no
+	case $deplibs_check_method in
+	pass_all)
+	  # Don't check for shared/static.  Everything works.
+	  # This might be a little naive.  We might want to check
+	  # whether the library exists or not.  But this is on
+	  # osf3 & osf4 and I'm not really sure... Just
+	  # implementing what was already the behavior.
+	  newdeplibs=$deplibs
+	  ;;
+	test_compile)
+	  # This code stresses the "libraries are programs" paradigm to its
+	  # limits. Maybe even breaks it.  We compile a program, linking it
+	  # against the deplibs as a proxy for the library.  Then we can check
+	  # whether they linked in statically or dynamically with ldd.
+	  $opt_dry_run || $RM conftest.c
+	  cat > conftest.c <<EOF
+	  int main() { return 0; }
+EOF
+	  $opt_dry_run || $RM conftest
+	  if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+	    ldd_output=`ldd conftest`
+	    for i in $deplibs; do
+	      case $i in
+	      -l*)
+		func_stripname -l '' "$i"
+		name=$func_stripname_result
+		if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		  case " $predeps $postdeps " in
+		  *" $i "*)
+		    func_append newdeplibs " $i"
+		    i=""
+		    ;;
+		  esac
+		fi
+		if test -n "$i" ; then
+		  libname=`eval "\\$ECHO \"$libname_spec\""`
+		  deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+		  set dummy $deplib_matches; shift
+		  deplib_match=$1
+		  if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+		    func_append newdeplibs " $i"
+		  else
+		    droppeddeps=yes
+		    echo
+		    $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+		    echo "*** I have the capability to make that library automatically link in when"
+		    echo "*** you link to this library.  But I can only do this if you have a"
+		    echo "*** shared version of the library, which I believe you do not have"
+		    echo "*** because a test_compile did reveal that the linker did not use it for"
+		    echo "*** its dynamic dependency list that programs get resolved with at runtime."
+		  fi
+		fi
+		;;
+	      *)
+		func_append newdeplibs " $i"
+		;;
+	      esac
+	    done
+	  else
+	    # Error occurred in the first compile.  Let's try to salvage
+	    # the situation: Compile a separate program for each library.
+	    for i in $deplibs; do
+	      case $i in
+	      -l*)
+		func_stripname -l '' "$i"
+		name=$func_stripname_result
+		$opt_dry_run || $RM conftest
+		if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+		  ldd_output=`ldd conftest`
+		  if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		    case " $predeps $postdeps " in
+		    *" $i "*)
+		      func_append newdeplibs " $i"
+		      i=""
+		      ;;
+		    esac
+		  fi
+		  if test -n "$i" ; then
+		    libname=`eval "\\$ECHO \"$libname_spec\""`
+		    deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+		    set dummy $deplib_matches; shift
+		    deplib_match=$1
+		    if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+		      func_append newdeplibs " $i"
+		    else
+		      droppeddeps=yes
+		      echo
+		      $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+		      echo "*** I have the capability to make that library automatically link in when"
+		      echo "*** you link to this library.  But I can only do this if you have a"
+		      echo "*** shared version of the library, which you do not appear to have"
+		      echo "*** because a test_compile did reveal that the linker did not use this one"
+		      echo "*** as a dynamic dependency that programs can get resolved with at runtime."
+		    fi
+		  fi
+		else
+		  droppeddeps=yes
+		  echo
+		  $ECHO "*** Warning!  Library $i is needed by this library but I was not able to"
+		  echo "*** make it link in!  You will probably need to install it or some"
+		  echo "*** library that it depends on before this library will be fully"
+		  echo "*** functional.  Installing it before continuing would be even better."
+		fi
+		;;
+	      *)
+		func_append newdeplibs " $i"
+		;;
+	      esac
+	    done
+	  fi
+	  ;;
+	file_magic*)
+	  set dummy $deplibs_check_method; shift
+	  file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+	  for a_deplib in $deplibs; do
+	    case $a_deplib in
+	    -l*)
+	      func_stripname -l '' "$a_deplib"
+	      name=$func_stripname_result
+	      if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		case " $predeps $postdeps " in
+		*" $a_deplib "*)
+		  func_append newdeplibs " $a_deplib"
+		  a_deplib=""
+		  ;;
+		esac
+	      fi
+	      if test -n "$a_deplib" ; then
+		libname=`eval "\\$ECHO \"$libname_spec\""`
+		if test -n "$file_magic_glob"; then
+		  libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
+		else
+		  libnameglob=$libname
+		fi
+		test "$want_nocaseglob" = yes && nocaseglob=`shopt -p nocaseglob`
+		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+		  if test "$want_nocaseglob" = yes; then
+		    shopt -s nocaseglob
+		    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+		    $nocaseglob
+		  else
+		    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+		  fi
+		  for potent_lib in $potential_libs; do
+		      # Follow soft links.
+		      if ls -lLd "$potent_lib" 2>/dev/null |
+			 $GREP " -> " >/dev/null; then
+			continue
+		      fi
+		      # The statement above tries to avoid entering an
+		      # endless loop below, in case of cyclic links.
+		      # We might still enter an endless loop, since a link
+		      # loop can be closed while we follow links,
+		      # but so what?
+		      potlib="$potent_lib"
+		      while test -h "$potlib" 2>/dev/null; do
+			potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+			case $potliblink in
+			[\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+			*) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";;
+			esac
+		      done
+		      if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+			 $SED -e 10q |
+			 $EGREP "$file_magic_regex" > /dev/null; then
+			func_append newdeplibs " $a_deplib"
+			a_deplib=""
+			break 2
+		      fi
+		  done
+		done
+	      fi
+	      if test -n "$a_deplib" ; then
+		droppeddeps=yes
+		echo
+		$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because I did check the linker path looking for a file starting"
+		if test -z "$potlib" ; then
+		  $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+		else
+		  $ECHO "*** with $libname and none of the candidates passed a file format test"
+		  $ECHO "*** using a file magic. Last file checked: $potlib"
+		fi
+	      fi
+	      ;;
+	    *)
+	      # Add a -L argument.
+	      func_append newdeplibs " $a_deplib"
+	      ;;
+	    esac
+	  done # Gone through all deplibs.
+	  ;;
+	match_pattern*)
+	  set dummy $deplibs_check_method; shift
+	  match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+	  for a_deplib in $deplibs; do
+	    case $a_deplib in
+	    -l*)
+	      func_stripname -l '' "$a_deplib"
+	      name=$func_stripname_result
+	      if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+		case " $predeps $postdeps " in
+		*" $a_deplib "*)
+		  func_append newdeplibs " $a_deplib"
+		  a_deplib=""
+		  ;;
+		esac
+	      fi
+	      if test -n "$a_deplib" ; then
+		libname=`eval "\\$ECHO \"$libname_spec\""`
+		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+		  potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+		  for potent_lib in $potential_libs; do
+		    potlib="$potent_lib" # see symlink-check above in file_magic test
+		    if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
+		       $EGREP "$match_pattern_regex" > /dev/null; then
+		      func_append newdeplibs " $a_deplib"
+		      a_deplib=""
+		      break 2
+		    fi
+		  done
+		done
+	      fi
+	      if test -n "$a_deplib" ; then
+		droppeddeps=yes
+		echo
+		$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because I did check the linker path looking for a file starting"
+		if test -z "$potlib" ; then
+		  $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+		else
+		  $ECHO "*** with $libname and none of the candidates passed a file format test"
+		  $ECHO "*** using a regex pattern. Last file checked: $potlib"
+		fi
+	      fi
+	      ;;
+	    *)
+	      # Add a -L argument.
+	      func_append newdeplibs " $a_deplib"
+	      ;;
+	    esac
+	  done # Gone through all deplibs.
+	  ;;
+	none | unknown | *)
+	  newdeplibs=""
+	  tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
+	  if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+	    for i in $predeps $postdeps ; do
+	      # can't use Xsed below, because $i might contain '/'
+	      tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"`
+	    done
+	  fi
+	  case $tmp_deplibs in
+	  *[!\	\ ]*)
+	    echo
+	    if test "X$deplibs_check_method" = "Xnone"; then
+	      echo "*** Warning: inter-library dependencies are not supported in this platform."
+	    else
+	      echo "*** Warning: inter-library dependencies are not known to be supported."
+	    fi
+	    echo "*** All declared inter-library dependencies are being dropped."
+	    droppeddeps=yes
+	    ;;
+	  esac
+	  ;;
+	esac
+	versuffix=$versuffix_save
+	major=$major_save
+	release=$release_save
+	libname=$libname_save
+	name=$name_save
+
+	case $host in
+	*-*-rhapsody* | *-*-darwin1.[012])
+	  # On Rhapsody replace the C library with the System framework
+	  newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'`
+	  ;;
+	esac
+
+	if test "$droppeddeps" = yes; then
+	  if test "$module" = yes; then
+	    echo
+	    echo "*** Warning: libtool could not satisfy all declared inter-library"
+	    $ECHO "*** dependencies of module $libname.  Therefore, libtool will create"
+	    echo "*** a static module, that should work as long as the dlopening"
+	    echo "*** application is linked with the -dlopen flag."
+	    if test -z "$global_symbol_pipe"; then
+	      echo
+	      echo "*** However, this would only work if libtool was able to extract symbol"
+	      echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+	      echo "*** not find such a program.  So, this module is probably useless."
+	      echo "*** \`nm' from GNU binutils and a full rebuild may help."
+	    fi
+	    if test "$build_old_libs" = no; then
+	      oldlibs="$output_objdir/$libname.$libext"
+	      build_libtool_libs=module
+	      build_old_libs=yes
+	    else
+	      build_libtool_libs=no
+	    fi
+	  else
+	    echo "*** The inter-library dependencies that have been dropped here will be"
+	    echo "*** automatically added whenever a program is linked with this library"
+	    echo "*** or is declared to -dlopen it."
+
+	    if test "$allow_undefined" = no; then
+	      echo
+	      echo "*** Since this library must not contain undefined symbols,"
+	      echo "*** because either the platform does not support them or"
+	      echo "*** it was explicitly requested with -no-undefined,"
+	      echo "*** libtool will only create a static version of it."
+	      if test "$build_old_libs" = no; then
+		oldlibs="$output_objdir/$libname.$libext"
+		build_libtool_libs=module
+		build_old_libs=yes
+	      else
+		build_libtool_libs=no
+	      fi
+	    fi
+	  fi
+	fi
+	# Done checking deplibs!
+	deplibs=$newdeplibs
+      fi
+      # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+      case $host in
+	*-*-darwin*)
+	  newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  ;;
+      esac
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+	case " $new_libs " in
+	*" -L$path/$objdir "*) ;;
+	*)
+	  case " $deplibs " in
+	  *" -L$path/$objdir "*)
+	    func_append new_libs " -L$path/$objdir" ;;
+	  esac
+	  ;;
+	esac
+      done
+      for deplib in $deplibs; do
+	case $deplib in
+	-L*)
+	  case " $new_libs " in
+	  *" $deplib "*) ;;
+	  *) func_append new_libs " $deplib" ;;
+	  esac
+	  ;;
+	*) func_append new_libs " $deplib" ;;
+	esac
+      done
+      deplibs="$new_libs"
+
+      # All the library-specific variables (install_libdir is set above).
+      library_names=
+      old_library=
+      dlname=
+
+      # Test again, we may have decided not to build it any more
+      if test "$build_libtool_libs" = yes; then
+	# Remove ${wl} instances when linking with ld.
+	# FIXME: should test the right _cmds variable.
+	case $archive_cmds in
+	  *\$LD\ *) wl= ;;
+        esac
+	if test "$hardcode_into_libs" = yes; then
+	  # Hardcode the library paths
+	  hardcode_libdirs=
+	  dep_rpath=
+	  rpath="$finalize_rpath"
+	  test "$opt_mode" != relink && rpath="$compile_rpath$rpath"
+	  for libdir in $rpath; do
+	    if test -n "$hardcode_libdir_flag_spec"; then
+	      if test -n "$hardcode_libdir_separator"; then
+		func_replace_sysroot "$libdir"
+		libdir=$func_replace_sysroot_result
+		if test -z "$hardcode_libdirs"; then
+		  hardcode_libdirs="$libdir"
+		else
+		  # Just accumulate the unique libdirs.
+		  case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+		  *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		    ;;
+		  *)
+		    func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		    ;;
+		  esac
+		fi
+	      else
+		eval flag=\"$hardcode_libdir_flag_spec\"
+		func_append dep_rpath " $flag"
+	      fi
+	    elif test -n "$runpath_var"; then
+	      case "$perm_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append perm_rpath " $libdir" ;;
+	      esac
+	    fi
+	  done
+	  # Substitute the hardcoded libdirs into the rpath.
+	  if test -n "$hardcode_libdir_separator" &&
+	     test -n "$hardcode_libdirs"; then
+	    libdir="$hardcode_libdirs"
+	    eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
+	  fi
+	  if test -n "$runpath_var" && test -n "$perm_rpath"; then
+	    # We should set the runpath_var.
+	    rpath=
+	    for dir in $perm_rpath; do
+	      func_append rpath "$dir:"
+	    done
+	    eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+	  fi
+	  test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+	fi
+
+	shlibpath="$finalize_shlibpath"
+	test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+	if test -n "$shlibpath"; then
+	  eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+	fi
+
+	# Get the real and link names of the library.
+	eval shared_ext=\"$shrext_cmds\"
+	eval library_names=\"$library_names_spec\"
+	set dummy $library_names
+	shift
+	realname="$1"
+	shift
+
+	if test -n "$soname_spec"; then
+	  eval soname=\"$soname_spec\"
+	else
+	  soname="$realname"
+	fi
+	if test -z "$dlname"; then
+	  dlname=$soname
+	fi
+
+	lib="$output_objdir/$realname"
+	linknames=
+	for link
+	do
+	  func_append linknames " $link"
+	done
+
+	# Use standard objects if they are pic
+	test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	test "X$libobjs" = "X " && libobjs=
+
+	delfiles=
+	if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	  $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+	  export_symbols="$output_objdir/$libname.uexp"
+	  func_append delfiles " $export_symbols"
+	fi
+
+	orig_export_symbols=
+	case $host_os in
+	cygwin* | mingw* | cegcc*)
+	  if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+	    # exporting using user supplied symfile
+	    if test "x`$SED 1q $export_symbols`" != xEXPORTS; then
+	      # and it's NOT already a .def file. Must figure out
+	      # which of the given symbols are data symbols and tag
+	      # them as such. So, trigger use of export_symbols_cmds.
+	      # export_symbols gets reassigned inside the "prepare
+	      # the list of exported symbols" if statement, so the
+	      # include_expsyms logic still works.
+	      orig_export_symbols="$export_symbols"
+	      export_symbols=
+	      always_export_symbols=yes
+	    fi
+	  fi
+	  ;;
+	esac
+
+	# Prepare the list of exported symbols
+	if test -z "$export_symbols"; then
+	  if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+	    func_verbose "generating symbol list for \`$libname.la'"
+	    export_symbols="$output_objdir/$libname.exp"
+	    $opt_dry_run || $RM $export_symbols
+	    cmds=$export_symbols_cmds
+	    save_ifs="$IFS"; IFS='~'
+	    for cmd1 in $cmds; do
+	      IFS="$save_ifs"
+	      # Take the normal branch if the nm_file_list_spec branch
+	      # doesn't work or if tool conversion is not needed.
+	      case $nm_file_list_spec~$to_tool_file_cmd in
+		*~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
+		  try_normal_branch=yes
+		  eval cmd=\"$cmd1\"
+		  func_len " $cmd"
+		  len=$func_len_result
+		  ;;
+		*)
+		  try_normal_branch=no
+		  ;;
+	      esac
+	      if test "$try_normal_branch" = yes \
+		 && { test "$len" -lt "$max_cmd_len" \
+		      || test "$max_cmd_len" -le -1; }
+	      then
+		func_show_eval "$cmd" 'exit $?'
+		skipped_export=false
+	      elif test -n "$nm_file_list_spec"; then
+		func_basename "$output"
+		output_la=$func_basename_result
+		save_libobjs=$libobjs
+		save_output=$output
+		output=${output_objdir}/${output_la}.nm
+		func_to_tool_file "$output"
+		libobjs=$nm_file_list_spec$func_to_tool_file_result
+		func_append delfiles " $output"
+		func_verbose "creating $NM input file list: $output"
+		for obj in $save_libobjs; do
+		  func_to_tool_file "$obj"
+		  $ECHO "$func_to_tool_file_result"
+		done > "$output"
+		eval cmd=\"$cmd1\"
+		func_show_eval "$cmd" 'exit $?'
+		output=$save_output
+		libobjs=$save_libobjs
+		skipped_export=false
+	      else
+		# The command line is too long to execute in one step.
+		func_verbose "using reloadable object file for export list..."
+		skipped_export=:
+		# Break out early, otherwise skipped_export may be
+		# set to false by a later but shorter cmd.
+		break
+	      fi
+	    done
+	    IFS="$save_ifs"
+	    if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then
+	      func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+	      func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+	    fi
+	  fi
+	fi
+
+	if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	  tmp_export_symbols="$export_symbols"
+	  test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+	  $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+	fi
+
+	if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then
+	  # The given exports_symbols file has to be filtered, so filter it.
+	  func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+	  # FIXME: $output_objdir/$libname.filter potentially contains lots of
+	  # 's' commands which not all seds can handle. GNU sed should be fine
+	  # though. Also, the filter scales superlinearly with the number of
+	  # global variables. join(1) would be nice here, but unfortunately
+	  # isn't a blessed tool.
+	  $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+	  func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+	  export_symbols=$output_objdir/$libname.def
+	  $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+	fi
+
+	tmp_deplibs=
+	for test_deplib in $deplibs; do
+	  case " $convenience " in
+	  *" $test_deplib "*) ;;
+	  *)
+	    func_append tmp_deplibs " $test_deplib"
+	    ;;
+	  esac
+	done
+	deplibs="$tmp_deplibs"
+
+	if test -n "$convenience"; then
+	  if test -n "$whole_archive_flag_spec" &&
+	    test "$compiler_needs_object" = yes &&
+	    test -z "$libobjs"; then
+	    # extract the archives, so we have objects to list.
+	    # TODO: could optimize this to just extract one archive.
+	    whole_archive_flag_spec=
+	  fi
+	  if test -n "$whole_archive_flag_spec"; then
+	    save_libobjs=$libobjs
+	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+	    test "X$libobjs" = "X " && libobjs=
+	  else
+	    gentop="$output_objdir/${outputname}x"
+	    func_append generated " $gentop"
+
+	    func_extract_archives $gentop $convenience
+	    func_append libobjs " $func_extract_archives_result"
+	    test "X$libobjs" = "X " && libobjs=
+	  fi
+	fi
+
+	if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+	  eval flag=\"$thread_safe_flag_spec\"
+	  func_append linker_flags " $flag"
+	fi
+
+	# Make a backup of the uninstalled library when relinking
+	if test "$opt_mode" = relink; then
+	  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+	fi
+
+	# Do each of the archive commands.
+	if test "$module" = yes && test -n "$module_cmds" ; then
+	  if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+	    eval test_cmds=\"$module_expsym_cmds\"
+	    cmds=$module_expsym_cmds
+	  else
+	    eval test_cmds=\"$module_cmds\"
+	    cmds=$module_cmds
+	  fi
+	else
+	  if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+	    eval test_cmds=\"$archive_expsym_cmds\"
+	    cmds=$archive_expsym_cmds
+	  else
+	    eval test_cmds=\"$archive_cmds\"
+	    cmds=$archive_cmds
+	  fi
+	fi
+
+	if test "X$skipped_export" != "X:" &&
+	   func_len " $test_cmds" &&
+	   len=$func_len_result &&
+	   test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	  :
+	else
+	  # The command line is too long to link in one step, link piecewise
+	  # or, if using GNU ld and skipped_export is not :, use a linker
+	  # script.
+
+	  # Save the value of $output and $libobjs because we want to
+	  # use them later.  If we have whole_archive_flag_spec, we
+	  # want to use save_libobjs as it was before
+	  # whole_archive_flag_spec was expanded, because we can't
+	  # assume the linker understands whole_archive_flag_spec.
+	  # This may have to be revisited, in case too many
+	  # convenience libraries get linked in and end up exceeding
+	  # the spec.
+	  if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+	    save_libobjs=$libobjs
+	  fi
+	  save_output=$output
+	  func_basename "$output"
+	  output_la=$func_basename_result
+
+	  # Clear the reloadable object creation command queue and
+	  # initialize k to one.
+	  test_cmds=
+	  concat_cmds=
+	  objlist=
+	  last_robj=
+	  k=1
+
+	  if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then
+	    output=${output_objdir}/${output_la}.lnkscript
+	    func_verbose "creating GNU ld script: $output"
+	    echo 'INPUT (' > $output
+	    for obj in $save_libobjs
+	    do
+	      func_to_tool_file "$obj"
+	      $ECHO "$func_to_tool_file_result" >> $output
+	    done
+	    echo ')' >> $output
+	    func_append delfiles " $output"
+	    func_to_tool_file "$output"
+	    output=$func_to_tool_file_result
+	  elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then
+	    output=${output_objdir}/${output_la}.lnk
+	    func_verbose "creating linker input file list: $output"
+	    : > $output
+	    set x $save_libobjs
+	    shift
+	    firstobj=
+	    if test "$compiler_needs_object" = yes; then
+	      firstobj="$1 "
+	      shift
+	    fi
+	    for obj
+	    do
+	      func_to_tool_file "$obj"
+	      $ECHO "$func_to_tool_file_result" >> $output
+	    done
+	    func_append delfiles " $output"
+	    func_to_tool_file "$output"
+	    output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+	  else
+	    if test -n "$save_libobjs"; then
+	      func_verbose "creating reloadable object files..."
+	      output=$output_objdir/$output_la-${k}.$objext
+	      eval test_cmds=\"$reload_cmds\"
+	      func_len " $test_cmds"
+	      len0=$func_len_result
+	      len=$len0
+
+	      # Loop over the list of objects to be linked.
+	      for obj in $save_libobjs
+	      do
+		func_len " $obj"
+		func_arith $len + $func_len_result
+		len=$func_arith_result
+		if test "X$objlist" = X ||
+		   test "$len" -lt "$max_cmd_len"; then
+		  func_append objlist " $obj"
+		else
+		  # The command $test_cmds is almost too long, add a
+		  # command to the queue.
+		  if test "$k" -eq 1 ; then
+		    # The first file doesn't have a previous command to add.
+		    reload_objs=$objlist
+		    eval concat_cmds=\"$reload_cmds\"
+		  else
+		    # All subsequent reloadable object files will link in
+		    # the last one created.
+		    reload_objs="$objlist $last_robj"
+		    eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
+		  fi
+		  last_robj=$output_objdir/$output_la-${k}.$objext
+		  func_arith $k + 1
+		  k=$func_arith_result
+		  output=$output_objdir/$output_la-${k}.$objext
+		  objlist=" $obj"
+		  func_len " $last_robj"
+		  func_arith $len0 + $func_len_result
+		  len=$func_arith_result
+		fi
+	      done
+	      # Handle the remaining objects by creating one last
+	      # reloadable object file.  All subsequent reloadable object
+	      # files will link in the last one created.
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      reload_objs="$objlist $last_robj"
+	      eval concat_cmds=\"\${concat_cmds}$reload_cmds\"
+	      if test -n "$last_robj"; then
+	        eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+	      fi
+	      func_append delfiles " $output"
+
+	    else
+	      output=
+	    fi
+
+	    if ${skipped_export-false}; then
+	      func_verbose "generating symbol list for \`$libname.la'"
+	      export_symbols="$output_objdir/$libname.exp"
+	      $opt_dry_run || $RM $export_symbols
+	      libobjs=$output
+	      # Append the command to create the export file.
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+	      if test -n "$last_robj"; then
+		eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+	      fi
+	    fi
+
+	    test -n "$save_libobjs" &&
+	      func_verbose "creating a temporary reloadable object file: $output"
+
+	    # Loop through the commands generated above and execute them.
+	    save_ifs="$IFS"; IFS='~'
+	    for cmd in $concat_cmds; do
+	      IFS="$save_ifs"
+	      $opt_silent || {
+		  func_quote_for_expand "$cmd"
+		  eval "func_echo $func_quote_for_expand_result"
+	      }
+	      $opt_dry_run || eval "$cmd" || {
+		lt_exit=$?
+
+		# Restore the uninstalled library and exit
+		if test "$opt_mode" = relink; then
+		  ( cd "$output_objdir" && \
+		    $RM "${realname}T" && \
+		    $MV "${realname}U" "$realname" )
+		fi
+
+		exit $lt_exit
+	      }
+	    done
+	    IFS="$save_ifs"
+
+	    if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+	      func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+	      func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+	    fi
+	  fi
+
+          if ${skipped_export-false}; then
+	    if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	      tmp_export_symbols="$export_symbols"
+	      test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+	      $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+	    fi
+
+	    if test -n "$orig_export_symbols"; then
+	      # The given exports_symbols file has to be filtered, so filter it.
+	      func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+	      # FIXME: $output_objdir/$libname.filter potentially contains lots of
+	      # 's' commands which not all seds can handle. GNU sed should be fine
+	      # though. Also, the filter scales superlinearly with the number of
+	      # global variables. join(1) would be nice here, but unfortunately
+	      # isn't a blessed tool.
+	      $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+	      func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+	      export_symbols=$output_objdir/$libname.def
+	      $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+	    fi
+	  fi
+
+	  libobjs=$output
+	  # Restore the value of output.
+	  output=$save_output
+
+	  if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+	    test "X$libobjs" = "X " && libobjs=
+	  fi
+	  # Expand the library linking commands again to reset the
+	  # value of $libobjs for piecewise linking.
+
+	  # Do each of the archive commands.
+	  if test "$module" = yes && test -n "$module_cmds" ; then
+	    if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+	      cmds=$module_expsym_cmds
+	    else
+	      cmds=$module_cmds
+	    fi
+	  else
+	    if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+	      cmds=$archive_expsym_cmds
+	    else
+	      cmds=$archive_cmds
+	    fi
+	  fi
+	fi
+
+	if test -n "$delfiles"; then
+	  # Append the command to remove temporary files to $cmds.
+	  eval cmds=\"\$cmds~\$RM $delfiles\"
+	fi
+
+	# Add any objects from preloaded convenience libraries
+	if test -n "$dlprefiles"; then
+	  gentop="$output_objdir/${outputname}x"
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $dlprefiles
+	  func_append libobjs " $func_extract_archives_result"
+	  test "X$libobjs" = "X " && libobjs=
+	fi
+
+	save_ifs="$IFS"; IFS='~'
+	for cmd in $cmds; do
+	  IFS="$save_ifs"
+	  eval cmd=\"$cmd\"
+	  $opt_silent || {
+	    func_quote_for_expand "$cmd"
+	    eval "func_echo $func_quote_for_expand_result"
+	  }
+	  $opt_dry_run || eval "$cmd" || {
+	    lt_exit=$?
+
+	    # Restore the uninstalled library and exit
+	    if test "$opt_mode" = relink; then
+	      ( cd "$output_objdir" && \
+	        $RM "${realname}T" && \
+		$MV "${realname}U" "$realname" )
+	    fi
+
+	    exit $lt_exit
+	  }
+	done
+	IFS="$save_ifs"
+
+	# Restore the uninstalled library and exit
+	if test "$opt_mode" = relink; then
+	  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+	  if test -n "$convenience"; then
+	    if test -z "$whole_archive_flag_spec"; then
+	      func_show_eval '${RM}r "$gentop"'
+	    fi
+	  fi
+
+	  exit $EXIT_SUCCESS
+	fi
+
+	# Create links to the real library.
+	for linkname in $linknames; do
+	  if test "$realname" != "$linkname"; then
+	    func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+	  fi
+	done
+
+	# If -module or -export-dynamic was specified, set the dlname.
+	if test "$module" = yes || test "$export_dynamic" = yes; then
+	  # On all known operating systems, these are identical.
+	  dlname="$soname"
+	fi
+      fi
+      ;;
+
+    obj)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+	func_warning "\`-dlopen' is ignored for objects"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+	func_warning "\`-l' and \`-L' are ignored for objects" ;;
+      esac
+
+      test -n "$rpath" && \
+	func_warning "\`-rpath' is ignored for objects"
+
+      test -n "$xrpath" && \
+	func_warning "\`-R' is ignored for objects"
+
+      test -n "$vinfo" && \
+	func_warning "\`-version-info' is ignored for objects"
+
+      test -n "$release" && \
+	func_warning "\`-release' is ignored for objects"
+
+      case $output in
+      *.lo)
+	test -n "$objs$old_deplibs" && \
+	  func_fatal_error "cannot build library object \`$output' from non-libtool objects"
+
+	libobj=$output
+	func_lo2o "$libobj"
+	obj=$func_lo2o_result
+	;;
+      *)
+	libobj=
+	obj="$output"
+	;;
+      esac
+
+      # Delete the old objects.
+      $opt_dry_run || $RM $obj $libobj
+
+      # Objects from convenience libraries.  This assumes
+      # single-version convenience libraries.  Whenever we create
+      # different ones for PIC/non-PIC, this we'll have to duplicate
+      # the extraction.
+      reload_conv_objs=
+      gentop=
+      # reload_cmds runs $LD directly, so let us get rid of
+      # -Wl from whole_archive_flag_spec and hope we can get by with
+      # turning comma into space..
+      wl=
+
+      if test -n "$convenience"; then
+	if test -n "$whole_archive_flag_spec"; then
+	  eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+	  reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+	else
+	  gentop="$output_objdir/${obj}x"
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $convenience
+	  reload_conv_objs="$reload_objs $func_extract_archives_result"
+	fi
+      fi
+
+      # If we're not building shared, we need to use non_pic_objs
+      test "$build_libtool_libs" != yes && libobjs="$non_pic_objects"
+
+      # Create the old-style object.
+      reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+      output="$obj"
+      func_execute_cmds "$reload_cmds" 'exit $?'
+
+      # Exit if we aren't doing a library object file.
+      if test -z "$libobj"; then
+	if test -n "$gentop"; then
+	  func_show_eval '${RM}r "$gentop"'
+	fi
+
+	exit $EXIT_SUCCESS
+      fi
+
+      if test "$build_libtool_libs" != yes; then
+	if test -n "$gentop"; then
+	  func_show_eval '${RM}r "$gentop"'
+	fi
+
+	# Create an invalid libtool object if no PIC, so that we don't
+	# accidentally link it into a program.
+	# $show "echo timestamp > $libobj"
+	# $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+	exit $EXIT_SUCCESS
+      fi
+
+      if test -n "$pic_flag" || test "$pic_mode" != default; then
+	# Only do commands if we really have different PIC objects.
+	reload_objs="$libobjs $reload_conv_objs"
+	output="$libobj"
+	func_execute_cmds "$reload_cmds" 'exit $?'
+      fi
+
+      if test -n "$gentop"; then
+	func_show_eval '${RM}r "$gentop"'
+      fi
+
+      exit $EXIT_SUCCESS
+      ;;
+
+    prog)
+      case $host in
+	*cygwin*) func_stripname '' '.exe' "$output"
+	          output=$func_stripname_result.exe;;
+      esac
+      test -n "$vinfo" && \
+	func_warning "\`-version-info' is ignored for programs"
+
+      test -n "$release" && \
+	func_warning "\`-release' is ignored for programs"
+
+      test "$preload" = yes \
+        && test "$dlopen_support" = unknown \
+	&& test "$dlopen_self" = unknown \
+	&& test "$dlopen_self_static" = unknown && \
+	  func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+      case $host in
+      *-*-rhapsody* | *-*-darwin1.[012])
+	# On Rhapsody replace the C library is the System framework
+	compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
+	finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'`
+	;;
+      esac
+
+      case $host in
+      *-*-darwin*)
+	# Don't allow lazy linking, it breaks C++ global constructors
+	# But is supposedly fixed on 10.4 or later (yay!).
+	if test "$tagname" = CXX ; then
+	  case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+	    10.[0123])
+	      func_append compile_command " ${wl}-bind_at_load"
+	      func_append finalize_command " ${wl}-bind_at_load"
+	    ;;
+	  esac
+	fi
+	# Time to change all our "foo.ltframework" stuff back to "-framework foo"
+	compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	;;
+      esac
+
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+	case " $new_libs " in
+	*" -L$path/$objdir "*) ;;
+	*)
+	  case " $compile_deplibs " in
+	  *" -L$path/$objdir "*)
+	    func_append new_libs " -L$path/$objdir" ;;
+	  esac
+	  ;;
+	esac
+      done
+      for deplib in $compile_deplibs; do
+	case $deplib in
+	-L*)
+	  case " $new_libs " in
+	  *" $deplib "*) ;;
+	  *) func_append new_libs " $deplib" ;;
+	  esac
+	  ;;
+	*) func_append new_libs " $deplib" ;;
+	esac
+      done
+      compile_deplibs="$new_libs"
+
+
+      func_append compile_command " $compile_deplibs"
+      func_append finalize_command " $finalize_deplibs"
+
+      if test -n "$rpath$xrpath"; then
+	# If the user specified any rpath flags, then add them.
+	for libdir in $rpath $xrpath; do
+	  # This is the magic to use -rpath.
+	  case "$finalize_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_rpath " $libdir" ;;
+	  esac
+	done
+      fi
+
+      # Now hardcode the library paths
+      rpath=
+      hardcode_libdirs=
+      for libdir in $compile_rpath $finalize_rpath; do
+	if test -n "$hardcode_libdir_flag_spec"; then
+	  if test -n "$hardcode_libdir_separator"; then
+	    if test -z "$hardcode_libdirs"; then
+	      hardcode_libdirs="$libdir"
+	    else
+	      # Just accumulate the unique libdirs.
+	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		;;
+	      *)
+		func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		;;
+	      esac
+	    fi
+	  else
+	    eval flag=\"$hardcode_libdir_flag_spec\"
+	    func_append rpath " $flag"
+	  fi
+	elif test -n "$runpath_var"; then
+	  case "$perm_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append perm_rpath " $libdir" ;;
+	  esac
+	fi
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+	  testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+	  case :$dllsearchpath: in
+	  *":$libdir:"*) ;;
+	  ::) dllsearchpath=$libdir;;
+	  *) func_append dllsearchpath ":$libdir";;
+	  esac
+	  case :$dllsearchpath: in
+	  *":$testbindir:"*) ;;
+	  ::) dllsearchpath=$testbindir;;
+	  *) func_append dllsearchpath ":$testbindir";;
+	  esac
+	  ;;
+	esac
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+	 test -n "$hardcode_libdirs"; then
+	libdir="$hardcode_libdirs"
+	eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      compile_rpath="$rpath"
+
+      rpath=
+      hardcode_libdirs=
+      for libdir in $finalize_rpath; do
+	if test -n "$hardcode_libdir_flag_spec"; then
+	  if test -n "$hardcode_libdir_separator"; then
+	    if test -z "$hardcode_libdirs"; then
+	      hardcode_libdirs="$libdir"
+	    else
+	      # Just accumulate the unique libdirs.
+	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		;;
+	      *)
+		func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		;;
+	      esac
+	    fi
+	  else
+	    eval flag=\"$hardcode_libdir_flag_spec\"
+	    func_append rpath " $flag"
+	  fi
+	elif test -n "$runpath_var"; then
+	  case "$finalize_perm_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_perm_rpath " $libdir" ;;
+	  esac
+	fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+	 test -n "$hardcode_libdirs"; then
+	libdir="$hardcode_libdirs"
+	eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      finalize_rpath="$rpath"
+
+      if test -n "$libobjs" && test "$build_old_libs" = yes; then
+	# Transform all the library objects into standard objects.
+	compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+      fi
+
+      func_generate_dlsyms "$outputname" "@PROGRAM@" "no"
+
+      # template prelinking step
+      if test -n "$prelink_cmds"; then
+	func_execute_cmds "$prelink_cmds" 'exit $?'
+      fi
+
+      wrappers_required=yes
+      case $host in
+      *cegcc* | *mingw32ce*)
+        # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+        wrappers_required=no
+        ;;
+      *cygwin* | *mingw* )
+        if test "$build_libtool_libs" != yes; then
+          wrappers_required=no
+        fi
+        ;;
+      *)
+        if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
+          wrappers_required=no
+        fi
+        ;;
+      esac
+      if test "$wrappers_required" = no; then
+	# Replace the output file specification.
+	compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+	link_command="$compile_command$compile_rpath"
+
+	# We have no uninstalled library dependencies, so finalize right now.
+	exit_status=0
+	func_show_eval "$link_command" 'exit_status=$?'
+
+	if test -n "$postlink_cmds"; then
+	  func_to_tool_file "$output"
+	  postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	  func_execute_cmds "$postlink_cmds" 'exit $?'
+	fi
+
+	# Delete the generated files.
+	if test -f "$output_objdir/${outputname}S.${objext}"; then
+	  func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"'
+	fi
+
+	exit $exit_status
+      fi
+
+      if test -n "$compile_shlibpath$finalize_shlibpath"; then
+	compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+      fi
+      if test -n "$finalize_shlibpath"; then
+	finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+      fi
+
+      compile_var=
+      finalize_var=
+      if test -n "$runpath_var"; then
+	if test -n "$perm_rpath"; then
+	  # We should set the runpath_var.
+	  rpath=
+	  for dir in $perm_rpath; do
+	    func_append rpath "$dir:"
+	  done
+	  compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+	fi
+	if test -n "$finalize_perm_rpath"; then
+	  # We should set the runpath_var.
+	  rpath=
+	  for dir in $finalize_perm_rpath; do
+	    func_append rpath "$dir:"
+	  done
+	  finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+	fi
+      fi
+
+      if test "$no_install" = yes; then
+	# We don't need to create a wrapper script.
+	link_command="$compile_var$compile_command$compile_rpath"
+	# Replace the output file specification.
+	link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+	# Delete the old output file.
+	$opt_dry_run || $RM $output
+	# Link the executable and exit
+	func_show_eval "$link_command" 'exit $?'
+
+	if test -n "$postlink_cmds"; then
+	  func_to_tool_file "$output"
+	  postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	  func_execute_cmds "$postlink_cmds" 'exit $?'
+	fi
+
+	exit $EXIT_SUCCESS
+      fi
+
+      if test "$hardcode_action" = relink; then
+	# Fast installation is not supported
+	link_command="$compile_var$compile_command$compile_rpath"
+	relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+	func_warning "this platform does not like uninstalled shared libraries"
+	func_warning "\`$output' will be relinked during installation"
+      else
+	if test "$fast_install" != no; then
+	  link_command="$finalize_var$compile_command$finalize_rpath"
+	  if test "$fast_install" = yes; then
+	    relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
+	  else
+	    # fast_install is set to needless
+	    relink_command=
+	  fi
+	else
+	  link_command="$compile_var$compile_command$compile_rpath"
+	  relink_command="$finalize_var$finalize_command$finalize_rpath"
+	fi
+      fi
+
+      # Replace the output file specification.
+      link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+      # Delete the old output files.
+      $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+      func_show_eval "$link_command" 'exit $?'
+
+      if test -n "$postlink_cmds"; then
+	func_to_tool_file "$output_objdir/$outputname"
+	postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	func_execute_cmds "$postlink_cmds" 'exit $?'
+      fi
+
+      # Now create the wrapper script.
+      func_verbose "creating $output"
+
+      # Quote the relink command for shipping.
+      if test -n "$relink_command"; then
+	# Preserve any variables that may affect compiler behavior
+	for var in $variables_saved_for_relink; do
+	  if eval test -z \"\${$var+set}\"; then
+	    relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+	  elif eval var_value=\$$var; test -z "$var_value"; then
+	    relink_command="$var=; export $var; $relink_command"
+	  else
+	    func_quote_for_eval "$var_value"
+	    relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+	  fi
+	done
+	relink_command="(cd `pwd`; $relink_command)"
+	relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+      fi
+
+      # Only actually do things if not in dry run mode.
+      $opt_dry_run || {
+	# win32 will think the script is a binary if it has
+	# a .exe suffix, so we strip it off here.
+	case $output in
+	  *.exe) func_stripname '' '.exe' "$output"
+	         output=$func_stripname_result ;;
+	esac
+	# test for cygwin because mv fails w/o .exe extensions
+	case $host in
+	  *cygwin*)
+	    exeext=.exe
+	    func_stripname '' '.exe' "$outputname"
+	    outputname=$func_stripname_result ;;
+	  *) exeext= ;;
+	esac
+	case $host in
+	  *cygwin* | *mingw* )
+	    func_dirname_and_basename "$output" "" "."
+	    output_name=$func_basename_result
+	    output_path=$func_dirname_result
+	    cwrappersource="$output_path/$objdir/lt-$output_name.c"
+	    cwrapper="$output_path/$output_name.exe"
+	    $RM $cwrappersource $cwrapper
+	    trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+	    func_emit_cwrapperexe_src > $cwrappersource
+
+	    # The wrapper executable is built using the $host compiler,
+	    # because it contains $host paths and files. If cross-
+	    # compiling, it, like the target executable, must be
+	    # executed on the $host or under an emulation environment.
+	    $opt_dry_run || {
+	      $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+	      $STRIP $cwrapper
+	    }
+
+	    # Now, create the wrapper script for func_source use:
+	    func_ltwrapper_scriptname $cwrapper
+	    $RM $func_ltwrapper_scriptname_result
+	    trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+	    $opt_dry_run || {
+	      # note: this script will not be executed, so do not chmod.
+	      if test "x$build" = "x$host" ; then
+		$cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+	      else
+		func_emit_wrapper no > $func_ltwrapper_scriptname_result
+	      fi
+	    }
+	  ;;
+	  * )
+	    $RM $output
+	    trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+	    func_emit_wrapper no > $output
+	    chmod +x $output
+	  ;;
+	esac
+      }
+      exit $EXIT_SUCCESS
+      ;;
+    esac
+
+    # See if we need to build an old-fashioned archive.
+    for oldlib in $oldlibs; do
+
+      if test "$build_libtool_libs" = convenience; then
+	oldobjs="$libobjs_save $symfileobj"
+	addlibs="$convenience"
+	build_libtool_libs=no
+      else
+	if test "$build_libtool_libs" = module; then
+	  oldobjs="$libobjs_save"
+	  build_libtool_libs=no
+	else
+	  oldobjs="$old_deplibs $non_pic_objects"
+	  if test "$preload" = yes && test -f "$symfileobj"; then
+	    func_append oldobjs " $symfileobj"
+	  fi
+	fi
+	addlibs="$old_convenience"
+      fi
+
+      if test -n "$addlibs"; then
+	gentop="$output_objdir/${outputname}x"
+	func_append generated " $gentop"
+
+	func_extract_archives $gentop $addlibs
+	func_append oldobjs " $func_extract_archives_result"
+      fi
+
+      # Do each command in the archive commands.
+      if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+	cmds=$old_archive_from_new_cmds
+      else
+
+	# Add any objects from preloaded convenience libraries
+	if test -n "$dlprefiles"; then
+	  gentop="$output_objdir/${outputname}x"
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $dlprefiles
+	  func_append oldobjs " $func_extract_archives_result"
+	fi
+
+	# POSIX demands no paths to be encoded in archives.  We have
+	# to avoid creating archives with duplicate basenames if we
+	# might have to extract them afterwards, e.g., when creating a
+	# static archive out of a convenience library, or when linking
+	# the entirety of a libtool archive into another (currently
+	# not supported by libtool).
+	if (for obj in $oldobjs
+	    do
+	      func_basename "$obj"
+	      $ECHO "$func_basename_result"
+	    done | sort | sort -uc >/dev/null 2>&1); then
+	  :
+	else
+	  echo "copying selected object files to avoid basename conflicts..."
+	  gentop="$output_objdir/${outputname}x"
+	  func_append generated " $gentop"
+	  func_mkdir_p "$gentop"
+	  save_oldobjs=$oldobjs
+	  oldobjs=
+	  counter=1
+	  for obj in $save_oldobjs
+	  do
+	    func_basename "$obj"
+	    objbase="$func_basename_result"
+	    case " $oldobjs " in
+	    " ") oldobjs=$obj ;;
+	    *[\ /]"$objbase "*)
+	      while :; do
+		# Make sure we don't pick an alternate name that also
+		# overlaps.
+		newobj=lt$counter-$objbase
+		func_arith $counter + 1
+		counter=$func_arith_result
+		case " $oldobjs " in
+		*[\ /]"$newobj "*) ;;
+		*) if test ! -f "$gentop/$newobj"; then break; fi ;;
+		esac
+	      done
+	      func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+	      func_append oldobjs " $gentop/$newobj"
+	      ;;
+	    *) func_append oldobjs " $obj" ;;
+	    esac
+	  done
+	fi
+	func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+	tool_oldlib=$func_to_tool_file_result
+	eval cmds=\"$old_archive_cmds\"
+
+	func_len " $cmds"
+	len=$func_len_result
+	if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	  cmds=$old_archive_cmds
+	elif test -n "$archiver_list_spec"; then
+	  func_verbose "using command file archive linking..."
+	  for obj in $oldobjs
+	  do
+	    func_to_tool_file "$obj"
+	    $ECHO "$func_to_tool_file_result"
+	  done > $output_objdir/$libname.libcmd
+	  func_to_tool_file "$output_objdir/$libname.libcmd"
+	  oldobjs=" $archiver_list_spec$func_to_tool_file_result"
+	  cmds=$old_archive_cmds
+	else
+	  # the command line is too long to link in one step, link in parts
+	  func_verbose "using piecewise archive linking..."
+	  save_RANLIB=$RANLIB
+	  RANLIB=:
+	  objlist=
+	  concat_cmds=
+	  save_oldobjs=$oldobjs
+	  oldobjs=
+	  # Is there a better way of finding the last object in the list?
+	  for obj in $save_oldobjs
+	  do
+	    last_oldobj=$obj
+	  done
+	  eval test_cmds=\"$old_archive_cmds\"
+	  func_len " $test_cmds"
+	  len0=$func_len_result
+	  len=$len0
+	  for obj in $save_oldobjs
+	  do
+	    func_len " $obj"
+	    func_arith $len + $func_len_result
+	    len=$func_arith_result
+	    func_append objlist " $obj"
+	    if test "$len" -lt "$max_cmd_len"; then
+	      :
+	    else
+	      # the above command should be used before it gets too long
+	      oldobjs=$objlist
+	      if test "$obj" = "$last_oldobj" ; then
+		RANLIB=$save_RANLIB
+	      fi
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+	      objlist=
+	      len=$len0
+	    fi
+	  done
+	  RANLIB=$save_RANLIB
+	  oldobjs=$objlist
+	  if test "X$oldobjs" = "X" ; then
+	    eval cmds=\"\$concat_cmds\"
+	  else
+	    eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+	  fi
+	fi
+      fi
+      func_execute_cmds "$cmds" 'exit $?'
+    done
+
+    test -n "$generated" && \
+      func_show_eval "${RM}r$generated"
+
+    # Now create the libtool archive.
+    case $output in
+    *.la)
+      old_library=
+      test "$build_old_libs" = yes && old_library="$libname.$libext"
+      func_verbose "creating $output"
+
+      # Preserve any variables that may affect compiler behavior
+      for var in $variables_saved_for_relink; do
+	if eval test -z \"\${$var+set}\"; then
+	  relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+	elif eval var_value=\$$var; test -z "$var_value"; then
+	  relink_command="$var=; export $var; $relink_command"
+	else
+	  func_quote_for_eval "$var_value"
+	  relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+	fi
+      done
+      # Quote the link command for shipping.
+      relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+      relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+      if test "$hardcode_automatic" = yes ; then
+	relink_command=
+      fi
+
+      # Only create the output if not a dry run.
+      $opt_dry_run || {
+	for installed in no yes; do
+	  if test "$installed" = yes; then
+	    if test -z "$install_libdir"; then
+	      break
+	    fi
+	    output="$output_objdir/$outputname"i
+	    # Replace all uninstalled libtool libraries with the installed ones
+	    newdependency_libs=
+	    for deplib in $dependency_libs; do
+	      case $deplib in
+	      *.la)
+		func_basename "$deplib"
+		name="$func_basename_result"
+		func_resolve_sysroot "$deplib"
+		eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
+		test -z "$libdir" && \
+		  func_fatal_error "\`$deplib' is not a valid libtool archive"
+		func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      -L*)
+		func_stripname -L '' "$deplib"
+		func_replace_sysroot "$func_stripname_result"
+		func_append newdependency_libs " -L$func_replace_sysroot_result"
+		;;
+	      -R*)
+		func_stripname -R '' "$deplib"
+		func_replace_sysroot "$func_stripname_result"
+		func_append newdependency_libs " -R$func_replace_sysroot_result"
+		;;
+	      *) func_append newdependency_libs " $deplib" ;;
+	      esac
+	    done
+	    dependency_libs="$newdependency_libs"
+	    newdlfiles=
+
+	    for lib in $dlfiles; do
+	      case $lib in
+	      *.la)
+	        func_basename "$lib"
+		name="$func_basename_result"
+		eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+		test -z "$libdir" && \
+		  func_fatal_error "\`$lib' is not a valid libtool archive"
+		func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      *) func_append newdlfiles " $lib" ;;
+	      esac
+	    done
+	    dlfiles="$newdlfiles"
+	    newdlprefiles=
+	    for lib in $dlprefiles; do
+	      case $lib in
+	      *.la)
+		# Only pass preopened files to the pseudo-archive (for
+		# eventual linking with the app. that links it) if we
+		# didn't already link the preopened objects directly into
+		# the library:
+		func_basename "$lib"
+		name="$func_basename_result"
+		eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+		test -z "$libdir" && \
+		  func_fatal_error "\`$lib' is not a valid libtool archive"
+		func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      esac
+	    done
+	    dlprefiles="$newdlprefiles"
+	  else
+	    newdlfiles=
+	    for lib in $dlfiles; do
+	      case $lib in
+		[\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+		*) abs=`pwd`"/$lib" ;;
+	      esac
+	      func_append newdlfiles " $abs"
+	    done
+	    dlfiles="$newdlfiles"
+	    newdlprefiles=
+	    for lib in $dlprefiles; do
+	      case $lib in
+		[\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+		*) abs=`pwd`"/$lib" ;;
+	      esac
+	      func_append newdlprefiles " $abs"
+	    done
+	    dlprefiles="$newdlprefiles"
+	  fi
+	  $RM $output
+	  # place dlname in correct position for cygwin
+	  # In fact, it would be nice if we could use this code for all target
+	  # systems that can't hard-code library paths into their executables
+	  # and that have no shared library path variable independent of PATH,
+	  # but it turns out we can't easily determine that from inspecting
+	  # libtool variables, so we have to hard-code the OSs to which it
+	  # applies here; at the moment, that means platforms that use the PE
+	  # object format with DLL files.  See the long comment at the top of
+	  # tests/bindir.at for full details.
+	  tdlname=$dlname
+	  case $host,$output,$installed,$module,$dlname in
+	    *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+	      # If a -bindir argument was supplied, place the dll there.
+	      if test "x$bindir" != x ;
+	      then
+		func_relative_path "$install_libdir" "$bindir"
+		tdlname=$func_relative_path_result$dlname
+	      else
+		# Otherwise fall back on heuristic.
+		tdlname=../bin/$dlname
+	      fi
+	      ;;
+	  esac
+	  $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+	  if test "$installed" = no && test "$need_relink" = yes; then
+	    $ECHO >> $output "\
+relink_command=\"$relink_command\""
+	  fi
+	done
+      }
+
+      # Do a symbolic link so that the libtool archive can be found in
+      # LD_LIBRARY_PATH before the program is installed.
+      func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+      ;;
+    esac
+    exit $EXIT_SUCCESS
+}
+
+{ test "$opt_mode" = link || test "$opt_mode" = relink; } &&
+    func_mode_link ${1+"$@"}
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+    $opt_debug
+    RM="$nonopt"
+    files=
+    rmforce=
+    exit_status=0
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    for arg
+    do
+      case $arg in
+      -f) func_append RM " $arg"; rmforce=yes ;;
+      -*) func_append RM " $arg" ;;
+      *) func_append files " $arg" ;;
+      esac
+    done
+
+    test -z "$RM" && \
+      func_fatal_help "you must specify an RM program"
+
+    rmdirs=
+
+    for file in $files; do
+      func_dirname "$file" "" "."
+      dir="$func_dirname_result"
+      if test "X$dir" = X.; then
+	odir="$objdir"
+      else
+	odir="$dir/$objdir"
+      fi
+      func_basename "$file"
+      name="$func_basename_result"
+      test "$opt_mode" = uninstall && odir="$dir"
+
+      # Remember odir for removal later, being careful to avoid duplicates
+      if test "$opt_mode" = clean; then
+	case " $rmdirs " in
+	  *" $odir "*) ;;
+	  *) func_append rmdirs " $odir" ;;
+	esac
+      fi
+
+      # Don't error if the file doesn't exist and rm -f was used.
+      if { test -L "$file"; } >/dev/null 2>&1 ||
+	 { test -h "$file"; } >/dev/null 2>&1 ||
+	 test -f "$file"; then
+	:
+      elif test -d "$file"; then
+	exit_status=1
+	continue
+      elif test "$rmforce" = yes; then
+	continue
+      fi
+
+      rmfiles="$file"
+
+      case $name in
+      *.la)
+	# Possibly a libtool archive, so verify it.
+	if func_lalib_p "$file"; then
+	  func_source $dir/$name
+
+	  # Delete the libtool libraries and symlinks.
+	  for n in $library_names; do
+	    func_append rmfiles " $odir/$n"
+	  done
+	  test -n "$old_library" && func_append rmfiles " $odir/$old_library"
+
+	  case "$opt_mode" in
+	  clean)
+	    case " $library_names " in
+	    *" $dlname "*) ;;
+	    *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
+	    esac
+	    test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i"
+	    ;;
+	  uninstall)
+	    if test -n "$library_names"; then
+	      # Do each command in the postuninstall commands.
+	      func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+	    fi
+
+	    if test -n "$old_library"; then
+	      # Do each command in the old_postuninstall commands.
+	      func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+	    fi
+	    # FIXME: should reinstall the best remaining shared library.
+	    ;;
+	  esac
+	fi
+	;;
+
+      *.lo)
+	# Possibly a libtool object, so verify it.
+	if func_lalib_p "$file"; then
+
+	  # Read the .lo file
+	  func_source $dir/$name
+
+	  # Add PIC object to the list of files to remove.
+	  if test -n "$pic_object" &&
+	     test "$pic_object" != none; then
+	    func_append rmfiles " $dir/$pic_object"
+	  fi
+
+	  # Add non-PIC object to the list of files to remove.
+	  if test -n "$non_pic_object" &&
+	     test "$non_pic_object" != none; then
+	    func_append rmfiles " $dir/$non_pic_object"
+	  fi
+	fi
+	;;
+
+      *)
+	if test "$opt_mode" = clean ; then
+	  noexename=$name
+	  case $file in
+	  *.exe)
+	    func_stripname '' '.exe' "$file"
+	    file=$func_stripname_result
+	    func_stripname '' '.exe' "$name"
+	    noexename=$func_stripname_result
+	    # $file with .exe has already been added to rmfiles,
+	    # add $file without .exe
+	    func_append rmfiles " $file"
+	    ;;
+	  esac
+	  # Do a test to see if this is a libtool program.
+	  if func_ltwrapper_p "$file"; then
+	    if func_ltwrapper_executable_p "$file"; then
+	      func_ltwrapper_scriptname "$file"
+	      relink_command=
+	      func_source $func_ltwrapper_scriptname_result
+	      func_append rmfiles " $func_ltwrapper_scriptname_result"
+	    else
+	      relink_command=
+	      func_source $dir/$noexename
+	    fi
+
+	    # note $name still contains .exe if it was in $file originally
+	    # as does the version of $file that was added into $rmfiles
+	    func_append rmfiles " $odir/$name $odir/${name}S.${objext}"
+	    if test "$fast_install" = yes && test -n "$relink_command"; then
+	      func_append rmfiles " $odir/lt-$name"
+	    fi
+	    if test "X$noexename" != "X$name" ; then
+	      func_append rmfiles " $odir/lt-${noexename}.c"
+	    fi
+	  fi
+	fi
+	;;
+      esac
+      func_show_eval "$RM $rmfiles" 'exit_status=1'
+    done
+
+    # Try to remove the ${objdir}s in the directories where we deleted files
+    for dir in $rmdirs; do
+      if test -d "$dir"; then
+	func_show_eval "rmdir $dir >/dev/null 2>&1"
+      fi
+    done
+
+    exit $exit_status
+}
+
+{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } &&
+    func_mode_uninstall ${1+"$@"}
+
+test -z "$opt_mode" && {
+  help="$generic_help"
+  func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+  func_fatal_help "invalid operation mode \`$opt_mode'"
+
+if test -n "$exec_cmd"; then
+  eval exec "$exec_cmd"
+  exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# in which we disable both kinds of libraries.  Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them.  This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration.  But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
+# vi:sw=2
+
diff --git a/sg3_utils/missing b/sg3_utils/missing
new file mode 100755
index 0000000..f62bbae
--- /dev/null
+++ b/sg3_utils/missing
@@ -0,0 +1,215 @@
+#! /bin/sh
+# Common wrapper for a few potentially missing GNU programs.
+
+scriptversion=2013-10-28.13; # UTC
+
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try '$0 --help' for more information"
+  exit 1
+fi
+
+case $1 in
+
+  --is-lightweight)
+    # Used by our autoconf macros to check whether the available missing
+    # script is modern enough.
+    exit 0
+    ;;
+
+  --run)
+    # Back-compat with the calling convention used by older automake.
+    shift
+    ;;
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+
+Supported PROGRAM values:
+  aclocal   autoconf  autoheader   autom4te  automake  makeinfo
+  bison     yacc      flex         lex       help2man
+
+Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
+'g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+    exit $?
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing $scriptversion (GNU Automake)"
+    exit $?
+    ;;
+
+  -*)
+    echo 1>&2 "$0: unknown '$1' option"
+    echo 1>&2 "Try '$0 --help' for more information"
+    exit 1
+    ;;
+
+esac
+
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch.  This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+  msg="probably too old"
+elif test $st -eq 127; then
+  # Program was missing.
+  msg="missing on your system"
+else
+  # Program was found and executed, but failed.  Give up.
+  exit $st
+fi
+
+perl_URL=http://www.perl.org/
+flex_URL=http://flex.sourceforge.net/
+gnu_software_URL=http://www.gnu.org/software
+
+program_details ()
+{
+  case $1 in
+    aclocal|automake)
+      echo "The '$1' program is part of the GNU Automake package:"
+      echo "<$gnu_software_URL/automake>"
+      echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/autoconf>"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+    autoconf|autom4te|autoheader)
+      echo "The '$1' program is part of the GNU Autoconf package:"
+      echo "<$gnu_software_URL/autoconf/>"
+      echo "It also requires GNU m4 and Perl in order to run:"
+      echo "<$gnu_software_URL/m4/>"
+      echo "<$perl_URL>"
+      ;;
+  esac
+}
+
+give_advice ()
+{
+  # Normalize program name to check for.
+  normalized_program=`echo "$1" | sed '
+    s/^gnu-//; t
+    s/^gnu//; t
+    s/^g//; t'`
+
+  printf '%s\n' "'$1' is $msg."
+
+  configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+  case $normalized_program in
+    autoconf*)
+      echo "You should only need it if you modified 'configure.ac',"
+      echo "or m4 files included by it."
+      program_details 'autoconf'
+      ;;
+    autoheader*)
+      echo "You should only need it if you modified 'acconfig.h' or"
+      echo "$configure_deps."
+      program_details 'autoheader'
+      ;;
+    automake*)
+      echo "You should only need it if you modified 'Makefile.am' or"
+      echo "$configure_deps."
+      program_details 'automake'
+      ;;
+    aclocal*)
+      echo "You should only need it if you modified 'acinclude.m4' or"
+      echo "$configure_deps."
+      program_details 'aclocal'
+      ;;
+   autom4te*)
+      echo "You might have modified some maintainer files that require"
+      echo "the 'autom4te' program to be rebuilt."
+      program_details 'autom4te'
+      ;;
+    bison*|yacc*)
+      echo "You should only need it if you modified a '.y' file."
+      echo "You may want to install the GNU Bison package:"
+      echo "<$gnu_software_URL/bison/>"
+      ;;
+    lex*|flex*)
+      echo "You should only need it if you modified a '.l' file."
+      echo "You may want to install the Fast Lexical Analyzer package:"
+      echo "<$flex_URL>"
+      ;;
+    help2man*)
+      echo "You should only need it if you modified a dependency" \
+           "of a man page."
+      echo "You may want to install the GNU Help2man package:"
+      echo "<$gnu_software_URL/help2man/>"
+    ;;
+    makeinfo*)
+      echo "You should only need it if you modified a '.texi' file, or"
+      echo "any other file indirectly affecting the aspect of the manual."
+      echo "You might want to install the Texinfo package:"
+      echo "<$gnu_software_URL/texinfo/>"
+      echo "The spurious makeinfo call might also be the consequence of"
+      echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+      echo "want to install GNU make:"
+      echo "<$gnu_software_URL/make/>"
+      ;;
+    *)
+      echo "You might have modified some files without having the proper"
+      echo "tools for further handling them.  Check the 'README' file, it"
+      echo "often tells you about the needed prerequisites for installing"
+      echo "this package.  You may also peek at any GNU archive site, in"
+      echo "case some other package contains this missing '$1' program."
+      ;;
+  esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+                       -e '2,$s/^/         /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/sg3_utils/scripts/55-scsi-sg3_id.rules b/sg3_utils/scripts/55-scsi-sg3_id.rules
new file mode 100644
index 0000000..e914a5d
--- /dev/null
+++ b/sg3_utils/scripts/55-scsi-sg3_id.rules
@@ -0,0 +1,46 @@
+# SCSI-ID mappings for sg3_utils
+
+ACTION=="remove", GOTO="sg3_utils_id_end"
+
+SUBSYSTEM!="block", GOTO="sg3_utils_id_end"
+
+# Import values for partitions
+ENV{DEVTYPE}=="partition", IMPORT{parent}="SCSI_*", ENV{ID_SCSI}="1"
+# SCSI INQUIRY values
+KERNEL=="sd*[!0-9]|sr*", IMPORT{program}="/usr/bin/sg_inq --export $tempnode", ENV{ID_SCSI}="1"
+# scsi_id compat mappings
+ENV{SCSI_VENDOR}=="?*", ENV{ID_VENDOR}="$env{SCSI_VENDOR}"
+ENV{SCSI_VENDOR_ENC}=="?*", ENV{ID_VENDOR_ENC}="$env{SCSI_VENDOR_ENC}"
+ENV{SCSI_MODEL}=="?*", ENV{ID_MODEL}="$env{SCSI_MODEL}"
+ENV{SCSI_MODEL_ENC}=="?*", ENV{ID_MODEL_ENC}="$env{SCSI_MODEL_ENC}"
+ENV{SCSI_REVISION}=="?*", ENV{ID_REVISION}="$env{SCSI_REVISION}"
+ENV{SCSI_TYPE}=="?*", ENV{ID_TYPE}="$env{SCSI_TYPE}"
+# SCSI EVPD page 0x80 values
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SCSI}=="1", IMPORT{program}="/usr/bin/sg_inq --export --inhex=/sys/block/$kernel/device/vpd_pg80 --raw", ENV{ID_SCSI_SN}="1"
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SCSI}=="1", ENV{ID_SCSI_SN}!="1", IMPORT{program}="/usr/bin/sg_inq --export --page=sn $tempnode", ENV{ID_SCSI_SN}="1"
+# SCSI EVPD page 0x83 values
+KERNEL=="sd*[!0-9]", ENV{ID_SCSI}=="1", IMPORT{program}="/usr/bin/sg_inq --export --inhex=/sys/block/$kernel/device/vpd_pg83 --raw", ENV{ID_SCSI_DI}="1"
+KERNEL=="sd*[!0-9]|sr*", ENV{ID_SCSI}=="1", ENV{ID_SCSI_DI}!="1", IMPORT{program}="/usr/bin/sg_inq --export --page=di $tempnode", ENV{ID_SCSI_DI}="1"
+
+# ID_WWN compat mapping
+ENV{SCSI_IDENT_LUN_NAA_REGEXT}=="?*", ENV{ID_WWN}!="?*", ENV{ID_WWN}="0x$env{SCSI_IDENT_LUN_NAA_REGEXT}"
+ENV{SCSI_IDENT_LUN_NAA_REG}=="?*", ENV{ID_WWN}!="?*", ENV{ID_WWN}="0x$env{SCSI_IDENT_LUN_NAA_REG}"
+ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{ID_WWN}!="?*", ENV{ID_WWN}="0x$env{SCSI_IDENT_LUN_NAA_EXT}"
+ENV{SCSI_IDENT_LUN_NAA_LOCAL}=="?*", ENV{ID_WWN}!="?*", ENV{ID_WWN}="0x$env{SCSI_IDENT_LUN_NAA_LOCAL}"
+ENV{SCSI_IDENT_LUN_NAA_REGEXT}=="?*", ENV{ID_WWN_WITH_EXTENSION}!="?*", ENV{ID_WWN_WITH_EXTENSION}="0x$env{SCSI_IDENT_LUN_NAA_REGEXT}"
+ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{ID_WWN_WITH_EXTENSION}!="?*", ENV{ID_WWN_WITH_EXTENSION}="0x$env{SCSI_IDENT_LUN_NAA_EXT}"
+ENV{ID_WWN}=="?*", ENV{ID_WWN_WITH_EXTENSION}!="?*", ENV{ID_WWN_WITH_EXTENSION}="$env{ID_WWN}"
+
+# ata_id compability
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_ATA}=="?*", ENV{ID_BUS}="ata", ENV{ID_ATA}="1", ENV{ID_SERIAL}="$env{SCSI_IDENT_LUN_ATA}"
+ENV{ID_SERIAL_SHORT}!="?*", ENV{SCSI_VENDOR}=="ATA", ENV{SCSI_IDENT_LUN_VENDOR}=="?*", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_VENDOR}"
+# Compat ID_SERIAL setting
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_REGEXT}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA_REGEXT}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_REGEXT}"
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_REG}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA_REG}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_REG}"
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_EXT}"
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_EUI64}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="2$env{SCSI_IDENT_LUN_EUI64}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_EUI64}"
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAME}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="8$env{SCSI_IDENT_LUN_NAME}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAME}"
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_T10}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="1$env{SCSI_IDENT_LUN_T10}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_T10}"
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_NAA_LOCAL}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="3$env{SCSI_IDENT_LUN_NAA_LOCAL}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_NAA_LOCAL}"
+ENV{ID_SERIAL}!="?*", ENV{SCSI_IDENT_LUN_VENDOR}=="?*", ENV{ID_BUS}="scsi", ENV{ID_SERIAL}="0$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_LUN_VENDOR}", ENV{ID_SERIAL_SHORT}="$env{SCSI_IDENT_LUN_VENDOR}"
+LABEL="sg3_utils_id_end"
diff --git a/sg3_utils/scripts/58-scsi-sg3_symlink.rules b/sg3_utils/scripts/58-scsi-sg3_symlink.rules
new file mode 100644
index 0000000..2e8dd96
--- /dev/null
+++ b/sg3_utils/scripts/58-scsi-sg3_symlink.rules
@@ -0,0 +1,40 @@
+# SCSI-ID symlinks for sg3_utils
+
+ACTION=="remove", GOTO="sg3_utils_symlink_end"
+
+SUBSYSTEM!="block", GOTO="sg3_utils_symlink_end"
+
+# Skip symlink generation for multipath
+ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="sg3_utils_symlink_end"
+
+# Select which identifier to use per default
+# 0: vpd page 0x80 identifier
+ENV{SCSI_IDENT_SERIAL}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-S$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_SERIAL}"
+ENV{SCSI_IDENT_SERIAL}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-S$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_SERIAL}-part%n"
+# NAA identifier (prefix 3)
+# 1: IEEE Registered Extended first
+ENV{SCSI_IDENT_LUN_NAA_REGEXT}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA_REGEXT}"
+ENV{SCSI_IDENT_LUN_NAA_REGEXT}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA_REGEXT}-part%n"
+# 2: IEEE Registered
+ENV{SCSI_IDENT_LUN_NAA_REG}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA_REG}"
+ENV{SCSI_IDENT_LUN_NAA_REG}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA_REG}-part%n"
+# 3: IEEE Extended
+ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA_EXT}"
+ENV{SCSI_IDENT_LUN_NAA_EXT}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA_EXT}-part%n"
+# 4: EUI-64 identifer (prefix 2)
+ENV{SCSI_IDENT_LUN_EUI64}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-2$env{SCSI_IDENT_LUN_EUI64}"
+ENV{SCSI_IDENT_LUN_EUI64}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-2$env{SCSI_IDENT_LUN_EUI64}-part%n"
+# 5: SCSI name identifier (prefix 8)
+ENV{SCSI_IDENT_LUN_NAME}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-8$env{SCSI_IDENT_LUN_NAME}"
+ENV{SCSI_IDENT_LUN_NAME}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-8$env{SCSI_IDENT_LUN_NAME}-part%n"
+# 6: T10 Vendor identifer (prefix 1)
+ENV{SCSI_IDENT_LUN_T10}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-1$env{SCSI_IDENT_LUN_T10}"
+ENV{SCSI_IDENT_LUN_T10}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-1$env{SCSI_IDENT_LUN_T10}-part%n"
+# 7: IEEE Locally assigned
+ENV{SCSI_IDENT_LUN_NAA_LOCAL}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA_LOCAL}"
+ENV{SCSI_IDENT_LUN_NAA_LOCAL}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-3$env{SCSI_IDENT_LUN_NAA_LOCAL}-part%n"
+# 8: Vendor-specific identifier (prefix 0)
+ENV{SCSI_IDENT_LUN_VENDOR}=="?*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/scsi-0$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_LUN_VENDOR}"
+ENV{SCSI_IDENT_LUN_VENDOR}=="?*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/scsi-0$env{SCSI_VENDOR}_$env{SCSI_MODEL}_$env{SCSI_IDENT_LUN_VENDOR}-part%n"
+
+LABEL="sg3_utils_symlink_end"
diff --git a/sg3_utils/scripts/Makefile.am b/sg3_utils/scripts/Makefile.am
new file mode 100644
index 0000000..c5e017e
--- /dev/null
+++ b/sg3_utils/scripts/Makefile.am
@@ -0,0 +1,2 @@
+bin_SCRIPTS = rescan-scsi-bus.sh scsi_logging_level scsi_mandat scsi_readcap scsi_ready \
+	      scsi_satl scsi_start scsi_stop scsi_temperature
diff --git a/sg3_utils/scripts/Makefile.in b/sg3_utils/scripts/Makefile.in
new file mode 100644
index 0000000..526e461
--- /dev/null
+++ b/sg3_utils/scripts/Makefile.in
@@ -0,0 +1,506 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = scripts
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(bindir)"
+SCRIPTS = $(bin_SCRIPTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETOPT_O_FILES = @GETOPT_O_FILES@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+bin_SCRIPTS = scsi_logging_level scsi_mandat scsi_readcap scsi_ready \
+	      scsi_satl scsi_start scsi_stop scsi_temperature
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu scripts/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binSCRIPTS: $(bin_SCRIPTS)
+	@$(NORMAL_INSTALL)
+	@list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n' \
+	    -e 'h;s|.*|.|' \
+	    -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+	      if (++n[d] == $(am__install_max)) { \
+		print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+	    else { print "f", d "/" $$4, $$1 } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	     if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	     test -z "$$files" || { \
+	       echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+	       $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+	     } \
+	; done
+
+uninstall-binSCRIPTS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	       sed -e 's,.*/,,;$(transform)'`; \
+	dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(SCRIPTS)
+installdirs:
+	for dir in "$(DESTDIR)$(bindir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binSCRIPTS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binSCRIPTS
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+	cscopelist-am ctags-am distclean distclean-generic \
+	distclean-libtool distdir dvi dvi-am html html-am info info-am \
+	install install-am install-binSCRIPTS install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-generic \
+	mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
+	uninstall-am uninstall-binSCRIPTS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/sg3_utils/scripts/README b/sg3_utils/scripts/README
new file mode 100644
index 0000000..082e0e6
--- /dev/null
+++ b/sg3_utils/scripts/README
@@ -0,0 +1,56 @@
+                      README for sg3_utils/scripts
+                      ============================
+Introduction
+============
+This directory contains bash shell scripts. Most of them call one or
+more utilities from the sg3_utils package. They assume the sg3_utils
+package utilities are on the PATH of the user.
+
+rescan-scsi-bus.sh is written by Kurt Garloff (see
+http://www.garloff.de/kurt/linux/ under the "Rescan SCSI bus" heading)
+with patches from Hannes Reinecke.
+
+scsi_logging_level is written by Andreas Herrmann <aherrman at de dot ibm
+dot com>. It sets the logging level of the SCSI subsystem in the Linux
+2.6 series kernels. See that file for more information.
+
+The other scripts are written by the author. Some do testing while others
+do bulk tasks (e.g. stopping multiple disks).
+
+Details
+=======
+Each script supplies more information, typically by supplying a '-h'
+or '--help' option. The script source often contains explanatory
+information. Following is a usage summary with a one line description:
+   rescan-scsi-bus.sh [OPTIONS]
+        - see the output of 'rescan-scsi-bus.sh --help'
+   scsi_logging_level [OPTIONS]
+        - set Linux SCSI subsystem logging level
+   scsi_mandat [-h] [-L] [-q] <device>
+        - check for mandatory SCSI command support
+   scsi_readcap [-b] [-h] [-v] <device>+
+        - fetch capacity/size information for each <device>
+   scsi_ready [-h] [-v] <device>+
+        - check the media ready status on each <device>
+   scsi_satl [-h] [-L] [-q] [-v] <device>
+        - check <device> for SCSI to ATA Translation Layer (SATL)
+   scsi_start [-h] [-v] [-w] <device>+
+        - start media (i.e. spin up) in each <device>
+   scsi_stop [-h] [-v] [-w] <device>+
+        - stop media (i.e. spin down) in each <device>
+   scsi_temperature [-h] [-v] <device>+
+        - check temperature in each <device>
+
+These scripts assume that the main sg3_utils utilities are installed
+and are on the user's PATH.
+
+This directory, prior to sg3_utils-1.28, contained the sas_disk_blink
+script. Since it depends on the sdparm utility it has been moved to
+the sdparm package in its scripts directory.
+
+59-scsi-sg3_utils.rules is a Linux specific file for udev. These rules use
+'sg_inq --export' to help udev create identifying device nodes, for example
+/dev/disk/by-id/wwn-0x5001501234567890-part1.
+
+Douglas Gilbert
+8th March 2014
diff --git a/sg3_utils/scripts/rescan-scsi-bus.sh b/sg3_utils/scripts/rescan-scsi-bus.sh
new file mode 100755
index 0000000..62c2778
--- /dev/null
+++ b/sg3_utils/scripts/rescan-scsi-bus.sh
@@ -0,0 +1,1306 @@
+#!/bin/bash
+# Skript to rescan SCSI bus, using the 
+# scsi add-single-device mechanism
+# (c) 1998--2010 Kurt Garloff <kurt@garloff.de>, GNU GPL v2 or v3
+# (c) 2006--2015 Hannes Reinecke, GNU GPL v2 or later
+# $Id: rescan-scsi-bus.sh,v 1.57 2012/03/31 14:08:48 garloff Exp $
+
+VERSION="20160201"
+SCAN_WILD_CARD=4294967295
+
+setcolor ()
+{
+  red="\e[0;31m"
+  green="\e[0;32m"
+  yellow="\e[0;33m"
+  bold="\e[0;1m"
+  norm="\e[0;0m"
+}
+
+unsetcolor () 
+{
+  red=""; green=""
+  yellow=""; norm=""
+}
+
+echo_debug()
+{
+  if [ $debug -eq 1 ] ; then
+     echo "$1"
+  fi
+}
+
+# Output some text and return cursor to previous position
+# (only works for simple strings)
+# Stores length of string in LN and returns it
+print_and_scroll_back ()
+{
+  STRG="$1"
+  LN=${#STRG}
+  BK=""
+  declare -i cntr=0
+  while test $cntr -lt $LN; do BK="$BK\e[D"; let cntr+=1; done
+  echo -en "$STRG$BK"
+  return $LN
+}
+
+# Overwrite a text of length $1 (fallback to $LN) with whitespace
+white_out ()
+{
+  BK=""; WH=""
+  if test -n "$1"; then LN=$1; fi
+  declare -i cntr=0
+  while test $cntr -lt $LN; do BK="$BK\e[D"; WH="$WH "; let cntr+=1; done
+  echo -en "$WH$BK"
+}
+
+# Return hosts. sysfs must be mounted
+findhosts_26 ()
+{
+  hosts=`find /sys/class/scsi_host/host* -maxdepth 4 -type d -o -type l 2> /dev/null | awk -F'/' '{print $5}' | sed -e 's~host~~' | sort -nu` 
+  scsi_host_data=`echo "$hosts" | sed -e 's~^~/sys/class/scsi_host/host~'` 
+  for hostdir in $scsi_host_data; do 
+    hostno=${hostdir#/sys/class/scsi_host/host}
+    if [ -f $hostdir/isp_name ] ; then
+      hostname="qla2xxx"
+    elif [ -f $hostdir/lpfc_drvr_version ] ; then
+      hostname="lpfc"
+    else
+      hostname=`cat $hostdir/proc_name`
+    fi
+    #hosts="$hosts $hostno"
+    echo_debug "Host adapter $hostno ($hostname) found."
+  done  
+  if [ -z "$hosts" ] ; then
+    echo "No SCSI host adapters found in sysfs"
+    exit 1;
+  fi
+  # Not necessary just use double quotes around variable to preserve new lines
+  #hosts=`echo $hosts | tr ' ' '\n'`
+}
+
+# Return hosts. /proc/scsi/HOSTADAPTER/? must exist
+findhosts ()
+{
+  hosts=
+  for driverdir in /proc/scsi/*; do
+    driver=${driverdir#/proc/scsi/}
+    if test $driver = scsi -o $driver = sg -o $driver = dummy -o $driver = device_info; then continue; fi
+    for hostdir in $driverdir/*; do
+      name=${hostdir#/proc/scsi/*/}
+      if test $name = add_map -o $name = map -o $name = mod_parm; then continue; fi
+      num=$name
+      driverinfo=$driver
+      if test -r $hostdir/status; then
+        num=$(printf '%d\n' `sed -n 's/SCSI host number://p' $hostdir/status`)
+        driverinfo="$driver:$name"
+      fi
+      hosts="$hosts $num"
+      echo "Host adapter $num ($driverinfo) found."
+    done
+  done
+}
+
+printtype ()
+{
+  local type=$1
+
+  case "$type" in
+    0) echo "Direct-Access" ;;
+    1) echo "Sequential-Access" ;;
+    2) echo "Printer" ;;
+    3) echo "Processor" ;;
+    4) echo "WORM" ;;
+    5) echo "CD-ROM" ;;
+    6) echo "Scanner" ;;
+    7) echo "Optical-Device" ;;
+    8) echo "Medium-Changer" ;;
+    9) echo "Communications" ;;
+    10) echo "Unknown" ;;
+    11) echo "Unknown" ;;
+    12) echo "RAID" ;;
+    13) echo "Enclosure" ;;
+    14) echo "Direct-Access-RBC" ;;
+    *) echo "Unknown" ;;
+  esac
+}
+
+print02i()
+{
+    if [ "$1" = "*" ] ; then 
+        echo "00"
+    else
+        printf "%02i" "$1"
+    fi
+}
+
+# Get /proc/scsi/scsi info for device $host:$channel:$id:$lun
+# Optional parameter: Number of lines after first (default = 2), 
+# result in SCSISTR, return code 1 means empty.
+procscsiscsi ()
+{  
+  if test -z "$1"; then LN=2; else LN=$1; fi
+  CHANNEL=`print02i "$channel"`
+  ID=`print02i "$id"`
+  LUN=`print02i "$lun"`
+  if [ -d /sys/class/scsi_device ]; then
+    SCSIPATH="/sys/class/scsi_device/${host}:${channel}:${id}:${lun}"
+    if [ -d  "$SCSIPATH" ] ; then
+      SCSISTR="Host: scsi${host} Channel: $CHANNEL Id: $ID Lun: $LUN"
+      if [ "$LN" -gt 0 ] ; then
+        IVEND=$(cat ${SCSIPATH}/device/vendor)
+        IPROD=$(cat ${SCSIPATH}/device/model)
+        IPREV=$(cat ${SCSIPATH}/device/rev)
+        SCSIDEV=$(printf '  Vendor: %-08s Model: %-16s Rev: %-4s' "$IVEND" "$IPROD" "$IPREV")
+        SCSISTR="$SCSISTR
+$SCSIDEV"
+      fi
+      if [ "$LN" -gt 1 ] ; then
+        ILVL=$(cat ${SCSIPATH}/device/scsi_level)
+        type=$(cat ${SCSIPATH}/device/type)
+        ITYPE=$(printtype $type)
+        SCSITMP=$(printf '  Type:   %-17s                ANSI SCSI revision: %02d' "$ITYPE" "$((ILVL - 1))")
+        SCSISTR="$SCSISTR
+$SCSITMP"
+      fi
+    else
+      return 1
+    fi
+  else
+    grepstr="scsi$host Channel: $CHANNEL Id: $ID Lun: $LUN"
+    SCSISTR=`cat /proc/scsi/scsi | grep -A$LN -e"$grepstr"`
+  fi
+  if test -z "$SCSISTR"; then return 1; else return 0; fi
+}
+
+# Find sg device with 2.6 sysfs support
+sgdevice26 ()
+{
+  if test -e /sys/class/scsi_device/$host\:$channel\:$id\:$lun/device/generic; then
+    SGDEV=`readlink /sys/class/scsi_device/$host\:$channel\:$id\:$lun/device/generic`
+    SGDEV=`basename $SGDEV`
+  else
+    for SGDEV in /sys/class/scsi_generic/sg*; do
+      DEV=`readlink $SGDEV/device`
+      if test "${DEV##*/}" = "$host:$channel:$id:$lun"; then
+        SGDEV=`basename $SGDEV`; return
+      fi
+    done
+    SGDEV=""
+  fi  
+}
+
+# Find sg device with 2.4 report-devs extensions
+sgdevice24 ()
+{
+  if procscsiscsi 3; then
+    SGDEV=`echo "$SCSISTR" | grep 'Attached drivers:' | sed 's/^ *Attached drivers: \(sg[0-9]*\).*/\1/'`
+  fi
+}
+
+# Find sg device that belongs to SCSI device $host $channel $id $lun
+# and return in SGDEV
+sgdevice ()
+{
+  SGDEV=
+  if test -d /sys/class/scsi_device; then
+    sgdevice26
+  else  
+    DRV=`grep 'Attached drivers:' /proc/scsi/scsi 2>/dev/null`
+    repdevstat=$((1-$?))
+    if [ $repdevstat = 0 ]; then
+      echo "scsi report-devs 1" >/proc/scsi/scsi
+      DRV=`grep 'Attached drivers:' /proc/scsi/scsi 2>/dev/null`
+      if [ $? = 1 ]; then return; fi
+    fi
+    if ! `echo $DRV | grep 'drivers: sg' >/dev/null`; then
+      modprobe sg
+    fi
+    sgdevice24
+    if [ $repdevstat = 0 ]; then
+      echo "scsi report-devs 0" >/proc/scsi/scsi
+    fi
+  fi
+}
+
+# Test if SCSI device is still responding to commands
+# Return values:
+#   0 device is present
+#   1 device has changed
+#   2 device has been removed
+testonline ()
+{
+  : testonline
+  ctr=0
+  RC=0
+  # Set default values
+  IPTYPE=31
+  IPQUAL=3
+  if test ! -x /usr/bin/sg_turs; then return 0; fi
+  sgdevice
+  if test -z "$SGDEV"; then return 0; fi
+  sg_turs /dev/$SGDEV >/dev/null 2>&1
+  RC=$?
+
+  # Handle in progress of becoming ready and unit attention
+  while test $RC = 2 -o $RC = 6 && test $ctr -le 30; do
+    if test $RC = 2 -a "$RMB" != "1"; then echo -n "."; let LN+=1; sleep 1
+    else sleep 0.02; fi
+    let ctr+=1
+    sg_turs /dev/$SGDEV >/dev/null 2>&1
+    RC=$?
+    # Check for removable device; TEST UNIT READY obviously will
+    # fail for a removable device with no medium
+    RMB=`sg_inq /dev/$SGDEV 2>/dev/null | grep 'RMB=' | sed 's/^.*RMB=\(.\).*$/\1/'`
+    print_and_scroll_back "$host:$channel:$id:$lun $SGDEV ($RMB) "
+    test $RC = 2 -a "$RMB" = "1" && break
+  done
+  if test $ctr != 0; then white_out; fi
+  # echo -e "\e[A\e[A\e[A${yellow}Test existence of $SGDEV = $RC ${norm} \n\n\n"
+  if test $RC = 1; then return $RC; fi
+  # Reset RC (might be !=0 for passive paths)
+  RC=0
+  # OK, device online, compare INQUIRY string
+  INQ=`sg_inq $sg_len_arg /dev/$SGDEV 2>/dev/null`
+  if [ -z "$INQ" ] ; then
+    echo -e "\e[A\e[A\e[A\e[A${red}$SGDEV changed: ${bold}INQUIRY failed${norm}    \n\n\n"
+    return 2
+  fi
+  IVEND=`echo "$INQ" | grep 'Vendor identification:' | sed 's/^[^:]*: \(.*\)$/\1/'`
+  IPROD=`echo "$INQ" | grep 'Product identification:' | sed 's/^[^:]*: \(.*\)$/\1/'`
+  IPREV=`echo "$INQ" | grep 'Product revision level:' | sed 's/^[^:]*: \(.*\)$/\1/'`
+  STR=`printf "  Vendor: %-08s Model: %-16s Rev: %-4s" "$IVEND" "$IPROD" "$IPREV"`
+  IPTYPE=`echo "$INQ" | sed -n 's/.* Device_type=\([0-9]*\) .*/\1/p'`
+  IPQUAL=`echo "$INQ" | sed -n 's/ *PQual=\([0-9]*\)  Device.*/\1/p'`
+  if [ "$IPQUAL" != 0 ] ; then
+    [ -z "$IPQUAL" ] && IPQUAL=3
+    [ -z "$IPTYPE" ] && IPTYPE=31
+    echo -e "\e[A\e[A\e[A\e[A${red}$SGDEV changed: ${bold}LU not available (PQual $IPQUAL)${norm}    \n\n\n"
+    return 2
+  fi
+
+  TYPE=$(printtype $IPTYPE)
+  if ! procscsiscsi ; then
+    echo -e "\e[A\e[A\e[A\e[A${red}$SGDEV removed.\n\n\n"
+    return 2
+  fi
+  TMPSTR=`echo "$SCSISTR" | grep 'Vendor:'`
+  if [ "$TMPSTR" != "$STR" ]; then
+    echo -e "\e[A\e[A\e[A\e[A${red}$SGDEV changed: ${bold}\nfrom:${SCSISTR#* } \nto: $STR ${norm} \n\n\n"
+    return 1
+  fi
+  TMPSTR=`echo "$SCSISTR" | sed -n 's/.*Type: *\(.*\) *ANSI.*/\1/p' | sed 's/ *$//g'`
+  if [ "$TMPSTR" != "$TYPE" ] ; then
+    echo -e "\e[A\e[A\e[A\e[A${red}$SGDEV changed: ${bold}\nfrom:${TMPSTR} \nto: $TYPE ${norm} \n\n\n"
+    return 1
+  fi
+  return $RC
+}
+
+# Test if SCSI device $host $channen $id $lun exists
+# Outputs description from /proc/scsi/scsi (unless arg passed)
+# Returns SCSISTR (empty if no dev)
+testexist ()
+{
+  : testexist
+  SCSISTR=
+  if procscsiscsi && test -z "$1"; then
+    echo "$SCSISTR" | head -n1
+    echo "$SCSISTR" | tail -n2 | pr -o4 -l1
+  fi
+}
+
+# Returns the list of existing channels per host
+chanlist ()
+{
+  local hcil
+  local cil
+  local chan
+  local tmpchan
+
+  for dev in /sys/class/scsi_device/${host}:* ; do
+    [ -d $dev ] || continue;
+    hcil=${dev##*/}
+    cil=${hcil#*:}
+    chan=${cil%%:*}
+    for tmpchan in $channelsearch ; do
+      if test "$chan" -eq $tmpchan ; then
+        chan=
+      fi
+    done
+    if test -n "$chan" ; then
+      channelsearch="$channelsearch $chan"
+    fi
+  done
+  if test -z "$channelsearch"; then channelsearch="0"; fi
+}
+
+# Returns the list of existing targets per host
+idlist ()
+{
+  local hcil
+  local target
+  local tmpid
+  local newid
+
+  idsearch=$(ls /sys/class/scsi_device/ | sed -n "s/${host}:${channel}:\([0-9]*\):[0-9]*/\1/p" | uniq)
+  echo "${channel} - -" > /sys/class/scsi_host/host${host}/scan
+  # Rescan to check if we found new targets
+  newsearch=$(ls /sys/class/scsi_device/ | sed -n "s/${host}:${channel}:\([0-9]*\):[0-9]*/\1/p" | uniq)
+  for id in $newsearch ; do
+    newid=$id
+    for tmpid in $idsearch ; do
+      if test $id -eq $tmpid ; then
+        newid=
+        break
+      fi
+    done
+    if test -n "$newid" ; then
+      id=$newid
+      for dev in /sys/class/scsi_device/${host}:${channel}:${newid}:* ; do
+        [ -d $dev ] || continue;
+        hcil=${dev##*/}
+        lun=${hcil##*:}
+        printf "\r${green}NEW: $norm"
+        testexist
+        if test "$SCSISTR" ; then
+          incrfound "$hcil"
+        fi
+      done
+    fi
+  done
+}
+
+# Returns the list of existing LUNs from device $host $channel $id $lun
+# and returns list to stdout
+getluns()
+{
+  sgdevice
+  if test -z "$SGDEV"; then return 1; fi
+  if test ! -x /usr/bin/sg_luns; then echo 0; return 1; fi
+  LLUN=`sg_luns /dev/$SGDEV 2>/dev/null | sed -n 's/    \(.*\)/\1/p'`
+  # Added -z $LLUN condition because $? gets the RC from sed, not sg_luns
+  if test $? != 0 -o -z "$LLUN"; then echo 0; return 1; fi
+  for lun in $LLUN ; do
+      # Swap LUN number
+      l0=$(printf '%u' 0x$lun)
+      l1=$(( ($l0 >> 48) & 0xffff ))
+      l2=$(( ($l0 >> 32) & 0xffff )) 
+      l3=$(( ($l0 >> 16) & 0xffff ))
+      l4=$(( $l0 & 0xffff ))
+      l0=$(( ( ( ($l4 * 0xffff) + $l3 ) * 0xffff + $l2 ) * 0xffff + $l1 ))
+      printf "%u\n" $l0
+  done
+  return 0
+}
+
+# Wait for udev to settle (create device nodes etc.)
+udevadm_settle()
+{
+  local tmo=60
+  if test -x /sbin/udevadm; then 
+    print_and_scroll_back " Calling udevadm settle (can take a while) "
+    # Loop for up to 60 seconds if sd devices still are settling..
+    # This allows us to continue if udev events are stuck on multipaths in recovery mode
+    while [ $tmo -gt 0 ] ; do
+      if ! /sbin/udevadm settle --timeout=1 | egrep -q sd[a-z]+ ; then
+        break;
+      fi
+      let tmo=$tmo-1
+    done
+    white_out
+  elif test -x /sbin/udevsettle; then
+    print_and_scroll_back " Calling udevsettle (can take a while) "
+    /sbin/udevsettle
+    white_out
+  else
+    sleep 0.02
+  fi
+}
+
+# Perform scan on a single lun $host $channel $id $lun
+dolunscan()
+{
+  local remappedlun0=
+  SCSISTR=
+  devnr="$host $channel $id $lun"
+  echo -e " Scanning for device $devnr ... "
+  printf "${yellow}OLD: $norm"
+  testexist
+  # Device exists: Test whether it's still online
+  # (testonline returns 2 if it's gone and 1 if it has changed)
+  if test "$SCSISTR" ; then
+    testonline
+    RC=$?
+    # Well known lun transition case. Only for Direct-Access devs (type 0)
+    # If block directory exists && and PQUAL != 0, we unmapped lun0 and just have a well-known lun
+    # If block directory doesn't exist && PQUAL == 0, we mapped a real lun0
+    if test $lun -eq 0 -a $IPTYPE -eq 0 ; then
+      if test $RC = 2 ; then
+        if test -e /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device; then
+          if test -d /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device/block ; then
+            remappedlun0=2  # Transition from real lun 0 to well-known
+          else
+            RC=0   # Set this so the system leaves the existing well known lun alone. This is a lun 0 with no block directory
+          fi
+        fi
+      elif test $RC = 0 -a $IPTYPE -eq 0; then
+        if test -e /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device; then
+          if test ! -d /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device/block ; then
+            remappedlun0=1  # Transition from well-known to real lun 0
+          fi
+        fi
+      fi
+    fi
+  fi
+
+  # Special case: lun 0 just got added (for reportlunscan),
+  # so make sure we correctly treat it as new
+  if test "$lun" = "0" -a "$1" = "1" -a -z "$remappedlun0"; then
+    SCSISTR=""
+    printf "\r\e[A\e[A\e[A"
+  fi
+
+  : f $remove s $SCSISTR
+  if test "$remove" -a "$SCSISTR" -o "$remappedlun0" = "1"; then
+    if test $RC != 0 -o ! -z "$forceremove" -o -n "$remappedlun0"; then
+      if test "$remappedlun0" != "1" ; then
+        echo -en "\r\e[A\e[A\e[A${red}REM: "
+        echo "$SCSISTR" | head -n1
+        echo -e "${norm}\e[B\e[B"
+      fi
+      if test -e /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device; then
+        # have to preemptively do this so we can figure out the mpath device
+        # Don't do this if we're deleting a well known lun to replace it
+        if test "$remappedlun0" != "1" ; then
+          incrrmvd "$host:$channel:$id:$lun"
+        fi
+        echo 1 > /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device/delete
+        sleep 0.02
+      else
+        echo "scsi remove-single-device $devnr" > /proc/scsi/scsi
+        if test $RC -eq 1 -o $lun -eq 0 ; then
+          # Try readding, should fail if device is gone
+          echo "scsi add-single-device $devnr" > /proc/scsi/scsi
+        fi
+      fi
+    fi
+    if test $RC = 0 -o "$forcerescan" ; then
+      if test -e /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device; then
+        echo 1 > /sys/class/scsi_device/${host}:${channel}:${id}:${lun}/device/rescan
+      fi
+    fi
+    printf "\r\e[A\e[A\e[A${yellow}OLD: $norm"
+    testexist
+    if test -z "$SCSISTR" -a $RC != 1 -a "$remappedlun0" != "1"; then
+      printf "\r${red}DEL: $norm\r\n\n"
+      # In the event we're replacing with a well known node, we need to let it continue, to create the replacement node
+      test "$remappedlun0" != "2" && return 1
+    fi
+  fi
+  if test -z "$SCSISTR" -o -n "$remappedlun0"; then
+    if test "$remappedlun0" != "2" ; then
+      # Device does not exist, try to add
+      printf "\r${green}NEW: $norm"
+    fi
+    if test -e /sys/class/scsi_host/host${host}/scan; then
+      echo "$channel $id $lun" > /sys/class/scsi_host/host${host}/scan 2> /dev/null
+    else
+      echo "scsi add-single-device $devnr" > /proc/scsi/scsi
+    fi
+    testexist
+    if test -z "$SCSISTR"; then
+      # Device not present
+      printf "\r\e[A";
+      # Optimization: if lun==0, stop here (only if in non-remove mode)
+      if test $lun = 0 -a -z "$remove" -a $optscan = 1; then 
+        break;
+      fi
+    else 
+      if test "$remappedlun0" != "2" ; then
+        incrfound "$host:$channel:$id:$lun"
+      fi
+    fi
+  fi
+}
+
+# Perform report lun scan on $host $channel $id using REPORT_LUNS
+doreportlun()
+{
+  lun=0
+  SCSISTR=
+  devnr="$host $channel $id $lun"
+  echo -en " Scanning for device $devnr ...\r"
+  lun0added=
+  #printf "${yellow}OLD: $norm"
+  # Phase one: If LUN0 does not exist, try to add
+  testexist -q
+  if test -z "$SCSISTR"; then
+    # Device does not exist, try to add
+    #printf "\r${green}NEW: $norm"
+    if test -e /sys/class/scsi_host/host${host}/scan; then
+      echo "$channel $id $lun" > /sys/class/scsi_host/host${host}/scan 2> /dev/null
+      udevadm_settle
+    else
+      echo "scsi add-single-device $devnr" > /proc/scsi/scsi
+    fi
+    testexist -q
+    if test -n "$SCSISTR"; then
+      lun0added=1
+      #testonline
+    else
+      # Device not present
+      # return
+      # Find alternative LUN to send getluns to
+      for dev in /sys/class/scsi_device/${host}:${channel}:${id}:*; do
+        [ -d "$dev" ] || continue
+        lun=${dev##*:}
+        break
+      done
+    fi
+  fi
+  targetluns=`getluns`
+  REPLUNSTAT=$?
+  lunremove=
+  #echo "getluns reports " $targetluns
+  olddev=`find /sys/class/scsi_device/ -name $host:$channel:$id:* 2>/dev/null | sort -t: -k4 -n`
+  oldluns=`echo "$olddev" | awk -F'/' '{print $5}' | awk -F':' '{print $4}'`
+  oldtargets="$targetluns"
+  # OK -- if we don't have a LUN to send a REPORT_LUNS to, we could
+  # fall back to wildcard scanning. Same thing if the device does not
+  # support REPORT_LUNS
+  # TODO: We might be better off to ALWAYS use wildcard scanning if 
+  # it works
+  if test "$REPLUNSTAT" = "1"; then
+    if test -e /sys/class/scsi_host/host${host}/scan; then
+      echo "$channel $id -" > /sys/class/scsi_host/host${host}/scan 2> /dev/null
+      udevadm_settle
+    else
+      echo "scsi add-single-device $host $channel $id $SCAN_WILD_CARD" > /proc/scsi/scsi
+    fi
+    targetluns=`find /sys/class/scsi_device/ -name $host:$channel:$id:* 2>/dev/null | awk -F'/' '{print $5}' | awk -F':' '{print $4}' | sort -n`
+    let found+=`echo "$targetluns" | wc -l`
+    let found-=`echo "$olddev" | wc -l`
+  fi
+  if test -z "$targetluns"; then targetluns="$oldtargets"; fi
+  # Check existing luns
+  for dev in $olddev; do
+    [ -d "$dev" ] || continue
+    lun=${dev##*:}
+    newsearch=
+    inlist=
+    # OK, is existing $lun (still) in reported list
+    for tmplun in $targetluns; do
+      if test $tmplun -eq $lun ; then
+        inlist=1
+        dolunscan $lun0added
+      else
+        newsearch="$newsearch $tmplun"
+      fi
+    done
+    # OK, we have now done a lunscan on $lun and 
+    # $newsearch is the old $targetluns without $lun
+    if [ -z "$inlist" ]; then
+      # Stale lun
+      lunremove="$lunremove $lun"
+    fi
+    # $lun removed from $lunsearch (echo for whitespace cleanup)
+    targetluns=`echo $newsearch`
+  done
+  # Add new ones and check stale ones
+  for lun in $targetluns $lunremove; do
+    dolunscan $lun0added
+  done
+}
+
+# Perform search (scan $host)
+dosearch ()
+{
+  if test -z "$channelsearch" ; then
+    chanlist
+  fi
+  for channel in $channelsearch; do
+    if test -z "$idsearch" ; then
+      idlist
+    fi
+    for id in $idsearch; do
+      if test -z "$lunsearch" ; then
+        doreportlun
+      else
+        for lun in $lunsearch; do
+          dolunscan
+        done
+      fi
+    done
+  done
+}
+ 
+expandlist ()
+{
+  list=$1
+  result=""
+  first=${list%%,*}
+  rest=${list#*,}
+  while test ! -z "$first"; do 
+    beg=${first%%-*};
+    if test "$beg" = "$first"; then
+      result="$result $beg";
+    else
+      end=${first#*-}
+      result="$result `seq $beg $end`"
+    fi
+    test "$rest" = "$first" && rest=""
+    first=${rest%%,*}
+    rest=${rest#*,}
+  done
+  echo $result
+}
+
+searchexisting()
+{
+  local tmpch;
+  local tmpid
+  local match=0
+  local targets=`ls -d /sys/class/scsi_device/$host:* 2> /dev/null | egrep -o $host:[0-9]+:[0-9]+ | sort | uniq`
+
+  # Nothing came back on this host, so we should skip it
+  test -z "$targets" && return
+
+  local target=;
+  for target in $targets ; do
+    channel=`echo $target | cut -d":" -f2`
+    id=`echo $target | cut -d":" -f 3`
+    if [ -n "$channelsearch" ] ; then
+      for tmpch in $channelsearch ; do
+        test $tmpch -eq $channel && match=1
+      done
+    else
+      match=1
+    fi
+
+    test $match -eq 0 && continue
+    match=0
+
+    if [ $filter_ids -eq 1 ] ; then
+      for tmpid in $idsearch ; do
+        if [ $tmpid -eq $id ] ; then
+          match=1
+        fi
+      done
+    else
+      match=1
+    fi
+    test $match -eq 1 && doreportlun
+  done
+}
+
+# Go through all of the existing devices and figure out any that have been remapped
+findremapped()
+{
+  local hctl=;
+  local devs=`ls /sys/class/scsi_device/`
+  local sddev=
+  local id_serial=
+  local id_serial_old=
+  local sysfs_devpath=
+  local mpath_uuid=
+  local remapped=
+  mpaths=""
+  local tmpfile=$(mktemp /tmp/rescan-scsi-bus.XXXXXXXX 2> /dev/null)
+
+  if [ -z "$tmpfile" ] ; then
+    tmpfile="/tmp/rescan-scsi-bus.$$"
+    rm -f $tmpfile
+  fi
+
+  # Get all of the ID_SERIAL attributes, after finding their sd node
+  for hctl in $devs ; do
+    if [ -d /sys/class/scsi_device/$hctl/device/block ] ; then
+      sddev=`ls /sys/class/scsi_device/$hctl/device/block`
+      id_serial_old=`udevadm info -q all -n $sddev | grep "ID_SERIAL=" | cut -d"=" -f2`
+      [ -z "$id_serial_old" ] && id_serial_old="none"
+      echo "$hctl $sddev $id_serial_old" >> $tmpfile
+    fi
+  done
+
+  # Trigger udev to update the info
+  echo -n "Triggering udev to update device information... "
+  /sbin/udevadm trigger
+  udevadm_settle 2>&1 /dev/null
+  echo "Done"
+
+  # See what changed and reload the respective multipath device if applicable
+  while read hctl sddev id_serial_old ; do
+    remapped=0
+    id_serial=`udevadm info -q all -n $sddev | grep "ID_SERIAL=" | cut -d"=" -f2`
+    [ -z "$id_serial" ] && id_serial="none"
+    if [ "$id_serial_old" != "$id_serial" ] ; then
+      remapped=1
+    fi
+    # If udev events updated the disks already, but the multipath device isn't update
+    # check for old devices to make sure we found remapped luns
+    if [ -n "$mp_enable" ] && [ $remapped -eq 0 ]; then
+      findmultipath $sddev $id_serial
+      if [ $? -eq 1 ] ; then
+        remapped=1
+      fi
+    fi
+
+    # if uuid is 1, it's unmapped, so we don't want to treat it as a remap
+    # if remapped flag is 0, just skip the rest of the logic
+    if [ "$id_serial" = "1" ] || [ $remapped -eq 0 ] ; then
+      continue
+    fi
+    printf "${yellow}REMAPPED: $norm"
+    host=`echo $hctl | cut -d":" -f1`
+    channel=`echo $hctl | cut -d":" -f2`
+    id=`echo $hctl | cut -d":" -f3`
+    lun=`echo $hctl | cut -d":" -f4`
+    procscsiscsi
+    echo "$SCSISTR"
+    incrchgd "$hctl"
+  done < $tmpfile
+  rm -f $tmpfile
+
+  if test -n "$mp_enable" -a -n "$mpaths" ; then
+    echo "Updating multipath device mappings"
+    flushmpaths
+    $MULTIPATH | grep "create:" 2> /dev/null
+  fi
+}
+
+incrfound()
+{
+  local hctl="$1"
+  if test -n "$hctl" ; then
+    let found+=1
+    FOUNDDEVS="$FOUNDDEVS\t[$hctl]\n"
+  else
+    return
+  fi
+}
+
+incrchgd()
+{
+  local hctl="$1"
+  if test -n "$hctl" ; then
+    if ! echo $CHGDEVS | grep -q "\[$hctl\]"; then
+      let updated+=1
+      CHGDEVS="$CHGDEVS\t[$hctl]\n"
+    fi
+  else
+    return
+  fi
+
+  if test -n "$mp_enable" ; then
+    local sdev="`findsddev \"$hctl\"`"
+    if test -n "$sdev" ; then
+      findmultipath "$sdev"
+    fi
+  fi
+}
+
+incrrmvd()
+{
+  local hctl="$1"
+  if test -n "$hctl" ; then
+    let rmvd+=1;
+    RMVDDEVS="$RMVDDEVS\t[$hctl]\n"
+  else
+    return
+  fi
+
+  if test -n "$mp_enable" ; then
+    local sdev="`findsddev \"$hctl\"`"
+    if test -n "$sdev" ; then
+      findmultipath "$sdev"
+    fi
+  fi
+}
+
+findsddev()
+{
+  local hctl="$1"
+  local sddev=  
+
+  if test ! -e /sys/class/scsi_device/$hctl/device/block ; then
+    return 1
+  fi 
+
+  sddev=`ls /sys/class/scsi_device/$hctl/device/block`
+  echo $sddev
+
+  return 0
+}
+
+addmpathtolist()
+{
+  local mp="$1"
+  local mp2=
+
+  for mp2 in $mpaths ; do
+    # The multipath device is already in the list
+    if [ "$mp2" = "$mp" ] ; then
+      return
+    fi
+  done
+  mpaths="$mpaths $mp"
+}
+
+findmultipath()
+{
+  local dev="$1"
+  local find_mismatch="$2"
+  local mp=
+  local mp2=
+  local found_dup=0
+
+  # Need a sdev, and executable multipath and dmsetup command here
+  if [ -z "$dev" ] || [ ! -x $DMSETUP ] || [ ! -x "$MULTIPATH" ] ; then
+    return 1
+  fi
+
+  local maj_min=`cat /sys/block/$dev/dev`
+  for mp in $($DMSETUP ls --target=multipath | cut -f 1) ; do
+    [ "$mp" = "No" ] && break;
+    if $($DMSETUP status $mp | grep -q " $maj_min ") ; then
+      # With two arguments, look up current uuid from sysfs
+      # if it doesn't match what was passed, this multipath
+      # device is not updated, so this is a remapped LUN
+      if [ -n "$find_mismatch" ] ; then
+        mp2=`$MULTIPATH -l $mp | egrep -o dm-[0-9]+`
+        mp2=`cat /sys/block/$mp2/dm/uuid | cut -f2 -d-`
+        if [ "$find_mismatch" != "$mp2" ] ; then
+          addmpathtolist $mp
+          found_dup=1
+        fi
+        continue
+      fi
+      # Normal mode: Find the first multipath with the sdev
+      # and add it to the list
+      addmpathtolist $mp
+      return
+    fi
+  done
+
+  # Return 1 to signal that a duplicate was found to the calling function
+  if [ $found_dup -eq 1 ] ; then
+    return 1
+  else
+    return 0
+  fi
+}
+
+reloadmpaths()
+{
+  local mpath
+  if [ ! -x "$MULTIPATH" ] ; then
+    echo "no -x multipath"
+    return
+  fi
+
+  # Pass 1 as the argument to reload all mpaths
+  if [ "$1" = "1" ] ; then
+    echo "Reloading all multipath devices"
+    $MULTIPATH -r > /dev/null 2>&1
+    return
+  fi
+
+  # Reload the multipath devices
+  for mpath in $mpaths ; do
+    echo -n "Reloading multipath device $mpath... "
+    $MULTIPATH -r $mpath > /dev/null 2>&1
+    if test "$?" = "0" ; then
+      echo "Done"
+    else
+      echo "Fail"
+    fi
+  done
+}
+
+resizempaths()
+{
+  local mpath
+
+  for mpath in $mpaths ; do
+    echo -n "Resizing multipath map $mpath ..."
+    multipathd -k"resize map $mpath"
+    let updated+=1
+  done
+}
+
+flushmpaths()
+{
+  local mpath
+  local remove=""
+  local i
+  local flush_retries=5
+
+  if test -n "$1" ; then
+    for mpath in $($DMSETUP ls --target=multipath | cut -f 1) ; do
+      [ "$mpath" = "No" ] && break
+      num=$($DMSETUP status $mpath | awk 'BEGIN{RS=" ";active=0}/[0-9]+:[0-9]+/{dev=1}/A/{if (dev == 1) active++; dev=0} END{ print active }')
+      if [ $num -eq 0 ] ; then
+        remove="$remove $mpath"
+      fi
+    done
+  else
+    remove="$mpaths"
+  fi
+
+  for mpath in $remove ; do
+    i=0
+    echo -n "Flushing multipath device $mpath... "
+    while [ $i -lt $flush_retries ] ; do
+      $DMSETUP message $mpath 0 fail_if_no_path > /dev/null 2>&1
+      $MULTIPATH -f $mpath > /dev/null 2>&1
+      if test "$?" = "0" ; then
+        echo "Done ($i retries)"
+        break
+      elif test $i -eq $flush_retries ; then
+        echo "Fail"
+      fi
+      sleep 0.02
+      let i=$i+1
+    done
+  done
+}
+
+
+# Find resized luns
+findresized()
+{
+  local devs=`ls /sys/class/scsi_device/`
+  local size=
+  local new_size=
+  local sysfs_path=
+  local sddev=
+  local i=
+  local m=
+  local mpathsize=
+  declare -a mpathsizes
+
+  for hctl in $devs ; do
+    sysfs_path="/sys/class/scsi_device/$hctl/device"
+    if [ -d "$sysfs_path/block" ] ; then
+      sddev=`ls $sysfs_path/block`
+      size=`cat $sysfs_path/block/$sddev/size`
+
+      echo 1 > $sysfs_path/rescan
+      new_size=`cat $sysfs_path/block/$sddev/size`
+
+      if [ "$size" != "$new_size" ] && [ "$size" != "0" ] && [ "$new_size" != "0" ] ; then
+        printf "${yellow}RESIZED: $norm"
+        host=`echo $hctl | cut -d":" -f1`
+        channel=`echo $hctl | cut -d":" -f2`
+        id=`echo $hctl | cut -d":" -f3`
+        lun=`echo $hctl | cut -d":" -f4`
+
+        procscsiscsi
+        echo "$SCSISTR"
+        incrchgd "$hctl"
+      fi
+    fi
+  done
+
+  if test -n "$mp_enable" -a -n "$mpaths" ; then
+    i=0
+    for m in $mpaths ; do
+      mpathsizes[$i]="`$MULTIPATH -l $m | egrep -o [0-9]+.[0-9]+[KMGT]`"
+      let i=$i+1
+    done
+    resizempaths
+    i=0
+    for m in $mpaths ; do
+      mpathsize="`$MULTIPATH -l $m | egrep -o [0-9\.]+[KMGT]`"
+      echo "$m ${mpathsizes[$i]} => $mpathsize"
+      let i=$i+1
+    done
+  fi
+}
+
+FOUNDDEVS=""
+CHGDEVS=""
+RMVDDEVS=""
+
+# main
+if test @$1 = @--help -o @$1 = @-h -o @$1 = @-?; then
+    echo "Usage: rescan-scsi-bus.sh [options] [host [host ...]]"
+    echo "Options:"
+    echo " -a      scan all targets, not just currently existing [default: disabled]"
+    echo " -c      enables scanning of channels 0 1   [default: 0 / all detected ones]"
+    echo " -d      enable debug                       [default: 0]"
+    echo " -f      flush failed multipath devices     [default: disabled]"
+    echo " -h      help: print this usage message then exit"
+    echo " -i      issue a FibreChannel LIP reset     [default: disabled]"
+    echo " -I SECS issue a FibreChannel LIP reset and wait for SECS seconds [default: disabled]"
+    echo " -l      activates scanning for LUNs 0--7   [default: 0]"
+    echo " -L NUM  activates scanning for LUNs 0--NUM [default: 0]"
+    echo " -m      update multipath devices           [default: disabled]"
+    echo " -r      enables removing of devices        [default: disabled]"
+    echo " -s      look for resized disks and reload associated multipath devices, if applicable"
+    echo " -u      look for existing disks that have been remapped"
+    echo " -V      print version date then exit"
+    echo " -w      scan for target device IDs 0--15   [default: 0--7]"
+    echo "--alltargets:    same as -a"
+    echo "--attachpq3:     Tell kernel to attach sg to LUN 0 that reports PQ=3"
+    echo "--channels=LIST: Scan only channel(s) in LIST"
+    echo "--color:         use coloured prefixes OLD/NEW/DEL"
+    echo "--flush:         same as -f"
+    echo "--forceremove:   Remove and readd every device (DANGEROUS)"
+    echo "--forcerescan:   Rescan existing devices"
+    echo "--help:          print this usage message then exit"
+    echo "--hosts=LIST:    Scan only host(s) in LIST"
+    echo "--ids=LIST:      Scan only target ID(s) in LIST"
+    echo "--issue-lip:     same as -i"
+    echo "--issue-lip-wait=SECS:     same as -I"
+    echo "--largelun:      Tell kernel to support LUNs > 7 even on SCSI2 devs"
+    echo "--luns=LIST:     Scan only lun(s) in LIST"  
+    echo "--multipath:     same as -m"
+    echo "--nooptscan:     don't stop looking for LUNs is 0 is not found"
+    echo "--remove:        same as -r"
+    echo "--reportlun2:    Tell kernel to try REPORT_LUN even on SCSI2 devices"
+    echo "--resize:        same as -s"
+    echo "--sparselun:     Tell kernel to support sparse LUN numbering"
+    echo "--sync/nosync:   Issue a sync / no sync [default: sync if remove]"
+    echo "--update:        same as -u"
+    echo "--version:       same as -V"
+    echo "--wide:          same as -w"
+    echo ""
+    echo "Host numbers may thus be specified either directly on cmd line (deprecated)"
+    echo "or with the --hosts=LIST parameter (recommended)."
+    echo "LIST: A[-B][,C[-D]]... is a comma separated list of single values and ranges"
+    echo "(No spaces allowed.)"
+    exit 0
+fi
+
+if test @$1 = @--version -o @$1 = @-V ; then
+    echo ${VERSION}
+    exit 0
+fi
+
+if test ! -d /sys/class/scsi_host/ -a ! -d /proc/scsi/; then
+  echo "Error: SCSI subsystem not active"
+  exit 1
+fi
+
+# Make sure sg is there
+modprobe sg >/dev/null 2>&1
+
+if test -x /usr/bin/sg_inq; then
+  sg_version=$(sg_inq -V 2>&1 | cut -d " " -f 3)
+  if test -n "$sg_version"; then
+    sg_ver_maj=${sg_version:0:1}
+    sg_version=${sg_version##?.}
+    let sg_version+=$((100*$sg_ver_maj))
+  fi
+  sg_version=${sg_version##0.}
+  #echo "\"$sg_version\""
+  if [ -z "$sg_version" -o "$sg_version" -lt 70 ] ; then
+    sg_len_arg="-36"
+  else
+    sg_len_arg="--len=36"
+  fi
+else
+  echo "WARN: /usr/bin/sg_inq not present -- please install sg3_utils"
+  echo " or rescan-scsi-bus.sh might not fully work."     
+fi    
+
+# defaults
+unsetcolor
+debug=0
+lunsearch=
+opt_idsearch=`seq 0 7`
+filter_ids=0
+opt_channelsearch=
+remove=
+updated=0
+update=0
+resize=0
+forceremove=
+optscan=1
+sync=1
+existing_targets=1
+mp_enable=
+lipreset=-1
+declare -i scan_flags=0
+
+# Scan options
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    a) existing_targets=;;  #Scan ALL targets when specified
+    d) debug=1 ;;
+    f) flush=1 ;;
+    l) lunsearch=`seq 0 7` ;;
+    L) lunsearch=`seq 0 $2`; shift ;;
+    m) mp_enable=1 ;;
+    w) opt_idsearch=`seq 0 15` ;;
+    c) opt_channelsearch="0 1" ;;
+    r) remove=1 ;;
+    s) resize=1; mp_enable=1 ;;
+    i) lipreset=0 ;;
+    I) shift; lipreset=$opt ;;
+    u) update=1 ;;
+    -alltargets)  existing_targets=;;
+    -flush)       flush=1 ;;
+    -remove)      remove=1 ;;
+    -forcerescan) remove=1; forcerescan=1 ;;
+    -forceremove) remove=1; forceremove=1 ;;
+    -hosts=*)     arg=${opt#-hosts=};   hosts=`expandlist $arg` ;;
+    -channels=*)  arg=${opt#-channels=};opt_channelsearch=`expandlist $arg` ;; 
+    -ids=*)   arg=${opt#-ids=};         opt_idsearch=`expandlist $arg` ; filter_ids=1;;
+    -luns=*)  arg=${opt#-luns=};        lunsearch=`expandlist $arg` ;; 
+    -color) setcolor ;;
+    -nooptscan) optscan=0 ;;
+    -issue-lip) lipreset=0 ;;
+    -issue-lip-wait) lipreset=${opt#-issue-lip-wait=};;
+    -sync) sync=2 ;;
+    -nosync) sync=0 ;;
+    -multipath) mp_enable=1 ;;
+    -attachpq3) scan_flags=$(($scan_flags|0x1000000)) ;;
+    -reportlun2) scan_flags=$(($scan_flags|0x20000)) ;;
+    -resize) resize=1;;
+    -largelun) scan_flags=$(($scan_flags|0x200)) ;;
+    -sparselun) scan_flags=$((scan_flags|0x40)) ;;
+    -update) update=1;;
+    -wide) opt_idsearch=`seq 0 15` ;;
+    *) echo "Unknown option -$opt !" ;;
+  esac
+  shift
+  opt="$1"
+done    
+
+if [ -z "$hosts" ] ; then
+  if test -d /sys/class/scsi_host; then 
+    findhosts_26
+  else  
+    findhosts
+  fi  
+fi
+
+if [ -d /sys/class/scsi_host -a ! -w /sys/class/scsi_host ]; then
+  echo "You need to run scsi-rescan-bus.sh as root"
+  exit 2
+fi  
+if test "$sync" = 1 -a "$remove" = 1; then sync=2; fi
+if test "$sync" = 2; then echo "Syncing file systems"; sync; fi
+if test -w /sys/module/scsi_mod/parameters/default_dev_flags -a $scan_flags != 0; then
+  OLD_SCANFLAGS=`cat /sys/module/scsi_mod/parameters/default_dev_flags`
+  NEW_SCANFLAGS=$(($OLD_SCANFLAGS|$scan_flags))
+  if test "$OLD_SCANFLAGS" != "$NEW_SCANFLAGS"; then
+    echo -n "Temporarily setting kernel scanning flags from "
+    printf "0x%08x to 0x%08x\n" $OLD_SCANFLAGS $NEW_SCANFLAGS
+    echo $NEW_SCANFLAGS > /sys/module/scsi_mod/parameters/default_dev_flags
+  else
+    unset OLD_SCANFLAGS
+  fi
+fi  
+DMSETUP=$(which dmsetup)
+[ -z "$DMSETUP" ] && flush= && mp_enable=
+MULTIPATH=$(which multipath)
+[ -z "$MULTIPATH" ] && flush= && mp_enable=
+
+echo -n "Scanning SCSI subsystem for new devices"
+test -z "$flush" || echo -n ", flush failed multipath devices,"
+test -z "$remove" || echo -n " and remove devices that have disappeared"
+echo
+declare -i found=0
+declare -i updated=0
+declare -i rmvd=0
+
+if [ -n "$flush" ] ; then
+  if [ -x "$MULTIPATH" ] ; then
+    flushmpaths 1
+  fi
+fi
+
+# Update existing mappings
+if [ $update -eq 1 ] ; then
+  echo "Searching for remapped LUNs"
+  findremapped
+  # If you've changed the mapping, there's a chance it's a different size
+  mpaths=""
+  findresized
+# Search for resized LUNs
+elif [ $resize -eq 1 ] ; then
+  echo "Searching for resized LUNs"
+  findresized
+# Normal rescan mode
+else
+  for host in $hosts; do
+  echo -n "Scanning host $host "
+  if test -e /sys/class/fc_host/host$host ; then
+    # It's pointless to do a target scan on FC
+    issue_lip=/sys/class/fc_host/host$host/issue_lip
+    if test -e $issue_lip -a $lipreset -ge 0 ; then
+      echo 1 > $issue_lip 2> /dev/null;
+      udevadm_settle
+      [ $lipreset -gt 0 ] && sleep $lipreset
+    fi
+    channelsearch=
+    idsearch=
+  else
+    channelsearch=$opt_channelsearch
+    idsearch=$opt_idsearch
+  fi
+  [ -n "$channelsearch" ] && echo -n "channels $channelsearch "
+  echo -n "for "
+  if [ -n "$idsearch" ] ; then
+    echo -n " SCSI target IDs " $idsearch
+  else
+    echo -n " all SCSI target IDs"
+  fi
+  if [ -n "$lunsearch" ] ; then
+    echo ", LUNs " $lunsearch
+  else
+    echo ", all LUNs"
+  fi
+
+  if [ -n "$existing_targets" ] ; then
+    searchexisting
+  else
+    dosearch
+  fi
+  done
+  if test -n "$OLD_SCANFLAGS"; then
+    echo $OLD_SCANFLAGS > /sys/module/scsi_mod/parameters/default_dev_flags
+  fi
+fi
+
+let rmvd_found=$rmvd+$found
+if test -n "$mp_enable" -a $rmvd_found -gt 0 ; then
+  echo "Attempting to update multipath devices..."
+  if test $rmvd -gt 0 ; then
+    udevadm_settle
+    echo "Removing multipath mappings for removed devices if all paths are now failed... "
+    flushmpaths 1
+  fi
+  if test $found -gt 0 ; then
+    /sbin/udevadm trigger --sysname-match=sd*
+    udevadm_settle
+    if [ -x "$MULTIPATH" ] ; then
+      echo "Trying to discover new multipath mappings for newly discovered devices... "
+      $MULTIPATH | grep "create:" 2> /dev/null
+    fi
+  fi 
+fi
+
+echo "$found new or changed device(s) found.          "
+if test ! -z "$FOUNDDEVS" ; then
+  printf "$FOUNDDEVS"
+fi
+echo "$updated remapped or resized device(s) found."
+if test ! -z "$CHGDEVS" ; then
+  printf "$CHGDEVS"
+fi
+echo "$rmvd device(s) removed.                 "
+if test ! -z "$RMVDDEVS" ; then
+  printf "$RMVDDEVS"
+fi
+
+# Local Variables:
+# sh-basic-offset: 2
+# End:
+
diff --git a/sg3_utils/scripts/scsi_logging_level b/sg3_utils/scripts/scsi_logging_level
new file mode 100755
index 0000000..c7c6d09
--- /dev/null
+++ b/sg3_utils/scripts/scsi_logging_level
@@ -0,0 +1,268 @@
+#! /bin/bash
+###############################################################################
+# Conveniently create and set scsi logging level, show SCSI_LOG fields in human
+# readable form.
+#
+# (C) Copyright IBM Corp. 2006
+#
+# Modified by D. Gilbert to replace the use of sysctl [20080218]
+# Lat change: D. Gilbert 20150219
+###############################################################################
+
+
+REVISION="1.0"
+SCRIPTNAME="scsi_logging_level"
+
+declare -i LOG_ERROR=0
+declare -i LOG_TIMEOUT=0
+declare -i LOG_SCAN=0
+declare -i LOG_MLQUEUE=0
+declare -i LOG_MLCOMPLETE=0
+declare -i LOG_LLQUEUE=0
+declare -i LOG_LLCOMPLETE=0
+declare -i LOG_HLQUEUE=0
+declare -i LOG_HLCOMPLETE=0
+declare -i LOG_IOCTL=0
+
+declare -i LEVEL=0
+
+SET=0
+GET=0
+CREATE=0
+
+OPTS=`getopt -o hvcgsa:E:T:S:I:M:L:H: --long \
+help,version,create,get,set,all:,error:,timeout:,scan:,ioctl:,\
+midlevel:,mlqueue:,mlcomplete:,lowlevel:,llqueue:,llcomplete:,\
+highlevel:,hlqueue:,hlcomplete: -n \'$SCRIPTNAME\' -- "$@"`
+eval set -- "$OPTS"
+
+# print version info
+printversion()
+{
+    cat <<EOF
+%S390_TOOLS_VERSION% ($SCRIPTNAME $REVISION)
+(C) Copyright IBM Corp. 2006
+EOF
+}
+
+# print usage and help
+printhelp()
+{
+    cat <<EOF
+Usage: $SCRIPTNAME [OPTIONS]
+
+Create, get or set scsi logging level.
+
+Options:
+
+        -h, --help       print this help
+        -v, --version    print version information
+        -s, --set        create and set logging level as specified on
+                         command line
+        -g, --get        get current logging level and display it
+        -c, --create     create logging level as specified on command line
+        -a, --all        specify value for all SCSI_LOG fields
+        -E, --error      specify SCSI_LOG_ERROR
+        -T, --timeout    specify SCSI_LOG_TIMEOUT
+        -S, --scan       specify SCSI_LOG_SCAN
+        -M, --midlevel   specify SCSI_LOG_MLQUEUE and SCSI_LOG_MLCOMPLETE
+            --mlqueue    specify SCSI_LOG_MLQUEUE
+            --mlcomplete specify SCSI_LOG_MLCOMPLETE
+        -L, --lowlevel   specify SCSI_LOG_LLQUEUE and SCSI_LOG_LLCOMPLETE
+            --llqueue    specify SCSI_LOG_LLQUEUE
+            --llcomplete specify SCSI_LOG_LLCOMPLETE
+        -H, --highlevel  specify SCSI_LOG_HLQUEUE and SCSI_LOG_HLCOMPLETE
+            --hlqueue    specify SCSI_LOG_HLQUEUE
+            --hlcomplete specify SCSI_LOG_HLCOMPLETE
+        -I, --ioctl      specify SCSI_LOG_IOCTL
+
+Exactly one of the options "-c", "-g" and "-s" has to be specified.
+Valid values for SCSI_LOG fields are integers from 0 to 7.
+
+Note: Several SCSI_LOG fields can be specified using several options.
+When multiple options specify same SCSI_LOG field the most specific
+option has precedence.
+
+Example: "scsi_logging_level --hlqueue 3 --highlevel 2 --all 1 -s" sets
+SCSI_LOG_HLQUEUE=3, SCSI_LOG_HLCOMPLETE=2 and assigns all other SCSI_LOG
+fields the value 1.
+EOF
+}
+
+check_level()
+{
+    num=$(($1))
+    if [ $num != "$1" ] ; then
+        invalid_cmdline "log level '$1' not a number"
+    elif [ $num -lt 0  ] || [ $num -gt 7 ] ; then
+        invalid_cmdline "log level '$1' out of range, expect '0' to '7'"
+    fi
+}
+
+# check cmd line arguments
+check_cmdline()
+{
+    while true ; do
+        case "$1" in
+            -a|--all)   _ALL=$2; check_level $2
+                        shift 2;;
+            -c|--create) CREATE=1;
+                        shift 1;;
+            -g|--get)   GET=1
+                        shift 1;;
+            -h|--help) printhelp
+                        exit 0;;
+            -s|--set)   SET=1
+                        shift 1;;
+            -v|--version) printversion
+                        exit 0;;
+            -E|--error) _ERROR=$2; check_level $2
+                        shift 2;;
+            -T|--timeout) _TIMEOUT=$2; check_level $2
+                        shift 2;;
+            -S|--scan)  _SCAN=$2; check_level $2
+                        shift 2;;
+            -M|--midlevel) _ML=$2; check_level $2
+                        shift 2;;
+            --mlqueue)  _MLQUEUE=$2; check_level $2
+                        shift 2;;
+            --mlcomplete) _MLCOMPLETE=$2; check_level $2
+                        shift 2;;
+            -L|--lowlevel) _LL=$2; check_level $2
+                        shift 2;;
+            --llqueue)  _LLQUEUE=$2; check_level $2
+                        shift 2;;
+            --llcomplete) _LLCOMPLETE=$2; check_level $2
+                        shift 2;;
+            -H|--highlevel) _HL=$2; check_level $2
+                        shift 2;;
+            --hlqueue)  _HLQUEUE=$2; check_level $2
+                        shift 2;;
+            --hlcomplete) _HLCOMPLETE=$2; check_level $2
+                        shift 2;;
+            -I|--ioctl) _IOCTL=$2; check_level $2
+                        shift 2;;
+            --) shift; break;;
+            *) echo "Internal error!" ; exit 1;;
+        esac
+    done
+
+    if [ -n "$*" ]
+    then
+        invalid_cmdline invalid parameter $*
+    fi
+
+    if [ $GET = "1" -a $SET = "1" ]
+    then
+        invalid_cmdline options \'-c\', \'-g\' and \'-s\' are mutual exclusive
+    elif [ $GET = "1" -a $CREATE = "1" ]
+    then
+        invalid_cmdline options \'-c\', \'-g\' and \'-s\' are mutual exclusive
+    elif [ $SET = "1" -a $CREATE = "1" ]
+    then
+        invalid_cmdline options \'-c\', \'-g\' and \'-s\' are mutual exclusive
+    fi
+
+    LOG_ERROR=${_ERROR:-${_ALL:-0}}
+    LOG_TIMEOUT=${_TIMEOUT:-${_ALL:-0}}
+    LOG_SCAN=${_SCAN:-${_ALL:-0}}
+    LOG_MLQUEUE=${_MLQUEUE:-${_ML:-${_ALL:-0}}}
+    LOG_MLCOMPLETE=${_MLCOMPLETE:-${_ML:-${_ALL:-0}}}
+    LOG_LLQUEUE=${_LLQUEUE:-${_LL:-${_ALL:-0}}}
+    LOG_LLCOMPLETE=${_LLCOMPLETE:-${_LL:-${_ALL:-0}}}
+    LOG_HLQUEUE=${_HLQUEUE:-${_HL:-${_ALL:-0}}}
+    LOG_HLCOMPLETE=${_HLCOMPLETE:-${_HL:-${_ALL:-0}}}
+    LOG_IOCTL=${_IOCTL:-${_ALL:-0}}
+}
+
+invalid_cmdline()
+{
+        echo "$SCRIPTNAME: $*"
+        echo "$SCRIPTNAME: Try '$SCRIPTNAME --help' for more information."
+        exit 1
+}
+
+get_logging_level()
+{
+    echo "Current scsi logging level:"
+#   LEVEL=`sysctl -n dev.scsi.logging_level`
+    LEVEL=`cat /proc/sys/dev/scsi/logging_level`
+    if [ $? != 0 ]
+    then
+        echo "$SCRIPTNAME: could not read scsi logging level" \
+             "(kernel probably without SCSI_LOGGING support)"
+        exit 1
+    fi
+}
+
+show_logging_level()
+{
+    echo "/proc/sys/dev/scsi/logging_level = $LEVEL"
+
+    LOG_ERROR=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3))
+    LOG_TIMEOUT=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3))
+    LOG_SCAN=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3))
+    LOG_MLQUEUE=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3))
+    LOG_MLCOMPLETE=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3))
+    LOG_LLQUEUE=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3))
+    LOG_LLCOMPLETE=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3))
+    LOG_HLQUEUE=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3));
+    LOG_HLCOMPLETE=$(($LEVEL & 7)); LEVEL=$(($LEVEL>>3));
+    LOG_IOCTL=$(($LEVEL & 7))
+
+    echo "SCSI_LOG_ERROR=$LOG_ERROR"
+    echo "SCSI_LOG_TIMEOUT=$LOG_TIMEOUT"
+    echo "SCSI_LOG_SCAN=$LOG_SCAN"
+    echo "SCSI_LOG_MLQUEUE=$LOG_MLQUEUE"
+    echo "SCSI_LOG_MLCOMPLETE=$LOG_MLCOMPLETE"
+    echo "SCSI_LOG_LLQUEUE=$LOG_LLQUEUE"
+    echo "SCSI_LOG_LLCOMPLETE=$LOG_LLCOMPLETE"
+    echo "SCSI_LOG_HLQUEUE=$LOG_HLQUEUE"
+    echo "SCSI_LOG_HLCOMPLETE=$LOG_HLCOMPLETE"
+    echo "SCSI_LOG_IOCTL=$LOG_IOCTL"
+}
+
+set_logging_level()
+{
+    echo "New scsi logging level:"
+#   sysctl -q -w dev.scsi.logging_level=$LEVEL
+    echo $LEVEL > /proc/sys/dev/scsi/logging_level
+    if [ $? != 0 ]
+    then
+        echo "$SCRIPTNAME: could not write scsi logging level $LEVEL"
+        echo "  kernel does not have SCSI_LOGGING support or needs superuser"
+        exit 1
+    fi
+}
+create_logging_level()
+{
+    LEVEL=$(($LOG_IOCTL & 7)); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_HLCOMPLETE & 7))); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_HLQUEUE & 7))); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_LLCOMPLETE & 7))); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_LLQUEUE & 7))); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_MLCOMPLETE & 7))); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_MLQUEUE & 7))); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_SCAN & 7))); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_TIMEOUT & 7))); LEVEL=$(($LEVEL<<3))
+    LEVEL=$(($LEVEL|($LOG_ERROR & 7)))
+}
+
+check_cmdline $*
+
+if [ $SET = "1" ]
+then
+    create_logging_level
+    set_logging_level
+    show_logging_level
+elif [ $GET = "1" ]
+then
+    get_logging_level
+    show_logging_level
+elif [ $CREATE = "1" ]
+then
+    create_logging_level
+    show_logging_level
+else
+    invalid_cmdline missing option \'-g\', \'-s\' or \'-c\'
+fi
diff --git a/sg3_utils/scripts/scsi_mandat b/sg3_utils/scripts/scsi_mandat
new file mode 100755
index 0000000..6de8658
--- /dev/null
+++ b/sg3_utils/scripts/scsi_mandat
@@ -0,0 +1,133 @@
+#!/bin/bash
+# scsi_mandat
+#
+# Script to test compliance with SCSI mandatory commands.
+# The vintage is SPC-3 and SPC-4 (see www.t10.org).
+#
+# Coverage:
+# Command                Standard/Draft (is mandatory in)
+# -------------------------------------------------------
+# INQUIRY (standard)     SCSI-2, SPC, SPC-2, SPC-3, SPC-4
+# INQUIRY (VPD pages 0, 0x83)     SPC-2, SPC-3, SPC-4
+# REPORT LUNS            SPC-3, SPC-4
+# TEST UNIT READY        SCSI-2, SPC, SPC-2, SPC-3, SPC-4
+# REQUEST SENSE          SCSI-2, SBC, SBC-2,3, MMC-4,5, SSC-2,3
+# SEND DIAGNOSTIC        SBC, SBC-2,3, SSC-2,3
+#
+# This script uses utilities frim sg3_utils package (version
+# 1.21 or later)
+#
+# Douglas Gilbert 20131016
+
+
+log=0
+quiet=0
+verbose=""
+
+file_err=0
+inv_opcode=0
+illeg_req=0
+not_ready=0
+medium=0
+other_err=0
+recovered=0
+sanity=0
+syntax=0
+timeout=0
+unit_attention=0
+aborted_command=0
+
+total_err=0
+
+usage()
+{
+  echo "Usage: scsi_mandat [-h] [-L] [-q] [-v] <device>"
+  echo "  where:  -h    print usage message"
+  echo "          -L, --log        append stderr to 'scsi_mandat.err'"
+  echo "          -q    suppress some output"
+  echo "          -v    increase verbosity of output"
+  echo ""
+  echo "Check <device> for mandatory SCSI command support"
+}
+
+
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    h|-help) usage ; exit 0 ;;
+    L|-log) let log=$log+1 ;;
+    q|-quiet) let quiet=$quiet+1 ;;
+    v|-verbose) verbose="-v" ;;
+    vv) verbose="-vv" ;;
+    vvv) verbose="-vvv" ;;
+    *) echo "Unknown option: -$opt " ; exit 1 ;;
+  esac
+  shift
+  opt="$1"
+done
+
+if [ $# -lt 1 ]
+  then
+    usage
+    exit 1
+fi 
+
+for command in "sg_inq" "sg_luns" "sg_turs" "sg_requests" "sg_vpd" \
+                "sg_vpd -i" "sg_senddiag -t"
+do
+  if [ $quiet -eq 0 ]
+    then echo "$command" $verbose $1
+  fi
+
+  if [ $verbose ]
+  then
+    if [ $log -eq 0 ]
+    then
+      $command $verbose $1
+    else
+      $command $verbose $1 >> scsi_mandat.err 2>> scsi_mandat.err
+    fi
+  else
+    if [ $log -eq 0 ]
+    then
+      $command $1 > /dev/null 2>> /dev/null
+    else
+      $command $1 > /dev/null 2>> scsi_mandat.err
+    fi
+  fi
+  res=$?
+  case "$res" in
+    0) ;;
+    1) echo "  syntax error" ; let syntax=$syntax+1 ;;
+    2) echo "  not ready" ; let not_ready=$not_ready+1 ;;
+    3) echo "  medium error" ; let medium=$medium+1 ;;
+    5) echo "  illegal request, general" ; let illeg_req=$illeg_req+1 ;;
+    6) echo "  unit attention" ; let unit_attention=$unit_attention+1 ;;
+    9) echo "  illegal request, invalid opcode" ; let inv_opcode=$inv_opcode+1 ;;
+    11) echo "  aborted command" ; let aborted_command=$aborted_command+1 ;;
+    15) echo "  file error with $1 " ; let file_err=$file_err+1 ;;
+    20) echo "  no sense" ; let other_err=$other_err+1 ;;
+    21) echo "  recovered error" ; let recovered_err=$recovered_err+1 ;;
+    33) echo "  timeout" ; let timeout=$timeout+1 ;;
+    97) echo "  response fails sanity" ; let sanity=$sanity+1 ;;
+    98) echo "  other SCSI error" ; let other_err=$other_err+1 ;;
+    99) echo "  other error" ; let other_err=$other_err+1 ;;
+    *) echo "  unknown exit status for sg_inq: $res" ; let other_err=$other_err+1 ;;
+  esac
+done
+
+echo ""
+let total_bad_err=$file_err+$inv_opcode+$illeg_req+$medium+$aborted_command
+let total_bad_err+=$other_err+$recovered+$sanity+$syntax+$timeout
+
+let total_allow_err=$not_ready+$unit_attention
+
+  echo "total number of bad errors: $total_bad_err "
+
+if [ $total_allow_err -gt 0 ]
+  then
+  echo "total number of allowable errors: $total_allow_err "
+fi
+
+exit $total_bad_err
diff --git a/sg3_utils/scripts/scsi_readcap b/sg3_utils/scripts/scsi_readcap
new file mode 100755
index 0000000..8f308f4
--- /dev/null
+++ b/sg3_utils/scripts/scsi_readcap
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+###################################################################
+#
+#  Fetch READ CAPACITY information for the given SCSI device(s).
+#
+#  This script assumes the sg3_utils package is installed.
+#
+##################################################################
+
+verbose=""
+brief=""
+long_opt=""
+
+usage()
+{
+  echo "Usage: scsi_readcap [-b] [-h] [-l] [-v] <device>+"
+  echo "  where:"
+  echo "    -b, --brief          output brief capacity data"
+  echo "    -h, --help           print usage message"
+  echo "    -l, --long           send longer SCSI READ CAPACITY (16) cdb"
+  echo "    -v, --verbose        more verbose output"
+  echo ""
+  echo "Use SCSI READ CAPACITY command to fetch the size of each <device>"
+}
+
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    b|-brief) brief="-b" ;;
+    h|-help) usage ; exit 0 ;;
+    l|-long) long_opt="--16" ;;
+    v|-verbose) verbose="-v" ;;
+    vv) verbose="-vv" ;;
+    vvv) verbose="-vvv" ;;
+    *) echo "Unknown option: -$opt " ; exit 1 ;;
+  esac
+  shift
+  opt="$1"
+done
+
+if [ $# -lt 1 ]
+  then
+    usage
+    exit 1
+fi
+
+for i
+do
+	if [ $brief ] ; then
+        	sg_readcap $brief $long_opt $verbose $i 2> /dev/null
+	else
+		echo "sg_readcap $brief $long_opt $verbose $i"
+        	sg_readcap $brief $long_opt $verbose $i
+	fi
+done
diff --git a/sg3_utils/scripts/scsi_ready b/sg3_utils/scripts/scsi_ready
new file mode 100755
index 0000000..724c2c6
--- /dev/null
+++ b/sg3_utils/scripts/scsi_ready
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+################################################
+#
+#  Send a TEST UNIT READY SCSI command to each given device.
+#
+#  This script assumes the sg3_utils package is installed and uses
+#  the sg_turs utility..
+#
+###############################################
+
+verbose=""
+brief=""
+
+usage()
+{
+  echo "Usage: scsi_ready [-b] [-h] [-v] <device>+"
+  echo "  where:"
+  echo "    -b, --brief          print 'ready' or 'device not ready' only"
+  echo "    -h, --help           print usage message"
+  echo "    -v, --verbose        more verbose output"
+  echo ""
+  echo "Send SCSI TEST UNIT READY to each <device>"
+}
+
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    b|-brief) brief="1" ;;
+    h|-help) usage ; exit 0 ;;
+    v|-verbose) verbose="-v" ;;
+    vv) verbose="-vv" ;;
+    vvv) verbose="-vvv" ;;
+    *) echo "Unknown option: -$opt " ; exit 1 ;;
+  esac
+  shift
+  opt="$1"
+done
+
+if [ $# -lt 1 ]
+  then
+    usage
+    exit 1
+fi
+
+for i
+do
+	if [ ! $brief ] ; then
+		echo "sg_turs $verbose $i"
+	fi
+	echo -n "    "
+        if sg_turs $verbose $i ; then
+		echo "ready"
+	fi
+done
diff --git a/sg3_utils/scripts/scsi_satl b/sg3_utils/scripts/scsi_satl
new file mode 100755
index 0000000..6099422
--- /dev/null
+++ b/sg3_utils/scripts/scsi_satl
@@ -0,0 +1,134 @@
+#!/bin/bash
+# scsi_satl
+#
+# Script to test compliance of SCSI commands on a SCSI to ATA
+# Translation (SAT) Layer (SATL). This script was compiled using
+# sat-r09.pdf found at www.t10.org .
+# The scripts still seems to be valid for sat2r09.pdf .
+# The vintage is SPC-3 and SPC-4 (see www.t10.org).
+#
+# Coverage:
+# Command                SATL notes
+# -------------------------------------------------------
+# INQUIRY (standard)
+# INQUIRY (VPD: 0)
+# INQUIRY (VPD: 0x83)    Device identification VPD page
+# INQUIRY (VPD: 0x89)    ATA Information VPD page
+# REPORT LUNS            SPC-3, SPC-4 (hardly mentioned in sat-r08c)
+# TEST UNIT READY        
+# REQUEST SENSE          
+# SEND DIAGNOSTIC        default self test        
+# MODE SENSE(10)         draft unclear which mode pages, so ask for all
+# ATA PASS THROUGH(16)   send IDENTIFY DEVICE command. Assume non-packet
+#                        device, if packet device add "-p" option
+#
+# This script uses utilities from sg3_utils package (version
+# 1.22 or later)
+#
+# Douglas Gilbert 20090930
+
+
+log=0
+quiet=0
+verbose=""
+
+file_err=0
+inv_opcode=0
+illeg_req=0
+not_ready=0
+medium=0
+other_err=0
+recovered=0
+sanity=0
+syntax=0
+timeout=0
+unit_attention=0
+aborted_command=0
+
+total_err=0
+
+usage()
+{
+  echo "Usage: scsi_satl [-h] [-L] [-q] [-v] <device>"
+  echo "  where:  -h, --help       print usage message"
+  echo "          -L, --log        append stderr to 'scsi_satl.err'"
+  echo "          -q, --quiet      suppress some output"
+  echo "          -v, --verbose    more verbose output"
+  echo ""
+  echo "Check <device> for SCSI to ATA Translation Layer (SATL) support"
+}
+
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    h|-help) usage ; exit 1 ;;
+    L|-log) let log=$log+1 ;;
+    q|-quiet) let quiet=$quiet+1 ;;
+    v|-verbose) verbose="-v" ;;
+    *) echo "Unknown option: -$opt " ; exit 1 ;;
+  esac
+  shift
+  opt="$1"
+done
+
+if [ $# -lt 1 ]
+  then
+    usage
+    exit 1
+fi 
+
+for command in "sg_inq" "sg_vpd" "sg_vpd -p di" "sg_vpd -p ai" "sg_luns" \
+               "sg_turs" "sg_requests -s" "sg_senddiag -t" "sg_modes -a" \
+               "sg_sat_identify"
+do
+  if [ $quiet -eq 0 ]
+    then echo "$command" $1
+  fi
+
+  if [ $log -eq 0 ]
+  then
+    if [ $verbose ]
+    then
+      $command $verbose $1 > /dev/null
+    else
+      $command $1 > /dev/null 2>> /dev/null
+    fi
+  else
+    $command $verbose $1 > /dev/null 2>> scsi_satl.err
+  fi
+  res=$?
+  case "$res" in
+    0) ;;
+    1) echo "  syntax error" ; let syntax=$syntax+1 ;;
+    2) echo "  not ready" ; let not_ready=$not_ready+1 ;;
+    3) echo "  medium error" ; let medium=$medium+1 ;;
+    5) echo "  illegal request, general" ; let illeg_req=$illeg_req+1 ;;
+    6) echo "  unit attention" ; let unit_attention=$unit_attention+1 ;;
+    9) echo "  illegal request, invalid opcode" ; let inv_opcode=$inv_opcode+1 ;;
+    11) echo "  aborted command" ; let aborted_command=$aborted_command+1 ;;
+    15) echo "  file error with $1 " ; let file_err=$file_err+1 ;;
+    20) echo "  no sense" ; let other_err=$other_err+1 ;;
+    21) echo "  recovered error" ; let recovered_err=$recovered_err+1 ;;
+    33) echo "  timeout" ; let timeout=$timeout+1 ;;
+    97) echo "  response fails sanity" ; let sanity=$sanity+1 ;;
+    98) echo "  other SCSI error" ; let other_err=$other_err+1 ;;
+    99) echo "  other error" ; let other_err=$other_err+1 ;;
+    *) echo "  unknown exit status for sg_inq: $res" ; let other_err=$other_err+1 ;;
+  esac
+done
+
+echo ""
+let total_bad_err=$file_err+$inv_opcode+$illeg_req+$medium+$aborted_command
+let total_bad_err+=$other_err+$recovered+$sanity+$syntax+$timeout
+
+let total_allow_err=$not_ready+$unit_attention
+
+  echo "total number of bad errors: $total_bad_err "
+
+if [ $total_allow_err -gt 0 ]
+  then
+  echo "total number of allowable errors: $total_allow_err "
+fi
+
+exit $total_bad_err
diff --git a/sg3_utils/scripts/scsi_start b/sg3_utils/scripts/scsi_start
new file mode 100755
index 0000000..aec7ab9
--- /dev/null
+++ b/sg3_utils/scripts/scsi_start
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+################################################
+#
+#  Spin up the given SCSI disk(s).
+#
+#  SCSI disks (or disks that understand SCSI commands)
+#  are assumed. By default, the immediate bit is set so the
+#  command should return immediately. The disk however will
+#  take 10 seconds or more to spin up. The '-w' option
+#  causes each start to wait until the disk reports that it
+#  has started.
+#
+#  This script assumes the sg3_utils package is installed.
+#
+###############################################
+
+verbose=""
+immediate="-i"
+
+usage()
+{
+  echo "Usage: scsi_start [-h] [-v] [-w] <device>+"
+  echo "  where:"
+  echo "    -h, --help           print usage message"
+  echo "    -v, --verbose        more verbose output"
+  echo "    -w, --wait           wait for each start to complete"
+  echo ""
+  echo "Send SCSI START STOP UNIT command to start each <device>"
+}
+
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    h|-help) usage ; exit 0 ;;
+    v|-verbose) verbose="-v" ;;
+    w|-wait) immediate="" ;;
+    *) echo "Unknown option: -$opt " ; exit 1 ;;
+  esac
+  shift
+  opt="$1"
+done
+
+if [ $# -lt 1 ]
+  then
+    usage
+    exit 1
+fi
+
+for i
+do
+	echo "sg_start $immediate 1 $verbose $i"
+        sg_start $immediate 1 $verbose $i
+done
diff --git a/sg3_utils/scripts/scsi_stop b/sg3_utils/scripts/scsi_stop
new file mode 100755
index 0000000..7680723
--- /dev/null
+++ b/sg3_utils/scripts/scsi_stop
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+################################################
+#
+#  Spin down the given SCS disk(s).
+#
+#  SCSI disks (or disks that understand SCSI commands)
+#  are assumed. By default, the immediate bit is set so the
+#  command should return immediately. The disk however will
+#  take 10 seconds or more to spin down. The '-w' option
+#  causes each stop to wait until the disk reports that it
+#  has stopped.
+#
+#  This script assumes the sg3_utils package is installed.
+#
+###############################################
+
+verbose=""
+immediate="-i"
+
+usage()
+{
+  echo "Usage: scsi_stop [-h] [-v] [-w] <device>+"
+  echo "  where:"
+  echo "    -h, --help           print usage message"
+  echo "    -v, --verbose        more verbose output"
+  echo "    -w, --wait           wait for each stop to complete"
+  echo ""
+  echo "Send SCSI START STOP UNIT command to stop each <device>"
+}
+
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    h|-help) usage ; exit 0 ;;
+    v|-verbose) verbose="-v" ;;
+    w|-wait) immediate="" ;;
+    *) echo "Unknown option: -$opt " ; exit 1 ;;
+  esac
+  shift
+  opt="$1"
+done
+
+if [ $# -lt 1 ]
+  then
+    usage
+    exit 1
+fi
+
+for i
+do
+# Use '-r' (read-only) otherwise using a block device node
+# (e.g. 'sg_start 0 /dev/sdb') can result in a change of state
+# event causing the disk to spin up again immediately.
+        echo "sg_start -r $immediate 0 $verbose $i"
+        sg_start -r $immediate 0 $verbose $i
+done
diff --git a/sg3_utils/scripts/scsi_temperature b/sg3_utils/scripts/scsi_temperature
new file mode 100755
index 0000000..f7d041c
--- /dev/null
+++ b/sg3_utils/scripts/scsi_temperature
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+###################################################################
+#
+#  Check the temperature of the given SCSI device(s).
+#
+#  This script assumes the sg3_utils package is installed.
+#
+##################################################################
+
+verbose=""
+
+usage()
+{
+  echo "Usage: scsi_temperature [-h] [-v] <device>+"
+  echo "  where:"
+  echo "    -h, --help           print usage message"
+  echo "    -v, --verbose        more verbose output"
+  echo ""
+  echo "Use SCSI LOG SENSE command to fetch temperature of each <device>"
+}
+
+opt="$1"
+while test ! -z "$opt" -a -z "${opt##-*}"; do
+  opt=${opt#-}
+  case "$opt" in
+    h|-help) usage ; exit 0 ;;
+    v|-verbose) verbose="-v" ;;
+    vv) verbose="-vv" ;;
+    *) echo "Unknown option: -$opt " ; exit 1 ;;
+  esac
+  shift
+  opt="$1"
+done
+
+if [ $# -lt 1 ]
+  then
+    usage
+    exit 1
+fi
+
+for i
+do
+	echo "sg_logs -t $verbose $i"
+        sg_logs -t $verbose $i
+done
diff --git a/sg3_utils/sg3_utils.spec b/sg3_utils/sg3_utils.spec
new file mode 100644
index 0000000..7dc42df
--- /dev/null
+++ b/sg3_utils/sg3_utils.spec
@@ -0,0 +1,252 @@
+Summary: Utilities for devices that use SCSI command sets
+Name: sg3_utils
+Version: 1.42
+# Release: 1%{?dist}
+Release: 1
+License: GPL
+Group: Utilities/System
+Source: ftp://sg.danny.cz/sg/p/sg3_utils-%{version}.tgz
+Url: http://sg.danny.cz/sg/sg3_utils.html
+Provides: sg_utils
+BuildRequires: libtool
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Packager: Douglas Gilbert <dgilbert@interlog.com>
+
+%description
+Collection of Linux utilities for devices that use the SCSI command set.
+Includes utilities to copy data based on "dd" syntax and semantics (called
+sg_dd, sgp_dd and sgm_dd); check INQUIRY data and VPD pages (sg_inq); check
+mode and log pages (sginfo, sg_modes and sg_logs); spin up and down
+disks (sg_start); do self tests (sg_senddiag); and various other functions.
+See the README, ChangeLog and COVERAGE files. Requires the linux kernel 2.4
+series or later. In the 2.4 series SCSI generic device names (e.g. /dev/sg0)
+must be used. In the 2.6 series and later other device names may be used as
+well (e.g. /dev/sda).
+
+Warning: Some of these tools access the internals of your system
+and the incorrect usage of them may render your system inoperable.
+
+%package libs
+Summary: Shared library for %{name}
+Group: System/Libraries
+
+%description libs
+This package contains the shared library for %{name}.
+
+%package devel
+Summary: Static library and header files for the sgutils library
+Group: Development/C
+Requires: %{name}-libs = %{version}-%{release}
+
+%description devel
+This package contains the static %{name} library and its header files for
+developing applications.
+
+%prep
+%setup -q
+
+%build
+%configure
+
+%install
+if [ "$RPM_BUILD_ROOT" != "/" ]; then
+        rm -rf $RPM_BUILD_ROOT
+fi
+
+make install \
+        DESTDIR=$RPM_BUILD_ROOT
+
+%clean
+if [ "$RPM_BUILD_ROOT" != "/" ]; then
+        rm -rf $RPM_BUILD_ROOT
+fi
+
+%files
+%defattr(-,root,root)
+%doc AUTHORS ChangeLog COPYING COVERAGE CREDITS INSTALL NEWS README README.sg_start
+%attr(755,root,root) %{_bindir}/*
+%{_mandir}/man8/*
+
+%files libs
+%defattr(-,root,root)
+%{_libdir}/*.so.*
+
+%files devel
+%defattr(-,root,root)
+%{_includedir}/scsi/*.h
+%{_libdir}/*.so
+%{_libdir}/*.a
+%{_libdir}/*.la
+
+%changelog
+* Wed Feb 17 2016 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.42
+
+* Tue Apr 28 2015 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.41
+
+* Mon Nov 10 2014 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.40
+
+* Thu Jun 12 2014 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.39
+
+* Tue Apr 01 2014 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.38
+
+* Mon Oct 14 2013 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.37
+
+* Fri May 31 2013 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.36
+
+* Thu Jan 17 2013 - dgilbert at interlog dot com
+- add sg_compare_and_write, track t10 changes
+  * sg3_utils-1.35
+
+* Sat Oct 13 2012 - dgilbert at interlog dot com
+- add sg_xcopy and sg_copy_results; track t10 changes
+  * sg3_utils-1.34
+
+* Wed Jan 18 2012 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.33
+
+* Wed Jun 22 2011 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.32
+
+* Wed Feb 16 2011 - dgilbert at interlog dot com
+- add sg_decode_sense; track t10 changes
+  * sg3_utils-1.31
+
+* Fri Nov 05 2010 - dgilbert at interlog dot com
+- add sg_referrals; track t10 changes
+  * sg3_utils-1.30
+
+* Wed Mar 31 2010 - dgilbert at interlog dot com
+- track t10 changes
+  * sg3_utils-1.29
+
+* Fri Oct 02 2009 - dgilbert at interlog dot com
+- add sg_get_lba_status, sg_unmap, sg_read_block_limits
+  * sg3_utils-1.28
+
+* Sat Apr 11 2009 - dgilbert at interlog dot com
+- add sg_write_same; sg_dd split; spc4r18 sync
+  * sg3_utils-1.27
+
+* Wed Jun 25 2008 - dgilbert at interlog dot com
+- add sg_sat_phy_event, sync with drafts prior to this date
+  * sg3_utils-1.26
+
+* Tue Oct 16 2007 - dgilbert at interlog dot com
+- add sg_sat_set_features, sg_stpg, sg_safte; sg_dd oflag=sparse,null
+  * sg3_utils-1.25
+
+* Mon May 07 2007 - dgilbert at interlog dot com
+- add sg_raw; sg_rtpg, sg_log, sg_inq and sg_format updates
+  * sg3_utils-1.24
+
+* Wed Jan 31 2007 - dgilbert at interlog dot com
+- add sg_read_buffer + sg_write_buffer
+  * sg3_utils-1.23
+
+* Mon Oct 16 2006 - dgilbert at interlog dot com
+- add sg_sat_identify, expand sg_format and sg_requests
+  * sg3_utils-1.22
+
+* Thu Jul 06 2006 - dgilbert at interlog dot com
+- add sg_vpd and sg_rdac, uniform exit statuses
+  * sg3_utils-1.21
+
+* Tue Apr 18 2006 - dgilbert at interlog dot com
+- sg_logs: sas port specific page decoding, sg*_dd updates
+  * sg3_utils-1.20
+
+* Fri Jan 27 2006 - dgilbert at interlog dot com
+- sg_get_config: resync features with mmc5 rev 1
+  * sg3_utils-1.19
+
+* Fri Nov 18 2005 - dgilbert at interlog dot com
+- add sg_map26; sg_inq '-rr' option to play with hdparm
+  * sg3_utils-1.18
+
+* Thu Sep 22 2005 - dgilbert at interlog dot com
+- add ATA information VPD page to sg_inq
+  * sg3_utils-1.17
+
+* Wed Aug 10 2005 - dgilbert at interlog dot com
+- add sg_ident, sg_inq VPD page extensions
+  * sg3_utils-1.16
+
+* Sun Jun 05 2005 - dgilbert at interlog dot com
+- use O_NONBLOCK on all fds that use SG_IO ioctl
+  * sg3_utils-1.15
+
+* Fri May 06 2005 - dgilbert at interlog dot com
+- produce libsgutils (+ -devel variant) as well as sg3_utils binary rpm
+  * sg3_utils-1.14
+
+* Sun Mar 13 2005 - dgilbert at interlog dot com
+- add sg_format, sg_dd extensions
+  * sg3_utils-1.13
+
+* Fri Jan 21 2005 - dgilbert at interlog dot com
+- add sg_wr_mode, sg_rtpg + sg_reassign; sginfo sas tweaks
+  * sg3_utils-1.12
+
+* Fri Nov 26 2004 - dgilbert at interlog dot com
+- add sg_sync, sg_prevent and sg_get_config; fix sg_requests
+  * sg3_utils-1.11
+
+* Sat Oct 30 2004 - dgilbert at interlog dot com
+- fix read capacity (10+16), add sg_luns
+  * sg3_utils-1.10
+
+* Thu Oct 21 2004 - dgilbert at interlog dot com
+- sg_requests, sg_ses, sg_verify, libsgutils(sg_lib.c+sg_cmds.c), devel rpm
+  * sg3_utils-1.09
+
+* Tue Aug 31 2004 - dgilbert at interlog dot com
+- 'register+move' in sg_persist, sg_opcodes sorts, sg_write_long
+  * sg3_utils-1.08
+
+* Thu Jul 08 2004 - dgilbert at interlog dot com
+- add '-fHead' to sginfo, '-i' for sg_inq, new sg_opcodes + sg_persist
+  * sg3_utils-1.07
+
+* Mon Apr 26 2004 - dgilbert at interlog dot com
+- sg3_utils.spec for mandrake; more sginfo work, sg_scan, sg_logs
+  * sg3_utils-1.06
+
+* Wed Nov 12 2003 - dgilbert at interlog dot com
+- sg_readcap: sizes; sg_logs: double fetch; sg_map 256 sg devices; sginfo
+  * sg3_utils-1.05
+
+* Tue May 13 2003 - dgilbert at interlog dot com
+- default sg_turs '-n=' to 1, sg_logs gets '-t' for temperature, CREDITS
+  * sg3_utils-1.04
+
+* Wed Apr 02 2003 - dgilbert at interlog dot com
+- 6 byte CDBs for sg_modes, sg_start on block devs, sg_senddiag, man pages
+  * sg3_utils-1.03
+
+* Wed Jan 01 2003 - dgilbert at interlog dot com
+- interwork with block SG_IO, fix in sginfo, '-t' for sg_turs
+  * sg3_utils-1.02
+
+* Wed Aug 14 2002 - dgilbert at interlog dot com
+- raw switch in sg_inq
+  * sg3_utils-1.01
+
+* Sun Jul 28 2002 - dgilbert at interlog dot com
+- decode sg_logs pages, add dio to sgm_dd, drop "gen=1" arg, "of=/dev/null"
+  * sg3_utils-1.00
diff --git a/sg3_utils/src/Makefile.am b/sg3_utils/src/Makefile.am
new file mode 100644
index 0000000..0fbf84e
--- /dev/null
+++ b/sg3_utils/src/Makefile.am
@@ -0,0 +1,178 @@
+
+bin_PROGRAMS = \
+	sg_compare_and_write sg_decode_sense sg_format sg_get_config \
+	sg_get_lba_status sg_ident sg_inq sg_logs sg_luns sg_modes \
+	sg_opcodes sg_persist sg_prevent sg_raw sg_rdac sg_read_attr \
+	sg_read_block_limits sg_read_buffer sg_read_long sg_readcap \
+	sg_reassign sg_referrals sg_rep_zones sg_requests sg_reset_wp \
+	sg_rmsn sg_rtpg sg_safte sg_sanitize sg_sat_identify \
+	sg_sat_phy_event sg_sat_read_gplog sg_sat_set_features sg_senddiag \
+	sg_ses sg_ses_microcode sg_start sg_stpg sg_sync sg_timestamp \
+	sg_turs sg_unmap sg_verify sg_vpd sg_wr_mode sg_write_buffer \
+	sg_write_long sg_write_same sg_write_verify sg_zone
+sg_scan_SOURCES =
+
+
+if OS_LINUX
+bin_PROGRAMS += \
+	sg_copy_results sg_dd sg_emc_trespass sg_map sg_map26 sg_rbuf \
+	sg_read sg_reset sg_scan sg_test_rwbuf sg_xcopy sginfo sgm_dd sgp_dd
+sg_scan_SOURCES += sg_scan_linux.c
+endif
+
+
+if OS_WIN32_MINGW
+bin_PROGRAMS += sg_scan
+sg_scan_SOURCES += sg_scan_win32.c
+endif
+
+
+if OS_WIN32_CYGWIN
+bin_PROGRAMS += sg_scan
+sg_scan_SOURCES += sg_scan_win32.c
+endif
+
+
+# For C++/clang testing
+## CC = gcc
+## CC = g++
+## CC = clang
+## CC = clang++
+
+# -std=<s> can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same)
+# -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more
+AM_CPPFLAGS = -iquote ${top_srcdir}/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+AM_CFLAGS = -Wall -W @os_cflags@
+# AM_CFLAGS = -Wall -W @os_cflags@ -pedantic -std=c11
+# AM_CFLAGS = -Wall -W @os_cflags@ -pedantic -std=c++11
+
+sg_compare_and_write_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_copy_results_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_dd_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_decode_sense_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_emc_trespass_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_format_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_get_config_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_get_lba_status_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_ident_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sginfo_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_inq_SOURCES = sg_inq.c sg_inq_data.c
+sg_inq_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_logs_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_luns_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_map26_LDADD = @os_libs@
+
+sg_map_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sgm_dd_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_modes_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_opcodes_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sgp_dd_LDADD = ../lib/libsgutils2.la @os_libs@ -lpthread
+
+sg_persist_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_prevent_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_raw_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_rbuf_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_rdac_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_read_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_read_attr_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_readcap_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_read_block_limits_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_read_buffer_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_read_long_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_reassign_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_requests_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_referrals_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_rep_zones_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_reset_LDADD = @os_libs@
+
+sg_reset_wp_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_rmsn_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_rtpg_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_safte_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_sanitize_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_sat_identify_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_sat_phy_event_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_sat_read_gplog_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_sat_set_features_LDADD = ../lib/libsgutils2.la @os_libs@
+
+# sg_scan_SOURCES list is already set above in the platform-specific sections
+sg_scan_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_senddiag_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_ses_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_ses_microcode_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_start_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_stpg_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_sync_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_test_rwbuf_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_timestamp_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_turs_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_unmap_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_verify_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_vpd_SOURCES = sg_vpd.c sg_vpd_vendor.c
+sg_vpd_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_write_buffer_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_write_long_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_write_same_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_write_verify_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_wr_mode_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_xcopy_LDADD = ../lib/libsgutils2.la @os_libs@
+
+sg_zone_LDADD = ../lib/libsgutils2.la @os_libs@
diff --git a/sg3_utils/src/Makefile.in b/sg3_utils/src/Makefile.in
new file mode 100644
index 0000000..cc268d2
--- /dev/null
+++ b/sg3_utils/src/Makefile.in
@@ -0,0 +1,1282 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = sg_compare_and_write$(EXEEXT) sg_decode_sense$(EXEEXT) \
+	sg_format$(EXEEXT) sg_get_config$(EXEEXT) \
+	sg_get_lba_status$(EXEEXT) sg_ident$(EXEEXT) sg_inq$(EXEEXT) \
+	sg_logs$(EXEEXT) sg_luns$(EXEEXT) sg_modes$(EXEEXT) \
+	sg_opcodes$(EXEEXT) sg_persist$(EXEEXT) sg_prevent$(EXEEXT) \
+	sg_raw$(EXEEXT) sg_rdac$(EXEEXT) sg_read_attr$(EXEEXT) \
+	sg_read_block_limits$(EXEEXT) sg_read_buffer$(EXEEXT) \
+	sg_read_long$(EXEEXT) sg_readcap$(EXEEXT) sg_reassign$(EXEEXT) \
+	sg_referrals$(EXEEXT) sg_rep_zones$(EXEEXT) \
+	sg_requests$(EXEEXT) sg_reset_wp$(EXEEXT) sg_rmsn$(EXEEXT) \
+	sg_rtpg$(EXEEXT) sg_safte$(EXEEXT) sg_sanitize$(EXEEXT) \
+	sg_sat_identify$(EXEEXT) sg_sat_phy_event$(EXEEXT) \
+	sg_sat_read_gplog$(EXEEXT) sg_sat_set_features$(EXEEXT) \
+	sg_senddiag$(EXEEXT) sg_ses$(EXEEXT) sg_ses_microcode$(EXEEXT) \
+	sg_start$(EXEEXT) sg_stpg$(EXEEXT) sg_sync$(EXEEXT) \
+	sg_timestamp$(EXEEXT) sg_turs$(EXEEXT) sg_unmap$(EXEEXT) \
+	sg_verify$(EXEEXT) sg_vpd$(EXEEXT) sg_wr_mode$(EXEEXT) \
+	sg_write_buffer$(EXEEXT) sg_write_long$(EXEEXT) \
+	sg_write_same$(EXEEXT) sg_write_verify$(EXEEXT) \
+	sg_zone$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2) \
+	$(am__EXEEXT_3)
+@OS_LINUX_TRUE@am__append_1 = \
+@OS_LINUX_TRUE@	sg_copy_results sg_dd sg_emc_trespass sg_map sg_map26 sg_rbuf \
+@OS_LINUX_TRUE@	sg_read sg_reset sg_scan sg_test_rwbuf sg_xcopy sginfo sgm_dd sgp_dd
+
+@OS_LINUX_TRUE@am__append_2 = sg_scan_linux.c
+@OS_WIN32_MINGW_TRUE@am__append_3 = sg_scan
+@OS_WIN32_MINGW_TRUE@am__append_4 = sg_scan_win32.c
+@OS_WIN32_CYGWIN_TRUE@am__append_5 = sg_scan
+@OS_WIN32_CYGWIN_TRUE@am__append_6 = sg_scan_win32.c
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+@OS_LINUX_TRUE@am__EXEEXT_1 = sg_copy_results$(EXEEXT) sg_dd$(EXEEXT) \
+@OS_LINUX_TRUE@	sg_emc_trespass$(EXEEXT) sg_map$(EXEEXT) \
+@OS_LINUX_TRUE@	sg_map26$(EXEEXT) sg_rbuf$(EXEEXT) \
+@OS_LINUX_TRUE@	sg_read$(EXEEXT) sg_reset$(EXEEXT) \
+@OS_LINUX_TRUE@	sg_scan$(EXEEXT) sg_test_rwbuf$(EXEEXT) \
+@OS_LINUX_TRUE@	sg_xcopy$(EXEEXT) sginfo$(EXEEXT) \
+@OS_LINUX_TRUE@	sgm_dd$(EXEEXT) sgp_dd$(EXEEXT)
+@OS_WIN32_MINGW_TRUE@am__EXEEXT_2 = sg_scan$(EXEEXT)
+@OS_WIN32_CYGWIN_TRUE@am__EXEEXT_3 = sg_scan$(EXEEXT)
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+sg_compare_and_write_SOURCES = sg_compare_and_write.c
+sg_compare_and_write_OBJECTS = sg_compare_and_write.$(OBJEXT)
+sg_compare_and_write_DEPENDENCIES = ../lib/libsgutils2.la
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+sg_copy_results_SOURCES = sg_copy_results.c
+sg_copy_results_OBJECTS = sg_copy_results.$(OBJEXT)
+sg_copy_results_DEPENDENCIES = ../lib/libsgutils2.la
+sg_dd_SOURCES = sg_dd.c
+sg_dd_OBJECTS = sg_dd.$(OBJEXT)
+sg_dd_DEPENDENCIES = ../lib/libsgutils2.la
+sg_decode_sense_SOURCES = sg_decode_sense.c
+sg_decode_sense_OBJECTS = sg_decode_sense.$(OBJEXT)
+sg_decode_sense_DEPENDENCIES = ../lib/libsgutils2.la
+sg_emc_trespass_SOURCES = sg_emc_trespass.c
+sg_emc_trespass_OBJECTS = sg_emc_trespass.$(OBJEXT)
+sg_emc_trespass_DEPENDENCIES = ../lib/libsgutils2.la
+sg_format_SOURCES = sg_format.c
+sg_format_OBJECTS = sg_format.$(OBJEXT)
+sg_format_DEPENDENCIES = ../lib/libsgutils2.la
+sg_get_config_SOURCES = sg_get_config.c
+sg_get_config_OBJECTS = sg_get_config.$(OBJEXT)
+sg_get_config_DEPENDENCIES = ../lib/libsgutils2.la
+sg_get_lba_status_SOURCES = sg_get_lba_status.c
+sg_get_lba_status_OBJECTS = sg_get_lba_status.$(OBJEXT)
+sg_get_lba_status_DEPENDENCIES = ../lib/libsgutils2.la
+sg_ident_SOURCES = sg_ident.c
+sg_ident_OBJECTS = sg_ident.$(OBJEXT)
+sg_ident_DEPENDENCIES = ../lib/libsgutils2.la
+am_sg_inq_OBJECTS = sg_inq.$(OBJEXT) sg_inq_data.$(OBJEXT)
+sg_inq_OBJECTS = $(am_sg_inq_OBJECTS)
+sg_inq_DEPENDENCIES = ../lib/libsgutils2.la
+sg_logs_SOURCES = sg_logs.c
+sg_logs_OBJECTS = sg_logs.$(OBJEXT)
+sg_logs_DEPENDENCIES = ../lib/libsgutils2.la
+sg_luns_SOURCES = sg_luns.c
+sg_luns_OBJECTS = sg_luns.$(OBJEXT)
+sg_luns_DEPENDENCIES = ../lib/libsgutils2.la
+sg_map_SOURCES = sg_map.c
+sg_map_OBJECTS = sg_map.$(OBJEXT)
+sg_map_DEPENDENCIES = ../lib/libsgutils2.la
+sg_map26_SOURCES = sg_map26.c
+sg_map26_OBJECTS = sg_map26.$(OBJEXT)
+sg_map26_DEPENDENCIES =
+sg_modes_SOURCES = sg_modes.c
+sg_modes_OBJECTS = sg_modes.$(OBJEXT)
+sg_modes_DEPENDENCIES = ../lib/libsgutils2.la
+sg_opcodes_SOURCES = sg_opcodes.c
+sg_opcodes_OBJECTS = sg_opcodes.$(OBJEXT)
+sg_opcodes_DEPENDENCIES = ../lib/libsgutils2.la
+sg_persist_SOURCES = sg_persist.c
+sg_persist_OBJECTS = sg_persist.$(OBJEXT)
+sg_persist_DEPENDENCIES = ../lib/libsgutils2.la
+sg_prevent_SOURCES = sg_prevent.c
+sg_prevent_OBJECTS = sg_prevent.$(OBJEXT)
+sg_prevent_DEPENDENCIES = ../lib/libsgutils2.la
+sg_raw_SOURCES = sg_raw.c
+sg_raw_OBJECTS = sg_raw.$(OBJEXT)
+sg_raw_DEPENDENCIES = ../lib/libsgutils2.la
+sg_rbuf_SOURCES = sg_rbuf.c
+sg_rbuf_OBJECTS = sg_rbuf.$(OBJEXT)
+sg_rbuf_DEPENDENCIES = ../lib/libsgutils2.la
+sg_rdac_SOURCES = sg_rdac.c
+sg_rdac_OBJECTS = sg_rdac.$(OBJEXT)
+sg_rdac_DEPENDENCIES = ../lib/libsgutils2.la
+sg_read_SOURCES = sg_read.c
+sg_read_OBJECTS = sg_read.$(OBJEXT)
+sg_read_DEPENDENCIES = ../lib/libsgutils2.la
+sg_read_attr_SOURCES = sg_read_attr.c
+sg_read_attr_OBJECTS = sg_read_attr.$(OBJEXT)
+sg_read_attr_DEPENDENCIES = ../lib/libsgutils2.la
+sg_read_block_limits_SOURCES = sg_read_block_limits.c
+sg_read_block_limits_OBJECTS = sg_read_block_limits.$(OBJEXT)
+sg_read_block_limits_DEPENDENCIES = ../lib/libsgutils2.la
+sg_read_buffer_SOURCES = sg_read_buffer.c
+sg_read_buffer_OBJECTS = sg_read_buffer.$(OBJEXT)
+sg_read_buffer_DEPENDENCIES = ../lib/libsgutils2.la
+sg_read_long_SOURCES = sg_read_long.c
+sg_read_long_OBJECTS = sg_read_long.$(OBJEXT)
+sg_read_long_DEPENDENCIES = ../lib/libsgutils2.la
+sg_readcap_SOURCES = sg_readcap.c
+sg_readcap_OBJECTS = sg_readcap.$(OBJEXT)
+sg_readcap_DEPENDENCIES = ../lib/libsgutils2.la
+sg_reassign_SOURCES = sg_reassign.c
+sg_reassign_OBJECTS = sg_reassign.$(OBJEXT)
+sg_reassign_DEPENDENCIES = ../lib/libsgutils2.la
+sg_referrals_SOURCES = sg_referrals.c
+sg_referrals_OBJECTS = sg_referrals.$(OBJEXT)
+sg_referrals_DEPENDENCIES = ../lib/libsgutils2.la
+sg_rep_zones_SOURCES = sg_rep_zones.c
+sg_rep_zones_OBJECTS = sg_rep_zones.$(OBJEXT)
+sg_rep_zones_DEPENDENCIES = ../lib/libsgutils2.la
+sg_requests_SOURCES = sg_requests.c
+sg_requests_OBJECTS = sg_requests.$(OBJEXT)
+sg_requests_DEPENDENCIES = ../lib/libsgutils2.la
+sg_reset_SOURCES = sg_reset.c
+sg_reset_OBJECTS = sg_reset.$(OBJEXT)
+sg_reset_DEPENDENCIES =
+sg_reset_wp_SOURCES = sg_reset_wp.c
+sg_reset_wp_OBJECTS = sg_reset_wp.$(OBJEXT)
+sg_reset_wp_DEPENDENCIES = ../lib/libsgutils2.la
+sg_rmsn_SOURCES = sg_rmsn.c
+sg_rmsn_OBJECTS = sg_rmsn.$(OBJEXT)
+sg_rmsn_DEPENDENCIES = ../lib/libsgutils2.la
+sg_rtpg_SOURCES = sg_rtpg.c
+sg_rtpg_OBJECTS = sg_rtpg.$(OBJEXT)
+sg_rtpg_DEPENDENCIES = ../lib/libsgutils2.la
+sg_safte_SOURCES = sg_safte.c
+sg_safte_OBJECTS = sg_safte.$(OBJEXT)
+sg_safte_DEPENDENCIES = ../lib/libsgutils2.la
+sg_sanitize_SOURCES = sg_sanitize.c
+sg_sanitize_OBJECTS = sg_sanitize.$(OBJEXT)
+sg_sanitize_DEPENDENCIES = ../lib/libsgutils2.la
+sg_sat_identify_SOURCES = sg_sat_identify.c
+sg_sat_identify_OBJECTS = sg_sat_identify.$(OBJEXT)
+sg_sat_identify_DEPENDENCIES = ../lib/libsgutils2.la
+sg_sat_phy_event_SOURCES = sg_sat_phy_event.c
+sg_sat_phy_event_OBJECTS = sg_sat_phy_event.$(OBJEXT)
+sg_sat_phy_event_DEPENDENCIES = ../lib/libsgutils2.la
+sg_sat_read_gplog_SOURCES = sg_sat_read_gplog.c
+sg_sat_read_gplog_OBJECTS = sg_sat_read_gplog.$(OBJEXT)
+sg_sat_read_gplog_DEPENDENCIES = ../lib/libsgutils2.la
+sg_sat_set_features_SOURCES = sg_sat_set_features.c
+sg_sat_set_features_OBJECTS = sg_sat_set_features.$(OBJEXT)
+sg_sat_set_features_DEPENDENCIES = ../lib/libsgutils2.la
+am__sg_scan_SOURCES_DIST = sg_scan_linux.c sg_scan_win32.c
+@OS_LINUX_TRUE@am__objects_1 = sg_scan_linux.$(OBJEXT)
+@OS_WIN32_MINGW_TRUE@am__objects_2 = sg_scan_win32.$(OBJEXT)
+@OS_WIN32_CYGWIN_TRUE@am__objects_3 = sg_scan_win32.$(OBJEXT)
+am_sg_scan_OBJECTS = $(am__objects_1) $(am__objects_2) \
+	$(am__objects_3)
+sg_scan_OBJECTS = $(am_sg_scan_OBJECTS)
+sg_scan_DEPENDENCIES = ../lib/libsgutils2.la
+sg_senddiag_SOURCES = sg_senddiag.c
+sg_senddiag_OBJECTS = sg_senddiag.$(OBJEXT)
+sg_senddiag_DEPENDENCIES = ../lib/libsgutils2.la
+sg_ses_SOURCES = sg_ses.c
+sg_ses_OBJECTS = sg_ses.$(OBJEXT)
+sg_ses_DEPENDENCIES = ../lib/libsgutils2.la
+sg_ses_microcode_SOURCES = sg_ses_microcode.c
+sg_ses_microcode_OBJECTS = sg_ses_microcode.$(OBJEXT)
+sg_ses_microcode_DEPENDENCIES = ../lib/libsgutils2.la
+sg_start_SOURCES = sg_start.c
+sg_start_OBJECTS = sg_start.$(OBJEXT)
+sg_start_DEPENDENCIES = ../lib/libsgutils2.la
+sg_stpg_SOURCES = sg_stpg.c
+sg_stpg_OBJECTS = sg_stpg.$(OBJEXT)
+sg_stpg_DEPENDENCIES = ../lib/libsgutils2.la
+sg_sync_SOURCES = sg_sync.c
+sg_sync_OBJECTS = sg_sync.$(OBJEXT)
+sg_sync_DEPENDENCIES = ../lib/libsgutils2.la
+sg_test_rwbuf_SOURCES = sg_test_rwbuf.c
+sg_test_rwbuf_OBJECTS = sg_test_rwbuf.$(OBJEXT)
+sg_test_rwbuf_DEPENDENCIES = ../lib/libsgutils2.la
+sg_timestamp_SOURCES = sg_timestamp.c
+sg_timestamp_OBJECTS = sg_timestamp.$(OBJEXT)
+sg_timestamp_DEPENDENCIES = ../lib/libsgutils2.la
+sg_turs_SOURCES = sg_turs.c
+sg_turs_OBJECTS = sg_turs.$(OBJEXT)
+sg_turs_DEPENDENCIES = ../lib/libsgutils2.la
+sg_unmap_SOURCES = sg_unmap.c
+sg_unmap_OBJECTS = sg_unmap.$(OBJEXT)
+sg_unmap_DEPENDENCIES = ../lib/libsgutils2.la
+sg_verify_SOURCES = sg_verify.c
+sg_verify_OBJECTS = sg_verify.$(OBJEXT)
+sg_verify_DEPENDENCIES = ../lib/libsgutils2.la
+am_sg_vpd_OBJECTS = sg_vpd.$(OBJEXT) sg_vpd_vendor.$(OBJEXT)
+sg_vpd_OBJECTS = $(am_sg_vpd_OBJECTS)
+sg_vpd_DEPENDENCIES = ../lib/libsgutils2.la
+sg_wr_mode_SOURCES = sg_wr_mode.c
+sg_wr_mode_OBJECTS = sg_wr_mode.$(OBJEXT)
+sg_wr_mode_DEPENDENCIES = ../lib/libsgutils2.la
+sg_write_buffer_SOURCES = sg_write_buffer.c
+sg_write_buffer_OBJECTS = sg_write_buffer.$(OBJEXT)
+sg_write_buffer_DEPENDENCIES = ../lib/libsgutils2.la
+sg_write_long_SOURCES = sg_write_long.c
+sg_write_long_OBJECTS = sg_write_long.$(OBJEXT)
+sg_write_long_DEPENDENCIES = ../lib/libsgutils2.la
+sg_write_same_SOURCES = sg_write_same.c
+sg_write_same_OBJECTS = sg_write_same.$(OBJEXT)
+sg_write_same_DEPENDENCIES = ../lib/libsgutils2.la
+sg_write_verify_SOURCES = sg_write_verify.c
+sg_write_verify_OBJECTS = sg_write_verify.$(OBJEXT)
+sg_write_verify_DEPENDENCIES = ../lib/libsgutils2.la
+sg_xcopy_SOURCES = sg_xcopy.c
+sg_xcopy_OBJECTS = sg_xcopy.$(OBJEXT)
+sg_xcopy_DEPENDENCIES = ../lib/libsgutils2.la
+sg_zone_SOURCES = sg_zone.c
+sg_zone_OBJECTS = sg_zone.$(OBJEXT)
+sg_zone_DEPENDENCIES = ../lib/libsgutils2.la
+sginfo_SOURCES = sginfo.c
+sginfo_OBJECTS = sginfo.$(OBJEXT)
+sginfo_DEPENDENCIES = ../lib/libsgutils2.la
+sgm_dd_SOURCES = sgm_dd.c
+sgm_dd_OBJECTS = sgm_dd.$(OBJEXT)
+sgm_dd_DEPENDENCIES = ../lib/libsgutils2.la
+sgp_dd_SOURCES = sgp_dd.c
+sgp_dd_OBJECTS = sgp_dd.$(OBJEXT)
+sgp_dd_DEPENDENCIES = ../lib/libsgutils2.la
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = sg_compare_and_write.c sg_copy_results.c sg_dd.c \
+	sg_decode_sense.c sg_emc_trespass.c sg_format.c \
+	sg_get_config.c sg_get_lba_status.c sg_ident.c \
+	$(sg_inq_SOURCES) sg_logs.c sg_luns.c sg_map.c sg_map26.c \
+	sg_modes.c sg_opcodes.c sg_persist.c sg_prevent.c sg_raw.c \
+	sg_rbuf.c sg_rdac.c sg_read.c sg_read_attr.c \
+	sg_read_block_limits.c sg_read_buffer.c sg_read_long.c \
+	sg_readcap.c sg_reassign.c sg_referrals.c sg_rep_zones.c \
+	sg_requests.c sg_reset.c sg_reset_wp.c sg_rmsn.c sg_rtpg.c \
+	sg_safte.c sg_sanitize.c sg_sat_identify.c sg_sat_phy_event.c \
+	sg_sat_read_gplog.c sg_sat_set_features.c $(sg_scan_SOURCES) \
+	sg_senddiag.c sg_ses.c sg_ses_microcode.c sg_start.c sg_stpg.c \
+	sg_sync.c sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
+	sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_buffer.c \
+	sg_write_long.c sg_write_same.c sg_write_verify.c sg_xcopy.c \
+	sg_zone.c sginfo.c sgm_dd.c sgp_dd.c
+DIST_SOURCES = sg_compare_and_write.c sg_copy_results.c sg_dd.c \
+	sg_decode_sense.c sg_emc_trespass.c sg_format.c \
+	sg_get_config.c sg_get_lba_status.c sg_ident.c \
+	$(sg_inq_SOURCES) sg_logs.c sg_luns.c sg_map.c sg_map26.c \
+	sg_modes.c sg_opcodes.c sg_persist.c sg_prevent.c sg_raw.c \
+	sg_rbuf.c sg_rdac.c sg_read.c sg_read_attr.c \
+	sg_read_block_limits.c sg_read_buffer.c sg_read_long.c \
+	sg_readcap.c sg_reassign.c sg_referrals.c sg_rep_zones.c \
+	sg_requests.c sg_reset.c sg_reset_wp.c sg_rmsn.c sg_rtpg.c \
+	sg_safte.c sg_sanitize.c sg_sat_identify.c sg_sat_phy_event.c \
+	sg_sat_read_gplog.c sg_sat_set_features.c \
+	$(am__sg_scan_SOURCES_DIST) sg_senddiag.c sg_ses.c \
+	sg_ses_microcode.c sg_start.c sg_stpg.c sg_sync.c \
+	sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
+	sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_buffer.c \
+	sg_write_long.c sg_write_same.c sg_write_verify.c sg_xcopy.c \
+	sg_zone.c sginfo.c sgm_dd.c sgp_dd.c
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GETOPT_O_FILES = @GETOPT_O_FILES@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+os_cflags = @os_cflags@
+os_libs = @os_libs@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+sg_scan_SOURCES = $(am__append_2) $(am__append_4) $(am__append_6)
+
+# For C++/clang testing
+
+# -std=<s> can be c99, c11, gnu11, etc. Default is gnu89 (gnu90 is the same)
+# -Wall is no longer all warnings. Add -W (since renamed to -Wextra) for more
+AM_CPPFLAGS = -iquote ${top_srcdir}/include -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+AM_CFLAGS = -Wall -W @os_cflags@
+# AM_CFLAGS = -Wall -W @os_cflags@ -pedantic -std=c11
+# AM_CFLAGS = -Wall -W @os_cflags@ -pedantic -std=c++11
+sg_compare_and_write_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_copy_results_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_dd_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_decode_sense_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_emc_trespass_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_format_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_get_config_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_get_lba_status_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_ident_LDADD = ../lib/libsgutils2.la @os_libs@
+sginfo_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_inq_SOURCES = sg_inq.c sg_inq_data.c
+sg_inq_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_logs_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_luns_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_map26_LDADD = @os_libs@
+sg_map_LDADD = ../lib/libsgutils2.la @os_libs@
+sgm_dd_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_modes_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_opcodes_LDADD = ../lib/libsgutils2.la @os_libs@
+sgp_dd_LDADD = ../lib/libsgutils2.la @os_libs@ -lpthread
+sg_persist_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_prevent_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_raw_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_rbuf_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_rdac_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_read_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_read_attr_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_readcap_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_read_block_limits_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_read_buffer_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_read_long_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_reassign_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_requests_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_referrals_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_rep_zones_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_reset_LDADD = @os_libs@
+sg_reset_wp_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_rmsn_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_rtpg_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_safte_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_sanitize_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_sat_identify_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_sat_phy_event_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_sat_read_gplog_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_sat_set_features_LDADD = ../lib/libsgutils2.la @os_libs@
+
+# sg_scan_SOURCES list is already set above in the platform-specific sections
+sg_scan_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_senddiag_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_ses_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_ses_microcode_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_start_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_stpg_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_sync_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_test_rwbuf_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_timestamp_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_turs_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_unmap_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_verify_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_vpd_SOURCES = sg_vpd.c sg_vpd_vendor.c
+sg_vpd_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_write_buffer_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_write_long_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_write_same_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_write_verify_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_wr_mode_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_xcopy_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_zone_LDADD = ../lib/libsgutils2.la @os_libs@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+	fi; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p \
+	 || test -f $$p1 \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-binPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+	@list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+sg_compare_and_write$(EXEEXT): $(sg_compare_and_write_OBJECTS) $(sg_compare_and_write_DEPENDENCIES) $(EXTRA_sg_compare_and_write_DEPENDENCIES) 
+	@rm -f sg_compare_and_write$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_compare_and_write_OBJECTS) $(sg_compare_and_write_LDADD) $(LIBS)
+
+sg_copy_results$(EXEEXT): $(sg_copy_results_OBJECTS) $(sg_copy_results_DEPENDENCIES) $(EXTRA_sg_copy_results_DEPENDENCIES) 
+	@rm -f sg_copy_results$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_copy_results_OBJECTS) $(sg_copy_results_LDADD) $(LIBS)
+
+sg_dd$(EXEEXT): $(sg_dd_OBJECTS) $(sg_dd_DEPENDENCIES) $(EXTRA_sg_dd_DEPENDENCIES) 
+	@rm -f sg_dd$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_dd_OBJECTS) $(sg_dd_LDADD) $(LIBS)
+
+sg_decode_sense$(EXEEXT): $(sg_decode_sense_OBJECTS) $(sg_decode_sense_DEPENDENCIES) $(EXTRA_sg_decode_sense_DEPENDENCIES) 
+	@rm -f sg_decode_sense$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_decode_sense_OBJECTS) $(sg_decode_sense_LDADD) $(LIBS)
+
+sg_emc_trespass$(EXEEXT): $(sg_emc_trespass_OBJECTS) $(sg_emc_trespass_DEPENDENCIES) $(EXTRA_sg_emc_trespass_DEPENDENCIES) 
+	@rm -f sg_emc_trespass$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_emc_trespass_OBJECTS) $(sg_emc_trespass_LDADD) $(LIBS)
+
+sg_format$(EXEEXT): $(sg_format_OBJECTS) $(sg_format_DEPENDENCIES) $(EXTRA_sg_format_DEPENDENCIES) 
+	@rm -f sg_format$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_format_OBJECTS) $(sg_format_LDADD) $(LIBS)
+
+sg_get_config$(EXEEXT): $(sg_get_config_OBJECTS) $(sg_get_config_DEPENDENCIES) $(EXTRA_sg_get_config_DEPENDENCIES) 
+	@rm -f sg_get_config$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_get_config_OBJECTS) $(sg_get_config_LDADD) $(LIBS)
+
+sg_get_lba_status$(EXEEXT): $(sg_get_lba_status_OBJECTS) $(sg_get_lba_status_DEPENDENCIES) $(EXTRA_sg_get_lba_status_DEPENDENCIES) 
+	@rm -f sg_get_lba_status$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_get_lba_status_OBJECTS) $(sg_get_lba_status_LDADD) $(LIBS)
+
+sg_ident$(EXEEXT): $(sg_ident_OBJECTS) $(sg_ident_DEPENDENCIES) $(EXTRA_sg_ident_DEPENDENCIES) 
+	@rm -f sg_ident$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_ident_OBJECTS) $(sg_ident_LDADD) $(LIBS)
+
+sg_inq$(EXEEXT): $(sg_inq_OBJECTS) $(sg_inq_DEPENDENCIES) $(EXTRA_sg_inq_DEPENDENCIES) 
+	@rm -f sg_inq$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_inq_OBJECTS) $(sg_inq_LDADD) $(LIBS)
+
+sg_logs$(EXEEXT): $(sg_logs_OBJECTS) $(sg_logs_DEPENDENCIES) $(EXTRA_sg_logs_DEPENDENCIES) 
+	@rm -f sg_logs$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_logs_OBJECTS) $(sg_logs_LDADD) $(LIBS)
+
+sg_luns$(EXEEXT): $(sg_luns_OBJECTS) $(sg_luns_DEPENDENCIES) $(EXTRA_sg_luns_DEPENDENCIES) 
+	@rm -f sg_luns$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_luns_OBJECTS) $(sg_luns_LDADD) $(LIBS)
+
+sg_map$(EXEEXT): $(sg_map_OBJECTS) $(sg_map_DEPENDENCIES) $(EXTRA_sg_map_DEPENDENCIES) 
+	@rm -f sg_map$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_map_OBJECTS) $(sg_map_LDADD) $(LIBS)
+
+sg_map26$(EXEEXT): $(sg_map26_OBJECTS) $(sg_map26_DEPENDENCIES) $(EXTRA_sg_map26_DEPENDENCIES) 
+	@rm -f sg_map26$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_map26_OBJECTS) $(sg_map26_LDADD) $(LIBS)
+
+sg_modes$(EXEEXT): $(sg_modes_OBJECTS) $(sg_modes_DEPENDENCIES) $(EXTRA_sg_modes_DEPENDENCIES) 
+	@rm -f sg_modes$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_modes_OBJECTS) $(sg_modes_LDADD) $(LIBS)
+
+sg_opcodes$(EXEEXT): $(sg_opcodes_OBJECTS) $(sg_opcodes_DEPENDENCIES) $(EXTRA_sg_opcodes_DEPENDENCIES) 
+	@rm -f sg_opcodes$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_opcodes_OBJECTS) $(sg_opcodes_LDADD) $(LIBS)
+
+sg_persist$(EXEEXT): $(sg_persist_OBJECTS) $(sg_persist_DEPENDENCIES) $(EXTRA_sg_persist_DEPENDENCIES) 
+	@rm -f sg_persist$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_persist_OBJECTS) $(sg_persist_LDADD) $(LIBS)
+
+sg_prevent$(EXEEXT): $(sg_prevent_OBJECTS) $(sg_prevent_DEPENDENCIES) $(EXTRA_sg_prevent_DEPENDENCIES) 
+	@rm -f sg_prevent$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_prevent_OBJECTS) $(sg_prevent_LDADD) $(LIBS)
+
+sg_raw$(EXEEXT): $(sg_raw_OBJECTS) $(sg_raw_DEPENDENCIES) $(EXTRA_sg_raw_DEPENDENCIES) 
+	@rm -f sg_raw$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_raw_OBJECTS) $(sg_raw_LDADD) $(LIBS)
+
+sg_rbuf$(EXEEXT): $(sg_rbuf_OBJECTS) $(sg_rbuf_DEPENDENCIES) $(EXTRA_sg_rbuf_DEPENDENCIES) 
+	@rm -f sg_rbuf$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_rbuf_OBJECTS) $(sg_rbuf_LDADD) $(LIBS)
+
+sg_rdac$(EXEEXT): $(sg_rdac_OBJECTS) $(sg_rdac_DEPENDENCIES) $(EXTRA_sg_rdac_DEPENDENCIES) 
+	@rm -f sg_rdac$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_rdac_OBJECTS) $(sg_rdac_LDADD) $(LIBS)
+
+sg_read$(EXEEXT): $(sg_read_OBJECTS) $(sg_read_DEPENDENCIES) $(EXTRA_sg_read_DEPENDENCIES) 
+	@rm -f sg_read$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_read_OBJECTS) $(sg_read_LDADD) $(LIBS)
+
+sg_read_attr$(EXEEXT): $(sg_read_attr_OBJECTS) $(sg_read_attr_DEPENDENCIES) $(EXTRA_sg_read_attr_DEPENDENCIES) 
+	@rm -f sg_read_attr$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_read_attr_OBJECTS) $(sg_read_attr_LDADD) $(LIBS)
+
+sg_read_block_limits$(EXEEXT): $(sg_read_block_limits_OBJECTS) $(sg_read_block_limits_DEPENDENCIES) $(EXTRA_sg_read_block_limits_DEPENDENCIES) 
+	@rm -f sg_read_block_limits$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_read_block_limits_OBJECTS) $(sg_read_block_limits_LDADD) $(LIBS)
+
+sg_read_buffer$(EXEEXT): $(sg_read_buffer_OBJECTS) $(sg_read_buffer_DEPENDENCIES) $(EXTRA_sg_read_buffer_DEPENDENCIES) 
+	@rm -f sg_read_buffer$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_read_buffer_OBJECTS) $(sg_read_buffer_LDADD) $(LIBS)
+
+sg_read_long$(EXEEXT): $(sg_read_long_OBJECTS) $(sg_read_long_DEPENDENCIES) $(EXTRA_sg_read_long_DEPENDENCIES) 
+	@rm -f sg_read_long$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_read_long_OBJECTS) $(sg_read_long_LDADD) $(LIBS)
+
+sg_readcap$(EXEEXT): $(sg_readcap_OBJECTS) $(sg_readcap_DEPENDENCIES) $(EXTRA_sg_readcap_DEPENDENCIES) 
+	@rm -f sg_readcap$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_readcap_OBJECTS) $(sg_readcap_LDADD) $(LIBS)
+
+sg_reassign$(EXEEXT): $(sg_reassign_OBJECTS) $(sg_reassign_DEPENDENCIES) $(EXTRA_sg_reassign_DEPENDENCIES) 
+	@rm -f sg_reassign$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_reassign_OBJECTS) $(sg_reassign_LDADD) $(LIBS)
+
+sg_referrals$(EXEEXT): $(sg_referrals_OBJECTS) $(sg_referrals_DEPENDENCIES) $(EXTRA_sg_referrals_DEPENDENCIES) 
+	@rm -f sg_referrals$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_referrals_OBJECTS) $(sg_referrals_LDADD) $(LIBS)
+
+sg_rep_zones$(EXEEXT): $(sg_rep_zones_OBJECTS) $(sg_rep_zones_DEPENDENCIES) $(EXTRA_sg_rep_zones_DEPENDENCIES) 
+	@rm -f sg_rep_zones$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_rep_zones_OBJECTS) $(sg_rep_zones_LDADD) $(LIBS)
+
+sg_requests$(EXEEXT): $(sg_requests_OBJECTS) $(sg_requests_DEPENDENCIES) $(EXTRA_sg_requests_DEPENDENCIES) 
+	@rm -f sg_requests$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_requests_OBJECTS) $(sg_requests_LDADD) $(LIBS)
+
+sg_reset$(EXEEXT): $(sg_reset_OBJECTS) $(sg_reset_DEPENDENCIES) $(EXTRA_sg_reset_DEPENDENCIES) 
+	@rm -f sg_reset$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_reset_OBJECTS) $(sg_reset_LDADD) $(LIBS)
+
+sg_reset_wp$(EXEEXT): $(sg_reset_wp_OBJECTS) $(sg_reset_wp_DEPENDENCIES) $(EXTRA_sg_reset_wp_DEPENDENCIES) 
+	@rm -f sg_reset_wp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_reset_wp_OBJECTS) $(sg_reset_wp_LDADD) $(LIBS)
+
+sg_rmsn$(EXEEXT): $(sg_rmsn_OBJECTS) $(sg_rmsn_DEPENDENCIES) $(EXTRA_sg_rmsn_DEPENDENCIES) 
+	@rm -f sg_rmsn$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_rmsn_OBJECTS) $(sg_rmsn_LDADD) $(LIBS)
+
+sg_rtpg$(EXEEXT): $(sg_rtpg_OBJECTS) $(sg_rtpg_DEPENDENCIES) $(EXTRA_sg_rtpg_DEPENDENCIES) 
+	@rm -f sg_rtpg$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_rtpg_OBJECTS) $(sg_rtpg_LDADD) $(LIBS)
+
+sg_safte$(EXEEXT): $(sg_safte_OBJECTS) $(sg_safte_DEPENDENCIES) $(EXTRA_sg_safte_DEPENDENCIES) 
+	@rm -f sg_safte$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_safte_OBJECTS) $(sg_safte_LDADD) $(LIBS)
+
+sg_sanitize$(EXEEXT): $(sg_sanitize_OBJECTS) $(sg_sanitize_DEPENDENCIES) $(EXTRA_sg_sanitize_DEPENDENCIES) 
+	@rm -f sg_sanitize$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_sanitize_OBJECTS) $(sg_sanitize_LDADD) $(LIBS)
+
+sg_sat_identify$(EXEEXT): $(sg_sat_identify_OBJECTS) $(sg_sat_identify_DEPENDENCIES) $(EXTRA_sg_sat_identify_DEPENDENCIES) 
+	@rm -f sg_sat_identify$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_sat_identify_OBJECTS) $(sg_sat_identify_LDADD) $(LIBS)
+
+sg_sat_phy_event$(EXEEXT): $(sg_sat_phy_event_OBJECTS) $(sg_sat_phy_event_DEPENDENCIES) $(EXTRA_sg_sat_phy_event_DEPENDENCIES) 
+	@rm -f sg_sat_phy_event$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_sat_phy_event_OBJECTS) $(sg_sat_phy_event_LDADD) $(LIBS)
+
+sg_sat_read_gplog$(EXEEXT): $(sg_sat_read_gplog_OBJECTS) $(sg_sat_read_gplog_DEPENDENCIES) $(EXTRA_sg_sat_read_gplog_DEPENDENCIES) 
+	@rm -f sg_sat_read_gplog$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_sat_read_gplog_OBJECTS) $(sg_sat_read_gplog_LDADD) $(LIBS)
+
+sg_sat_set_features$(EXEEXT): $(sg_sat_set_features_OBJECTS) $(sg_sat_set_features_DEPENDENCIES) $(EXTRA_sg_sat_set_features_DEPENDENCIES) 
+	@rm -f sg_sat_set_features$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_sat_set_features_OBJECTS) $(sg_sat_set_features_LDADD) $(LIBS)
+
+sg_scan$(EXEEXT): $(sg_scan_OBJECTS) $(sg_scan_DEPENDENCIES) $(EXTRA_sg_scan_DEPENDENCIES) 
+	@rm -f sg_scan$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_scan_OBJECTS) $(sg_scan_LDADD) $(LIBS)
+
+sg_senddiag$(EXEEXT): $(sg_senddiag_OBJECTS) $(sg_senddiag_DEPENDENCIES) $(EXTRA_sg_senddiag_DEPENDENCIES) 
+	@rm -f sg_senddiag$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_senddiag_OBJECTS) $(sg_senddiag_LDADD) $(LIBS)
+
+sg_ses$(EXEEXT): $(sg_ses_OBJECTS) $(sg_ses_DEPENDENCIES) $(EXTRA_sg_ses_DEPENDENCIES) 
+	@rm -f sg_ses$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_ses_OBJECTS) $(sg_ses_LDADD) $(LIBS)
+
+sg_ses_microcode$(EXEEXT): $(sg_ses_microcode_OBJECTS) $(sg_ses_microcode_DEPENDENCIES) $(EXTRA_sg_ses_microcode_DEPENDENCIES) 
+	@rm -f sg_ses_microcode$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_ses_microcode_OBJECTS) $(sg_ses_microcode_LDADD) $(LIBS)
+
+sg_start$(EXEEXT): $(sg_start_OBJECTS) $(sg_start_DEPENDENCIES) $(EXTRA_sg_start_DEPENDENCIES) 
+	@rm -f sg_start$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_start_OBJECTS) $(sg_start_LDADD) $(LIBS)
+
+sg_stpg$(EXEEXT): $(sg_stpg_OBJECTS) $(sg_stpg_DEPENDENCIES) $(EXTRA_sg_stpg_DEPENDENCIES) 
+	@rm -f sg_stpg$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_stpg_OBJECTS) $(sg_stpg_LDADD) $(LIBS)
+
+sg_sync$(EXEEXT): $(sg_sync_OBJECTS) $(sg_sync_DEPENDENCIES) $(EXTRA_sg_sync_DEPENDENCIES) 
+	@rm -f sg_sync$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_sync_OBJECTS) $(sg_sync_LDADD) $(LIBS)
+
+sg_test_rwbuf$(EXEEXT): $(sg_test_rwbuf_OBJECTS) $(sg_test_rwbuf_DEPENDENCIES) $(EXTRA_sg_test_rwbuf_DEPENDENCIES) 
+	@rm -f sg_test_rwbuf$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_test_rwbuf_OBJECTS) $(sg_test_rwbuf_LDADD) $(LIBS)
+
+sg_timestamp$(EXEEXT): $(sg_timestamp_OBJECTS) $(sg_timestamp_DEPENDENCIES) $(EXTRA_sg_timestamp_DEPENDENCIES) 
+	@rm -f sg_timestamp$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_timestamp_OBJECTS) $(sg_timestamp_LDADD) $(LIBS)
+
+sg_turs$(EXEEXT): $(sg_turs_OBJECTS) $(sg_turs_DEPENDENCIES) $(EXTRA_sg_turs_DEPENDENCIES) 
+	@rm -f sg_turs$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_turs_OBJECTS) $(sg_turs_LDADD) $(LIBS)
+
+sg_unmap$(EXEEXT): $(sg_unmap_OBJECTS) $(sg_unmap_DEPENDENCIES) $(EXTRA_sg_unmap_DEPENDENCIES) 
+	@rm -f sg_unmap$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_unmap_OBJECTS) $(sg_unmap_LDADD) $(LIBS)
+
+sg_verify$(EXEEXT): $(sg_verify_OBJECTS) $(sg_verify_DEPENDENCIES) $(EXTRA_sg_verify_DEPENDENCIES) 
+	@rm -f sg_verify$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_verify_OBJECTS) $(sg_verify_LDADD) $(LIBS)
+
+sg_vpd$(EXEEXT): $(sg_vpd_OBJECTS) $(sg_vpd_DEPENDENCIES) $(EXTRA_sg_vpd_DEPENDENCIES) 
+	@rm -f sg_vpd$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_vpd_OBJECTS) $(sg_vpd_LDADD) $(LIBS)
+
+sg_wr_mode$(EXEEXT): $(sg_wr_mode_OBJECTS) $(sg_wr_mode_DEPENDENCIES) $(EXTRA_sg_wr_mode_DEPENDENCIES) 
+	@rm -f sg_wr_mode$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_wr_mode_OBJECTS) $(sg_wr_mode_LDADD) $(LIBS)
+
+sg_write_buffer$(EXEEXT): $(sg_write_buffer_OBJECTS) $(sg_write_buffer_DEPENDENCIES) $(EXTRA_sg_write_buffer_DEPENDENCIES) 
+	@rm -f sg_write_buffer$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_write_buffer_OBJECTS) $(sg_write_buffer_LDADD) $(LIBS)
+
+sg_write_long$(EXEEXT): $(sg_write_long_OBJECTS) $(sg_write_long_DEPENDENCIES) $(EXTRA_sg_write_long_DEPENDENCIES) 
+	@rm -f sg_write_long$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_write_long_OBJECTS) $(sg_write_long_LDADD) $(LIBS)
+
+sg_write_same$(EXEEXT): $(sg_write_same_OBJECTS) $(sg_write_same_DEPENDENCIES) $(EXTRA_sg_write_same_DEPENDENCIES) 
+	@rm -f sg_write_same$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_write_same_OBJECTS) $(sg_write_same_LDADD) $(LIBS)
+
+sg_write_verify$(EXEEXT): $(sg_write_verify_OBJECTS) $(sg_write_verify_DEPENDENCIES) $(EXTRA_sg_write_verify_DEPENDENCIES) 
+	@rm -f sg_write_verify$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_write_verify_OBJECTS) $(sg_write_verify_LDADD) $(LIBS)
+
+sg_xcopy$(EXEEXT): $(sg_xcopy_OBJECTS) $(sg_xcopy_DEPENDENCIES) $(EXTRA_sg_xcopy_DEPENDENCIES) 
+	@rm -f sg_xcopy$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_xcopy_OBJECTS) $(sg_xcopy_LDADD) $(LIBS)
+
+sg_zone$(EXEEXT): $(sg_zone_OBJECTS) $(sg_zone_DEPENDENCIES) $(EXTRA_sg_zone_DEPENDENCIES) 
+	@rm -f sg_zone$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sg_zone_OBJECTS) $(sg_zone_LDADD) $(LIBS)
+
+sginfo$(EXEEXT): $(sginfo_OBJECTS) $(sginfo_DEPENDENCIES) $(EXTRA_sginfo_DEPENDENCIES) 
+	@rm -f sginfo$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sginfo_OBJECTS) $(sginfo_LDADD) $(LIBS)
+
+sgm_dd$(EXEEXT): $(sgm_dd_OBJECTS) $(sgm_dd_DEPENDENCIES) $(EXTRA_sgm_dd_DEPENDENCIES) 
+	@rm -f sgm_dd$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sgm_dd_OBJECTS) $(sgm_dd_LDADD) $(LIBS)
+
+sgp_dd$(EXEEXT): $(sgp_dd_OBJECTS) $(sgp_dd_DEPENDENCIES) $(EXTRA_sgp_dd_DEPENDENCIES) 
+	@rm -f sgp_dd$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(sgp_dd_OBJECTS) $(sgp_dd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_compare_and_write.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_copy_results.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_dd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_decode_sense.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_emc_trespass.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_format.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_get_config.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_get_lba_status.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_ident.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_inq.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_inq_data.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_logs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_luns.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_map.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_map26.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_modes.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_opcodes.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_persist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_prevent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_raw.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rbuf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rdac.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read_attr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read_block_limits.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read_buffer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read_long.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_readcap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_reassign.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_referrals.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rep_zones.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_requests.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_reset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_reset_wp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rmsn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rtpg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_safte.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sanitize.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_identify.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_phy_event.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_read_gplog.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sat_set_features.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_scan_linux.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_scan_win32.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_senddiag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_ses.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_ses_microcode.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_start.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_stpg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_sync.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_test_rwbuf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_timestamp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_turs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_unmap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_verify.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_vpd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_vpd_vendor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_wr_mode.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_buffer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_long.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_same.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_write_verify.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_xcopy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_zone.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sginfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sgm_dd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sgp_dd.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+	for dir in "$(DESTDIR)$(bindir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
+	clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \
+	ctags ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-binPROGRAMS \
+	install-data install-data-am install-dvi install-dvi-am \
+	install-exec install-exec-am install-html install-html-am \
+	install-info install-info-am install-man install-pdf \
+	install-pdf-am install-ps install-ps-am install-strip \
+	installcheck installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am uninstall-binPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/sg3_utils/src/sg_compare_and_write.c b/sg3_utils/src/sg_compare_and_write.c
new file mode 100644
index 0000000..c274403
--- /dev/null
+++ b/sg3_utils/src/sg_compare_and_write.c
@@ -0,0 +1,570 @@
+/*
+*  Copyright (c) 2012-2016, Kaminario Technologies LTD
+*  All rights reserved.
+*  Redistribution and use in source and binary forms, with or without
+*  modification, are permitted provided that the following conditions are met:
+*    * Redistributions of source code must retain the above copyright
+*        notice, this list of conditions and the following disclaimer.
+*    * Redistributions in binary form must reproduce the above copyright
+*        notice, this list of conditions and the following disclaimer in the
+*        documentation and/or other materials provided with the distribution.
+*    * Neither the name of the <organization> nor the
+*        names of its contributors may be used to endorse or promote products
+*        derived from this software without specific prior written permission.
+*
+*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+*  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+*  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+*  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+*  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This command performs a SCSI COMPARE AND WRITE. See SBC-3 at
+ * http://www.t10.org
+ *
+ */
+
+#ifndef __sun
+#define _XOPEN_SOURCE 500
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pt.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.14 20160121";
+
+#define DEF_BLOCK_SIZE 512
+#define DEF_NUM_BLOCKS (1)
+#define DEF_BLOCKS_PER_TRANSFER 8
+#define DEF_TIMEOUT_SECS 60
+
+#define COMPARE_AND_WRITE_OPCODE (0x89)
+#define COMPARE_AND_WRITE_CDB_SIZE (16)
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+
+#define ME "sg_compare_and_write: "
+
+static struct option long_options[] = {
+        {"dpo", no_argument, 0, 'd'},
+        {"fua", no_argument, 0, 'f'},
+        {"fua_nv", no_argument, 0, 'F'},
+        {"group", required_argument, 0, 'g'},
+        {"help", no_argument, 0, 'h'},
+        {"in", required_argument, 0, 'i'},
+        {"inc", required_argument, 0, 'C'},
+        {"inw", required_argument, 0, 'D'},
+        {"lba", required_argument, 0, 'l'},
+        {"num", required_argument, 0, 'n'},
+        {"quiet", no_argument, 0, 'q'},
+        {"timeout", required_argument, 0, 't'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {"wrprotect", required_argument, 0, 'w'},
+        {"xferlen", required_argument, 0, 'x'},
+        {0, 0, 0, 0},
+};
+
+struct caw_flags {
+        int dpo;
+        int fua;
+        int fua_nv;
+        int group;
+        int wrprotect;
+};
+
+struct opts_t {
+        const char * ifn;
+        const char * wfn;
+        int wfn_given;
+        uint64_t lba;
+        int numblocks;
+        int quiet;
+        int verbose;
+        int timeout;
+        int xfer_len;
+        const char * device_name;
+        struct caw_flags flags;
+};
+
+
+static void
+usage()
+{
+        pr2serr("Usage: sg_compare_and_write [--dpo] [--fua] [--fua_nv] "
+                "[--group=GN] [--help]\n"
+                "                            --in=IF [--inw=WF] --lba=LBA "
+                "[--num=NUM]\n"
+                "                            [--quiet] [--timeout=TO] "
+                "[--verbose] [--version]\n"
+                "                            [--wrpotect=WP] [--xferlen=LEN] "
+                "DEVICE\n"
+                "  where:\n"
+                "    --dpo|-d            set the dpo bit in cdb (def: "
+                "clear)\n"
+                "    --fua|-f            set the fua bit in cdb (def: "
+                "clear)\n"
+                "    --fua_nv|-F         set the fua_nv bit in cdb (def: "
+                "clear)\n"
+                "    --group=GN|-g GN    GN is GROUP NUMBER to set in "
+                "cdb (def: 0)\n"
+                "    --help|-h           print out usage message\n"
+                "    --in=IF|-i IF       IF is a file containing a compare "
+                "buffer and\n"
+                "                        optionally a write buffer (when "
+                "--inw=WF is\n"
+                "                        not given)\n"
+                "    --inw=WF|-D WF      WF is a file containing a write "
+                "buffer\n"
+                "    --lba=LBA|-l LBA    LBA of the first block to compare "
+                "and write\n"
+                "    --num=NUM|-n NUM    number of blocks to "
+                "compare/write (def: 1)\n"
+                "    --quiet|-q          suppress MISCOMPARE report to "
+                "stderr,\n"
+                "                        still sets exit status of 14\n"
+                "    --timeout=TO|-t TO    timeout for the command "
+                "(def: 60 secs)\n"
+                "    --verbose|-v        increase verbosity (use '-vv' for "
+                "more)\n"
+                "    --version|-V        print version string then exit\n"
+                "    --wrprotect=WP|-w WP    write protect information "
+                "(def: 0)\n"
+                "    --xferlen=LEN|-x LEN    number of bytes to transfer. "
+                "Default is\n"
+                "                            (2 * NUM * 512) or 1024 when "
+                "NUM is 1\n"
+                "\n"
+                "Performs a SCSI COMPARE AND WRITE operation.\n");
+}
+
+static int
+parse_args(int argc, char* argv[], struct opts_t * op)
+{
+        int c;
+        int lba_given = 0;
+        int if_given = 0;
+        int64_t ll;
+
+        op->numblocks = DEF_NUM_BLOCKS;
+        /* COMPARE AND WRITE defines 2*buffers compare + write */
+        op->xfer_len = 0;
+        op->timeout = DEF_TIMEOUT_SECS;
+        op->device_name = NULL;
+        while (1) {
+                int option_index = 0;
+
+                c = getopt_long(argc, argv, "C:dD:fFg:hi:l:n:qt:vVw:x:",
+                                long_options, &option_index);
+                if (c == -1)
+                        break;
+
+                switch (c) {
+                case 'C':
+                case 'i':
+                        op->ifn = optarg;
+                        if_given = 1;
+                        break;
+                case 'd':
+                        op->flags.dpo = 1;
+                        break;
+                case 'D':
+                        op->wfn = optarg;
+                        op->wfn_given = 1;
+                        break;
+                case 'F':
+                        op->flags.fua_nv = 1;
+                        break;
+                case 'f':
+                        op->flags.fua = 1;
+                        break;
+                case 'g':
+                        op->flags.group = sg_get_num(optarg);
+                        if ((op->flags.group < 0) ||
+                            (op->flags.group > 31))  {
+                                pr2serr("argument to '--group' expected to "
+                                        "be 0 to 31\n");
+                                goto out_err_no_usage;
+                        }
+                        break;
+                case 'h':
+                case '?':
+                        usage();
+                        exit(0);
+                case 'l':
+                        ll = sg_get_llnum(optarg);
+                        if (-1 == ll) {
+                                pr2serr("bad argument to '--lba'\n");
+                                goto out_err_no_usage;
+                        }
+                        op->lba = (uint64_t)ll;
+                        lba_given = 1;
+                        break;
+                case 'n':
+                        op->numblocks = sg_get_num(optarg);
+                        if ((op->numblocks < 0) || (op->numblocks > 255))  {
+                                pr2serr("bad argument to '--num', expect 0 "
+                                        "to 255\n");
+                                goto out_err_no_usage;
+                        }
+                        break;
+                case 'q':
+                        ++op->quiet;
+                        break;
+                case 't':
+                        op->timeout = sg_get_num(optarg);
+                        if (op->timeout < 0)  {
+                                pr2serr("bad argument to '--timeout'\n");
+                                goto out_err_no_usage;
+                        }
+                        break;
+                case 'v':
+                        ++op->verbose;
+                        break;
+                case 'V':
+                        pr2serr(ME "version: %s\n", version_str);
+                        exit(0);
+                case 'w':
+                        op->flags.wrprotect = sg_get_num(optarg);
+                        if (op->flags.wrprotect >> 3) {
+                                pr2serr("bad argument to '--wrprotect' not "
+                                        "in range 0-7\n");
+                                goto out_err_no_usage;
+                        }
+                        break;
+                case 'x':
+                        op->xfer_len = sg_get_num(optarg);
+                        if (op->xfer_len < 0) {
+                                pr2serr("bad argument to '--xferlen'\n");
+                                goto out_err_no_usage;
+                        }
+                        break;
+                default:
+                        pr2serr("unrecognised option code 0x%x ??\n", c);
+                        goto out_err;
+                }
+        }
+        if (optind < argc) {
+                if (NULL == op->device_name) {
+                        op->device_name = argv[optind];
+                        ++optind;
+                }
+                if (optind < argc) {
+                        for (; optind < argc; ++optind)
+                                pr2serr("Unexpected extra argument: %s\n",
+                                        argv[optind]);
+                        goto out_err;
+                }
+        }
+        if (NULL == op->device_name) {
+                pr2serr("missing device name!\n");
+                goto out_err;
+        }
+        if (!if_given) {
+                pr2serr("missing input file\n");
+                goto out_err;
+        }
+        if (!lba_given) {
+                pr2serr("missing lba\n");
+                goto out_err;
+        }
+        if (0 == op->xfer_len)
+            op->xfer_len = 2 * op->numblocks * DEF_BLOCK_SIZE;
+        return 0;
+
+out_err:
+        usage();
+
+out_err_no_usage:
+        exit(1);
+}
+
+#define FLAG_FUA        (0x8)
+#define FLAG_FUA_NV     (0x2)
+#define FLAG_DPO        (0x10)
+#define WRPROTECT_MASK  (0x7)
+#define WRPROTECT_SHIFT (5)
+
+static int
+sg_build_scsi_cdb(unsigned char * cdbp, unsigned int blocks,
+                  int64_t start_block, struct caw_flags flags)
+{
+        memset(cdbp, 0, COMPARE_AND_WRITE_CDB_SIZE);
+        cdbp[0] = COMPARE_AND_WRITE_OPCODE;
+        cdbp[1] = (flags.wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT;
+        if (flags.dpo)
+                cdbp[1] |= FLAG_DPO;
+        if (flags.fua)
+                cdbp[1] |= FLAG_FUA;
+        if (flags.fua_nv)
+                cdbp[1] |= FLAG_FUA_NV;
+        sg_put_unaligned_be64((uint64_t)start_block, cdbp + 2);
+        /* cdbp[10-12] are reserved */
+        cdbp[13] = (unsigned char)(blocks & 0xff);
+        cdbp[14] = (unsigned char)(flags.group & 0x1f);
+        return 0;
+}
+
+/* Returns 0 for success, SG_LIB_CAT_MISCOMPARE if compare fails,
+ * various other SG_LIB_CAT_*, otherwise -1 . */
+static int
+sg_compare_and_write(int sg_fd, unsigned char * buff, int blocks,
+                     int64_t lba, int xfer_len, struct caw_flags flags,
+                     int noisy, int verbose)
+{
+        int k, sense_cat, valid, slen, res, ret;
+        unsigned char cawCmd[COMPARE_AND_WRITE_CDB_SIZE];
+        unsigned char sense_b[SENSE_BUFF_LEN];
+        struct sg_pt_base * ptvp;
+        uint64_t ull = 0;
+
+        if (sg_build_scsi_cdb(cawCmd, blocks, lba, flags)) {
+                pr2serr(ME "bad cdb build, lba=0x%" PRIx64 ", blocks=%d\n",
+                        lba, blocks);
+                return -1;
+        }
+        ptvp = construct_scsi_pt_obj();
+        if (NULL == ptvp) {
+                pr2serr("Could not construct scsit_pt_obj, out of memory\n");
+                return -1;
+        }
+
+        set_scsi_pt_cdb(ptvp, cawCmd, COMPARE_AND_WRITE_CDB_SIZE);
+        set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+        set_scsi_pt_data_out(ptvp, buff, xfer_len);
+        if (verbose > 1) {
+                pr2serr("    Compare and write cdb: ");
+                for (k = 0; k < COMPARE_AND_WRITE_CDB_SIZE; ++k)
+                        pr2serr("%02x ", cawCmd[k]);
+                pr2serr("\n");
+        }
+        if ((verbose > 2) && (xfer_len > 0)) {
+                pr2serr("    Data-out buffer contents:\n");
+                dStrHexErr((const char *)buff, xfer_len, 1);
+        }
+        res = do_scsi_pt(ptvp, sg_fd, DEF_TIMEOUT_SECS, verbose);
+        ret = sg_cmds_process_resp(ptvp, "COMPARE AND WRITE", res, 0,
+                                   sense_b, noisy, verbose,
+                                   &sense_cat);
+        if (-1 == ret)
+                ;
+        else if (-2 == ret) {
+                switch (sense_cat) {
+                case SG_LIB_CAT_RECOVERED:
+                case SG_LIB_CAT_NO_SENSE:
+                        ret = 0;
+                        break;
+                case SG_LIB_CAT_MEDIUM_HARD:
+                        slen = get_scsi_pt_sense_len(ptvp);
+                        valid = sg_get_sense_info_fld(sense_b, slen,
+                                                      &ull);
+                        if (valid)
+                                pr2serr("Medium or hardware error starting "
+                                        "at lba=%" PRIu64 " [0x%" PRIx64
+                                        "]\n", ull, ull);
+                        else
+                                pr2serr("Medium or hardware error\n");
+                        ret = sense_cat;
+                        break;
+                case SG_LIB_CAT_MISCOMPARE:
+                        ret = sense_cat;
+                        if (! (noisy || verbose))
+                                break;
+                        slen = get_scsi_pt_sense_len(ptvp);
+                        valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                        if (valid)
+                                pr2serr("Miscompare at byte offset: %" PRIu64
+                                        " [0x%" PRIx64 "]\n", ull, ull);
+                        else
+                                pr2serr("Miscompare reported\n");
+                        break;
+                default:
+                        ret = sense_cat;
+                        break;
+                }
+        } else
+                ret = 0;
+
+        destruct_scsi_pt_obj(ptvp);
+        return ret;
+}
+
+static int
+open_if(const char * fn, int got_stdin)
+{
+        int fd;
+
+        if (got_stdin)
+                fd = STDIN_FILENO;
+        else {
+                fd = open(fn, O_RDONLY);
+                if (fd < 0) {
+                        pr2serr(ME "open error: %s: %s\n", fn,
+                                safe_strerror(errno));
+                        return -SG_LIB_FILE_ERROR;
+                }
+        }
+        if (sg_set_binary_mode(fd) < 0) {
+                perror("sg_set_binary_mode");
+                return -SG_LIB_FILE_ERROR;
+        }
+        return fd;
+}
+
+static int
+open_dev(const char * outf, int verbose)
+{
+        int sg_fd = sg_cmds_open_device(outf, 0 /* rw */, verbose);
+        if (sg_fd < 0) {
+                pr2serr(ME "open error: %s: %s\n", outf,
+                        safe_strerror(-sg_fd));
+                return -SG_LIB_FILE_ERROR;
+        }
+
+        return sg_fd;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+        int res, half_xlen, ifn_stdin;
+        int infd = -1;
+        int wfd = -1;
+        int devfd = -1;
+        unsigned char * wrkBuff = NULL;
+        struct opts_t opts;
+        struct opts_t * op;
+
+        op = &opts;
+        memset(op, 0, sizeof(opts));
+        res = parse_args(argc, argv, op);
+        if (res != 0) {
+                pr2serr("Failed parsing args\n");
+                goto out;
+        }
+
+        if (op->verbose) {
+                pr2serr("Running COMPARE AND WRITE command with the "
+                        "following options:\n  in=%s ", op->ifn);
+                if (op->wfn_given)
+                        pr2serr("inw=%s ", op->wfn);
+                pr2serr("device=%s\n  lba=0x%" PRIx64 " num_blocks=%d "
+                        "xfer_len=%d timeout=%d\n", op->device_name,
+                        op->lba, op->numblocks, op->xfer_len, op->timeout);
+        }
+        ifn_stdin = ((1 == strlen(op->ifn)) && ('-' == op->ifn[0]));
+        infd = open_if(op->ifn, ifn_stdin);
+        if (infd < 0) {
+                res = -infd;
+                goto out;
+        }
+        if (op->wfn_given) {
+                if ((1 == strlen(op->wfn)) && ('-' == op->wfn[0])) {
+                        pr2serr(ME "don't allow stdin for write file\n");
+                        res = SG_LIB_FILE_ERROR;
+                        goto out;
+                }
+                wfd = open_if(op->wfn, 0);
+                if (wfd < 0) {
+                        res = -wfd;
+                        goto out;
+                }
+        }
+
+        devfd = open_dev(op->device_name, op->verbose);
+        if (devfd < 0) {
+                res = -devfd;
+                goto out;
+        }
+
+        wrkBuff = (unsigned char *)malloc(op->xfer_len);
+        if (0 == wrkBuff) {
+                pr2serr("Not enough user memory\n");
+                res = SG_LIB_CAT_OTHER;
+                goto out;
+        }
+
+        if (op->wfn_given) {
+                half_xlen = op->xfer_len / 2;
+                res = read(infd, wrkBuff, half_xlen);
+                if (res < 0) {
+                        pr2serr("Could not read from %s", op->ifn);
+                        goto out;
+                } else if (res < half_xlen) {
+                        pr2serr("Read only %d bytes (expected %d) from %s\n",
+                                res, half_xlen, op->ifn);
+                        goto out;
+                }
+                res = read(wfd, wrkBuff + half_xlen, half_xlen);
+                if (res < 0) {
+                        pr2serr("Could not read from %s", op->wfn);
+                        goto out;
+                } else if (res < half_xlen) {
+                        pr2serr("Read only %d bytes (expected %d) from %s\n",
+                                res, half_xlen, op->wfn);
+                        goto out;
+                }
+        } else {
+                res = read(infd, wrkBuff, op->xfer_len);
+                if (res < 0) {
+                        pr2serr("Could not read from %s", op->ifn);
+                        goto out;
+                } else if (res < op->xfer_len) {
+                        pr2serr("Read only %d bytes (expected %d) from %s\n",
+                                res, op->xfer_len, op->ifn);
+                        goto out;
+                }
+        }
+        res = sg_compare_and_write(devfd, wrkBuff, op->numblocks, op->lba,
+                op->xfer_len, op->flags, !op->quiet, op->verbose);
+
+out:
+        if (0 != res) {
+                char b[80];
+
+                switch (res) {
+                case SG_LIB_CAT_MEDIUM_HARD:
+                case SG_LIB_CAT_MISCOMPARE:
+                case SG_LIB_FILE_ERROR:
+                        break;  /* already reported */
+                default:
+                        sg_get_category_sense_str(res, sizeof(b), b,
+                                                  op->verbose);
+                        pr2serr(ME "SCSI COMPARE AND WRITE: %s\n", b);
+                        break;
+                }
+        }
+
+        if (wrkBuff)
+                free(wrkBuff);
+        if ((infd >= 0) && (! ifn_stdin))
+                close(infd);
+        if (wfd >= 0)
+                close(wfd);
+        if (devfd >= 0)
+                close(devfd);
+        return res;
+}
diff --git a/sg3_utils/src/sg_copy_results.c b/sg3_utils/src/sg_copy_results.c
new file mode 100644
index 0000000..38d7f9a
--- /dev/null
+++ b/sg3_utils/src/sg_copy_results.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2011-2015 Hannes Reinecke, SUSE Labs
+ * 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+   *  Copyright (C) 2004-2010 D. Gilbert
+   *  This program is free software; you can redistribute it and/or modify
+   *  it under the terms of the GNU General Public License as published by
+   *  the Free Software Foundation; either version 2, or (at your option)
+   *  any later version.
+
+   This program issues the SCSI command RECEIVE COPY RESULTS to a given
+   SCSI device.
+   It sends the command with the service action passed as the sa argument,
+   and the optional list identifier passed as the list_id argument.
+*/
+
+static const char * version_str = "1.12 20150227";
+
+
+#define MAX_XFER_LEN 10000
+
+/* #define SG_DEBUG */
+
+#define ME "sg_copy_results: "
+
+#define EBUFF_SZ 256
+
+struct descriptor_type {
+    int code;
+    char desc[124];
+};
+
+struct descriptor_type target_descriptor_codes[] = {
+    { 0xe0, "Fibre Channel N_Port_Name"},
+    { 0xe1, "Fibre Channel N_port_ID"},
+    { 0xe2, "Fibre Channesl N_port_ID with N_Port_Name checking"},
+    { 0xe3, "Parallel Interface T_L" },
+    { 0xe4, "Identification descriptor" },
+    { 0xe5, "IPv4" },
+    { 0xe6, "Alias" },
+    { 0xe7, "RDMA" },
+    { 0xe8, "IEEE 1395 EUI-64" },
+    { 0xe9, "SAS Serial SCSI Protocol" },
+    { 0xea, "IPv6" },
+    { 0xeb, "IP Copy Service" },
+    { -1, "" }
+};
+
+struct descriptor_type segment_descriptor_codes [] = {
+    { 0x00, "Copy from block device to stream device" },
+    { 0x01, "Copy from stream device to block device" },
+    { 0x02, "Copy from block device to block device" },
+    { 0x03, "Copy from stream device to stream device" },
+    { 0x04, "Copy inline data to stream device" },
+    { 0x05, "Copy embedded data to stream device" },
+    { 0x06, "Read from stream device and discard" },
+    { 0x07, "Verify block or stream device operation" },
+    { 0x08, "Copy block device with offset to stream device" },
+    { 0x09, "Copy stream device to block device with offset" },
+    { 0x0A, "Copy block device with offset to block device with offset" },
+    { 0x0B, "Copy from block device to stream device "
+      "and hold a copy of processed data for the application client" },
+    { 0x0C, "Copy from stream device to block device "
+      "and hold a copy of processed data for the application client" },
+    { 0x0D, "Copy from block device to block device "
+      "and hold a copy of processed data for the application client" },
+    { 0x0E, "Copy from stream device to stream device "
+      "and hold a copy of processed data for the application client" },
+    { 0x0F, "Read from stream device "
+      "and hold a copy of processed data for the application client" },
+    { 0x10, "Write filemarks to sequential-access device" },
+    { 0x11, "Space records or filemarks on sequential-access device" },
+    { 0x12, "Locate on sequential-access device" },
+    { 0x13, "Image copy from sequential-access device to sequential-access "
+            "device" },
+    { 0x14, "Register persistent reservation key" },
+    { 0x15, "Third party persistent reservations source I_T nexus" },
+    { -1, "" }
+};
+
+
+static void
+scsi_failed_segment_details(unsigned char *rcBuff, unsigned int rcBuffLen)
+{
+    unsigned int len;
+    char senseBuff[1024];
+    int senseLen;
+
+    if (rcBuffLen < 4) {
+        pr2serr("  <<not enough data to procedd report>>\n");
+        return;
+    }
+    len = sg_get_unaligned_be32(rcBuff + 0);
+    if (len + 4 > rcBuffLen) {
+        pr2serr("  <<report len %d > %d too long for internal buffer, output "
+                "truncated\n", len, rcBuffLen);
+    }
+    if (len < 52) {
+        pr2serr("  <<no segment details, response data length %d\n", len);
+        return;
+    }
+    printf("Receive copy results (failed segment details):\n");
+    printf("    Extended copy command status: %d\n", rcBuff[56]);
+    senseLen = sg_get_unaligned_be16(rcBuff + 58);
+    sg_get_sense_str("    ", &rcBuff[60], senseLen, 0, 1024, senseBuff);
+    printf("%s", senseBuff);
+}
+
+static void
+scsi_copy_status(unsigned char *rcBuff, unsigned int rcBuffLen)
+{
+    unsigned int len;
+
+    if (rcBuffLen < 4) {
+        pr2serr("  <<not enough data to proceed report>>\n");
+        return;
+    }
+    len = sg_get_unaligned_be32(rcBuff + 0);
+    if (len + 4 > rcBuffLen) {
+        pr2serr("  <<report len %d > %d too long for internal buffer, output "
+                "truncated\n", len, rcBuffLen);
+    }
+    printf("Receive copy results (copy status):\n");
+    printf("    Held data discarded: %s\n", rcBuff[4] & 0x80 ? "Yes":"No");
+    printf("    Copy manager status: ");
+    switch (rcBuff[4] & 0x7f) {
+    case 0:
+        printf("Operation in progress\n");
+        break;
+    case 1:
+        printf("Operation completed without errors\n");
+        break;
+    case 2:
+        printf("Operation completed with errors\n");
+        break;
+    default:
+        printf("Unknown/Reserved\n");
+        break;
+    }
+    printf("    Segments processed: %u\n", sg_get_unaligned_be16(rcBuff + 5));
+    printf("    Transfer count units: %u\n", rcBuff[7]);
+    printf("    Transfer count: %u\n", sg_get_unaligned_be32(rcBuff + 8));
+}
+
+static void
+scsi_operating_parameters(unsigned char *rcBuff, unsigned int rcBuffLen)
+{
+    unsigned int len, n;
+
+    len = sg_get_unaligned_be32(rcBuff + 0);
+    if (len + 4 > rcBuffLen) {
+        pr2serr("  <<report len %d > %d too long for internal buffer, output "
+                "truncated\n", len, rcBuffLen);
+    }
+    printf("Receive copy results (report operating parameters):\n");
+    printf("    Supports no list identifier (SNLID): %s\n",
+           rcBuff[4] & 1 ? "yes" : "no");
+    n = sg_get_unaligned_be16(rcBuff + 8);
+    printf("    Maximum target descriptor count: %u\n", n);
+    n = sg_get_unaligned_be16(rcBuff + 10);
+    printf("    Maximum segment descriptor count: %u\n", n);
+    n = sg_get_unaligned_be32(rcBuff + 12);
+    printf("    Maximum descriptor list length: %u bytes\n", n);
+    n = sg_get_unaligned_be32(rcBuff + 16);
+    printf("    Maximum segment length: %u bytes\n", n);
+    n = sg_get_unaligned_be32(rcBuff + 20);
+    if (n == 0) {
+        printf("    Inline data not supported\n");
+    } else {
+        printf("    Maximum inline data length: %u bytes\n", n);
+    }
+    n = sg_get_unaligned_be32(rcBuff + 24);
+    printf("    Held data limit: %u bytes\n", n);
+    n = sg_get_unaligned_be32(rcBuff + 28);
+    printf("    Maximum stream device transfer size: %u bytes\n", n);
+    n = sg_get_unaligned_be16(rcBuff + 34);
+    printf("    Total concurrent copies: %u\n", n);
+    printf("    Maximum concurrent copies: %u\n", rcBuff[36]);
+    if (rcBuff[37] > 30)
+        printf("    Data segment granularity: 2**%u bytes\n", rcBuff[37]);
+    else
+        printf("    Data segment granularity: %u bytes\n",
+               (unsigned int)(1 << rcBuff[37]));
+    if (rcBuff[38] > 30)
+        printf("    Inline data granularity: %u bytes\n", rcBuff[38]);
+    else
+        printf("    Inline data granularity: %u bytes\n",
+               (unsigned int)(1 << rcBuff[38]));
+    if (rcBuff[39] > 30)
+        printf("    Held data granularity: 2**%u bytes\n", rcBuff[39]);
+    else
+        printf("    Held data granularity: %u bytes\n",
+               (unsigned int)(1 << rcBuff[39]));
+
+    printf("    Implemented descriptor list:\n");
+    for (n = 0; n < rcBuff[43]; n++) {
+        int code = rcBuff[44 + n];
+
+        if (code < 0x16) {
+            struct descriptor_type *seg_desc = segment_descriptor_codes;
+            while (strlen(seg_desc->desc)) {
+                if (seg_desc->code == code)
+                    break;
+                seg_desc++;
+            }
+            printf("        Segment descriptor 0x%02x: %s\n", code,
+                   strlen(seg_desc->desc) ? seg_desc->desc : "Reserved");
+        } else if (code < 0xc0) {
+            printf("        Segment descriptor 0x%02x: Reserved\n", code);
+        } else if (code < 0xe0) {
+            printf("        Vendor specific descriptor 0x%02x\n", code);
+        } else {
+            struct descriptor_type *tgt_desc = target_descriptor_codes;
+
+            while (strlen(tgt_desc->desc)) {
+                if (tgt_desc->code == code)
+                    break;
+                tgt_desc++;
+            }
+            printf("        Target descriptor 0x%02x: %s\n", code,
+                   strlen(tgt_desc->desc) ? tgt_desc->desc : "Reserved");
+        }
+    }
+    printf("\n");
+}
+
+static struct option long_options[] = {
+        {"failed", 0, 0, 'f'},
+        {"help", 0, 0, 'h'},
+        {"hex", 0, 0, 'H'},
+        {"list_id", 1, 0, 'l'},
+        {"params", 0, 0, 'p'},
+        {"readonly", 0, 0, 'R'},
+        {"receive", 0, 0, 'r'},
+        {"status", 0, 0, 's'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {"xfer_len", 1, 0, 'x'},
+        {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+  pr2serr("Usage: "
+          "sg_copy_results [--failed|--params|--receive|--status] [--help]\n"
+          "                       [--hex] [--list_id=ID] [--readonly] "
+          "[--verbose]\n"
+          "                       [--version] [--xfer_len=BTL] DEVICE\n"
+          "  where:\n"
+          "    --failed|-f          use FAILED SEGMENT DETAILS service "
+          "action\n"
+          "    --help|-h            print out usage message\n"
+          "    --hex|-H             print out response buffer in hex\n"
+          "    --list_id=ID|-l ID   list identifier (default: 0)\n"
+          "    --params|-p          use OPERATING PARAMETERS service "
+          "action\n"
+          "    --readonly|-R        open DEVICE read-only (def: read-write)\n"
+          "    --receive|-r         use RECEIVE DATA service action\n"
+          "    --status|-s          use COPY STATUS service action\n"
+          "    --verbose|-v         increase verbosity\n"
+          "    --version|-V         print version string then exit\n"
+          "    --xfer_len=BTL|-x BTL    byte transfer length (< 10000) "
+          "(default:\n"
+          "                             520 bytes)\n\n"
+          "Performs a SCSI RECEIVE COPY RESULTS command. Returns the "
+          "response as\nspecified by the service action parameters.\n"
+          );
+}
+
+static const char * rec_copy_name_arr[] = {
+    "Receive copy status(LID1)",
+    "Receive copy data(LID1)",
+    "Receive copy [0x2]",
+    "Receive copy operating parameters",
+    "Receive copy failure details(LID1)",
+};
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, k;
+    unsigned char * cpResultBuff = NULL;
+    int xfer_len = 520;
+    int sa = 3;
+    uint32_t list_id = 0;
+    int do_hex = 0;
+    int o_readonly = 0;
+    int verbose = 0;
+    const char * cp;
+    const char * device_name = NULL;
+    char file_name[256];
+    int ret = 1;
+
+    memset(file_name, 0, sizeof file_name);
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "fhHl:prRsvVx:", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'f':
+            sa = 4;
+            break;
+        case 'H':
+            do_hex = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'l':
+            k = sg_get_num(optarg);
+            if (-1 == k) {
+                pr2serr("bad argument to '--list_id'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            list_id = (uint32_t)k;
+            break;
+        case 'p':
+            sa = 3;
+            break;
+        case 'r':
+            sa = 1;
+            break;
+        case 'R':
+            ++o_readonly;
+            break;
+        case 's':
+            sa = 0;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        case 'x':
+            xfer_len = sg_get_num(optarg);
+            if (-1 == xfer_len) {
+                pr2serr("bad argument to '--xfer_len'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (xfer_len >= MAX_XFER_LEN) {
+        pr2serr("xfer_len (%d) is out of range ( < %d)\n", xfer_len,
+                MAX_XFER_LEN);
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (NULL == (cpResultBuff = (unsigned char *)malloc(xfer_len))) {
+            pr2serr(ME "out of memory\n");
+            return SG_LIB_FILE_ERROR;
+    }
+    memset(cpResultBuff, 0x00, xfer_len);
+
+    sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if ((sa < 0) || (sa >= (int)(sizeof(rec_copy_name_arr) / sizeof(char *))))
+        cp = "Out of range service action";
+    else
+        cp = rec_copy_name_arr[sa];
+    if (verbose)
+        pr2serr(ME "issue %s to device %s\n\t\txfer_len= %d (0x%x), list_id=%"
+                PRIu32 "\n", cp, device_name, xfer_len, xfer_len, list_id);
+
+    res = sg_ll_receive_copy_results(sg_fd, sa, list_id, cpResultBuff,
+                                     xfer_len, 1, verbose);
+    ret = res;
+    if (res) {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("  SCSI %s failed: %s\n", cp, b);
+        goto finish;
+    }
+    if (1 == do_hex) {
+        dStrHex((const char *)cpResultBuff, xfer_len, 1);
+        res = 0;
+        goto finish;
+    }
+    switch (sa) {
+    case 4: /* Failed segment details */
+        scsi_failed_segment_details(cpResultBuff, xfer_len);
+        res = 0;
+        break;
+    case 3: /* Operating parameters */
+        scsi_operating_parameters(cpResultBuff, xfer_len);
+        res = 0;
+        break;
+    case 0: /* Copy status */
+        scsi_copy_status(cpResultBuff, xfer_len);
+        res = 0;
+        break;
+    default:
+        dStrHex((const char *)cpResultBuff, xfer_len, 1);
+        res = 0;
+        break;
+    }
+
+finish:
+    free(cpResultBuff);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr(ME "close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_dd.c b/sg3_utils/src/sg_dd.c
new file mode 100644
index 0000000..b0370a5
--- /dev/null
+++ b/sg3_utils/src/sg_dd.c
@@ -0,0 +1,2202 @@
+/* A utility program for copying files. Specialised for "files" that
+ * represent devices that understand the SCSI command set.
+ *
+ * Copyright (C) 1999 - 2016 D. Gilbert and P. Allworth
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+
+   This program is a specialisation of the Unix "dd" command in which
+   either the input or the output file is a scsi generic device, raw
+   device, a block device or a normal file. The block size ('bs') is
+   assumed to be 512 if not given. This program complains if 'ibs' or
+   'obs' are given with a value that differs from 'bs' (or the default 512).
+   If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
+   not given or 'of=-' then stdout assumed.
+
+   A non-standard argument "bpt" (blocks per transfer) is added to control
+   the maximum number of blocks in each transfer. The default value is 128.
+   For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
+   in this case) is transferred to or from the sg device in a single SCSI
+   command. The actual size of the SCSI READ or WRITE command block can be
+   selected with the "cdbsz" argument.
+
+   This version is designed for the linux kernel 2.4, 2.6 and 3 series.
+*/
+
+#define _XOPEN_SOURCE 600
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <linux/major.h>
+#include <linux/fs.h>   /* <sys/mount.h> */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "5.86 20151219";
+
+
+#define ME "sg_dd: "
+
+/* #define SG_DEBUG */
+
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+#define EBUFF_SZ 512
+
+#define DEF_BLOCK_SIZE 512
+#define DEF_BLOCKS_PER_TRANSFER 128
+#define DEF_BLOCKS_PER_2048TRANSFER 32
+#define DEF_SCSI_CDBSZ 10
+#define MAX_SCSI_CDBSZ 16
+
+#define DEF_MODE_CDB_SZ 10
+#define DEF_MODE_RESP_LEN 252
+#define RW_ERR_RECOVERY_MP 1
+#define CACHING_MP 8
+#define CONTROL_MP 0xa
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define READ_CAP_REPLY_LEN 8
+#define RCAP16_REPLY_LEN 32
+#define READ_LONG_OPCODE 0x3E
+#define READ_LONG_CMD_LEN 10
+#define READ_LONG_DEF_BLK_INC 8
+
+#define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
+
+#ifndef RAW_MAJOR
+#define RAW_MAJOR 255   /*unlikey value */
+#endif
+
+#define SG_LIB_FLOCK_ERR 90
+
+#define FT_OTHER 1              /* filetype is probably normal */
+#define FT_SG 2                 /* filetype is sg char device or supports
+                                   SG_IO ioctl */
+#define FT_RAW 4                /* filetype is raw char device */
+#define FT_DEV_NULL 8           /* either "/dev/null" or "." as filename */
+#define FT_ST 16                /* filetype is st char device (tape) */
+#define FT_BLOCK 32             /* filetype is block device */
+#define FT_FIFO 64              /* filetype is a fifo (name pipe) */
+#define FT_ERROR 128            /* couldn't "stat" file */
+
+#define DEV_NULL_MINOR_NUM 3
+
+/* If platform does not support O_DIRECT then define it harmlessly */
+#ifndef O_DIRECT
+#define O_DIRECT 0
+#endif
+
+#define MIN_RESERVED_SIZE 8192
+
+#define MAX_UNIT_ATTENTIONS 10
+#define MAX_ABORTED_CMDS 256
+
+static int sum_of_resids = 0;
+
+static int64_t dd_count = -1;
+static int64_t req_count = 0;
+static int64_t in_full = 0;
+static int in_partial = 0;
+static int64_t out_full = 0;
+static int out_partial = 0;
+static int64_t out_sparse = 0;
+static int recovered_errs = 0;
+static int unrecovered_errs = 0;
+static int read_longs = 0;
+static int num_retries = 0;
+
+static int do_time = 0;
+static int verbose = 0;
+static int start_tm_valid = 0;
+static struct timeval start_tm;
+static int blk_sz = 0;
+static int max_uas = MAX_UNIT_ATTENTIONS;
+static int max_aborted = MAX_ABORTED_CMDS;
+static int coe_limit = 0;
+static int coe_count = 0;
+
+static unsigned char * zeros_buff = NULL;
+static int read_long_blk_inc = READ_LONG_DEF_BLK_INC;
+
+static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
+
+struct flags_t {
+    int append;
+    int cdbsz;
+    int coe;
+    int dio;
+    int direct;
+    int dpo;
+    int dsync;
+    int excl;
+    int fua;
+    int flock;
+    int nocache;
+    int sgio;
+    int pdt;
+    int sparse;
+    int retries;
+};
+
+static struct flags_t iflag;
+static struct flags_t oflag;
+
+static void calc_duration_throughput(int contin);
+
+
+static void
+install_handler(int sig_num, void (*sig_handler) (int sig))
+{
+    struct sigaction sigact;
+    sigaction (sig_num, NULL, &sigact);
+    if (sigact.sa_handler != SIG_IGN)
+    {
+        sigact.sa_handler = sig_handler;
+        sigemptyset (&sigact.sa_mask);
+        sigact.sa_flags = 0;
+        sigaction (sig_num, &sigact, NULL);
+    }
+}
+
+
+static void
+print_stats(const char * str)
+{
+    if (0 != dd_count)
+        pr2serr("  remaining block count=%" PRId64 "\n", dd_count);
+    pr2serr("%s%" PRId64 "+%d records in\n", str, in_full - in_partial,
+            in_partial);
+    pr2serr("%s%" PRId64 "+%d records out\n", str, out_full - out_partial,
+            out_partial);
+    if (oflag.sparse)
+        pr2serr("%s%" PRId64 " bypassed records out\n", str, out_sparse);
+    if (recovered_errs > 0)
+        pr2serr("%s%d recovered errors\n", str, recovered_errs);
+    if (num_retries > 0)
+        pr2serr("%s%d retries attempted\n", str, num_retries);
+    if (iflag.coe || oflag.coe) {
+        pr2serr("%s%d unrecovered errors\n", str, unrecovered_errs);
+        pr2serr("%s%d read_longs fetched part of unrecovered read errors\n",
+                str, read_longs);
+    } else if (unrecovered_errs)
+        pr2serr("%s%d unrecovered error(s)\n", str, unrecovered_errs);
+}
+
+
+static void
+interrupt_handler(int sig)
+{
+    struct sigaction sigact;
+
+    sigact.sa_handler = SIG_DFL;
+    sigemptyset(&sigact.sa_mask);
+    sigact.sa_flags = 0;
+    sigaction(sig, &sigact, NULL);
+    pr2serr("Interrupted by signal,");
+    if (do_time)
+        calc_duration_throughput(0);
+    print_stats("");
+    kill(getpid (), sig);
+}
+
+
+static void
+siginfo_handler(int sig)
+{
+    if (sig) { ; }      /* unused, dummy to suppress warning */
+    pr2serr("Progress report, continuing ...\n");
+    if (do_time)
+        calc_duration_throughput(1);
+    print_stats("  ");
+}
+
+static int bsg_major_checked = 0;
+static int bsg_major = 0;
+
+static void
+find_bsg_major(void)
+{
+    const char * proc_devices = "/proc/devices";
+    FILE *fp;
+    char a[128];
+    char b[128];
+    char * cp;
+    int n;
+
+    if (NULL == (fp = fopen(proc_devices, "r"))) {
+        if (verbose)
+            pr2serr("fopen %s failed: %s\n", proc_devices, strerror(errno));
+        return;
+    }
+    while ((cp = fgets(b, sizeof(b), fp))) {
+        if ((1 == sscanf(b, "%126s", a)) &&
+            (0 == memcmp(a, "Character", 9)))
+            break;
+    }
+    while (cp && (cp = fgets(b, sizeof(b), fp))) {
+        if (2 == sscanf(b, "%d %126s", &n, a)) {
+            if (0 == strcmp("bsg", a)) {
+                bsg_major = n;
+                break;
+            }
+        } else
+            break;
+    }
+    if (verbose > 5) {
+        if (cp)
+            pr2serr("found bsg_major=%d\n", bsg_major);
+        else
+            pr2serr("found no bsg char device in %s\n", proc_devices);
+    }
+    fclose(fp);
+}
+
+
+static int
+dd_filetype(const char * filename)
+{
+    struct stat st;
+    size_t len = strlen(filename);
+
+    if ((1 == len) && ('.' == filename[0]))
+        return FT_DEV_NULL;
+    if (stat(filename, &st) < 0)
+        return FT_ERROR;
+    if (S_ISCHR(st.st_mode)) {
+        /* major() and minor() defined in sys/sysmacros.h */
+        if ((MEM_MAJOR == major(st.st_rdev)) &&
+            (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
+            return FT_DEV_NULL;
+        if (RAW_MAJOR == major(st.st_rdev))
+            return FT_RAW;
+        if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
+            return FT_SG;
+        if (SCSI_TAPE_MAJOR == major(st.st_rdev))
+            return FT_ST;
+        if (! bsg_major_checked) {
+            bsg_major_checked = 1;
+            find_bsg_major();
+        }
+        if (bsg_major == (int)major(st.st_rdev))
+            return FT_SG;
+    } else if (S_ISBLK(st.st_mode))
+        return FT_BLOCK;
+    else if (S_ISFIFO(st.st_mode))
+        return FT_FIFO;
+    return FT_OTHER;
+}
+
+
+static char *
+dd_filetype_str(int ft, char * buff)
+{
+    int off = 0;
+
+    if (FT_DEV_NULL & ft)
+        off += snprintf(buff + off, 32, "null device ");
+    if (FT_SG & ft)
+        off += snprintf(buff + off, 32, "SCSI generic (sg) device ");
+    if (FT_BLOCK & ft)
+        off += snprintf(buff + off, 32, "block device ");
+    if (FT_FIFO & ft)
+        off += snprintf(buff + off, 32, "fifo (named pipe) ");
+    if (FT_ST & ft)
+        off += snprintf(buff + off, 32, "SCSI tape device ");
+    if (FT_RAW & ft)
+        off += snprintf(buff + off, 32, "raw device ");
+    if (FT_OTHER & ft)
+        off += snprintf(buff + off, 32, "other (perhaps ordinary file) ");
+    if (FT_ERROR & ft)
+        off += snprintf(buff + off, 32, "unable to 'stat' file ");
+    return buff;
+}
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_dd  [bs=BS] [count=COUNT] [ibs=BS] [if=IFILE] "
+            "[iflag=FLAGS]\n"
+            "              [obs=BS] [of=OFILE] [oflag=FLAGS] "
+            "[seek=SEEK] [skip=SKIP]\n"
+            "              [--help] [--version]\n\n"
+            "              [blk_sgio=0|1] [bpt=BPT] [cdbsz=6|10|12|16] "
+            "[coe=0|1|2|3]\n"
+            "              [coe_limit=CL] [dio=0|1] [odir=0|1] "
+            "[of2=OFILE2] [retries=RETR]\n"
+            "              [sync=0|1] [time=0|1] [verbose=VERB]\n"
+            "  where:\n"
+            "    blk_sgio    0->block device use normal I/O(def), 1->use "
+            "SG_IO\n"
+            "    bpt         is blocks_per_transfer (default is 128 or 32 "
+            "when BS>=2048)\n"
+            "    bs          block size (default is 512)\n");
+    pr2serr("    cdbsz       size of SCSI READ or WRITE cdb (default is "
+            "10)\n"
+            "    coe         0->exit on error (def), 1->continue on sg "
+            "error (zero\n"
+            "                fill), 2->also try read_long on unrecovered "
+            "reads,\n"
+            "                3->and set the CORRCT bit on the read long\n"
+            "    coe_limit   limit consecutive 'bad' blocks on reads to CL "
+            "times\n"
+            "                when COE>1 (default: 0 which is no limit)\n"
+            "    count       number of blocks to copy (def: device size)\n"
+            "    dio         for direct IO, 1->attempt, 0->indirect IO (def)\n"
+            "    ibs         input block size (if given must be same as "
+            "'bs=')\n"
+            "    if          file or device to read from (def: stdin)\n"
+            "    iflag       comma separated list from: [coe,dio,direct,"
+            "dpo,dsync,excl,\n"
+            "                flock,fua,nocache,null,sgio]\n"
+            "    obs         output block size (if given must be same as "
+            "'bs=')\n"
+            "    odir        1->use O_DIRECT when opening block dev, "
+            "0->don't(def)\n"
+            "    of          file or device to write to (def: stdout), "
+            "OFILE of '.'\n");
+    pr2serr("                treated as /dev/null\n"
+            "    of2         additional output file (def: /dev/null), "
+            "OFILE2 should be\n"
+            "                normal file or pipe\n"
+            "    oflag       comma separated list from: [append,coe,dio,"
+            "direct,dpo,\n"
+            "                dsync,excl,flock,fua,nocache,null,sgio,"
+            "sparse]\n"
+            "    retries     retry sgio errors RETR times (def: 0)\n"
+            "    seek        block position to start writing to OFILE\n"
+            "    skip        block position to start reading from IFILE\n"
+            "    sync        0->no sync(def), 1->SYNCHRONIZE CACHE on "
+            "OFILE after copy\n"
+            "    time        0->no timing(def), 1->time plus calculate "
+            "throughput\n"
+            "    verbose     0->quiet(def), 1->some noise, 2->more noise, "
+            "etc\n"
+            "    --help      print out this usage message then exit\n"
+            "    --version   print version information then exit\n\n"
+            "copy from IFILE to OFILE, similar to dd command; "
+            "specialized for SCSI devices\n");
+}
+
+
+/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
+static int
+scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
+{
+    int res;
+    unsigned int ui;
+    unsigned char rcBuff[RCAP16_REPLY_LEN];
+    int verb;
+
+    verb = (verbose ? verbose - 1: 0);
+    res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, 1, verb);
+    if (0 != res)
+        return res;
+
+    if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
+        (0xff == rcBuff[3])) {
+        int64_t ls;
+
+        res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, 1,
+                               verb);
+        if (0 != res)
+            return res;
+        ls = (int64_t)sg_get_unaligned_be64(rcBuff);
+        *num_sect = ls + 1;
+        *sect_sz = (int)sg_get_unaligned_be32(rcBuff + 8);
+    } else {
+        ui = sg_get_unaligned_be32(rcBuff);
+        /* take care not to sign extend values > 0x7fffffff */
+        *num_sect = (int64_t)ui + 1;
+        *sect_sz = (int)sg_get_unaligned_be32(rcBuff + 4);
+    }
+    if (verbose)
+        pr2serr("      number of blocks=%" PRId64 " [0x%" PRIx64 "], "
+                "block size=%d\n", *num_sect, *num_sect, *sect_sz);
+    return 0;
+}
+
+
+/* Return of 0 -> success, -1 -> failure. BLKGETSIZE64, BLKGETSIZE and */
+/* BLKSSZGET macros problematic (from <linux/fs.h> or <sys/mount.h>). */
+static int
+read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
+{
+#ifdef BLKSSZGET
+    if ((ioctl(sg_fd, BLKSSZGET, sect_sz) < 0) && (*sect_sz > 0)) {
+        perror("BLKSSZGET ioctl error");
+        return -1;
+    } else {
+ #ifdef BLKGETSIZE64
+        uint64_t ull;
+
+        if (ioctl(sg_fd, BLKGETSIZE64, &ull) < 0) {
+
+            perror("BLKGETSIZE64 ioctl error");
+            return -1;
+        }
+        *num_sect = ((int64_t)ull / (int64_t)*sect_sz);
+        if (verbose)
+            pr2serr("      [bgs64] number of blocks=%" PRId64 " [0x%" PRIx64
+                    "], block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ #else
+        unsigned long ul;
+
+        if (ioctl(sg_fd, BLKGETSIZE, &ul) < 0) {
+            perror("BLKGETSIZE ioctl error");
+            return -1;
+        }
+        *num_sect = (int64_t)ul;
+        if (verbose)
+            pr2serr("      [bgs] number of blocks=%" PRId64 " [0x%" PRIx64
+                    "],  block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ #endif
+    }
+    return 0;
+#else
+    if (verbose)
+        pr2serr("      BLKSSZGET+BLKGETSIZE ioctl not available\n");
+    *num_sect = 0;
+    *sect_sz = 0;
+    return -1;
+#endif
+}
+
+
+static int
+sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
+                  int64_t start_block, int write_true, int fua, int dpo)
+{
+    int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+    int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
+    int sz_ind;
+
+    memset(cdbp, 0, cdb_sz);
+    if (dpo)
+        cdbp[1] |= 0x10;
+    if (fua)
+        cdbp[1] |= 0x8;
+    switch (cdb_sz) {
+    case 6:
+        sz_ind = 0;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
+        cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
+        if (blocks > 256) {
+            pr2serr(ME "for 6 byte commands, maximum number of blocks is "
+                    "256\n");
+            return 1;
+        }
+        if ((start_block + blocks - 1) & (~0x1fffff)) {
+            pr2serr(ME "for 6 byte commands, can't address blocks beyond "
+                    "%d\n", 0x1fffff);
+            return 1;
+        }
+        if (dpo || fua) {
+            pr2serr(ME "for 6 byte commands, neither dpo nor fua bits "
+                    "supported\n");
+            return 1;
+        }
+        break;
+    case 10:
+        sz_ind = 1;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be32(start_block, cdbp + 2);
+        sg_put_unaligned_be16(blocks, cdbp + 7);
+        if (blocks & (~0xffff)) {
+            pr2serr(ME "for 10 byte commands, maximum number of blocks is "
+                    "%d\n", 0xffff);
+            return 1;
+        }
+        break;
+    case 12:
+        sz_ind = 2;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be32(start_block, cdbp + 2);
+        sg_put_unaligned_be32(blocks, cdbp + 6);
+        break;
+    case 16:
+        sz_ind = 3;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be64(start_block, cdbp + 2);
+        sg_put_unaligned_be32(blocks, cdbp + 10);
+        break;
+    default:
+        pr2serr(ME "expected cdb size of 6, 10, 12, or 16 but got %d\n",
+                cdb_sz);
+        return 1;
+    }
+    return 0;
+}
+
+
+/* 0 -> successful, SG_LIB_SYNTAX_ERROR -> unable to build cdb,
+   SG_LIB_CAT_UNIT_ATTENTION -> try again,
+   SG_LIB_CAT_MEDIUM_HARD_WITH_INFO -> 'io_addrp' written to,
+   SG_LIB_CAT_MEDIUM_HARD -> no info field,
+   SG_LIB_CAT_NOT_READY, SG_LIB_CAT_ABORTED_COMMAND,
+   -2 -> ENOMEM
+   -1 other errors */
+static int
+sg_read_low(int sg_fd, unsigned char * buff, int blocks, int64_t from_block,
+            int bs, const struct flags_t * ifp, int * diop,
+            uint64_t * io_addrp)
+{
+    unsigned char rdCmd[MAX_SCSI_CDBSZ];
+    unsigned char senseBuff[SENSE_BUFF_LEN];
+    const unsigned char * sbp;
+    struct sg_io_hdr io_hdr;
+    int res, k, info_valid, slen;
+
+    if (sg_build_scsi_cdb(rdCmd, ifp->cdbsz, blocks, from_block, 0,
+                          ifp->fua, ifp->dpo)) {
+        pr2serr(ME "bad rd cdb build, from_block=%" PRId64 ", blocks=%d\n",
+                from_block, blocks);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = ifp->cdbsz;
+    io_hdr.cmdp = rdCmd;
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = bs * blocks;
+    io_hdr.dxferp = buff;
+    io_hdr.mx_sb_len = SENSE_BUFF_LEN;
+    io_hdr.sbp = senseBuff;
+    io_hdr.timeout = DEF_TIMEOUT;
+    io_hdr.pack_id = (int)from_block;
+    if (diop && *diop)
+        io_hdr.flags |= SG_FLAG_DIRECT_IO;
+
+    if (verbose > 2) {
+        pr2serr("    read cdb: ");
+        for (k = 0; k < ifp->cdbsz; ++k)
+            pr2serr("%02x ", rdCmd[k]);
+        pr2serr("\n");
+    }
+    while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        if (ENOMEM == errno)
+            return -2;
+        perror("reading (SG_IO) on sg device, error");
+        return -1;
+    }
+    if (verbose > 2)
+        pr2serr("      duration=%u ms\n", io_hdr.duration);
+    res = sg_err_category3(&io_hdr);
+    sbp = io_hdr.sbp;
+    slen = io_hdr.sb_len_wr;
+    switch (res) {
+    case SG_LIB_CAT_CLEAN:
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        ++recovered_errs;
+        info_valid = sg_get_sense_info_fld(sbp, slen, io_addrp);
+        if (info_valid) {
+            pr2serr("    lba of last recovered error in this READ=0x%" PRIx64
+                    "\n", *io_addrp);
+            if (verbose > 1)
+                sg_chk_n_print3("reading", &io_hdr, 1);
+        } else {
+            pr2serr("Recovered error: [no info] reading from block=0x%" PRIx64
+                    ", num=%d\n", from_block, blocks);
+            sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+        }
+        break;
+    case SG_LIB_CAT_ABORTED_COMMAND:
+    case SG_LIB_CAT_UNIT_ATTENTION:
+        sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+        return res;
+    case SG_LIB_CAT_MEDIUM_HARD:
+        if (verbose > 1)
+            sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+        ++unrecovered_errs;
+        info_valid = sg_get_sense_info_fld(sbp, slen, io_addrp);
+        /* MMC devices don't necessarily set VALID bit */
+        if ((info_valid) || ((5 == ifp->pdt) && (*io_addrp > 0)))
+            return SG_LIB_CAT_MEDIUM_HARD_WITH_INFO;
+        else {
+            pr2serr("Medium, hardware or blank check error but no lba of "
+                    "failure in sense\n");
+            return res;
+        }
+        break;
+    case SG_LIB_CAT_NOT_READY:
+        ++unrecovered_errs;
+        if (verbose > 0)
+            sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+        return res;
+    case SG_LIB_CAT_ILLEGAL_REQ:
+        if (5 == ifp->pdt) {    /* MMC READs can go down this path */
+            struct sg_scsi_sense_hdr ssh;
+            int ili;
+
+            if (verbose > 1)
+                sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+            if (sg_scsi_normalize_sense(sbp, slen, &ssh) &&
+                (0x64 == ssh.asc) && (0x0 == ssh.ascq)) {
+                if (sg_get_sense_filemark_eom_ili(sbp, slen, NULL, NULL,
+                                                  &ili) && ili) {
+                    info_valid = sg_get_sense_info_fld(sbp, slen, io_addrp);
+                    if (*io_addrp > 0) {
+                        ++unrecovered_errs;
+                        return SG_LIB_CAT_MEDIUM_HARD_WITH_INFO;
+                    } else
+                        pr2serr("MMC READ gave 'illegal mode for this track' "
+                                "and ILI but no LBA of failure\n");
+                }
+                ++unrecovered_errs;
+                return SG_LIB_CAT_MEDIUM_HARD;
+            }
+        }
+        /* drop through */
+    default:
+        ++unrecovered_errs;
+        if (verbose > 0)
+            sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+        return res;
+    }
+    if (diop && *diop &&
+        ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+        *diop = 0;      /* flag that dio not done (completely) */
+    sum_of_resids += io_hdr.resid;
+    return 0;
+}
+
+
+/* 0 -> successful, SG_LIB_SYNTAX_ERROR -> unable to build cdb,
+   SG_LIB_CAT_UNIT_ATTENTION -> try again, SG_LIB_CAT_NOT_READY,
+   SG_LIB_CAT_MEDIUM_HARD, SG_LIB_CAT_ABORTED_COMMAND,
+   -2 -> ENOMEM, -1 other errors */
+static int
+sg_read(int sg_fd, unsigned char * buff, int blocks, int64_t from_block,
+        int bs, struct flags_t * ifp, int * diop, int * blks_readp)
+{
+    uint64_t io_addr;
+    int64_t lba;
+    int res, blks, repeat, xferred;
+    unsigned char * bp;
+    int retries_tmp;
+    int ret = 0;
+    int may_coe = 0;
+
+    retries_tmp = ifp->retries;
+    for (xferred = 0, blks = blocks, lba = from_block, bp = buff;
+         blks > 0; blks = blocks - xferred) {
+        io_addr = 0;
+        repeat = 0;
+        may_coe = 0;
+        res = sg_read_low(sg_fd, bp, blks, lba, bs, ifp, diop, &io_addr);
+        switch (res) {
+        case 0:
+            if (blks_readp)
+                *blks_readp = xferred + blks;
+            if (coe_limit > 0)
+                coe_count = 0;  /* good read clears coe_count */
+            return 0;
+        case -2:        /* ENOMEM */
+            return res;
+        case SG_LIB_CAT_NOT_READY:
+            pr2serr("Device (r) not ready\n");
+            return res;
+        case SG_LIB_CAT_ABORTED_COMMAND:
+            if (--max_aborted > 0) {
+                pr2serr("Aborted command, continuing (r)\n");
+                repeat = 1;
+            } else {
+                pr2serr("Aborted command, too many (r)\n");
+                return res;
+            }
+            break;
+        case SG_LIB_CAT_UNIT_ATTENTION:
+            if (--max_uas > 0) {
+                pr2serr("Unit attention, continuing (r)\n");
+                repeat = 1;
+            } else {
+                pr2serr("Unit attention, too many (r)\n");
+                return res;
+            }
+            break;
+        case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO:
+            if (retries_tmp > 0) {
+                pr2serr(">>> retrying a sgio read, lba=0x%" PRIx64 "\n",
+                        (uint64_t)lba);
+                --retries_tmp;
+                ++num_retries;
+                if (unrecovered_errs > 0)
+                    --unrecovered_errs;
+                repeat = 1;
+            }
+            ret = SG_LIB_CAT_MEDIUM_HARD;
+            break; /* unrecovered read error at lba=io_addr */
+        case SG_LIB_SYNTAX_ERROR:
+            ifp->coe = 0;
+            ret = res;
+            goto err_out;
+        case -1:
+            ret = res;
+            goto err_out;
+        case SG_LIB_CAT_MEDIUM_HARD:
+            may_coe = 1;
+        default:
+            if (retries_tmp > 0) {
+                pr2serr(">>> retrying a sgio read, lba=0x%" PRIx64 "\n",
+                        (uint64_t)lba);
+                --retries_tmp;
+                ++num_retries;
+                if (unrecovered_errs > 0)
+                    --unrecovered_errs;
+                repeat = 1;
+                break;
+            }
+            ret = res;
+            goto err_out;
+        }
+        if (repeat)
+            continue;
+        if ((io_addr < (uint64_t)lba) ||
+            (io_addr >= (uint64_t)(lba + blks))) {
+                pr2serr("  Unrecovered error lba 0x%" PRIx64 " not in "
+                        "correct range:\n\t[0x%" PRIx64 ",0x%" PRIx64 "]\n",
+                        io_addr, (uint64_t)lba,
+                        (uint64_t)(lba + blks - 1));
+            may_coe = 1;
+            goto err_out;
+        }
+        blks = (int)(io_addr - (uint64_t)lba);
+        if (blks > 0) {
+            if (verbose)
+                pr2serr("  partial read of %d blocks prior to medium error\n",
+                        blks);
+            res = sg_read_low(sg_fd, bp, blks, lba, bs, ifp, diop, &io_addr);
+            switch (res) {
+            case 0:
+                break;
+            case -1:
+                ifp->coe = 0;
+                ret = res;
+                goto err_out;
+            case -2:
+                pr2serr("ENOMEM again, unexpected (r)\n");
+                return -1;
+            case SG_LIB_CAT_NOT_READY:
+                pr2serr("device (r) not ready\n");
+                return res;
+            case SG_LIB_CAT_UNIT_ATTENTION:
+                pr2serr("Unit attention, unexpected (r)\n");
+                return res;
+            case SG_LIB_CAT_ABORTED_COMMAND:
+                pr2serr("Aborted command, unexpected (r)\n");
+                return res;
+            case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO:
+            case SG_LIB_CAT_MEDIUM_HARD:
+                ret = SG_LIB_CAT_MEDIUM_HARD;
+                goto err_out;
+            case SG_LIB_SYNTAX_ERROR:
+            default:
+                pr2serr(">> unexpected result=%d from sg_read_low() 2\n",
+                        res);
+                ret = res;
+                goto err_out;
+            }
+        }
+        xferred += blks;
+        if (0 == ifp->coe) {
+            /* give up at block before problem unless 'coe' */
+            if (blks_readp)
+                *blks_readp = xferred;
+            return ret;
+        }
+        if (bs < 32) {
+            pr2serr(">> bs=%d too small for read_long\n", bs);
+            return -1;  /* nah, block size can't be that small */
+        }
+        bp += (blks * bs);
+        lba += blks;
+        if ((0 != ifp->pdt) || (ifp->coe < 2)) {
+            pr2serr(">> unrecovered read error at blk=%" PRId64 ", pdt=%d, "
+                    "use zeros\n", lba, ifp->pdt);
+            memset(bp, 0, bs);
+        } else if (io_addr < UINT_MAX) {
+            unsigned char * buffp;
+            int offset, nl, r, ok, corrct;
+
+            buffp = (unsigned char*)malloc(bs * 2);
+            if (NULL == buffp) {
+                pr2serr(">> heap problems\n");
+                return -1;
+            }
+            corrct = (ifp->coe > 2) ? 1 : 0;
+            res = sg_ll_read_long10(sg_fd, /* pblock */0, corrct, lba, buffp,
+                                    bs + read_long_blk_inc, &offset, 1,
+                                    verbose);
+            ok = 0;
+            switch (res) {
+            case 0:
+                ok = 1;
+                ++read_longs;
+                break;
+            case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:
+                nl = bs + read_long_blk_inc - offset;
+                if ((nl < 32) || (nl > (bs * 2))) {
+                    pr2serr(">> read_long(10) len=%d unexpected\n", nl);
+                    break;
+                }
+                /* remember for next read_long attempt, if required */
+                read_long_blk_inc = nl - bs;
+
+                if (verbose)
+                    pr2serr("read_long(10): adjusted len=%d\n", nl);
+                r = sg_ll_read_long10(sg_fd, 0, corrct, lba, buffp, nl,
+                                      &offset, 1, verbose);
+                if (0 == r) {
+                    ok = 1;
+                    ++read_longs;
+                    break;
+                } else
+                    pr2serr(">> unexpected result=%d on second "
+                            "read_long(10)\n", r);
+                break;
+            case SG_LIB_CAT_INVALID_OP:
+                pr2serr(">> read_long(10); not supported\n");
+                break;
+            case SG_LIB_CAT_ILLEGAL_REQ:
+                pr2serr(">> read_long(10): bad cdb field\n");
+                break;
+            case SG_LIB_CAT_NOT_READY:
+                pr2serr(">> read_long(10): device not ready\n");
+                break;
+            case SG_LIB_CAT_UNIT_ATTENTION:
+                pr2serr(">> read_long(10): unit attention\n");
+                break;
+            case SG_LIB_CAT_ABORTED_COMMAND:
+                pr2serr(">> read_long(10): aborted command\n");
+                break;
+            default:
+                pr2serr(">> read_long(10): problem (%d)\n", res);
+                break;
+            }
+            if (ok)
+                memcpy(bp, buffp, bs);
+            else
+                memset(bp, 0, bs);
+            free(buffp);
+        } else {
+            pr2serr(">> read_long(10) cannot handle blk=%" PRId64 ", use "
+                    "zeros\n", lba);
+            memset(bp, 0, bs);
+        }
+        ++xferred;
+        bp += bs;
+        ++lba;
+        if ((coe_limit > 0) && (++coe_count > coe_limit)) {
+            if (blks_readp)
+                *blks_readp = xferred + blks;
+            pr2serr(">> coe_limit on consecutive reads exceeded\n");
+            return SG_LIB_CAT_MEDIUM_HARD;
+        }
+    }
+    if (blks_readp)
+        *blks_readp = xferred;
+    return 0;
+
+err_out:
+    if (ifp->coe) {
+        memset(bp, 0, bs * blks);
+        pr2serr(">> unable to read at blk=%" PRId64 " for %d bytes, use "
+                "zeros\n", lba, bs * blks);
+        if (blks > 1)
+            pr2serr(">>   try reducing bpt to limit number of zeros written "
+                    "near bad block(s)\n");
+        /* fudge success */
+        if (blks_readp)
+            *blks_readp = xferred + blks;
+        if ((coe_limit > 0) && (++coe_count > coe_limit)) {
+            pr2serr(">> coe_limit on consecutive reads exceeded\n");
+            return ret;
+        }
+        return may_coe ? 0 : ret;
+    } else
+        return ret ? ret : -1;
+}
+
+
+/* 0 -> successful, SG_LIB_SYNTAX_ERROR -> unable to build cdb,
+   SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_CAT_MEDIUM_HARD,
+   SG_LIB_CAT_ABORTED_COMMAND, -2 -> recoverable (ENOMEM),
+   -1 -> unrecoverable error + others */
+static int
+sg_write(int sg_fd, unsigned char * buff, int blocks, int64_t to_block,
+         int bs, const struct flags_t * ofp, int * diop)
+{
+    unsigned char wrCmd[MAX_SCSI_CDBSZ];
+    unsigned char senseBuff[SENSE_BUFF_LEN];
+    struct sg_io_hdr io_hdr;
+    int res, k, info_valid;
+    uint64_t io_addr = 0;
+
+    if (sg_build_scsi_cdb(wrCmd, ofp->cdbsz, blocks, to_block, 1, ofp->fua,
+                          ofp->dpo)) {
+        pr2serr(ME "bad wr cdb build, to_block=%" PRId64 ", blocks=%d\n",
+                to_block, blocks);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = ofp->cdbsz;
+    io_hdr.cmdp = wrCmd;
+    io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+    io_hdr.dxfer_len = bs * blocks;
+    io_hdr.dxferp = buff;
+    io_hdr.mx_sb_len = SENSE_BUFF_LEN;
+    io_hdr.sbp = senseBuff;
+    io_hdr.timeout = DEF_TIMEOUT;
+    io_hdr.pack_id = (int)to_block;
+    if (diop && *diop)
+        io_hdr.flags |= SG_FLAG_DIRECT_IO;
+
+    if (verbose > 2) {
+        pr2serr("    write cdb: ");
+        for (k = 0; k < ofp->cdbsz; ++k)
+            pr2serr("%02x ", wrCmd[k]);
+        pr2serr("\n");
+    }
+    while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        if (ENOMEM == errno)
+            return -2;
+        perror("writing (SG_IO) on sg device, error");
+        return -1;
+    }
+
+    if (verbose > 2)
+        pr2serr("      duration=%u ms\n", io_hdr.duration);
+    res = sg_err_category3(&io_hdr);
+    switch (res) {
+    case SG_LIB_CAT_CLEAN:
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        ++recovered_errs;
+        info_valid = sg_get_sense_info_fld(io_hdr.sbp, io_hdr.sb_len_wr,
+                                           &io_addr);
+        if (info_valid) {
+            pr2serr("    lba of last recovered error in this WRITE=0x%" PRIx64
+                    "\n", io_addr);
+            if (verbose > 1)
+                sg_chk_n_print3("writing", &io_hdr, 1);
+        } else {
+            pr2serr("Recovered error: [no info] writing to block=0x%" PRIx64
+                    ", num=%d\n", to_block, blocks);
+            sg_chk_n_print3("writing", &io_hdr, verbose > 1);
+        }
+        break;
+    case SG_LIB_CAT_ABORTED_COMMAND:
+    case SG_LIB_CAT_UNIT_ATTENTION:
+        sg_chk_n_print3("writing", &io_hdr, verbose > 1);
+        return res;
+    case SG_LIB_CAT_NOT_READY:
+        ++unrecovered_errs;
+        pr2serr("device not ready (w)\n");
+        return res;
+    case SG_LIB_CAT_MEDIUM_HARD:
+    default:
+        sg_chk_n_print3("writing", &io_hdr, verbose > 1);
+        ++unrecovered_errs;
+        if (ofp->coe) {
+            pr2serr(">> ignored errors for out blk=%" PRId64 " for %d "
+                    "bytes\n", to_block, bs * blocks);
+            return 0; /* fudge success */
+        } else
+            return res;
+    }
+    if (diop && *diop &&
+        ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+        *diop = 0;      /* flag that dio not done (completely) */
+    return 0;
+}
+
+
+static void
+calc_duration_throughput(int contin)
+{
+    struct timeval end_tm, res_tm;
+    double a, b;
+    int64_t blks;
+
+    if (start_tm_valid && (start_tm.tv_sec || start_tm.tv_usec)) {
+        blks = (in_full > out_full) ? in_full : out_full;
+        gettimeofday(&end_tm, NULL);
+        res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+        res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+        if (res_tm.tv_usec < 0) {
+            --res_tm.tv_sec;
+            res_tm.tv_usec += 1000000;
+        }
+        a = res_tm.tv_sec;
+        a += (0.000001 * res_tm.tv_usec);
+        b = (double)blk_sz * blks;
+        pr2serr("time to transfer data%s: %d.%06d secs",
+                (contin ? " so far" : ""), (int)res_tm.tv_sec,
+                (int)res_tm.tv_usec);
+        if ((a > 0.00001) && (b > 511))
+            pr2serr(" at %.2f MB/sec\n", b / (a * 1000000.0));
+        else
+            pr2serr("\n");
+    }
+}
+
+/* Process arguments given to 'iflag=" or 'oflag=" options. Returns 0
+ * on success, 1 on error. */
+static int
+process_flags(const char * arg, struct flags_t * fp)
+{
+    char buff[256];
+    char * cp;
+    char * np;
+
+    strncpy(buff, arg, sizeof(buff));
+    buff[sizeof(buff) - 1] = '\0';
+    if ('\0' == buff[0]) {
+        pr2serr("no flag found\n");
+        return 1;
+    }
+    cp = buff;
+    do {
+        np = strchr(cp, ',');
+        if (np)
+            *np++ = '\0';
+        if (0 == strcmp(cp, "append"))
+            fp->append = 1;
+        else if (0 == strcmp(cp, "coe"))
+            ++fp->coe;
+        else if (0 == strcmp(cp, "dio"))
+            fp->dio = 1;
+        else if (0 == strcmp(cp, "direct"))
+            fp->direct = 1;
+        else if (0 == strcmp(cp, "dpo"))
+            fp->dpo = 1;
+        else if (0 == strcmp(cp, "dsync"))
+            ++fp->dsync;
+        else if (0 == strcmp(cp, "excl"))
+            fp->excl = 1;
+        else if (0 == strcmp(cp, "fua"))
+            ++fp->fua;
+        else if (0 == strcmp(cp, "nocache"))
+            ++fp->nocache;
+        else if (0 == strcmp(cp, "null"))
+            ;
+        else if (0 == strcmp(cp, "sgio"))
+            fp->sgio = 1;
+        else if (0 == strcmp(cp, "sparse"))
+            ++fp->sparse;
+        else if (0 == strcmp(cp, "flock"))
+            ++fp->flock;
+        else {
+            pr2serr("unrecognised flag: %s\n", cp);
+            return 1;
+        }
+        cp = np;
+    } while (cp);
+    return 0;
+}
+
+/* Process arguments given to 'conv=" option. Returns 0 on success,
+ * 1 on error. */
+static int
+process_conv(const char * arg, struct flags_t * ifp, struct flags_t * ofp)
+{
+    char buff[256];
+    char * cp;
+    char * np;
+
+    strncpy(buff, arg, sizeof(buff));
+    buff[sizeof(buff) - 1] = '\0';
+    if ('\0' == buff[0]) {
+        pr2serr("no conversions found\n");
+        return 1;
+    }
+    cp = buff;
+    do {
+        np = strchr(cp, ',');
+        if (np)
+            *np++ = '\0';
+#if 0
+        if (0 == strcmp(cp, "fdatasync"))
+            ++ofp->fdatasync;
+        else if (0 == strcmp(cp, "fsync"))
+            ++ofp->fsync;
+#endif
+        if (0 == strcmp(cp, "noerror"))
+            ++ifp->coe;         /* will still fail on write error */
+        else if (0 == strcmp(cp, "notrunc"))
+            ;         /* this is the default action of ddpt so ignore */
+        else if (0 == strcmp(cp, "null"))
+            ;
+#if 0
+        else if (0 == strcmp(cp, "sparing"))
+            ++ofp->sparing;
+#endif
+        else if (0 == strcmp(cp, "sparse"))
+            ++ofp->sparse;
+        else if (0 == strcmp(cp, "sync"))
+            ;   /* dd(susv4): pad errored block(s) with zeros but ddpt does
+                 * that by default. Typical dd use: 'conv=noerror,sync' */
+#if 0
+        else if (0 == strcmp(cp, "trunc"))
+            ++ofp->trunc;
+#endif
+        else {
+            pr2serr("unrecognised flag: %s\n", cp);
+            return 1;
+        }
+        cp = np;
+    } while (cp);
+    return 0;
+}
+
+/* Returns open input file descriptor (>= 0) or a negative value
+ * (-SG_LIB_FILE_ERROR or -SG_LIB_CAT_OTHER) if error.
+ */
+static int
+open_if(const char * inf, int64_t skip, int bpt, struct flags_t * ifp,
+        int * in_typep, int verbose)
+{
+    int infd, flags, fl, t, verb, res;
+    char ebuff[EBUFF_SZ];
+    struct sg_simple_inquiry_resp sir;
+
+    verb = (verbose ? verbose - 1: 0);
+    *in_typep = dd_filetype(inf);
+    if (verbose)
+        pr2serr(" >> Input file type: %s\n",
+                dd_filetype_str(*in_typep, ebuff));
+    if (FT_ERROR & *in_typep) {
+        pr2serr(ME "unable access %s\n", inf);
+        goto file_err;
+    } else if ((FT_BLOCK & *in_typep) && ifp->sgio)
+        *in_typep |= FT_SG;
+
+    if (FT_ST & *in_typep) {
+        pr2serr(ME "unable to use scsi tape device %s\n", inf);
+        goto file_err;
+    } else if (FT_SG & *in_typep) {
+        flags = O_NONBLOCK;
+        if (ifp->direct)
+            flags |= O_DIRECT;
+        if (ifp->excl)
+            flags |= O_EXCL;
+        if (ifp->dsync)
+            flags |= O_SYNC;
+        fl = O_RDWR;
+        if ((infd = open(inf, fl | flags)) < 0) {
+            fl = O_RDONLY;
+            if ((infd = open(inf, fl | flags)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for sg reading", inf);
+                perror(ebuff);
+                goto file_err;
+            }
+        }
+        if (verbose)
+            pr2serr("        open input(sg_io), flags=0x%x\n", fl | flags);
+        if (sg_simple_inquiry(infd, &sir, 0, verb)) {
+            pr2serr("INQUIRY failed on %s\n", inf);
+            goto other_err;
+        }
+        ifp->pdt = sir.peripheral_type;
+        if (verbose)
+            pr2serr("    %s: %.8s  %.16s  %.4s  [pdt=%d]\n", inf, sir.vendor,
+                    sir.product, sir.revision, ifp->pdt);
+        if (! (FT_BLOCK & *in_typep)) {
+            t = blk_sz * bpt;
+            res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
+            if (res < 0)
+                perror(ME "SG_SET_RESERVED_SIZE error");
+            res = ioctl(infd, SG_GET_VERSION_NUM, &t);
+            if ((res < 0) || (t < 30000)) {
+                if (FT_BLOCK & *in_typep)
+                    pr2serr(ME "SG_IO unsupported on this block device\n");
+                else
+                    pr2serr(ME "sg driver prior to 3.x.y\n");
+                goto file_err;
+            }
+        }
+    } else {
+        flags = O_RDONLY;
+        if (ifp->direct)
+            flags |= O_DIRECT;
+        if (ifp->excl)
+            flags |= O_EXCL;
+        if (ifp->dsync)
+            flags |= O_SYNC;
+        infd = open(inf, flags);
+        if (infd < 0) {
+            snprintf(ebuff, EBUFF_SZ,
+                     ME "could not open %s for reading", inf);
+            perror(ebuff);
+            goto file_err;
+        } else {
+            if (verbose)
+                pr2serr("        open input, flags=0x%x\n", flags);
+            if (skip > 0) {
+                off64_t offset = skip;
+
+                offset *= blk_sz;       /* could exceed 32 bits here! */
+                if (lseek64(infd, offset, SEEK_SET) < 0) {
+                    snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
+                             "required position on %s", inf);
+                    perror(ebuff);
+                    goto file_err;
+                }
+                if (verbose)
+                    pr2serr("  >> skip: lseek64 SEEK_SET, byte offset=0x%"
+                            PRIx64 "\n", (uint64_t)offset);
+            }
+#ifdef HAVE_POSIX_FADVISE
+            if (ifp->nocache) {
+                int rt;
+
+                rt = posix_fadvise(infd, 0, 0, POSIX_FADV_SEQUENTIAL);
+                if (rt)
+                    pr2serr("open_if: posix_fadvise(SEQUENTIAL), err=%d\n",
+                            rt);
+            }
+#endif
+        }
+    }
+    if (ifp->flock) {
+        res = flock(infd, LOCK_EX | LOCK_NB);
+        if (res < 0) {
+            close(infd);
+            snprintf(ebuff, EBUFF_SZ, ME "flock(LOCK_EX | LOCK_NB) on %s "
+                     "failed", inf);
+            perror(ebuff);
+            return -SG_LIB_FLOCK_ERR;
+        }
+    }
+    return infd;
+
+file_err:
+    return -SG_LIB_FILE_ERROR;
+other_err:
+    return -SG_LIB_CAT_OTHER;
+}
+
+/* Returns open output file descriptor (>= 0), -1 for don't
+ * bother opening (e.g. /dev/null), or a more negative value
+ * (-SG_LIB_FILE_ERROR or -SG_LIB_CAT_OTHER) if error.
+ */
+static int
+open_of(const char * outf, int64_t seek, int bpt, struct flags_t * ofp,
+        int * out_typep, int verbose)
+{
+    int outfd, flags, t, verb, res;
+    char ebuff[EBUFF_SZ];
+    struct sg_simple_inquiry_resp sir;
+
+    verb = (verbose ? verbose - 1: 0);
+    *out_typep = dd_filetype(outf);
+    if (verbose)
+        pr2serr(" >> Output file type: %s\n",
+                dd_filetype_str(*out_typep, ebuff));
+
+    if ((FT_BLOCK & *out_typep) && ofp->sgio)
+        *out_typep |= FT_SG;
+
+    if (FT_ST & *out_typep) {
+        pr2serr(ME "unable to use scsi tape device %s\n", outf);
+        goto file_err;
+    } else if (FT_SG & *out_typep) {
+        flags = O_RDWR | O_NONBLOCK;
+        if (ofp->direct)
+            flags |= O_DIRECT;
+        if (ofp->excl)
+            flags |= O_EXCL;
+        if (ofp->dsync)
+            flags |= O_SYNC;
+        if ((outfd = open(outf, flags)) < 0) {
+            snprintf(ebuff, EBUFF_SZ,
+                     ME "could not open %s for sg writing", outf);
+            perror(ebuff);
+            goto file_err;
+        }
+        if (verbose)
+            pr2serr("        open output(sg_io), flags=0x%x\n", flags);
+        if (sg_simple_inquiry(outfd, &sir, 0, verb)) {
+            pr2serr("INQUIRY failed on %s\n", outf);
+            goto other_err;
+        }
+        ofp->pdt = sir.peripheral_type;
+        if (verbose)
+            pr2serr("    %s: %.8s  %.16s  %.4s  [pdt=%d]\n", outf, sir.vendor,
+                    sir.product, sir.revision, ofp->pdt);
+        if (! (FT_BLOCK & *out_typep)) {
+            t = blk_sz * bpt;
+            res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t);
+            if (res < 0)
+                perror(ME "SG_SET_RESERVED_SIZE error");
+            res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
+            if ((res < 0) || (t < 30000)) {
+                pr2serr(ME "sg driver prior to 3.x.y\n");
+                goto file_err;
+            }
+        }
+    } else if (FT_DEV_NULL & *out_typep)
+        outfd = -1; /* don't bother opening */
+    else {
+        if (! (FT_RAW & *out_typep)) {
+            flags = O_WRONLY | O_CREAT;
+            if (ofp->direct)
+                flags |= O_DIRECT;
+            if (ofp->excl)
+                flags |= O_EXCL;
+            if (ofp->dsync)
+                flags |= O_SYNC;
+            if (ofp->append)
+                flags |= O_APPEND;
+            if ((outfd = open(outf, flags, 0666)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                        ME "could not open %s for writing", outf);
+                perror(ebuff);
+                goto file_err;
+            }
+        } else {
+            flags = O_WRONLY;
+            if (ofp->direct)
+                flags |= O_DIRECT;
+            if (ofp->excl)
+                flags |= O_EXCL;
+            if (ofp->dsync)
+                flags |= O_SYNC;
+            if ((outfd = open(outf, flags)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                        ME "could not open %s for raw writing", outf);
+                perror(ebuff);
+                goto file_err;
+            }
+        }
+        if (verbose)
+            pr2serr("        %s output, flags=0x%x\n",
+                    ((O_CREAT & flags) ? "create" : "open"), flags);
+        if (seek > 0) {
+            off64_t offset = seek;
+
+            offset *= blk_sz;       /* could exceed 32 bits here! */
+            if (lseek64(outfd, offset, SEEK_SET) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                    ME "couldn't seek to required position on %s", outf);
+                perror(ebuff);
+                goto file_err;
+            }
+            if (verbose)
+                pr2serr("   >> seek: lseek64 SEEK_SET, byte offset=0x%" PRIx64
+                        "\n", (uint64_t)offset);
+        }
+    }
+    if (ofp->flock) {
+        res = flock(outfd, LOCK_EX | LOCK_NB);
+        if (res < 0) {
+            close(outfd);
+            snprintf(ebuff, EBUFF_SZ, ME "flock(LOCK_EX | LOCK_NB) on %s "
+                     "failed", outf);
+            perror(ebuff);
+            return -SG_LIB_FLOCK_ERR;
+        }
+    }
+    return outfd;
+
+file_err:
+    return -SG_LIB_FILE_ERROR;
+other_err:
+    return -SG_LIB_CAT_OTHER;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int64_t skip = 0;
+    int64_t seek = 0;
+    int64_t out2_off = 0;
+    int ibs = 0;
+    int obs = 0;
+    int bpt = DEF_BLOCKS_PER_TRANSFER;
+    int bpt_given = 0;
+    char str[STR_SZ];
+    char * key;
+    char * buf;
+    char inf[INOUTF_SZ];
+    int in_type = FT_OTHER;
+    char outf[INOUTF_SZ];
+    char out2f[INOUTF_SZ];
+    int out_type = FT_OTHER;
+    int out2_type = FT_OTHER;
+    int dio_incomplete = 0;
+    int cdbsz_given = 0;
+    int do_sync = 0;
+    int blocks = 0;
+    int res, k, t, buf_sz, dio_tmp, first, blocks_per;
+    int infd, outfd, out2fd, retries_tmp, blks_read;
+    int bytes_read, bytes_of2, bytes_of;
+    unsigned char * wrkBuff;
+    unsigned char * wrkPos;
+    int64_t in_num_sect = -1;
+    int64_t out_num_sect = -1;
+    int in_sect_sz, out_sect_sz;
+    char ebuff[EBUFF_SZ];
+    int sparse_skip = 0;
+    int penult_sparse_skip = 0;
+    int penult_blocks = 0;
+    int ret = 0;
+
+    inf[0] = '\0';
+    outf[0] = '\0';
+    out2f[0] = '\0';
+    iflag.cdbsz = DEF_SCSI_CDBSZ;
+    oflag.cdbsz = DEF_SCSI_CDBSZ;
+    if (argc < 2) {
+        pr2serr("Won't default both IFILE to stdin _and_ OFILE to stdout\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    for (k = 1; k < argc; k++) {
+        if (argv[k]) {
+            strncpy(str, argv[k], STR_SZ);
+            str[STR_SZ - 1] = '\0';
+        } else
+            continue;
+        for (key = str, buf = key; *buf && *buf != '=';)
+            buf++;
+        if (*buf)
+            *buf++ = '\0';
+        if (0 == strncmp(key, "app", 3)) {
+            iflag.append = sg_get_num(buf);
+            oflag.append = iflag.append;
+        } else if (0 == strcmp(key, "blk_sgio")) {
+            iflag.sgio = sg_get_num(buf);
+            oflag.sgio = iflag.sgio;
+        } else if (0 == strcmp(key, "bpt")) {
+            bpt = sg_get_num(buf);
+            if (-1 == bpt) {
+                pr2serr(ME "bad argument to 'bpt='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            bpt_given = 1;
+        } else if (0 == strcmp(key, "bs")) {
+            blk_sz = sg_get_num(buf);
+            if (-1 == blk_sz) {
+                pr2serr(ME "bad argument to 'bs='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "cdbsz")) {
+            iflag.cdbsz = sg_get_num(buf);
+            oflag.cdbsz = iflag.cdbsz;
+            cdbsz_given = 1;
+        } else if (0 == strcmp(key, "coe")) {
+            iflag.coe = sg_get_num(buf);
+            oflag.coe = iflag.coe;
+        } else if (0 == strcmp(key, "coe_limit")) {
+            coe_limit = sg_get_num(buf);
+            if (-1 == coe_limit) {
+                pr2serr(ME "bad argument to 'coe_limit='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "conv")) {
+            if (process_conv(buf, &iflag, &oflag)) {
+                pr2serr(ME "bad argument to 'conv='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "count")) {
+            if (0 != strcmp("-1", buf)) {
+                dd_count = sg_get_llnum(buf);
+                if (-1LL == dd_count) {
+                    pr2serr(ME "bad argument to 'count='\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }   /* treat 'count=-1' as calculate count (same as not given) */
+        } else if (0 == strcmp(key, "dio")) {
+            oflag.dio = sg_get_num(buf);
+            iflag.dio = oflag.dio;
+        } else if (0 == strcmp(key, "fua")) {
+            t = sg_get_num(buf);
+            oflag.fua = (t & 1) ? 1 : 0;
+            iflag.fua = (t & 2) ? 1 : 0;
+        } else if (0 == strcmp(key, "ibs"))
+            ibs = sg_get_num(buf);
+        else if (strcmp(key, "if") == 0) {
+            if ('\0' != inf[0]) {
+                pr2serr("Second IFILE argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(inf, buf, INOUTF_SZ);
+        } else if (0 == strcmp(key, "iflag")) {
+            if (process_flags(buf, &iflag)) {
+                pr2serr(ME "bad argument to 'iflag='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "obs"))
+            obs = sg_get_num(buf);
+        else if (0 == strcmp(key, "odir")) {
+            iflag.direct = sg_get_num(buf);
+            oflag.direct = iflag.direct;
+        } else if (strcmp(key, "of") == 0) {
+            if ('\0' != outf[0]) {
+                pr2serr("Second OFILE argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(outf, buf, INOUTF_SZ);
+        } else if (strcmp(key, "of2") == 0) {
+            if ('\0' != out2f[0]) {
+                pr2serr("Second OFILE2 argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(out2f, buf, INOUTF_SZ);
+        } else if (0 == strcmp(key, "oflag")) {
+            if (process_flags(buf, &oflag)) {
+                pr2serr(ME "bad argument to 'oflag='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "retries")) {
+            iflag.retries = sg_get_num(buf);
+            oflag.retries = iflag.retries;
+            if (-1 == iflag.retries) {
+                pr2serr(ME "bad argument to 'retries='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "seek")) {
+            seek = sg_get_llnum(buf);
+            if (-1LL == seek) {
+                pr2serr(ME "bad argument to 'seek='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "skip")) {
+            skip = sg_get_llnum(buf);
+            if (-1LL == skip) {
+                pr2serr(ME "bad argument to 'skip='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "sync"))
+            do_sync = sg_get_num(buf);
+        else if (0 == strcmp(key, "time"))
+            do_time = sg_get_num(buf);
+        else if (0 == strncmp(key, "verb", 4))
+            verbose = sg_get_num(buf);
+        else if ((0 == strncmp(key, "--help", 7)) ||
+                 (0 == strncmp(key, "-h", 2)) ||
+                 (0 == strcmp(key, "-?"))) {
+            usage();
+            return 0;
+        } else if ((0 == strncmp(key, "--vers", 6)) ||
+                   (0 == strcmp(key, "-V"))) {
+            pr2serr(ME "%s\n", version_str);
+            return 0;
+        } else {
+            pr2serr("Unrecognized option '%s'\n", key);
+            pr2serr("For more information use '--help'\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (blk_sz <= 0) {
+        blk_sz = DEF_BLOCK_SIZE;
+        pr2serr("Assume default 'bs' (block size) of %d bytes\n", blk_sz);
+    }
+    if ((ibs && (ibs != blk_sz)) || (obs && (obs != blk_sz))) {
+        pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((skip < 0) || (seek < 0)) {
+        pr2serr("skip and seek cannot be negative\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((oflag.append > 0) && (seek > 0)) {
+        pr2serr("Can't use both append and seek switches\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (bpt < 1) {
+        pr2serr("bpt must be greater than 0\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (iflag.sparse)
+        pr2serr("sparse flag ignored for iflag\n");
+
+    /* defaulting transfer size to 128*2048 for CD/DVDs is too large
+       for the block layer in lk 2.6 and results in an EIO on the
+       SG_IO ioctl. So reduce it in that case. */
+    if ((blk_sz >= 2048) && (0 == bpt_given))
+        bpt = DEF_BLOCKS_PER_2048TRANSFER;
+#ifdef SG_DEBUG
+    pr2serr(ME "if=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%" PRId64
+            "\n", inf, skip, outf, seek, dd_count);
+#endif
+    install_handler(SIGINT, interrupt_handler);
+    install_handler(SIGQUIT, interrupt_handler);
+    install_handler(SIGPIPE, interrupt_handler);
+    install_handler(SIGUSR1, siginfo_handler);
+
+    infd = STDIN_FILENO;
+    outfd = STDOUT_FILENO;
+    iflag.pdt = -1;
+    oflag.pdt = -1;
+    if (inf[0] && ('-' != inf[0])) {
+        infd = open_if(inf, skip, bpt, &iflag, &in_type, verbose);
+        if (infd < 0)
+            return -infd;
+    }
+
+    if (outf[0] && ('-' != outf[0])) {
+        outfd = open_of(outf, seek, bpt, &oflag, &out_type, verbose);
+        if (outfd < -1)
+            return -outfd;
+    }
+
+    if (out2f[0]) {
+        out2_type = dd_filetype(out2f);
+        if ((out2fd = open(out2f, O_WRONLY | O_CREAT, 0666)) < 0) {
+            res = errno;
+            snprintf(ebuff, EBUFF_SZ,
+                     ME "could not open %s for writing", out2f);
+            perror(ebuff);
+            return res;
+        }
+    } else
+        out2fd = -1;
+
+    if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
+        pr2serr("Can't have both 'if' as stdin _and_ 'of' as stdout\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (oflag.sparse) {
+        if (STDOUT_FILENO == outfd) {
+            pr2serr("oflag=sparse needs seekable output file\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if ((dd_count < 0) || ((verbose > 0) && (0 == dd_count))) {
+        in_num_sect = -1;
+        in_sect_sz = -1;
+        if (FT_SG & in_type) {
+            res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
+            if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+                pr2serr("Unit attention (readcap in), continuing\n");
+                res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
+            } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
+                pr2serr("Aborted command (readcap in), continuing\n");
+                res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
+            }
+            if (0 != res) {
+                if (res == SG_LIB_CAT_INVALID_OP)
+                    pr2serr("read capacity not supported on %s\n", inf);
+                else if (res == SG_LIB_CAT_NOT_READY)
+                    pr2serr("read capacity failed on %s - not ready\n", inf);
+                else
+                    pr2serr("Unable to read capacity on %s\n", inf);
+                in_num_sect = -1;
+            } else if (in_sect_sz != blk_sz)
+                pr2serr(">> warning: block size on %s confusion: bs=%d, "
+                        "device claims=%d\n", inf, blk_sz, in_sect_sz);
+        } else if (FT_BLOCK & in_type) {
+            if (0 != read_blkdev_capacity(infd, &in_num_sect, &in_sect_sz)) {
+                pr2serr("Unable to read block capacity on %s\n", inf);
+                in_num_sect = -1;
+            }
+            if (blk_sz != in_sect_sz) {
+                pr2serr("block size on %s confusion: bs=%d, device "
+                        "claims=%d\n", inf, blk_sz, in_sect_sz);
+                in_num_sect = -1;
+            }
+        }
+        if (in_num_sect > skip)
+            in_num_sect -= skip;
+
+        out_num_sect = -1;
+        out_sect_sz = -1;
+        if (FT_SG & out_type) {
+            res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
+            if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+                pr2serr("Unit attention (readcap out), continuing\n");
+                res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
+            } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
+                pr2serr("Aborted command (readcap out), continuing\n");
+                res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
+            }
+            if (0 != res) {
+                if (res == SG_LIB_CAT_INVALID_OP)
+                    pr2serr("read capacity not supported on %s\n", outf);
+                else
+                    pr2serr("Unable to read capacity on %s\n", outf);
+                out_num_sect = -1;
+            } else if (blk_sz != out_sect_sz)
+                pr2serr(">> warning: block size on %s confusion: bs=%d, "
+                        "device claims=%d\n", outf, blk_sz, out_sect_sz);
+        } else if (FT_BLOCK & out_type) {
+            if (0 != read_blkdev_capacity(outfd, &out_num_sect,
+                                          &out_sect_sz)) {
+                pr2serr("Unable to read block capacity on %s\n", outf);
+                out_num_sect = -1;
+            } else if (blk_sz != out_sect_sz) {
+                pr2serr("block size on %s confusion: bs=%d, device "
+                        "claims=%d\n", outf, blk_sz, out_sect_sz);
+                out_num_sect = -1;
+            }
+        }
+        if (out_num_sect > seek)
+            out_num_sect -= seek;
+#ifdef SG_DEBUG
+        pr2serr("Start of loop, count=%" PRId64 ", in_num_sect=%" PRId64
+                ", out_num_sect=%" PRId64 "\n", dd_count, in_num_sect,
+                out_num_sect);
+#endif
+        if (dd_count < 0) {
+            if (in_num_sect > 0) {
+                if (out_num_sect > 0)
+                    dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
+                                                           in_num_sect;
+                else
+                    dd_count = in_num_sect;
+            } else
+                dd_count = out_num_sect;
+        }
+    }
+
+    if (dd_count < 0) {
+        pr2serr("Couldn't calculate count, please give one\n");
+        return SG_LIB_CAT_OTHER;
+    }
+    if (! cdbsz_given) {
+        if ((FT_SG & in_type) && (MAX_SCSI_CDBSZ != iflag.cdbsz) &&
+            (((dd_count + skip) > UINT_MAX) || (bpt > USHRT_MAX))) {
+            pr2serr("Note: SCSI command size increased to 16 bytes (for "
+                    "'if')\n");
+            iflag.cdbsz = MAX_SCSI_CDBSZ;
+        }
+        if ((FT_SG & out_type) && (MAX_SCSI_CDBSZ != oflag.cdbsz) &&
+            (((dd_count + seek) > UINT_MAX) || (bpt > USHRT_MAX))) {
+            pr2serr("Note: SCSI command size increased to 16 bytes (for "
+                    "'of')\n");
+            oflag.cdbsz = MAX_SCSI_CDBSZ;
+        }
+    }
+
+    if (iflag.dio || iflag.direct || oflag.direct || (FT_RAW & in_type) ||
+        (FT_RAW & out_type)) {
+        size_t psz;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+        psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
+#else
+        psz = 4096;     /* give up, pick likely figure */
+#endif
+
+#ifdef HAVE_POSIX_MEMALIGN
+        {
+            int err;
+
+            err = posix_memalign((void **)&wrkBuff, psz, blk_sz * bpt);
+            if (err) {
+                pr2serr("posix_memalign: error [%d] out of memory?\n", err);
+                return SG_LIB_CAT_OTHER;
+            }
+            wrkPos = wrkBuff;
+        }
+#else
+        wrkBuff = (unsigned char*)malloc(blk_sz * bpt + psz);
+        if (0 == wrkBuff) {
+            pr2serr("Not enough user memory for work buffer\n");
+            return SG_LIB_CAT_OTHER;
+        }
+        wrkPos = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) &
+                                   (~(psz - 1)));
+#endif
+    } else {
+        wrkBuff = (unsigned char*)malloc(blk_sz * bpt);
+        if (0 == wrkBuff) {
+            pr2serr("Not enough user memory\n");
+            return SG_LIB_CAT_OTHER;
+        }
+        wrkPos = wrkBuff;
+    }
+
+    blocks_per = bpt;
+#ifdef SG_DEBUG
+    pr2serr("Start of loop, count=%" PRId64 ", blocks_per=%d\n", dd_count,
+            blocks_per);
+#endif
+    if (do_time) {
+        start_tm.tv_sec = 0;
+        start_tm.tv_usec = 0;
+        gettimeofday(&start_tm, NULL);
+        start_tm_valid = 1;
+    }
+    req_count = dd_count;
+
+    /* <<< main loop that does the copy >>> */
+    while (dd_count > 0) {
+        bytes_read = 0;
+        bytes_of = 0;
+        bytes_of2 = 0;
+        penult_sparse_skip = sparse_skip;
+        penult_blocks = penult_sparse_skip ? blocks : 0;
+        sparse_skip = 0;
+        blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
+        if (FT_SG & in_type) {
+            dio_tmp = iflag.dio;
+            res = sg_read(infd, wrkPos, blocks, skip, blk_sz, &iflag,
+                          &dio_tmp, &blks_read);
+            if (-2 == res) {     /* ENOMEM, find what's available+try that */
+                if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
+                    perror("RESERVED_SIZE ioctls failed");
+                    ret = res;
+                    break;
+                }
+                if (buf_sz < MIN_RESERVED_SIZE)
+                    buf_sz = MIN_RESERVED_SIZE;
+                blocks_per = (buf_sz + blk_sz - 1) / blk_sz;
+                if (blocks_per < blocks) {
+                    blocks = blocks_per;
+                    pr2serr("Reducing read to %d blocks per loop\n",
+                            blocks_per);
+                    res = sg_read(infd, wrkPos, blocks, skip, blk_sz,
+                                  &iflag, &dio_tmp, &blks_read);
+                }
+            }
+            if (res) {
+                pr2serr("sg_read failed,%s at or after lba=%" PRId64 " [0x%"
+                        PRIx64 "]\n", ((-2 == res) ?
+                                 " try reducing bpt," : ""), skip, skip);
+                ret = res;
+                break;
+            } else {
+                if (blks_read < blocks) {
+                    dd_count = 0;   /* force exit after write */
+                    blocks = blks_read;
+                }
+                in_full += blocks;
+                if (iflag.dio && (0 == dio_tmp))
+                    dio_incomplete++;
+            }
+        } else {
+            while (((res = read(infd, wrkPos, blocks * blk_sz)) < 0) &&
+                   ((EINTR == errno) || (EAGAIN == errno)))
+                ;
+            if (verbose > 2)
+                pr2serr("read(unix): count=%d, res=%d\n", blocks * blk_sz,
+                        res);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%" PRId64 " ",
+                         skip);
+                perror(ebuff);
+                ret = -1;
+                break;
+            } else if (res < blocks * blk_sz) {
+                dd_count = 0;
+                blocks = res / blk_sz;
+                if ((res % blk_sz) > 0) {
+                    blocks++;
+                    in_partial++;
+                }
+            }
+            bytes_read = res;
+            in_full += blocks;
+        }
+
+        if (0 == blocks)
+            break;      /* nothing read so leave loop */
+
+        if (out2f[0]) {
+            while (((res = write(out2fd, wrkPos, blocks * blk_sz)) < 0) &&
+                   ((EINTR == errno) || (EAGAIN == errno)))
+                ;
+            if (verbose > 2)
+                pr2serr("write to of2: count=%d, res=%d\n", blocks * blk_sz,
+                        res);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "writing to of2, seek=%" PRId64
+                         " ", seek);
+                perror(ebuff);
+                ret = -1;
+                break;
+            }
+            bytes_of2 = res;
+            out2_off += res;
+        }
+
+        if ((oflag.sparse) && (dd_count > blocks) &&
+            (! (FT_DEV_NULL & out_type))) {
+            if (NULL == zeros_buff) {
+                zeros_buff = (unsigned char *)malloc(blocks * blk_sz);
+                if (NULL == zeros_buff) {
+                    pr2serr("zeros_buff malloc failed\n");
+                    ret = -1;
+                    break;
+                }
+                memset(zeros_buff, 0, blocks * blk_sz);
+            }
+            if (0 == memcmp(wrkPos, zeros_buff, blocks * blk_sz))
+                sparse_skip = 1;
+        }
+        if (sparse_skip) {
+            if (FT_SG & out_type) {
+                out_sparse += blocks;
+                if (verbose > 2)
+                    pr2serr("sparse bypassing sg_write: seek blk=%" PRId64
+                            ", offset blks=%d\n", seek, blocks);
+            } else if (FT_DEV_NULL & out_type)
+                ;
+            else {
+                off64_t offset = blocks * blk_sz;
+                off64_t off_res;
+
+                if (verbose > 2)
+                    pr2serr("sparse bypassing write: seek=%" PRId64 ", rel "
+                            "offset=%" PRId64 "\n", (seek * blk_sz),
+                            (int64_t)offset);
+                off_res = lseek64(outfd, offset, SEEK_CUR);
+                if (off_res < 0) {
+                    pr2serr("sparse tried to bypass write: seek=%" PRId64
+                            ", rel offset=%" PRId64 " but ...\n",
+                            (seek * blk_sz), (int64_t)offset);
+                    perror("lseek64 on output");
+                    ret = SG_LIB_FILE_ERROR;
+                    break;
+                } else if (verbose > 4)
+                    pr2serr("oflag=sparse lseek64 result=%" PRId64 "\n",
+                            (int64_t)off_res);
+                out_sparse += blocks;
+            }
+        } else if (FT_SG & out_type) {
+            dio_tmp = oflag.dio;
+            retries_tmp = oflag.retries;
+            first = 1;
+            while (1) {
+                ret = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
+                               &oflag, &dio_tmp);
+                if (0 == ret)
+                    break;
+                if ((SG_LIB_CAT_NOT_READY == ret) ||
+                    (SG_LIB_SYNTAX_ERROR == ret))
+                    break;
+                else if ((-2 == ret) && first) {
+                    /* ENOMEM: find what's available and try that */
+                    if (ioctl(outfd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
+                        perror("RESERVED_SIZE ioctls failed");
+                        break;
+                    }
+                    if (buf_sz < MIN_RESERVED_SIZE)
+                        buf_sz = MIN_RESERVED_SIZE;
+                    blocks_per = (buf_sz + blk_sz - 1) / blk_sz;
+                    if (blocks_per < blocks) {
+                        blocks = blocks_per;
+                        pr2serr("Reducing write to %d blocks per loop\n",
+                                blocks);
+                    } else
+                        break;
+                } else if ((SG_LIB_CAT_UNIT_ATTENTION == ret) && first) {
+                    if (--max_uas > 0)
+                        pr2serr("Unit attention, continuing (w)\n");
+                    else {
+                        pr2serr("Unit attention, too many (w)\n");
+                        break;
+                    }
+                } else if ((SG_LIB_CAT_ABORTED_COMMAND == ret) && first) {
+                    if (--max_aborted > 0)
+                        pr2serr("Aborted command, continuing (w)\n");
+                    else {
+                        pr2serr("Aborted command, too many (w)\n");
+                        break;
+                    }
+                } else if (ret < 0)
+                    break;
+                else if (retries_tmp > 0) {
+                    pr2serr(">>> retrying a sgio write, lba=0x%" PRIx64 "\n",
+                            (uint64_t)seek);
+                    --retries_tmp;
+                    ++num_retries;
+                    if (unrecovered_errs > 0)
+                        --unrecovered_errs;
+                } else
+                    break;
+                first = 0;
+            }
+            if (0 != ret) {
+                pr2serr("sg_write failed,%s seek=%" PRId64 "\n",
+                        ((-2 == ret) ? " try reducing bpt," : ""), seek);
+                break;
+            } else {
+                out_full += blocks;
+                if (oflag.dio && (0 == dio_tmp))
+                    dio_incomplete++;
+            }
+        } else if (FT_DEV_NULL & out_type)
+            out_full += blocks; /* act as if written out without error */
+        else {
+            while (((res = write(outfd, wrkPos, blocks * blk_sz)) < 0) &&
+                   ((EINTR == errno) || (EAGAIN == errno)))
+                ;
+            if (verbose > 2)
+                pr2serr("write(unix): count=%d, res=%d\n", blocks * blk_sz,
+                        res);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%" PRId64 " ",
+                         seek);
+                perror(ebuff);
+                ret = -1;
+                break;
+            } else if (res < blocks * blk_sz) {
+                pr2serr("output file probably full, seek=%" PRId64 " ", seek);
+                blocks = res / blk_sz;
+                out_full += blocks;
+                if ((res % blk_sz) > 0)
+                    out_partial++;
+                ret = -1;
+                break;
+            } else {
+                out_full += blocks;
+                bytes_of = res;
+            }
+        }
+#ifdef HAVE_POSIX_FADVISE
+        {
+            int rt, in_valid, out2_valid, out_valid;
+
+            in_valid = ((FT_OTHER == in_type) || (FT_BLOCK == in_type));
+            out2_valid = ((FT_OTHER == out2_type) || (FT_BLOCK == out2_type));
+            out_valid = ((FT_OTHER == out_type) || (FT_BLOCK == out_type));
+            if (iflag.nocache && (bytes_read > 0) && in_valid) {
+                rt = posix_fadvise(infd, 0, (skip * blk_sz) + bytes_read,
+                                   POSIX_FADV_DONTNEED);
+                // rt = posix_fadvise(infd, (skip * blk_sz), bytes_read,
+                                   // POSIX_FADV_DONTNEED);
+                // rt = posix_fadvise(infd, 0, 0, POSIX_FADV_DONTNEED);
+                if (rt)         /* returns error as result */
+                    pr2serr("posix_fadvise on read, skip=%" PRId64
+                            " ,err=%d\n", skip, rt);
+            }
+            if ((oflag.nocache & 2) && (bytes_of2 > 0) && out2_valid) {
+                rt = posix_fadvise(out2fd, 0, 0, POSIX_FADV_DONTNEED);
+                if (rt)
+                    pr2serr("posix_fadvise on of2, seek=%" PRId64
+                            " ,err=%d\n", seek, rt);
+            }
+            if ((oflag.nocache & 1) && (bytes_of > 0) && out_valid) {
+                rt = posix_fadvise(outfd, 0, 0, POSIX_FADV_DONTNEED);
+                if (rt)
+                    pr2serr("posix_fadvise on output, seek=%" PRId64
+                            " ,err=%d\n", seek, rt);
+            }
+        }
+#endif
+        if (dd_count > 0)
+            dd_count -= blocks;
+        skip += blocks;
+        seek += blocks;
+    } /* end of main loop that does the copy ... */
+    if (ret && penult_sparse_skip && (penult_blocks > 0)) {
+        /* if error and skipped last output due to sparse ... */
+        if ((FT_SG & out_type) || (FT_DEV_NULL & out_type))
+            ;
+        else {
+            /* ... try writing to extend ofile to length prior to error */
+            while (((res = write(outfd, zeros_buff, penult_blocks * blk_sz))
+                    < 0) && ((EINTR == errno) || (EAGAIN == errno)))
+                ;
+            if (verbose > 2)
+                pr2serr("write(unix, sparse after error): count=%d, res=%d\n",
+                        penult_blocks * blk_sz, res);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "writing(sparse after error), "
+                        "seek=%" PRId64 " ", seek);
+                perror(ebuff);
+            }
+        }
+    }
+
+    if (do_time)
+        calc_duration_throughput(0);
+
+    if (do_sync) {
+        if (FT_SG & out_type) {
+            pr2serr(">> Synchronizing cache on %s\n", outf);
+            res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 1, 0);
+            if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+                pr2serr("Unit attention (out, sync cache), continuing\n");
+                res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0);
+            }
+            if (0 != res)
+                pr2serr("Unable to synchronize cache\n");
+        }
+    }
+    free(wrkBuff);
+    if (zeros_buff)
+        free(zeros_buff);
+    if (STDIN_FILENO != infd)
+        close(infd);
+    if (! ((STDOUT_FILENO == outfd) || (FT_DEV_NULL & out_type)))
+        close(outfd);
+    if (0 != dd_count) {
+        pr2serr("Some error occurred,");
+        if (0 == ret)
+            ret = SG_LIB_CAT_OTHER;
+    }
+    print_stats("");
+    if (dio_incomplete) {
+        int fd;
+        char c;
+
+        pr2serr(">> Direct IO requested but incomplete %d times\n",
+                dio_incomplete);
+        if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
+            if (1 == read(fd, &c, 1)) {
+                if ('0' == c)
+                    pr2serr(">>> %s set to '0' but should be set to '1' for "
+                            "direct IO\n", proc_allow_dio);
+            }
+            close(fd);
+        }
+    }
+    if (sum_of_resids)
+        pr2serr(">> Non-zero sum of residual counts=%d\n", sum_of_resids);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_decode_sense.c b/sg3_utils/src/sg_decode_sense.c
new file mode 100644
index 0000000..d8d8dfd
--- /dev/null
+++ b/sg3_utils/src/sg_decode_sense.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2010-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "1.08 20151219";
+
+#define MAX_SENSE_LEN 1024 /* max descriptor format actually: 256+8 */
+
+static struct option long_options[] = {
+    {"binary", required_argument, 0, 'b'},
+    {"file", required_argument, 0, 'f'},
+    {"help", no_argument, 0, 'h'},
+    {"hex", no_argument, 0, 'H'},
+    {"nospace", no_argument, 0, 'n'},
+    {"status", required_argument, 0, 's'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {"write", required_argument, 0, 'w'},
+    {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_binary;
+    const char * fname;
+    int do_file;
+    int do_help;
+    int do_hex;
+    int no_space;
+    int do_status;
+    int sstatus;
+    int do_verbose;
+    int do_version;
+    const char * wfname;
+    unsigned char sense[MAX_SENSE_LEN + 4];
+    const char * no_space_str;
+    int sense_len;
+};
+
+static char concat_buff[1024];
+
+
+static void
+usage()
+{
+  pr2serr("Usage: sg_decode_sense [--binary=FN] [--file=FN] [--help] [--hex] "
+          "[--nospace]\n"
+          "                       [--status=SS] [--verbose] [--version] "
+          "[--write=WFN]\n"
+          "                       [H1 H2 H3 ...]\n"
+          "  where:\n"
+          "    --binary=FN|-b FN     FN is a file name to read sense "
+          "data in\n"
+          "                          binary from. If FN is '-' then read "
+          "from stdin\n"
+          "    --file=FN|-f FN       FN is a file name from which to read "
+          "sense data\n"
+          "                          in ASCII hexadecimal. Interpret '-' "
+          "as stdin\n"
+          "    --help|-h             print out usage message\n"
+          "    --hex|-H              used together with --write=WFN, to "
+          "write out\n"
+          "                          C language style ASCII hex (instead "
+          "of binary)\n"
+          "    --nospace|-n          no spaces or other separators between "
+          "pairs of\n"
+          "                          hex digits (e.g. '3132330A')\n"
+          "    --status=SS |-s SS    SCSI status value in hex\n"
+          "    --verbose|-v          increase verbosity\n"
+          "    --version|-V          print version string then exit\n"
+          "    --write=WFN |-w WFN    write sense data in binary to WFN, "
+          "create if\n"
+          "                           required else truncate prior to "
+          "writing\n\n"
+          "Decodes SCSI sense data given on the command line as a sequence "
+          "of\nhexadecimal bytes (H1 H2 H3 ...) . Alternatively the sense "
+          "data can\nbe in a binary file or in a file containing ASCII "
+          "hexadecimal.\n"
+          );
+}
+
+static int
+process_cl(struct opts_t *optsp, int argc, char *argv[])
+{
+    int c;
+    unsigned int ui;
+    char * opt;
+    char *endptr;
+    long val;
+
+    while (1) {
+        c = getopt_long(argc, argv, "b:f:hHns:vVw:", long_options, NULL);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            if (optsp->fname) {
+                pr2serr("expect only one '--binary=FN' or '--file=FN' "
+                        "option\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++optsp->do_binary;
+            optsp->fname = optarg;
+            break;
+        case 'f':
+            if (optsp->fname) {
+                pr2serr("expect only one '--binary=FN' or '--file=FN' "
+                        "option\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++optsp->do_file;
+            optsp->fname = optarg;
+            break;
+        case 'h':
+        case '?':
+            optsp->do_help = 1;
+            return 0;
+        case 'H':
+            ++optsp->do_hex;
+            break;
+        case 'n':
+            ++optsp->no_space;
+            break;
+        case 's':
+            if (1 != sscanf(optarg, "%x", &ui)) {
+                pr2serr("'--status=SS' expects a byte value\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if (ui > 0xff) {
+                pr2serr("'--status=SS' byte value exceeds FF\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++optsp->do_status;
+            optsp->sstatus = ui;
+            break;
+        case 'v':
+            ++optsp->do_verbose;
+            break;
+        case 'V':
+            optsp->do_version = 1;
+            return 0;
+        case 'w':
+            optsp->wfname = optarg;
+            break;
+        default:
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    while (optind < argc) {
+        opt = argv[optind++];
+        if (optsp->no_space) {
+            if (optsp->no_space_str) {
+                if ('\0' == concat_buff[0]) {
+                    if (strlen(optsp->no_space_str) > sizeof(concat_buff)) {
+                        pr2serr("'--nospace' concat_buff overflow\n");
+                        return SG_LIB_SYNTAX_ERROR;
+                    }
+                    strcpy(concat_buff, optsp->no_space_str);
+                }
+                if ((strlen(concat_buff) + strlen(opt)) >=
+                    sizeof(concat_buff)) {
+                    pr2serr("'--nospace' concat_buff overflow\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                if (optsp->do_version)
+                    pr2serr("'--nospace' and found whitespace so "
+                            "concatenate\n");
+                strcat(concat_buff, opt);
+                optsp->no_space_str = concat_buff;
+            } else
+                optsp->no_space_str = opt;
+            continue;
+        }
+        val = strtol(opt, &endptr, 16);
+        if (*opt == '\0' || *endptr != '\0' || val < 0x00 || val > 0xff) {
+            pr2serr("Invalid byte '%s'\n", opt);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+
+        if (optsp->sense_len > MAX_SENSE_LEN) {
+            pr2serr("sense data too long (max. %d bytes)\n", MAX_SENSE_LEN);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        optsp->sense[optsp->sense_len++] = (unsigned char)val;
+    }
+    return 0;
+}
+
+/* Read ASCII hex bytes from fname (a file named '-' taken as stdin).
+ * There should be either one entry per line or a comma, space or tab
+ * separated list of bytes. If no_space is set then a string of ACSII hex
+ * digits is expected, 2 per byte. Everything from and including a '#'
+ * on a line is ignored.  Returns 0 if ok, or 1 if error. */
+static int
+f2hex_arr(const char * fname, int no_space, unsigned char * mp_arr,
+          int * mp_arr_len, int max_arr_len)
+{
+    int fn_len, in_len, k, j, m, split_line;
+    unsigned int h;
+    const char * lcp;
+    FILE * fp;
+    char line[512];
+    char carry_over[4];
+    int off = 0;
+
+    if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len))
+        return 1;
+    fn_len = strlen(fname);
+    if (0 == fn_len)
+        return 1;
+    if ((1 == fn_len) && ('-' == fname[0]))        /* read from stdin */
+        fp = stdin;
+    else {
+        fp = fopen(fname, "r");
+        if (NULL == fp) {
+            pr2serr("Unable to open %s for reading\n", fname);
+            return 1;
+        }
+    }
+
+    carry_over[0] = 0;
+    for (j = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+                split_line = 0;
+            } else
+                split_line = 1;
+        }
+        if (in_len < 1) {
+            carry_over[0] = 0;
+            continue;
+        }
+        if (carry_over[0]) {
+            if (isxdigit(line[0])) {
+                carry_over[1] = line[0];
+                carry_over[2] = '\0';
+                if (1 == sscanf(carry_over, "%x", &h))
+                    mp_arr[off - 1] = h;       /* back up and overwrite */
+                else {
+                    pr2serr("f2hex_arr: carry_over error ['%s'] around line "
+                            "%d\n", carry_over, j + 1);
+                    goto bad;
+                }
+                lcp = line + 1;
+                --in_len;
+            } else
+                lcp = line;
+            carry_over[0] = 0;
+        } else
+            lcp = line;
+
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+        if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) {
+            pr2serr("f2hex_arr: syntax error at line %d, pos %d\n", j + 1,
+                    m + k + 1);
+            goto bad;
+        }
+        if (no_space) {
+            for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
+                 ++k, lcp += 2) {
+                if (1 != sscanf(lcp, "%2x", &h)) {
+                    pr2serr("f2hex_arr: bad hex number in line %d, pos %d\n",
+                            j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+                if ((off + k) >= max_arr_len) {
+                    pr2serr("f2hex_arr: array length exceeded\n");
+                    goto bad;
+                }
+                mp_arr[off + k] = h;
+            }
+            if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
+                carry_over[0] = *lcp;
+            off += k;
+        } else {
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("f2hex_arr: hex number larger than 0xff in "
+                                "line %d, pos %d\n", j + 1,
+                                (int)(lcp - line + 1));
+                        goto bad;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("f2hex_arr: array length exceeded\n");
+                        goto bad;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if (('#' == *lcp) || ('\r' == *lcp)) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("f2hex_arr: error in line %d, at pos %d\n", j + 1,
+                            (int)(lcp - line + 1));
+                    goto bad;
+                }
+            }
+            off += (k + 1);
+        }
+    }
+    *mp_arr_len = off;
+    if (stdin != fp)
+        fclose(fp);
+    return 0;
+bad:
+    if (stdin != fp)
+        fclose(fp);
+    return 1;
+}
+
+static void
+write2wfn(FILE * fp, struct opts_t * optsp)
+{
+    int k, n;
+    size_t s;
+    char b[128];
+
+    if (optsp->do_hex) {
+        for (k = 0, n = 0; k < optsp->sense_len; ++k) {
+            n += sprintf(b + n, "0x%02x,", optsp->sense[k]);
+            if (15 == (k % 16)) {
+                b[n] = '\n';
+                s = fwrite(b, 1, n + 1, fp);
+                n = 0;
+            }
+        }
+        if (n > 0) {
+            b[n] = '\n';
+            s = fwrite(b, 1, n + 1, fp);
+        }
+    } else {
+        s = fwrite(optsp->sense, 1, optsp->sense_len, fp);
+        if ((int)s != optsp->sense_len)
+            pr2serr("only able to write %d of %d bytes to %s\n", (int)s,
+                    optsp->sense_len, optsp->wfname);
+    }
+}
+
+
+int
+main(int argc, char *argv[])
+{
+    int k;
+    int ret = 0;
+    unsigned int ui;
+    size_t s;
+    struct opts_t opts;
+    char b[2048];
+    FILE * fp = NULL;
+    const char * cp;
+
+    memset(&opts, 0, sizeof(opts));
+    memset(b, 0, sizeof(b));
+    ret = process_cl(&opts, argc, argv);
+    if (ret != 0) {
+        usage();
+        return ret;
+    } else if (opts.do_help) {
+        usage();
+        return 0;
+    } else if (opts.do_version) {
+        pr2serr("version: %s\n", version_str);
+        return 0;
+    }
+
+
+    if (opts.do_status) {
+        sg_get_scsi_status_str(opts.sstatus, sizeof(b) - 1, b);
+        printf("SCSI status: %s\n", b);
+    }
+
+    if ((0 == opts.sense_len) && opts.no_space_str) {
+        if (opts.do_verbose > 2)
+            pr2serr("no_space str: %s\n", opts.no_space_str);
+        cp = opts.no_space_str;
+        for (k = 0; isxdigit(cp[k]) && isxdigit(cp[k + 1]); k += 2) {
+            if (1 != sscanf(cp + k, "%2x", &ui)) {
+                pr2serr("bad no_space hex string: %s\n", cp);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            opts.sense[opts.sense_len++] = (unsigned char)ui;
+        }
+    }
+
+    if ((0 == opts.sense_len) && (! opts.do_binary) && (! opts.do_file)) {
+        if (opts.do_status)
+            return 0;
+        pr2serr(">> Need sense data on the command line or in a file\n\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (opts.sense_len && (opts.do_binary || opts.do_file)) {
+        pr2serr(">> Need sense data on command line or in a file, not "
+                "both\n\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (opts.do_binary && opts.do_file) {
+        pr2serr(">> Either a binary file or a ASCII hexadecimal, file not "
+                "both\n\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (opts.do_binary) {
+        fp = fopen(opts.fname, "r");
+        if (NULL == fp) {
+            pr2serr("unable to open file: %s\n", opts.fname);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        s = fread(opts.sense, 1, MAX_SENSE_LEN, fp);
+        fclose(fp);
+        if (0 == s) {
+            pr2serr("read nothing from file: %s\n", opts.fname);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        opts.sense_len = s;
+    } else if (opts.do_file) {
+        ret = f2hex_arr(opts.fname, opts.no_space, opts.sense,
+                        &opts.sense_len, MAX_SENSE_LEN);
+        if (ret) {
+            pr2serr("unable to decode ASCII hex from file: %s\n", opts.fname);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (opts.sense_len) {
+        if (opts.wfname) {
+            if ((fp = fopen(opts.wfname, "w"))) {
+                write2wfn(fp, &opts);
+                fclose(fp);
+            } else {
+                perror("open");
+                pr2serr("trying to write to %s\n", opts.wfname);
+            }
+        }
+        sg_get_sense_str(NULL, opts.sense, opts.sense_len, opts.do_verbose,
+                         sizeof(b) - 1, b);
+        printf("%s\n", b);
+    }
+
+    return 0;
+}
diff --git a/sg3_utils/src/sg_emc_trespass.c b/sg3_utils/src/sg_emc_trespass.c
new file mode 100644
index 0000000..ac7191c
--- /dev/null
+++ b/sg3_utils/src/sg_emc_trespass.c
@@ -0,0 +1,170 @@
+/* The program allows the user to send a trespass command to change the
+ * LUN ownership from one Service-Processor to this one on an EMC
+ * CLARiiON and potentially other devices.
+ *
+ * Copyright (C) 2004-2015 Lars Marowsky-Bree <lmb@suse.de>
+ *
+ * Based on sg_start.c; credits from there also apply.
+ * Minor modifications for sg_lib, D. Gilbert 2004/10/19
+ *
+ *  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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "0.20 20141219";
+
+static int debug = 0;
+
+#define TRESPASS_PAGE           0x22
+
+static int do_trespass(int fd, int hr, int short_cmd)
+{
+        unsigned char long_trespass_pg[] =
+                { 0, 0, 0, 0, 0, 0, 0, 0x00,
+                  TRESPASS_PAGE,        /* Page code */
+                  0x09,                 /* Page length - 2 */
+                  0x81,                 /* Trespass code + Honor reservation
+                                         * bit */
+                  0xff, 0xff,           /* Trespass target */
+                  0, 0, 0, 0, 0, 0      /* Reserved bytes / unknown */
+        };
+        unsigned char short_trespass_pg[] =
+                { 0, 0, 0, 0,
+                  TRESPASS_PAGE,        /* Page code */
+                  0x02,                 /* Page length - 2 */
+                  0x81,                 /* Trespass code + Honor reservation
+                                         * bit */
+                  0xff,                 /* Trespass target */
+        };
+        int res;
+        char b[80];
+
+        if (hr) {       /* override Trespass code + Honor reservation bit */
+                short_trespass_pg[6] = 0x01;
+                long_trespass_pg[10] = 0x01;
+        }
+        if (short_cmd)
+                res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
+                                 short_trespass_pg, sizeof(short_trespass_pg),
+                                 1, (debug ? 2 : 0));
+        else
+                res = sg_ll_mode_select10(fd, 1 /* pf */, 0 /* sp */,
+                                 long_trespass_pg, sizeof(long_trespass_pg),
+                                 1, (debug ? 2 : 0));
+
+        switch (res) {
+        case 0:
+                if (debug)
+                        pr2serr("%s trespass successful\n",
+                                short_cmd ? "short" : "long");
+                break;
+        case SG_LIB_CAT_INVALID_OP:
+        case SG_LIB_CAT_ILLEGAL_REQ:
+                pr2serr("%s form trepass page failed, try again %s '-s' "
+                        "option\n", short_cmd ? "short" : "long",
+                        short_cmd ? "without" : "with");
+                break;
+        case SG_LIB_CAT_NOT_READY:
+                pr2serr("device not ready\n");
+                break;
+        case SG_LIB_CAT_UNIT_ATTENTION:
+                pr2serr("unit attention\n");
+                break;
+        default:
+                sg_get_category_sense_str(res, sizeof(b), b, debug);
+                pr2serr("%s trespass failed: %s\n",
+                        (short_cmd ? "short" : "long"), b);
+                break;
+        }
+        return res;
+}
+
+void usage ()
+{
+        pr2serr("Usage:  sg_emc_trespass [-d] [-hr] [-s] [-V] DEVICE\n"
+                "  Change ownership of a LUN from another SP to this one.\n"
+                "  EMC CLARiiON CX-/AX-family + FC5300/FC4500/FC4700.\n"
+                "    -d : output debug\n"
+                "    -hr: Set Honor Reservation bit\n"
+                "    -s : Send Short Trespass Command page (default: long)\n"
+                "         (for FC series)\n"
+                "    -V: print version string then exit\n"
+                "     DEVICE   sg or block device (latter in lk 2.6 or lk 3 "
+                "series)\n"
+                "        Example: sg_emc_trespass /dev/sda\n");
+        exit (1);
+}
+
+int main(int argc, char * argv[])
+{
+        char **argptr;
+        char * file_name = 0;
+        int k, fd;
+        int hr = 0;
+        int short_cmd = 0;
+        int ret = 0;
+
+        if (argc < 2)
+                usage ();
+
+        for (k = 1; k < argc; ++k) {
+                argptr = argv + k;
+                if (!strcmp (*argptr, "-d"))
+                        ++debug;
+                else if (!strcmp (*argptr, "-s"))
+                        short_cmd = 1;
+                else if (!strcmp (*argptr, "-hr"))
+                        hr = 1;
+                else if (!strcmp (*argptr, "-V")) {
+                        printf("Version string: %s\n", version_str);
+                        exit(0);
+                }
+                else if (*argv[k] == '-') {
+                        pr2serr("Unrecognized switch: %s\n", argv[k]);
+                        file_name = 0;
+                        break;
+                }
+                else if (0 == file_name)
+                        file_name = argv[k];
+                else {
+                        pr2serr("too many arguments\n");
+                        file_name = 0;
+                        break;
+                }
+        }
+        if (0 == file_name) {
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+
+        fd = open(file_name, O_RDWR | O_NONBLOCK);
+        if (fd < 0) {
+                pr2serr("Error trying to open %s\n", file_name);
+                perror("");
+                usage();
+                return SG_LIB_FILE_ERROR;
+        }
+
+        ret = do_trespass(fd, hr, short_cmd);
+
+        close (fd);
+        return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_format.c b/sg3_utils/src/sg_format.c
new file mode 100644
index 0000000..9535a86
--- /dev/null
+++ b/sg3_utils/src/sg_format.c
@@ -0,0 +1,1251 @@
+/*
+ * sg_format : format a SCSI disk
+ *             potentially with a different number of blocks and block size
+ *
+ * formerly called blk512-linux.c (v0.4)
+ *
+ * Copyright (C) 2003  Grant Grundler    grundler at parisc-linux dot org
+ * Copyright (C) 2003  James Bottomley       jejb at parisc-linux dot org
+ * Copyright (C) 2005-2016  Douglas Gilbert   dgilbert at interlog dot 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.
+ *
+ * See http://www.t10.org for relevant standards and drafts. The most recent
+ * draft is SBC-4 revision 2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+#include "sg_pt.h"
+
+static const char * version_str = "1.34 20160209";
+
+
+#define RW_ERROR_RECOVERY_PAGE 1  /* can give alternate with --mode=MP */
+
+#define SHORT_TIMEOUT           20   /* 20 seconds unless --wait given */
+#define FORMAT_TIMEOUT          (20 * 3600)       /* 20 hours ! */
+/* Seagate ST32000444SS 2TB disk takes 9.5 hours, now there are 4TB disks */
+
+#define POLL_DURATION_SECS 60
+#define DEF_POLL_TYPE 0
+
+#if defined(MSC_VER) || defined(__MINGW32__)
+#define HAVE_MS_SLEEP
+#endif
+
+#ifdef HAVE_MS_SLEEP
+#include <windows.h>
+#define sleep_for(seconds)    Sleep( (seconds) * 1000)
+#else
+#define sleep_for(seconds)    sleep(seconds)
+#endif
+
+/* FORMAT UNIT (SBC) and FORMAT MEDIUM (SSC) share the same opcode */
+#define SG_FORMAT_MEDIUM_CMD 0x4
+#define SG_FORMAT_MEDIUM_CMDLEN 6
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+
+struct opts_t {
+        int64_t blk_count;      /* -c value */
+        int blk_size;           /* -s value */
+        int cmplst;             /* -C value */
+        bool dcrt;              /* -D */
+        bool early;             /* -e */
+        int ffmt;               /* -t value */
+        int fmtpinfo;
+        int format;             /* -F */
+        bool fwait;             /* -w (negate for immed) */
+        bool ip_def;            /* -I */
+        bool long_lba;          /* -l */
+        int mode_page;          /* -M value */
+        bool mode6;             /* -6 */
+        int pfu;                /* -P value */
+        int pie;                /* -q value */
+        bool pinfo;             /* -p, deprecated, prefer fmtpinfo */
+        int pollt;              /* -x value */
+        bool pollt_given;
+        bool do_rcap16;         /* -l */
+        bool resize;            /* -r */
+        bool rto_req;           /* -R, deprecated, prefer fmtpinfo */
+        int tape;               /* -T <format>, def: -1 */
+        int sec_init;           /* -S */
+        int verbose;            /* -v */
+        int verify;             /* -y */
+        const char * device_name;
+};
+
+#define MAX_BUFF_SZ     252
+static unsigned char dbuff[MAX_BUFF_SZ];
+
+
+static struct option long_options[] = {
+        {"count", required_argument, 0, 'c'},
+        {"cmplst", required_argument, 0, 'C'},
+        {"dcrt", no_argument, 0, 'D'},
+        {"early", no_argument, 0, 'e'},
+        {"ffmt", required_argument, 0, 't'},
+        {"fmtpinfo", required_argument, 0, 'f'},
+        {"format", no_argument, 0, 'F'},
+        {"help", no_argument, 0, 'h'},
+        {"ip_def", no_argument, 0, 'I'},
+        {"long", no_argument, 0, 'l'},
+        {"mode", required_argument, 0, 'M'},
+        {"pinfo", no_argument, 0, 'p'},
+        {"pfu", required_argument, 0, 'P'},
+        {"pie", required_argument, 0, 'q'},
+        {"poll", required_argument, 0, 'x'},
+        {"resize", no_argument, 0, 'r'},
+        {"rto_req", no_argument, 0, 'R'},
+        {"security", no_argument, 0, 'S'},
+        {"six", no_argument, 0, '6'},
+        {"size", required_argument, 0, 's'},
+        {"tape", required_argument, 0, 'T'},
+        {"verbose", no_argument, 0, 'v'},
+        {"verify", no_argument, 0, 'y'},
+        {"version", no_argument, 0, 'V'},
+        {"wait", no_argument, 0, 'w'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+        printf("usage: sg_format [--cmplst=0|1] [--count=COUNT] [--dcrt] "
+               "[--early]\n"
+               "                 [--ffmt] [--fmtpinfo=FPI] [--format] "
+               "[--help] [--ip_def]\n"
+               "                 [--long] [--mode=MP] [--pfu=PFU] "
+               "[--pie=PIE] [--pinfo]\n"
+               "                 [--poll=PT] [--resize] [--rto_req] "
+               "[--security] [--six]\n"
+               "                 [--size=SIZE] [--tape=FM] [--verbose] "
+               "[--verify]\n"
+               "                 [--version] [--wait] DEVICE\n"
+               "  where:\n"
+               "    --cmplst=0|1\n"
+               "      -C 0|1        sets CMPLST bit in format cdb "
+               "(default: 1)\n"
+               "    --count=COUNT|-c COUNT    number of blocks to report "
+               "after format or\n"
+               "                              resize. Format default is "
+               "same as current\n"
+               "    --dcrt|-D       disable certification (doesn't "
+               "verify media)\n"
+               "    --early|-e      exit once format started (user can "
+               "monitor progress)\n"
+               "    --ffmt=FFMT|-t FFMT      fast format (def: 0 -> "
+               "possibly write\n"
+               "                             to whole medium\n"
+               "    --fmtpinfo=FPI|-f FPI    FMTPINFO field value "
+               "(default: 0)\n"
+               "    --format|-F     do FORMAT UNIT (default: report current "
+               "count and size)\n"
+               "                    use thrice for FORMAT UNIT command "
+               "only\n"
+               "    --help|-h       prints out this usage message\n"
+               "    --ip_def|-I     initialization pattern: default\n"
+               "    --long|-l       allow for 64 bit lbas (default: assume "
+               "32 bit lbas)\n"
+               "    --mode=MP|-M MP     mode page (def: 1 -> RW error "
+               "recovery mpage)\n"
+               "    --pie=PIE|-q PIE    Protection Information Exponent "
+               "(default: 0)\n"
+               "    --pinfo|-p      set upper bit of FMTPINFO field\n"
+               "                    (deprecated, use '--fmtpinfo=FPI' "
+               "instead)\n"
+               "    --poll=PT|-x PT    PT is poll type, 0 for test unit "
+               "ready\n"
+               "                       1 for request sense (def: 0 (1 "
+               "for tape))\n");
+        printf("    --resize|-r     resize (rather than format) to COUNT "
+               "value\n"
+               "    --rto_req|-R    set lower bit of FMTPINFO field\n"
+               "                    (deprecated use '--fmtpinfo=FPI' "
+               "instead)\n"
+               "    --security|-S    set security initialization (SI) bit\n"
+               "    --six|-6        use 6 byte MODE SENSE/SELECT to probe "
+               "disk\n"
+               "                    (def: use 10 byte MODE SENSE/SELECT)\n"
+               "    --size=SIZE|-s SIZE    bytes per logical block, "
+               "defaults to DEVICE's\n"
+               "                           current logical block size. Only "
+               "needed to\n"
+               "                           change current logical block "
+               "size\n"
+               "    --tape=FM|-T FM    request FORMAT MEDIUM with FORMAT "
+               "field set\n"
+               "                       to FM (def: 0 --> default format)\n"
+               "    --verbose|-v    increase verbosity\n"
+               "    --verify|-y     sets VERIFY bit in FORMAT MEDIUM (tape)\n"
+               "    --version|-V    print version details and exit\n"
+               "    --wait|-w       format command waits until format "
+               "operation completes\n"
+               "                    (default: set IMMED=1 and poll with "
+               "Test Unit Ready)\n\n"
+               "\tExample: sg_format --format /dev/sdc\n\n"
+               "This utility formats a SCSI disk [FORMAT UNIT] or resizes "
+               "it. Alternatively\nif '--tape=FM' is given formats a tape "
+               "[FORMAT MEDIUM].\n");
+        printf("WARNING: This utility will destroy all the data on "
+               "DEVICE when '--format'\n\t or '--tape' is given. Check that "
+               "you have specified the correct\n\t DEVICE.\n");
+}
+
+/* Invokes a SCSI FORMAT MEDIUM command (SSC).  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_format_medium(int sg_fd, int verify, int immed, int format,
+                    void * paramp, int transfer_len, int timeout, int noisy,
+                    int verbose)
+{
+        int k, ret, res, sense_cat;
+        unsigned char fmCmdBlk[SG_FORMAT_MEDIUM_CMDLEN] =
+                                  {SG_FORMAT_MEDIUM_CMD, 0, 0, 0, 0, 0};
+        unsigned char sense_b[SENSE_BUFF_LEN];
+        struct sg_pt_base * ptvp;
+
+        if (verify)
+                fmCmdBlk[1] |= 0x2;
+        if (immed)
+                fmCmdBlk[1] |= 0x1;
+        if (format)
+                fmCmdBlk[2] |= (0xf & format);
+        if (transfer_len > 0)
+                sg_put_unaligned_be16(transfer_len, fmCmdBlk + 3);
+        if (verbose) {
+                pr2serr("    Format medium cdb: ");
+                for (k = 0; k < SG_FORMAT_MEDIUM_CMDLEN; ++k)
+                        pr2serr("%02x ", fmCmdBlk[k]);
+                pr2serr("\n");
+        }
+
+        ptvp = construct_scsi_pt_obj();
+        if (NULL == ptvp) {
+                pr2serr("%s: out of memory\n", __func__);
+                return -1;
+        }
+        set_scsi_pt_cdb(ptvp, fmCmdBlk, sizeof(fmCmdBlk));
+        set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+        set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, transfer_len);
+        res = do_scsi_pt(ptvp, sg_fd, timeout, verbose);
+        ret = sg_cmds_process_resp(ptvp, "format medium", res, transfer_len,
+                                   sense_b, noisy, verbose, &sense_cat);
+        if (-1 == ret)
+                ;
+        else if (-2 == ret) {
+                switch (sense_cat) {
+                case SG_LIB_CAT_RECOVERED:
+                case SG_LIB_CAT_NO_SENSE:
+                        ret = 0;
+                        break;
+                default:
+                        ret = sense_cat;
+                        break;
+                }
+        } else
+                ret = 0;
+        destruct_scsi_pt_obj(ptvp);
+        return ret;
+}
+
+/* Return 0 on success, else see sg_ll_format_unit2() */
+static int
+scsi_format_unit(int fd, const struct opts_t * op)
+{
+        int res, need_hdr, progress, pr, rem, verb, fmt_pl_sz, longlist, off;
+        int resp_len, ip_desc;
+        int immed = ! op->fwait;
+        const int SH_FORMAT_HEADER_SZ = 4;
+        const int LO_FORMAT_HEADER_SZ = 8;
+        const char INIT_PATTERN_DESC_SZ = 4;
+        unsigned char fmt_pl[LO_FORMAT_HEADER_SZ + INIT_PATTERN_DESC_SZ];
+        unsigned char reqSense[MAX_BUFF_SZ];
+        char b[80];
+
+        memset(fmt_pl, 0, sizeof(fmt_pl));
+        longlist = (op->pie > 0);
+        ip_desc = (op->ip_def || op->sec_init);
+        off = longlist ? LO_FORMAT_HEADER_SZ : SH_FORMAT_HEADER_SZ;
+        fmt_pl[0] = op->pfu & 0x7;  /* PROTECTION_FIELD_USAGE (bits 2-0) */
+        fmt_pl[1] = (immed ? 0x2 : 0); /* FOV=0, [DPRY,DCRT,STPF,IP=0] */
+        if (op->dcrt)
+                fmt_pl[1] |= 0xa0;     /* FOV=1, DCRT=1 */
+        if (ip_desc) {
+                fmt_pl[1] |= 0x88;     /* FOV=1, IP=1 */
+                if (op->sec_init)
+                        fmt_pl[off + 0] = 0x20; /* SI=1 in IP desc */
+        }
+        if (longlist)
+                fmt_pl[3] = (op->pie & 0xf);/* PROTECTION_INTERVAL_EXPONENT */
+        /* with the long parameter list header, P_I_INFORMATION is always 0 */
+
+        need_hdr = (immed || op->cmplst || op->dcrt || ip_desc ||
+                    (op->pfu > 0) || (op->pie > 0));
+        fmt_pl_sz = 0;
+        if (need_hdr)
+                fmt_pl_sz = off + (ip_desc ? INIT_PATTERN_DESC_SZ : 0);
+
+        res = sg_ll_format_unit2(fd, op->fmtpinfo, longlist,
+                                 need_hdr/* FMTDATA*/, op->cmplst,
+                                 0 /* DEFECT_LIST_FORMAT */, op->ffmt,
+                                 (immed ? SHORT_TIMEOUT : FORMAT_TIMEOUT),
+                                 fmt_pl, fmt_pl_sz, 1, op->verbose);
+        if (res) {
+                sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+                pr2serr("Format unit command: %s\n", b);
+                return res;
+        }
+        if (! immed)
+                return 0;
+
+        printf("\nFormat unit has started\n");
+        if (op->early) {
+                if (immed)
+                        printf("Format continuing,\n    request sense or "
+                               "test unit ready can be used to monitor "
+                               "progress\n");
+                return 0;
+        }
+
+        verb = (op->verbose > 1) ? (op->verbose - 1) : 0;
+        if (0 == op->pollt) {
+                for(;;) {
+                        sleep_for(POLL_DURATION_SECS);
+                        progress = -1;
+                        res = sg_ll_test_unit_ready_progress(fd, 0, &progress,
+                                                             1, verb);
+                        if (progress >= 0) {
+                                pr = (progress * 100) / 65536;
+                                rem = ((progress * 100) % 65536) / 656;
+                                printf("Format in progress, %d.%02d%% done\n",
+                                       pr, rem);
+                        } else
+                                break;
+                }
+        }
+        if (op->pollt || (SG_LIB_CAT_NOT_READY == res)) {
+                for(;;) {
+                        sleep_for(POLL_DURATION_SECS);
+                        memset(reqSense, 0x0, sizeof(reqSense));
+                        res = sg_ll_request_sense(fd, 0, reqSense,
+                                                  sizeof(reqSense), 0, verb);
+                        if (res) {
+                                pr2serr("polling with Request Sense command "
+                                        "failed [res=%d]\n", res);
+                                break;
+                        }
+                        resp_len = reqSense[7] + 8;
+                        if (verb) {
+                                pr2serr("Parameter data in hex:\n");
+                                dStrHexErr((const char *)reqSense, resp_len,
+                                           1);
+                        }
+                        progress = -1;
+                        sg_get_sense_progress_fld(reqSense, resp_len,
+                                                  &progress);
+                        if (progress >= 0) {
+                                pr = (progress * 100) / 65536;
+                                rem = ((progress * 100) % 65536) / 656;
+                                printf("Format in progress, %d.%02d%% done\n",
+                                       pr, rem);
+                        } else
+                                break;
+                }
+        }
+#if 0
+        for (k = 0; k < num_rs; ++k) {
+            if (k > 0)
+                sleep_for(30);
+            memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
+            res = sg_ll_request_sense(sg_fd, desc, requestSenseBuff, maxlen,
+                                      1, op->verbose);
+            if (res) {
+                ret = res;
+                sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+                pr2serr("Request Sense command: %s\n", b);
+                break;
+            }
+            /* "Additional sense length" same in descriptor and fixed */
+            resp_len = requestSenseBuff[7] + 8;
+            if (op->verbose > 1) {
+                pr2serr("Parameter data in hex\n");
+                dStrHexErr((const char *)requestSenseBuff, resp_len, 1);
+            }
+            progress = -1;
+            sg_get_sense_progress_fld(requestSenseBuff, resp_len,
+                                      &progress);
+            if (progress < 0) {
+                ret = res;
+                if (op->verbose > 1)
+                     pr2serr("No progress indication found, iteration %d\n",
+                             k + 1);
+                /* N.B. exits first time there isn't a progress indication */
+                break;
+            } else
+                printf("Progress indication: %d.%02d%% done\n",
+                       (progress * 100) / 65536,
+                       ((progress * 100) % 65536) / 656);
+        }
+#endif
+        printf("FORMAT UNIT Complete\n");
+        return 0;
+}
+
+/* Return 0 on success, else see sg_ll_format_medium() above */
+static int
+scsi_format_medium(int fd, const struct opts_t * op)
+{
+        int res, progress, pr, rem, verb, resp_len;
+        int immed = ! op->fwait;
+        unsigned char reqSense[MAX_BUFF_SZ];
+        char b[80];
+
+        res = sg_ll_format_medium(fd, op->verify, immed, 0xf & op->tape, NULL,
+                                  0, (immed ? SHORT_TIMEOUT : FORMAT_TIMEOUT),
+                                  1, op->verbose);
+        if (res) {
+                sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+                pr2serr("Format medium command: %s\n", b);
+                return res;
+        }
+        if (! immed)
+                return 0;
+
+        printf("\nFormat medium has started\n");
+        if (op->early) {
+                if (immed)
+                        printf("Format continuing,\n    request sense or "
+                               "test unit ready can be used to monitor "
+                               "progress\n");
+                return 0;
+        }
+
+        verb = (op->verbose > 1) ? (op->verbose - 1) : 0;
+        if (0 == op->pollt) {
+                for(;;) {
+                        sleep_for(POLL_DURATION_SECS);
+                        progress = -1;
+                        res = sg_ll_test_unit_ready_progress(fd, 0, &progress,
+                                                             1, verb);
+                        if (progress >= 0) {
+                                pr = (progress * 100) / 65536;
+                                rem = ((progress * 100) % 65536) / 656;
+                                printf("Format in progress, %d.%02d%% done\n",
+                                       pr, rem);
+                        } else
+                                break;
+                }
+        }
+        if (op->pollt || (SG_LIB_CAT_NOT_READY == res)) {
+                for(;;) {
+                        sleep_for(POLL_DURATION_SECS);
+                        memset(reqSense, 0x0, sizeof(reqSense));
+                        res = sg_ll_request_sense(fd, 0, reqSense,
+                                                  sizeof(reqSense), 0, verb);
+                        if (res) {
+                                pr2serr("polling with Request Sense command "
+                                        "failed [res=%d]\n", res);
+                                break;
+                        }
+                        resp_len = reqSense[7] + 8;
+                        if (verb) {
+                                pr2serr("Parameter data in hex:\n");
+                                dStrHexErr((const char *)reqSense, resp_len,
+                                           1);
+                        }
+                        progress = -1;
+                        sg_get_sense_progress_fld(reqSense, resp_len,
+                                                  &progress);
+                        if (progress >= 0) {
+                                pr = (progress * 100) / 65536;
+                                rem = ((progress * 100) % 65536) / 656;
+                                printf("Format in progress, %d.%02d%% done\n",
+                                       pr, rem);
+                        } else
+                                break;
+                }
+        }
+        printf("FORMAT MEDIUM Complete\n");
+        return 0;
+}
+
+#define VPD_DEVICE_ID 0x83
+#define VPD_ASSOC_LU 0
+#define VPD_ASSOC_TPORT 1
+#define TPROTO_ISCSI 5
+
+static char *
+get_lu_name(const unsigned char * ucp, int u_len, char * b, int b_len)
+{
+        int len, off, sns_dlen, dlen, k;
+        unsigned char u_sns[512];
+        char * cp;
+
+        len = u_len - 4;
+        ucp += 4;
+        off = -1;
+        if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU,
+                                    8 /* SCSI name string (sns) */,
+                                    3 /* UTF-8 */)) {
+                sns_dlen = ucp[off + 3];
+                memcpy(u_sns, ucp + off + 4, sns_dlen);
+                /* now want to check if this is iSCSI */
+                off = -1;
+                if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_TPORT,
+                                            8 /* SCSI name string (sns) */,
+                                            3 /* UTF-8 */)) {
+                        if ((0x80 & ucp[1]) &&
+                            (TPROTO_ISCSI == (ucp[0] >> 4))) {
+                                snprintf(b, b_len, "%.*s", sns_dlen, u_sns);
+                                return b;
+                        }
+                }
+        } else
+                sns_dlen = 0;
+        if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU,
+                                    3 /* NAA */, 1 /* binary */)) {
+                dlen = ucp[off + 3];
+                if (! ((8 == dlen) || (16 ==dlen)))
+                        return b;
+                cp = b;
+                for (k = 0; ((k < dlen) && (b_len > 1)); ++k) {
+                        snprintf(cp, b_len, "%02x", ucp[off + 4 + k]);
+                        cp += 2;
+                        b_len -= 2;
+                }
+        } else if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU,
+                                           2 /* EUI */, 1 /* binary */)) {
+                dlen = ucp[off + 3];
+                if (! ((8 == dlen) || (12 == dlen) || (16 ==dlen)))
+                        return b;
+                cp = b;
+                for (k = 0; ((k < dlen) && (b_len > 1)); ++k) {
+                        snprintf(cp, b_len, "%02x", ucp[off + 4 + k]);
+                        cp += 2;
+                        b_len -= 2;
+                }
+        } else if (sns_dlen > 0)
+                snprintf(b, b_len, "%.*s", sns_dlen, u_sns);
+        return b;
+}
+
+#define SAFE_STD_INQ_RESP_LEN 36
+#define VPD_SUPPORTED_VPDS 0x0
+#define VPD_UNIT_SERIAL_NUM 0x80
+#define VPD_DEVICE_ID 0x83
+
+static int
+print_dev_id(int fd, unsigned char * sinq_resp, int max_rlen,
+             const struct opts_t * op)
+{
+        int res, k, n, verb, pdt, has_sn, has_di;
+        unsigned char b[256];
+        char a[256];
+        char pdt_name[64];
+
+        verb = (op->verbose > 1) ? op->verbose - 1 : 0;
+        memset(sinq_resp, 0, max_rlen);
+        res = sg_ll_inquiry(fd, 0, 0 /* evpd */, 0 /* pg_op */, b,
+                            SAFE_STD_INQ_RESP_LEN, 1, verb);
+        if (res)
+                return res;
+        n = b[4] + 5;
+        if (n > SAFE_STD_INQ_RESP_LEN)
+                n = SAFE_STD_INQ_RESP_LEN;
+        memcpy(sinq_resp, b, (n < max_rlen) ? n : max_rlen);
+        if (n == SAFE_STD_INQ_RESP_LEN) {
+                pdt = b[0] & 0x1f;
+                printf("    %.8s  %.16s  %.4s   peripheral_type: %s [0x%x]\n",
+                       (const char *)(b + 8), (const char *)(b + 16),
+                       (const char *)(b + 32),
+                       sg_get_pdt_str(pdt, sizeof(pdt_name), pdt_name), pdt);
+                if (op->verbose)
+                        printf("      PROTECT=%d\n", !!(b[5] & 1));
+                if (b[5] & 1)
+                        printf("      << supports protection information>>"
+                               "\n");
+        } else {
+                pr2serr("Short INQUIRY response: %d bytes, expect at least "
+                        "36\n", n);
+                return SG_LIB_CAT_OTHER;
+        }
+        res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_SUPPORTED_VPDS, b,
+                            SAFE_STD_INQ_RESP_LEN, 1, verb);
+        if (res) {
+                if (op->verbose)
+                        pr2serr("VPD_SUPPORTED_VPDS gave res=%d\n", res);
+                return 0;
+        }
+        if (VPD_SUPPORTED_VPDS != b[1]) {
+                if (op->verbose)
+                        pr2serr("VPD_SUPPORTED_VPDS corrupted\n");
+                return 0;
+        }
+        n = sg_get_unaligned_be16(b + 2);
+        if (n > (SAFE_STD_INQ_RESP_LEN - 4))
+                n = (SAFE_STD_INQ_RESP_LEN - 4);
+        for (k = 0, has_sn = 0, has_di = 0; k < n; ++k) {
+                if (VPD_UNIT_SERIAL_NUM == b[4 + k]) {
+                        if (has_di) {
+                                if (op->verbose)
+                                        pr2serr("VPD_SUPPORTED_VPDS "
+                                                "dis-ordered\n");
+                                return 0;
+                        }
+                        ++has_sn;
+                } else if (VPD_DEVICE_ID == b[4 + k]) {
+                        ++has_di;
+                        break;
+                }
+        }
+        if (has_sn) {
+                res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_UNIT_SERIAL_NUM,
+                                    b, sizeof(b), 1, verb);
+                if (res) {
+                        if (op->verbose)
+                                pr2serr("VPD_UNIT_SERIAL_NUM gave res=%d\n",
+                                        res);
+                        return 0;
+                }
+                if (VPD_UNIT_SERIAL_NUM != b[1]) {
+                        if (op->verbose)
+                                pr2serr("VPD_UNIT_SERIAL_NUM corrupted\n");
+                        return 0;
+                }
+                n = sg_get_unaligned_be16(b + 2);
+                if (n > (int)(sizeof(b) - 4))
+                        n = (sizeof(b) - 4);
+                printf("      Unit serial number: %.*s\n", n,
+                       (const char *)(b + 4));
+        }
+        if (has_di) {
+                res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_DEVICE_ID, b,
+                                    sizeof(b), 1, verb);
+                if (res) {
+                        if (op->verbose)
+                                pr2serr("VPD_DEVICE_ID gave res=%d\n", res);
+                        return 0;
+                }
+                if (VPD_DEVICE_ID != b[1]) {
+                        if (op->verbose)
+                                pr2serr("VPD_DEVICE_ID corrupted\n");
+                        return 0;
+                }
+                n = sg_get_unaligned_be16(b + 2);
+                if (n > (int)(sizeof(b) - 4))
+                        n = (sizeof(b) - 4);
+                n = strlen(get_lu_name(b, n + 4, a, sizeof(a)));
+                if (n > 0)
+                        printf("      LU name: %.*s\n", n, a);
+        }
+        return 0;
+}
+
+#define RCAP_REPLY_LEN 32
+
+/* Returns block size or -2 if do_16==0 and the number of blocks is too
+ * big, or returns -1 for other error. */
+static int
+print_read_cap(int fd, const struct opts_t * op)
+{
+        int res;
+        unsigned char resp_buff[RCAP_REPLY_LEN];
+        unsigned int last_blk_addr, block_size;
+        uint64_t llast_blk_addr;
+        char b[80];
+
+        if (op->do_rcap16) {
+                res = sg_ll_readcap_16(fd, 0 /* pmi */, 0 /* llba */,
+                                       resp_buff, 32, 1, op->verbose);
+                if (0 == res) {
+                        llast_blk_addr = sg_get_unaligned_be64(resp_buff + 0);
+                        block_size = sg_get_unaligned_be32(resp_buff + 8);
+                        printf("Read Capacity (16) results:\n");
+                        printf("   Protection: prot_en=%d, p_type=%d, "
+                               "p_i_exponent=%d\n",
+                               !!(resp_buff[12] & 0x1),
+                               ((resp_buff[12] >> 1) & 0x7),
+                               ((resp_buff[13] >> 4) & 0xf));
+                        printf("   Logical block provisioning: lbpme=%d, "
+                               "lbprz=%d\n", !!(resp_buff[14] & 0x80),
+                               !!(resp_buff[14] & 0x40));
+                        printf("   Logical blocks per physical block "
+                               "exponent=%d\n", resp_buff[13] & 0xf);
+                        printf("   Lowest aligned logical block address=%d\n",
+                               0x3fff & sg_get_unaligned_be16(resp_buff +
+                                                              14));
+                        printf("   Number of logical blocks=%" PRIu64 "\n",
+                               llast_blk_addr + 1);
+                        printf("   Logical block size=%u bytes\n",
+                               block_size);
+                        return (int)block_size;
+                }
+        } else {
+                res = sg_ll_readcap_10(fd, 0 /* pmi */, 0 /* lba */,
+                                       resp_buff, 8, 1, op->verbose);
+                if (0 == res) {
+                        last_blk_addr = sg_get_unaligned_be32(resp_buff + 0);
+                        block_size = sg_get_unaligned_be32(resp_buff + 4);
+                        if (0xffffffff == last_blk_addr) {
+                            if (op->verbose)
+                                printf("Read Capacity (10) reponse "
+                                       "indicates that Read Capacity (16) "
+                                       "is required\n");
+                            return -2;
+                        }
+                        printf("Read Capacity (10) results:\n");
+                        printf("   Number of logical blocks=%u\n",
+                               last_blk_addr + 1);
+                        printf("   Logical block size=%u bytes\n",
+                               block_size);
+                        return (int)block_size;
+                }
+        }
+        sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+        pr2serr("READ CAPACITY (%d): %s\n", (op->do_rcap16 ? 16 : 10), b);
+        return -1;
+}
+
+
+int
+main(int argc, char **argv)
+{
+        int fd, res, calc_len, bd_len, dev_specific_param;
+        int offset, j, n, bd_blk_len, prob, len, pdt;
+        uint64_t ull;
+        char b[80];
+        unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN];
+        int ret = 0;
+        struct opts_t opts;
+        struct opts_t * op;
+
+        op = &opts;
+        memset(op, 0, sizeof(opts));
+        op->cmplst = 1;
+        op->mode_page = RW_ERROR_RECOVERY_PAGE;
+        op->pollt = DEF_POLL_TYPE;
+        op->tape = -1;
+        while (1) {
+                int option_index = 0;
+                int c;
+
+                c = getopt_long(argc, argv,
+                                "c:C:Def:FhIlM:pP:q:rRs:St:T:vVwx:y6",
+                                long_options, &option_index);
+                if (c == -1)
+                        break;
+
+                switch (c) {
+                case 'c':
+                        if (0 == strcmp("-1", optarg))
+                                op->blk_count = -1;
+                        else {
+                                op->blk_count = sg_get_llnum(optarg);
+                                if (-1 == op->blk_count) {
+                                        pr2serr("bad argument to '--count'\n");
+                                        return SG_LIB_SYNTAX_ERROR;
+                                }
+                        }
+                        break;
+                case 'C':
+                        op->cmplst = sg_get_num(optarg);
+                        if ((op->cmplst < 0) || (op->cmplst > 1)) {
+                                pr2serr("bad argument to '--cmplst', want 0 "
+                                        "or 1\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'D':
+                        op->dcrt = 1;
+                        break;
+                case 'e':
+                        op->early = true;
+                        break;
+                case 'f':
+                        op->fmtpinfo = sg_get_num(optarg);
+                        if ((op->fmtpinfo < 0) || ( op->fmtpinfo > 3)) {
+                                pr2serr("bad argument to '--fmtpinfo', "
+                                        "accepts 0 to 3 inclusive\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'F':
+                        ++op->format;
+                        break;
+                case 'h':
+                        usage();
+                        return 0;
+                case 'I':
+                        op->ip_def = true;
+                        break;
+                case 'l':
+                        op->long_lba = true;
+                        op->do_rcap16 = true;
+                        break;
+                case 'M':
+                        op->mode_page = sg_get_num(optarg);
+                        if ((op->mode_page < 0) || ( op->mode_page > 62)) {
+                                pr2serr("bad argument to '--mode', accepts "
+                                        "0 to 62 inclusive\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'p':
+                        op->pinfo = true;
+                        break;
+                case 'P':
+                        op->pfu = sg_get_num(optarg);
+                        if ((op->pfu < 0) || ( op->pfu > 7)) {
+                                pr2serr("bad argument to '--pfu', accepts 0 "
+                                        "to 7 inclusive\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'q':
+                        op->pie = sg_get_num(optarg);
+                        if ((op->pie < 0) || (op->pie > 15)) {
+                                pr2serr("bad argument to '--pie', accepts 0 "
+                                        "to 15 inclusive\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'r':
+                        op->resize = true;
+                        break;
+                case 'R':
+                        op->rto_req = true;
+                        break;
+                case 's':
+                        op->blk_size = sg_get_num(optarg);
+                        if (op->blk_size <= 0) {
+                                pr2serr("bad argument to '--size', want arg "
+                                        "> 0\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'S':
+                        op->sec_init = true;
+                        break;
+                case 't':
+                        op->ffmt = sg_get_num(optarg);
+                        if ((op->ffmt < 0) || ( op->ffmt > 3)) {
+                                pr2serr("bad argument to '--ffmt', "
+                                        "accepts 0 to 3 inclusive\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'T':
+                        if (('-' == optarg[0]) && ('1' == optarg[1]) &&
+                            ('\0' == optarg[2])) {
+                                op->tape = -1;
+                                break;
+                        }
+                        op->tape = sg_get_num(optarg);
+                        if ((op->tape < 0) || ( op->tape > 15)) {
+                                pr2serr("bad argument to '--tape', accepts "
+                                        "0 to 15 inclusive\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'v':
+                        op->verbose++;
+                        break;
+                case 'V':
+                        pr2serr("sg_format version: %s\n", version_str);
+                        return 0;
+                case 'w':
+                        op->fwait = true;
+                        break;
+                case 'x':
+                        op->pollt = !!sg_get_num(optarg);
+                        op->pollt_given = true;
+                        break;
+                case 'y':
+                        op->verify++;
+                        break;
+                case '6':
+                        op->mode6 = true;
+                        break;
+                default:
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                }
+        }
+        if (optind < argc) {
+                if (NULL == op->device_name) {
+                        op->device_name = argv[optind];
+                        ++optind;
+                }
+        }
+        if (optind < argc) {
+                for (; optind < argc; ++optind)
+                        pr2serr("Unexpected extra argument: %s\n",
+                                argv[optind]);
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+        if (NULL == op->device_name) {
+                pr2serr("no DEVICE name given\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->format && (op->tape >= 0)) {
+                pr2serr("Cannot choose both '--format' and '--tape='; disk "
+                        "or tape, choose one only\n");
+                return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->ip_def && op->sec_init) {
+                pr2serr("'--ip_def' and '--security' contradict, choose "
+                        "one\n");
+                return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->resize) {
+                if (op->format) {
+                        pr2serr("both '--format' and '--resize' not "
+                                "permitted\n");
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                } else if (0 == op->blk_count) {
+                        pr2serr("'--resize' needs a '--count' (other than "
+                                "0)\n");
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                } else if (0 != op->blk_size) {
+                        pr2serr("'--resize' not compatible with '--size'\n");
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                }
+        }
+        if ((op->pinfo > 0) || (op->rto_req > 0) || (op->fmtpinfo > 0)) {
+                if ((op->pinfo || op->rto_req) && op->fmtpinfo) {
+                        pr2serr("confusing with both '--pinfo' or "
+                                "'--rto_req' together with\n'--fmtpinfo', "
+                                "best use '--fmtpinfo' only\n");
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                }
+                if (op->pinfo)
+                        op->fmtpinfo |= 2;
+                if (op->rto_req)
+                        op->fmtpinfo |= 1;
+        }
+
+        if ((fd = sg_cmds_open_device(op->device_name, 0 /* read write */,
+                                      op->verbose)) < 0) {
+                pr2serr("error opening device file: %s: %s\n",
+                        op->device_name, safe_strerror(-fd));
+                return SG_LIB_FILE_ERROR;
+        }
+
+        if (op->format > 2)
+                goto format_only;
+
+        ret = print_dev_id(fd, inq_resp, sizeof(inq_resp), op);
+        if (ret)
+                goto out;
+        pdt = 0x1f & inq_resp[0];
+        if (op->format) {
+                if ((PDT_DISK != pdt) && (PDT_OPTICAL != pdt) &&
+                    (PDT_RBC != pdt)) {
+                        pr2serr("This format is only defined for disks "
+                                "(using SBC-2 or RBC) and MO media\n");
+                        ret = SG_LIB_CAT_MALFORMED;
+                        goto out;
+                }
+        } else if (op->tape >= 0) {
+                if (! ((PDT_TAPE == pdt) || (PDT_MCHANGER == pdt) ||
+                       (PDT_ADC == pdt))) {
+                        pr2serr("This format is only defined for tapes\n");
+                        ret = SG_LIB_CAT_MALFORMED;
+                        goto out;
+                }
+                goto format_med;
+        }
+
+again_with_long_lba:
+        memset(dbuff, 0, MAX_BUFF_SZ);
+        if (op->mode6)
+                res = sg_ll_mode_sense6(fd, 0 /* DBD */, 0 /* current */,
+                                        op->mode_page, 0 /* subpage */, dbuff,
+                                        MAX_BUFF_SZ, 1, op->verbose);
+        else
+                res = sg_ll_mode_sense10(fd, op->long_lba, 0 /* DBD */,
+                                         0 /* current */, op->mode_page,
+                                         0 /* subpage */, dbuff,
+                                         MAX_BUFF_SZ, 1, op->verbose);
+        ret = res;
+        if (res) {
+                if (SG_LIB_CAT_ILLEGAL_REQ == res) {
+                        if (op->long_lba && (! op->mode6))
+                                pr2serr("bad field in MODE SENSE (%d) "
+                                        "[longlba flag not supported?]\n",
+                                        (op->mode6 ? 6 : 10));
+                        else
+                                pr2serr("bad field in MODE SENSE (%d) "
+                                        "[mode_page %d not supported?]\n",
+                                        (op->mode6 ? 6 : 10), op->mode_page);
+                } else {
+                        sg_get_category_sense_str(res, sizeof(b), b,
+                                                  op->verbose);
+                        pr2serr("MODE SENSE (%d) command: %s\n",
+                                (op->mode6 ? 6 : 10), b);
+                }
+                if (0 == op->verbose)
+                        pr2serr("    try '-v' for more information\n");
+                goto out;
+        }
+        if (op->mode6) {
+                calc_len = dbuff[0] + 1;
+                dev_specific_param = dbuff[2];
+                bd_len = dbuff[3];
+                op->long_lba = 0;
+                offset = 4;
+                /* prepare for mode select */
+                dbuff[0] = 0;
+                dbuff[1] = 0;
+                dbuff[2] = 0;
+        } else {
+                calc_len = sg_get_unaligned_be16(dbuff + 0);
+                dev_specific_param = dbuff[3];
+                bd_len = sg_get_unaligned_be16(dbuff + 6);
+                op->long_lba = (dbuff[4] & 1);
+                offset = 8;
+                /* prepare for mode select */
+                dbuff[0] = 0;
+                dbuff[1] = 0;
+                dbuff[2] = 0;
+                dbuff[3] = 0;
+        }
+        if ((offset + bd_len) < calc_len)
+                dbuff[offset + bd_len] &= 0x7f;  /* clear PS bit in mpage */
+        prob = 0;
+        bd_blk_len = 0;
+        printf("Mode Sense (block descriptor) data, prior to changes:\n");
+        if (dev_specific_param & 0x40)
+                printf("  <<< Write Protect (WP) bit set >>>\n");
+        if (bd_len > 0) {
+                ull = op->long_lba ? sg_get_unaligned_be64(dbuff + offset) :
+                                 sg_get_unaligned_be32(dbuff + offset);
+                if ((0 == op->long_lba) && (0xffffffff == ull)) {
+                        if (op->verbose)
+                                pr2serr("Mode sense number of blocks maxed "
+                                        "out, set longlba\n");
+                        op->long_lba = 1;
+                        op->mode6 = 0;
+                        op->do_rcap16 = 1;
+                        goto again_with_long_lba;
+                }
+                bd_blk_len = op->long_lba ?
+                                 sg_get_unaligned_be32(dbuff + offset + 12) :
+                                 sg_get_unaligned_be24(dbuff + offset + 5);
+                if (op->long_lba) {
+                        printf("  <<< longlba flag set (64 bit lba) >>>\n");
+                        if (bd_len != 16)
+                                prob = 1;
+                } else if (bd_len != 8)
+                        prob = 1;
+                printf("  Number of blocks=%" PRIu64 " [0x%" PRIx64 "]\n",
+                       ull, ull);
+                printf("  Block size=%d [0x%x]\n", bd_blk_len, bd_blk_len);
+        } else {
+                printf("  No block descriptors present\n");
+                prob = 1;
+        }
+        if (op->resize || (op->format && ((op->blk_count != 0) ||
+              ((op->blk_size > 0) && (op->blk_size != bd_blk_len))))) {
+                /* want to run MODE SELECT */
+
+/* Working Draft SCSI Primary Commands - 3 (SPC-3)    pg 255
+**
+** If the SCSI device doesn't support changing its capacity by changing
+** the NUMBER OF BLOCKS field using the MODE SELECT command, the value
+** in the NUMBER OF BLOCKS field is ignored. If the device supports changing
+** its capacity by changing the NUMBER OF BLOCKS field, then the
+** NUMBER OF BLOCKS field is interpreted as follows:
+**      a) If the number of blocks is set to zero, the device shall retain
+**         its current capacity if the block size has not changed. If the
+**         number of blocks is set to zero and the block size has changed,
+**         the device shall be set to its maximum capacity when the new
+**         block size takes effect;
+**
+**      b) If the number of blocks is greater than zero and less than or
+**         equal to its maximum capacity, the device shall be set to that
+**         number of blocks. If the block size has not changed, the device
+**         shall not become format corrupted. This capacity setting shall be
+**         retained through power cycles, hard resets, logical unit resets,
+**         and I_T nexus losses;
+**
+**      c) If the number of blocks field is set to a value greater than the
+**         maximum capacity of the device and less than FFFF FFFFh, then the
+**         command is terminated with a CHECK CONDITION status. The sense key
+**         is set to ILLEGAL REQUEST. The device shall retain its previous
+**         block descriptor settings; or
+**
+**      d) If the number of blocks is set to FFFF FFFFh, the device shall be
+**         set to its maximum capacity. If the block size has not changed,
+**         the device shall not become format corrupted. This capacity setting
+**         shall be retained through power cycles, hard resets, logical unit
+**         resets, and I_T nexus losses.
+*/
+
+                if (prob) {
+                        pr2serr("Need to perform MODE SELECT (to change "
+                                "number or blocks or block length)\n");
+                        pr2serr("but (single) block descriptor not found "
+                                "in earlier MODE SENSE\n");
+                        ret = SG_LIB_CAT_MALFORMED;
+                        goto out;
+                }
+                if (op->blk_count != 0)  {
+                        len = (op->long_lba ? 8 : 4);
+                        for (j = 0; j < len; ++j) {
+                                n = (len - j - 1) * 8;
+                                dbuff[offset + j] =
+                                            (op->blk_count >> n) & 0xff;
+                        }
+                } else if ((op->blk_size > 0) &&
+                           (op->blk_size != bd_blk_len)) {
+                        len = (op->long_lba ? 8 : 4);
+                        for (j = 0; j < len; ++j)
+                                dbuff[offset + j] = 0;
+                }
+                if ((op->blk_size > 0) && (op->blk_size != bd_blk_len)) {
+                        if (op->long_lba)
+                                sg_put_unaligned_be32((uint32_t)op->blk_size,
+                                                      dbuff + offset + 12);
+                        else
+                                sg_put_unaligned_be24((uint32_t)op->blk_size,
+                                                      dbuff + offset + 5);
+                }
+                if (op->mode6)
+                        res = sg_ll_mode_select6(fd, 1 /* PF */, 1 /* SP */,
+                                         dbuff, calc_len, 1, op->verbose);
+                else
+                        res = sg_ll_mode_select10(fd, 1 /* PF */, 1 /* SP */,
+                                          dbuff, calc_len, 1, op->verbose);
+                ret = res;
+                if (res) {
+                        sg_get_category_sense_str(res, sizeof(b), b,
+                                                  op->verbose);
+                        pr2serr("MODE SELECT command: %s\n", b);
+                        if (0 == op->verbose)
+                                pr2serr("    try '-v' for more information\n");
+                        goto out;
+                }
+        }
+        if (op->resize) {
+                printf("Resize operation seems to have been successful\n");
+                goto out;
+        }
+        else if (! op->format) {
+                res = print_read_cap(fd, op);
+                if (-2 == res) {
+                        op->do_rcap16 = 1;
+                        res = print_read_cap(fd, op);
+                }
+                if (res < 0)
+                        ret = -1;
+                if ((res > 0) && (bd_blk_len > 0) &&
+                    (res != (int)bd_blk_len)) {
+                        printf("  Warning: mode sense and read capacity "
+                               "report different block sizes [%d,%d]\n",
+                               bd_blk_len, res);
+                        printf("           Probably needs format\n");
+                }
+                if ((PDT_TAPE == pdt) || (PDT_MCHANGER == pdt) ||
+                    (PDT_ADC == pdt))
+                    printf("No changes made. To format use '--tape='.\n");
+                else
+                    printf("No changes made. To format use '--format'. To "
+                           "resize use '--resize'\n");
+                goto out;
+        }
+
+        if (op->format) {
+format_only:
+#if 1
+                printf("\nA FORMAT UNIT will commence in 15 seconds\n");
+                printf("    ALL data on %s will be DESTROYED\n",
+                       op->device_name);
+                printf("        Press control-C to abort\n");
+                sleep_for(5);
+                printf("\nA FORMAT UNIT will commence in 10 seconds\n");
+                printf("    ALL data on %s will be DESTROYED\n",
+                       op->device_name);
+                printf("        Press control-C to abort\n");
+                sleep_for(5);
+                printf("\nA FORMAT UNIT will commence in 5 seconds\n");
+                printf("    ALL data on %s will be DESTROYED\n",
+                       op->device_name);
+                printf("        Press control-C to abort\n");
+                sleep_for(5);
+                res = scsi_format_unit(fd, op);
+                ret = res;
+                if (res) {
+                        pr2serr("FORMAT UNIT failed\n");
+                        if (0 == op->verbose)
+                                pr2serr("    try '-v' for more "
+                                        "information\n");
+                }
+#else
+                pr2serr("FORMAT UNIT ignored, testing\n");
+#endif
+        }
+        goto out;
+
+format_med:
+        if (! op->pollt_given)
+                op->pollt = 1;  /* SSC-5 specifies REQUEST SENSE polling */
+        printf("\nA FORMAT MEDIUM will commence in 15 seconds\n");
+        printf("    ALL data on %s will be DESTROYED\n",
+               op->device_name);
+        printf("        Press control-C to abort\n");
+        sleep_for(5);
+        printf("\nA FORMAT MEDIUM will commence in 10 seconds\n");
+        printf("    ALL data on %s will be DESTROYED\n",
+               op->device_name);
+        printf("        Press control-C to abort\n");
+        sleep_for(5);
+        printf("\nA FORMAT MEDIUM will commence in 5 seconds\n");
+        printf("    ALL data on %s will be DESTROYED\n",
+               op->device_name);
+        printf("        Press control-C to abort\n");
+        sleep_for(5);
+        res = scsi_format_medium(fd, op);
+        ret = res;
+        if (res) {
+                pr2serr("FORMAT MEDIUM failed\n");
+                if (0 == op->verbose)
+                        pr2serr("    try '-v' for more "
+                                "information\n");
+        }
+
+out:
+        res = sg_cmds_close_device(fd);
+        if (res < 0) {
+                pr2serr("close error: %s\n", safe_strerror(-res));
+                if (0 == ret)
+                        return SG_LIB_FILE_ERROR;
+        }
+        return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_get_config.c b/sg3_utils/src/sg_get_config.c
new file mode 100644
index 0000000..2e20a6b
--- /dev/null
+++ b/sg3_utils/src/sg_get_config.c
@@ -0,0 +1,1114 @@
+/*
+ * Copyright (c) 2004-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_mmc.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ * This program outputs information provided by a SCSI "Get Configuration"
+   command [0x46] which is only defined for CD/DVDs (in MMC-2,3,4,5,6).
+
+*/
+
+static const char * version_str = "0.41 20160131";    /* mmc6r02 */
+
+#define MX_ALLOC_LEN 8192
+#define NAME_BUFF_SZ 64
+
+#define ME "sg_get_config: "
+
+
+static unsigned char resp_buffer[MX_ALLOC_LEN];
+
+static struct option long_options[] = {
+        {"brief", 0, 0, 'b'},
+        {"current", 0, 0, 'c'},
+        {"help", 0, 0, 'h'},
+        {"hex", 0, 0, 'H'},
+        {"inner-hex", 0, 0, 'i'},
+        {"list", 0, 0, 'l'},
+        {"raw", 0, 0, 'R'},
+        {"readonly", 0, 0, 'q'},
+        {"rt", 1, 0, 'r'},
+        {"starting", 1, 0, 's'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage:  sg_get_config [--brief] [--current] [--help] [--hex] "
+            "[--inner-hex]\n"
+            "                      [--list] [--raw] [--readonly] [--rt=RT]\n"
+            "                      [--starting=FC] [--verbose] [--version] "
+            "DEVICE\n"
+            "  where:\n"
+            "    --brief|-b       only give feature names of DEVICE "
+            "(don't decode)\n"
+            "    --current|-c     equivalent to '--rt=1' (show "
+            "current)\n"
+            "    --help|-h        print usage message then exit\n"
+            "    --hex|-H         output response in hex\n"
+            "    --inner-hex|-i    decode to feature name, then output "
+            "features in hex\n"
+            "    --list|-l        list all known features + profiles "
+            "(ignore DEVICE)\n"
+            "    --raw|-R         output in binary (to stdout)\n"
+            "    --readonly|-q    open DEVICE read-only (def: open it "
+            "read-write)\n"
+            "    --rt=RT|-r RT    default value is 0\n"
+            "                     0 -> all feature descriptors (regardless "
+            "of currency)\n"
+            "                     1 -> all current feature descriptors\n"
+            "                     2 -> only feature descriptor matching "
+            "'starting'\n"
+            "    --starting=FC|-s FC    starting from feature "
+            "code (FC) value\n"
+            "    --verbose|-v     verbose\n"
+            "    --version|-V     output version string\n\n"
+            "Get configuration information for MMC drive and/or media\n");
+}
+
+struct val_desc_t {
+        int val;
+        const char * desc;
+};
+
+static struct val_desc_t profile_desc_arr[] = {
+        {0x0, "No current profile"},
+        {0x1, "Non-removable disk (obs)"},
+        {0x2, "Removable disk"},
+        {0x3, "Magneto optical erasable"},
+        {0x4, "Optical write once"},
+        {0x5, "AS-MO"},
+        {0x8, "CD-ROM"},
+        {0x9, "CD-R"},
+        {0xa, "CD-RW"},
+        {0x10, "DVD-ROM"},
+        {0x11, "DVD-R sequential recording"},
+        {0x12, "DVD-RAM"},
+        {0x13, "DVD-RW restricted overwrite"},
+        {0x14, "DVD-RW sequential recording"},
+        {0x15, "DVD-R dual layer sequental recording"},
+        {0x16, "DVD-R dual layer jump recording"},
+        {0x17, "DVD-RW dual layer"},
+        {0x18, "DVD-Download disc recording"},
+        {0x1a, "DVD+RW"},
+        {0x1b, "DVD+R"},
+        {0x20, "DDCD-ROM"},
+        {0x21, "DDCD-R"},
+        {0x22, "DDCD-RW"},
+        {0x2a, "DVD+RW dual layer"},
+        {0x2b, "DVD+R dual layer"},
+        {0x40, "BD-ROM"},
+        {0x41, "BD-R SRM"},
+        {0x42, "BD-R RRM"},
+        {0x43, "BD-RE"},
+        {0x50, "HD DVD-ROM"},
+        {0x51, "HD DVD-R"},
+        {0x52, "HD DVD-RAM"},
+        {0x53, "HD DVD-RW"},
+        {0x58, "HD DVD-R dual layer"},
+        {0x5a, "HD DVD-RW dual layer"},
+        {0xffff, "Non-conforming profile"},
+        {-1, NULL},
+};
+
+static const char *
+get_profile_str(int profile_num, char * buff)
+{
+    const struct val_desc_t * pdp;
+
+    for (pdp = profile_desc_arr; pdp->desc; ++pdp) {
+        if (pdp->val == profile_num) {
+            strcpy(buff, pdp->desc);
+            return buff;
+        }
+    }
+    snprintf(buff, 64, "0x%x", profile_num);
+    return buff;
+}
+
+static struct val_desc_t feature_desc_arr[] = {
+        {0x0, "Profile list"},
+        {0x1, "Core"},
+        {0x2, "Morphing"},
+        {0x3, "Removable media"},
+        {0x4, "Write Protect"},
+        {0x10, "Random readable"},
+        {0x1d, "Multi-read"},
+        {0x1e, "CD read"},
+        {0x1f, "DVD read"},
+        {0x20, "Random writable"},
+        {0x21, "Incremental streaming writable"},
+        {0x22, "Sector erasable"},
+        {0x23, "Formattable"},
+        {0x24, "Hardware defect management"},
+        {0x25, "Write once"},
+        {0x26, "Restricted overwrite"},
+        {0x27, "CD-RW CAV write"},
+        {0x28, "MRW"},          /* Mount Rainier reWritable */
+        {0x29, "Enhanced defect reporting"},
+        {0x2a, "DVD+RW"},
+        {0x2b, "DVD+R"},
+        {0x2c, "Rigid restricted overwrite"},
+        {0x2d, "CD track-at-once"},
+        {0x2e, "CD mastering (session at once)"},
+        {0x2f, "DVD-R/-RW write"},
+        {0x30, "Double density CD read"},
+        {0x31, "Double density CD-R write"},
+        {0x32, "Double density CD-RW write"},
+        {0x33, "Layer jump recording"},
+        {0x34, "LJ rigid restricted oberwrite"},
+        {0x35, "Stop long operation"},
+        {0x37, "CD-RW media write support"},
+        {0x38, "BD-R POW"},
+        {0x3a, "DVD+RW dual layer"},
+        {0x3b, "DVD+R dual layer"},
+        {0x40, "BD read"},
+        {0x41, "BD write"},
+        {0x42, "TSR (timely safe recording)"},
+        {0x50, "HD DVD read"},
+        {0x51, "HD DVD write"},
+        {0x52, "HD DVD-RW fragment recording"},
+        {0x80, "Hybrid disc"},
+        {0x100, "Power management"},
+        {0x101, "SMART"},
+        {0x102, "Embedded changer"},
+        {0x103, "CD audio external play"},
+        {0x104, "Microcode upgrade"},
+        {0x105, "Timeout"},
+        {0x106, "DVD CSS"},
+        {0x107, "Real time streaming"},
+        {0x108, "Drive serial number"},
+        {0x109, "Media serial number"},
+        {0x10a, "Disc control blocks"},
+        {0x10b, "DVD CPRM"},
+        {0x10c, "Firmware information"},
+        {0x10d, "AACS"},
+        {0x10e, "DVD CSS managed recording"},
+        {0x110, "VCPS"},
+        {0x113, "SecurDisc"},
+        {0x120, "BD CPS"},
+        {0x142, "OSSC"},
+};
+
+static const char *
+get_feature_str(int feature_num, char * buff)
+{
+    int k, num;
+
+    num = sizeof(feature_desc_arr) / sizeof(feature_desc_arr[0]);
+    for (k = 0; k < num; ++k) {
+        if (feature_desc_arr[k].val == feature_num) {
+            strcpy(buff, feature_desc_arr[k].desc);
+            return buff;
+        }
+    }
+    snprintf(buff, 64, "0x%x", feature_num);
+    return buff;
+}
+
+static void
+dStrRaw(const char * str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+static void
+decode_feature(int feature, unsigned char * ucp, int len)
+{
+    int k, num, n, profile;
+    char buff[128];
+    const char * cp;
+
+    cp = "";
+    switch (feature) {
+    case 0:     /* Profile list */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 2), !!(ucp[2] & 1),
+               feature);
+        printf("    available profiles [more recent typically higher "
+               "in list]:\n");
+        for (k = 4; k < len; k += 4) {
+            profile = sg_get_unaligned_be16(ucp + k);
+            printf("      profile: %s , currentP=%d\n",
+                   get_profile_str(profile, buff), !!(ucp[k + 2] & 1));
+        }
+        break;
+    case 1:     /* Core */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 2), !!(ucp[2] & 1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        num = sg_get_unaligned_be32(ucp + 4);
+        switch (num) {
+        case 0: cp = "unspecified"; break;
+        case 1: cp = "SCSI family"; break;
+        case 2: cp = "ATAPI"; break;
+        case 3: cp = "IEEE 1394 - 1995"; break;
+        case 4: cp = "IEEE 1394A"; break;
+        case 5: cp = "Fibre channel"; break;
+        case 6: cp = "IEEE 1394B"; break;
+        case 7: cp = "Serial ATAPI"; break;
+        case 8: cp = "USB (both 1 and 2)"; break;
+        case 0xffff: cp = "vendor unique"; break;
+        default:
+            snprintf(buff, sizeof(buff), "[0x%x]", num);
+            cp = buff;
+            break;
+        }
+        printf("      Physical interface standard: %s", cp);
+        if (len > 8)
+            printf(", INQ2=%d, DBE=%d\n", !!(ucp[8] & 2), !!(ucp[8] & 1));
+        else
+            printf("\n");
+        break;
+    case 2:     /* Morphing */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 2), !!(ucp[2] & 1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      OCEvent=%d, ASYNC=%d\n", !!(ucp[4] & 2),
+               !!(ucp[4] & 1));
+        break;
+    case 3:     /* Removable medium */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 2), !!(ucp[2] & 1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        num = (ucp[4] >> 5) & 0x7;
+        switch (num) {
+        case 0: cp = "Caddy/slot type"; break;
+        case 1: cp = "Tray type"; break;
+        case 2: cp = "Pop-up type"; break;
+        case 4: cp = "Embedded changer with individually changeable discs";
+            break;
+        case 5: cp = "Embedded changer using a magazine"; break;
+        default:
+            snprintf(buff, sizeof(buff), "[0x%x]", num);
+            cp = buff;
+            break;
+        }
+        printf("      Loading mechanism: %s\n", cp);
+        printf("      Load=%d, Eject=%d, Prevent jumper=%d, Lock=%d\n",
+               !!(ucp[4] & 0x10), !!(ucp[4] & 0x8), !!(ucp[4] & 0x4),
+               !!(ucp[4] & 0x1));
+        break;
+    case 4:     /* Write protect */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      DWP=%d, WDCB=%d, SPWP=%d, SSWPP=%d\n", !!(ucp[4] & 0x8),
+               !!(ucp[4] & 0x4), !!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
+        break;
+    case 0x10:     /* Random readable */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 12) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        num = sg_get_unaligned_be32(ucp + 4);
+        printf("      Logical block size=0x%x, blocking=0x%x, PP=%d\n",
+               num, sg_get_unaligned_be16(ucp + 8), !!(ucp[10] & 0x1));
+        break;
+    case 0x1d:     /* Multi-read */
+    case 0x22:     /* Sector erasable */
+    case 0x26:     /* Restricted overwrite */
+    case 0x27:     /* CDRW CAV write */
+    case 0x35:     /* Stop long operation */
+    case 0x38:     /* BD-R pseudo-overwrite (POW) */
+    case 0x42:     /* TSR (timely safe recording) */
+    case 0x100:    /* Power management */
+    case 0x109:    /* Media serial number */
+    case 0x110:    /* VCPS */
+    case 0x113:    /* SecurDisc */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        break;
+    case 0x1e:     /* CD read */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      DAP=%d, C2 flags=%d, CD-Text=%d\n", !!(ucp[4] & 0x80),
+               !!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
+        break;
+    case 0x1f:     /* DVD read */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len > 7)
+            printf("      MULTI110=%d, Dual-RW=%d, Dual-R=%d\n",
+                   !!(ucp[4] & 0x1), !!(ucp[6] & 0x2), !!(ucp[6] & 0x1));
+        break;
+    case 0x20:     /* Random writable */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 16) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        num = sg_get_unaligned_be32(ucp + 4);
+        n = sg_get_unaligned_be32(ucp + 8);
+        printf("      Last lba=0x%x, Logical block size=0x%x, blocking=0x%x,"
+               " PP=%d\n", num, n, sg_get_unaligned_be16(ucp + 12),
+               !!(ucp[14] & 0x1));
+        break;
+    case 0x21:     /* Incremental streaming writable */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      Data block types supported=0x%x, TRIO=%d, ARSV=%d, "
+               "BUF=%d\n", sg_get_unaligned_be16(ucp + 4), !!(ucp[6] & 0x4),
+               !!(ucp[6] & 0x2), !!(ucp[6] & 0x1));
+        num = ucp[7];
+        printf("      Number of link sizes=%d\n", num);
+        for (k = 0; k < num; ++k)
+            printf("        %d\n", ucp[8 + k]);
+        break;
+    /* case 0x22:     Sector erasable -> see 0x1d entry */
+    case 0x23:     /* Formattable */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len > 4)
+            printf("      BD-RE: RENoSA=%d, Expand=%d, QCert=%d, Cert=%d, "
+                   "FRF=%d\n", !!(ucp[4] & 0x8), !!(ucp[4] & 0x4),
+                   !!(ucp[4] & 0x2), !!(ucp[4] & 0x1), !!(ucp[5] & 0x80));
+        if (len > 8)
+            printf("      BD-R: RRM=%d\n", !!(ucp[8] & 0x1));
+        break;
+    case 0x24:     /* Hardware defect management */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len > 4)
+            printf("      SSA=%d\n", !!(ucp[4] & 0x80));
+        break;
+    case 0x25:     /* Write once */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 12) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        num = sg_get_unaligned_be16(ucp + 4);
+        printf("      Logical block size=0x%x, blocking=0x%x, PP=%d\n",
+               num, sg_get_unaligned_be16(ucp + 8), !!(ucp[10] & 0x1));
+        break;
+    /* case 0x26:     Restricted overwrite -> see 0x1d entry */
+    /* case 0x27:     CDRW CAV write -> see 0x1d entry */
+    case 0x28:     /* MRW  (Mount Rainier reWriteable) */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len > 4)
+            printf("      DVD+Write=%d, DVD+Read=%d, Write=%d\n",
+                   !!(ucp[4] & 0x4), !!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
+        break;
+    case 0x29:     /* Enhanced defect reporting */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      DRT-DM=%d, number of DBI cache zones=0x%x, number of "
+               "entries=0x%x\n", !!(ucp[4] & 0x1), ucp[5],
+               sg_get_unaligned_be16(ucp + 6));
+        break;
+    case 0x2a:     /* DVD+RW */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      Write=%d, Quick start=%d, Close only=%d\n",
+               !!(ucp[4] & 0x1), !!(ucp[5] & 0x2), !!(ucp[5] & 0x1));
+        break;
+    case 0x2b:     /* DVD+R */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      Write=%d\n", !!(ucp[4] & 0x1));
+        break;
+    case 0x2c:     /* Rigid restricted overwrite */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      DSDG=%d, DSDR=%d, Intermediate=%d, Blank=%d\n",
+               !!(ucp[4] & 0x8), !!(ucp[4] & 0x4), !!(ucp[4] & 0x2),
+               !!(ucp[4] & 0x1));
+        break;
+    case 0x2d:     /* CD Track at once */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      BUF=%d, R-W raw=%d, R-W pack=%d, Test write=%d\n",
+               !!(ucp[4] & 0x40), !!(ucp[4] & 0x10), !!(ucp[4] & 0x8),
+               !!(ucp[4] & 0x4));
+        printf("      CD-RW=%d, R-W sub-code=%d, Data type supported=%d\n",
+               !!(ucp[4] & 0x2), !!(ucp[4] & 0x1),
+               sg_get_unaligned_be16(ucp + 6));
+        break;
+    case 0x2e:     /* CD mastering (session at once) */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      BUF=%d, SAO=%d, Raw MS=%d, Raw=%d\n",
+               !!(ucp[4] & 0x40), !!(ucp[4] & 0x20), !!(ucp[4] & 0x10),
+               !!(ucp[4] & 0x8));
+        printf("      Test write=%d, CD-RW=%d, R-W=%d\n",
+               !!(ucp[4] & 0x4), !!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
+        printf("      Maximum cue sheet length=0x%x\n",
+               sg_get_unaligned_be24(ucp + 5));
+        break;
+    case 0x2f:     /* DVD-R/-RW write */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      BUF=%d, RDL=%d, Test write=%d, DVD-RW SL=%d\n",
+               !!(ucp[4] & 0x40), !!(ucp[4] & 0x8), !!(ucp[4] & 0x4),
+               !!(ucp[4] & 0x2));
+        break;
+    case 0x33:     /* Layer jump recording */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        num = ucp[7];
+        printf("      Number of link sizes=%d\n", num);
+        for (k = 0; k < num; ++k)
+            printf("        %d\n", ucp[8 + k]);
+        break;
+    case 0x34:     /* Layer jump rigid restricted overwrite */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      CLJB=%d\n", !!(ucp[4] & 0x1));
+        printf("      Buffer block size=%d\n", ucp[7]);
+        break;
+    /* case 0x35:     Stop long operation -> see 0x1d entry */
+    case 0x37:     /* CD-RW media write support */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      CD-RW media sub-type support (bitmask)=0x%x\n", ucp[5]);
+        break;
+    /* case 0x38:     BD-R pseudo-overwrite (POW) -> see 0x1d entry */
+    case 0x3a:     /* DVD+RW dual layer */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      write=%d, quick_start=%d, close_only=%d\n",
+               !!(ucp[4] & 0x1), !!(ucp[5] & 0x2), !!(ucp[5] & 0x1));
+        break;
+    case 0x3b:     /* DVD+R dual layer */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      write=%d\n", !!(ucp[4] & 0x1));
+        break;
+    case 0x40:     /* BD Read */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 32) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      Bitmaps for BD-RE read support:\n");
+        printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+               "Class 3=0x%x\n", sg_get_unaligned_be16(ucp + 8),
+               sg_get_unaligned_be16(ucp + 10),
+               sg_get_unaligned_be16(ucp + 12),
+               sg_get_unaligned_be16(ucp + 14));
+        printf("      Bitmaps for BD-R read support:\n");
+        printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+               "Class 3=0x%x\n", sg_get_unaligned_be16(ucp + 16),
+               sg_get_unaligned_be16(ucp + 18),
+               sg_get_unaligned_be16(ucp + 20),
+               sg_get_unaligned_be16(ucp + 22));
+        printf("      Bitmaps for BD-ROM read support:\n");
+        printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+               "Class 3=0x%x\n", sg_get_unaligned_be16(ucp + 24),
+               sg_get_unaligned_be16(ucp + 26),
+               sg_get_unaligned_be16(ucp + 28),
+               sg_get_unaligned_be16(ucp + 30));
+        break;
+    case 0x41:     /* BD Write */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 32) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      SVNR=%d\n", !!(ucp[4] & 0x1));
+        printf("      Bitmaps for BD-RE write support:\n");
+        printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+               "Class 3=0x%x\n", sg_get_unaligned_be16(ucp + 8),
+               sg_get_unaligned_be16(ucp + 10),
+               sg_get_unaligned_be16(ucp + 12),
+               sg_get_unaligned_be16(ucp + 14));
+        printf("      Bitmaps for BD-R write support:\n");
+        printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+               "Class 3=0x%x\n", sg_get_unaligned_be16(ucp + 16),
+               sg_get_unaligned_be16(ucp + 18),
+               sg_get_unaligned_be16(ucp + 20),
+               sg_get_unaligned_be16(ucp + 22));
+        printf("      Bitmaps for BD-ROM write support:\n");
+        printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+               "Class 3=0x%x\n", sg_get_unaligned_be16(ucp + 24),
+               sg_get_unaligned_be16(ucp + 26),
+               sg_get_unaligned_be16(ucp + 28),
+               sg_get_unaligned_be16(ucp + 30));
+        break;
+    /* case 0x42:     TSR (timely safe recording) -> see 0x1d entry */
+    case 0x50:     /* HD DVD Read */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      HD DVD-R=%d, HD DVD-RAM=%d\n", !!(ucp[4] & 0x1),
+               !!(ucp[6] & 0x1));
+        break;
+    case 0x51:     /* HD DVD Write */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      HD DVD-R=%d, HD DVD-RAM=%d\n", !!(ucp[4] & 0x1),
+               !!(ucp[6] & 0x1));
+        break;
+    case 0x52:     /* HD DVD-RW fragment recording */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      BGP=%d\n", !!(ucp[4] & 0x1));
+        break;
+    case 0x80:     /* Hybrid disc */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      RI=%d\n", !!(ucp[4] & 0x1));
+        break;
+    /* case 0x100:    Power management -> see 0x1d entry */
+    case 0x101:    /* SMART */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      PP=%d\n", !!(ucp[4] & 0x1));
+        break;
+    case 0x102:    /* Embedded changer */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      SCC=%d, SDP=%d, highest slot number=%d\n",
+               !!(ucp[4] & 0x10), !!(ucp[4] & 0x4), (ucp[7] & 0x1f));
+        break;
+    case 0x103:    /* CD audio external play (obsolete) */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      Scan=%d, SCM=%d, SV=%d, number of volume levels=%d\n",
+               !!(ucp[4] & 0x4), !!(ucp[4] & 0x2), !!(ucp[4] & 0x1),
+               sg_get_unaligned_be16(ucp + 6));
+        break;
+    case 0x104:    /* Firmware upgrade */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 4) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        if (len > 4)
+            printf("      M5=%d\n", !!(ucp[4] & 0x1));
+        break;
+    case 0x105:    /* Timeout */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len > 7) {
+            printf("      Group 3=%d, unit length=%d\n",
+                   !!(ucp[4] & 0x1), sg_get_unaligned_be16(ucp + 6));
+        }
+        break;
+    case 0x106:    /* DVD CSS */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      CSS version=%d\n", ucp[7]);
+        break;
+    case 0x107:    /* Real time streaming */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      RBCB=%d, SCS=%d, MP2A=%d, WSPD=%d, SW=%d\n",
+               !!(ucp[4] & 0x10), !!(ucp[4] & 0x8), !!(ucp[4] & 0x4),
+               !!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
+        break;
+    case 0x108:    /* Drive serial number */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        num = len - 4;
+        n = sizeof(buff) - 1;
+        n = ((num < n) ? num : n);
+        strncpy(buff, (const char *)(ucp + 4), n);
+        buff[n] = '\0';
+        printf("      Drive serial number: %s\n", buff);
+        break;
+    /* case 0x109:    Media serial number -> see 0x1d entry */
+    case 0x10a:    /* Disc control blocks */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        printf("      Disc control blocks:\n");
+        for (k = 4; k < len; k += 4) {
+            printf("        0x%x\n", sg_get_unaligned_be32(ucp + k));
+        }
+        break;
+    case 0x10b:    /* DVD CPRM */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      CPRM version=%d\n", ucp[7]);
+        break;
+    case 0x10c:    /* firmware information */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 20) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      %.2s%.2s/%.2s/%.2s %.2s:%.2s:%.2s\n", ucp + 4,
+               ucp + 6, ucp + 8, ucp + 10, ucp + 12, ucp + 14, ucp + 16);
+        break;
+    case 0x10d:    /* AACS */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      BNG=%d, Block count for binding nonce=%d\n",
+               !!(ucp[4] & 0x1), ucp[5]);
+        printf("      Number of AGIDs=%d, AACS version=%d\n",
+               (ucp[6] & 0xf), ucp[7]);
+        break;
+    case 0x10e:    /* DVD CSS managed recording */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      Maximum number of scrambled extent information "
+               "entries=%d\n", ucp[4]);
+        break;
+    /* case 0x110:    VCPS -> see 0x1d entry */
+    /* case 0x113:    SecurDisc -> see 0x1d entry */
+    case 0x120:    /* BD CPS */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("      BD CPS major:minor version number=%d:%d, max open "
+               "SACs=%d\n", ((ucp[5] >> 4) & 0xf), (ucp[5] & 0xf),
+               ucp[6] & 0x3);
+        break;
+    case 0x142:    /* OSSC (Optical Security Subsystem Class) */
+        printf("    version=%d, persist=%d, current=%d [0x%x]\n",
+               ((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
+               feature);
+        if (len < 8) {
+            printf("      additional length [%d] too short\n", len - 4);
+            break;
+        }
+        printf("    PSAU=%d, LOSPB=%d, ME=%d\n", !!(ucp[4] & 0x80),
+               !!(ucp[4] & 0x40), !!(ucp[4] & 0x1));
+        num = ucp[5];
+        printf("      Profile numbers:\n");
+        for (k = 6; (num > 0) && (k < len); --num, k += 2) {
+            printf("        %u\n", sg_get_unaligned_be16(ucp + k));
+        }
+        break;
+    default:
+        pr2serr("    Unknown feature [0x%x], version=%d persist=%d, "
+                "current=%d\n", feature, ((ucp[2] >> 2) & 0xf),
+                !!(ucp[2] & 0x2), !!(ucp[2] & 0x1));
+        dStrHexErr((const char *)ucp, len, 1);
+        break;
+    }
+}
+
+static void
+decode_config(unsigned char * resp, int max_resp_len, int len, int brief,
+              int inner_hex)
+{
+    int k, curr_profile, extra_len, feature;
+    unsigned char * ucp;
+    char buff[128];
+
+    if (max_resp_len < len) {
+        pr2serr("<<<warning: response to long for buffer, resp_len=%d>>>\n",
+                len);
+            len = max_resp_len;
+    }
+    if (len < 8) {
+        pr2serr("response length too short: %d\n", len);
+        return;
+    }
+    curr_profile = sg_get_unaligned_be16(resp + 6);
+    if (0 == curr_profile)
+        pr2serr("No current profile\n");
+    else
+        printf("Current profile: %s\n", get_profile_str(curr_profile, buff));
+    printf("Features%s:\n", (brief ? " (in brief)" : ""));
+    ucp = resp + 8;
+    len -= 8;
+    for (k = 0; k < len; k += extra_len, ucp += extra_len) {
+        extra_len = 4 + ucp[3];
+        feature = sg_get_unaligned_be16(ucp + 0);
+        printf("  %s feature\n", get_feature_str(feature, buff));
+        if (brief)
+            continue;
+        if (inner_hex) {
+            dStrHex((const char *)ucp, extra_len, 1);
+            continue;
+        }
+        if (0 != (extra_len % 4))
+            printf("    additional length [%d] not a multiple of 4, ignore\n",
+                   extra_len - 4);
+        else
+            decode_feature(feature, ucp, extra_len);
+    }
+}
+
+static void
+list_known(int brief)
+{
+    int k, num;
+
+    num = sizeof(feature_desc_arr) / sizeof(feature_desc_arr[0]);
+    printf("Known features:\n");
+    for (k = 0; k < num; ++k)
+        printf("  %s [0x%x]\n", feature_desc_arr[k].desc,
+               feature_desc_arr[k].val);
+    if (! brief) {
+        printf("Known profiles:\n");
+        num = sizeof(profile_desc_arr) / sizeof(profile_desc_arr[0]);
+        for (k = 0; k < num; ++k)
+            printf("  %s [0x%x]\n", profile_desc_arr[k].desc,
+                   profile_desc_arr[k].val);
+    }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, len;
+    int peri_type = 0;
+    int brief = 0;
+    int do_hex = 0;
+    int inner_hex = 0;
+    int list = 0;
+    int do_raw = 0;
+    int readonly = 0;
+    int rt = 0;
+    int starting = 0;
+    int verbose = 0;
+    const char * device_name = NULL;
+    char buff[64];
+    const char * cp;
+    struct sg_simple_inquiry_resp inq_resp;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "bchHilqr:Rs:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            brief = 1;
+            break;
+        case 'c':
+            rt = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'i':
+            inner_hex = 1;
+            break;
+        case 'l':
+            list = 1;
+            break;
+        case 'q':
+            ++readonly;
+            break;
+        case 'r':
+            rt = sg_get_num(optarg);
+            if ((rt < 0) || (rt > 3)) {
+                pr2serr("bad argument to '--rt'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'R':
+            ++do_raw;
+            break;
+        case 's':
+            starting = sg_get_num(optarg);
+            if ((starting < 0) || (starting > 0xffff)) {
+                pr2serr("bad argument to '--starting'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (list) {
+        list_known(brief);
+        return 0;
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((sg_fd = sg_cmds_open_device(device_name, 1 /* ro */, verbose)) < 0) {
+        pr2serr(ME "error opening file: %s (ro): %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, verbose)) {
+        if (0 == do_raw)
+            printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor, inq_resp.product,
+                   inq_resp.revision);
+        peri_type = inq_resp.peripheral_type;
+        cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
+        if (0 == do_raw) {
+            if (strlen(cp) > 0)
+                printf("  Peripheral device type: %s\n", cp);
+            else
+                printf("  Peripheral device type: 0x%x\n", peri_type);
+        }
+    } else {
+        pr2serr(ME "%s doesn't respond to a SCSI INQUIRY\n", device_name);
+        return SG_LIB_CAT_OTHER;
+    }
+    sg_cmds_close_device(sg_fd);
+
+    sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error (rw): %s\n", safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    res = sg_ll_get_config(sg_fd, rt, starting, resp_buffer,
+                              sizeof(resp_buffer), 1, verbose);
+    ret = res;
+    if (0 == res) {
+        len = sg_get_unaligned_be32(resp_buffer + 0) + 4;
+        if (do_hex) {
+            if (len > (int)sizeof(resp_buffer))
+                len = sizeof(resp_buffer);
+            dStrHex((const char *)resp_buffer, len, 0);
+        } else if (do_raw)
+            dStrRaw((const char *)resp_buffer, len);
+        else
+            decode_config(resp_buffer, sizeof(resp_buffer), len, brief,
+                          inner_hex);
+    } else {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Get Configuration command: %s\n", b);
+        if (0 == verbose)
+            pr2serr("    try '-v' option for more information\n");
+    }
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_get_lba_status.c b/sg3_utils/src/sg_get_lba_status.c
new file mode 100644
index 0000000..69bfa18
--- /dev/null
+++ b/sg3_utils/src/sg_get_lba_status.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2009-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI GET LBA STATUS command to the given SCSI
+ * device.
+ */
+
+static const char * version_str = "1.07 20151219";
+
+#define MAX_GLBAS_BUFF_LEN (1024 * 1024)
+#define DEF_GLBAS_BUFF_LEN 24
+
+static unsigned char glbasBuff[DEF_GLBAS_BUFF_LEN];
+static unsigned char * glbasBuffp = glbasBuff;
+
+
+static struct option long_options[] = {
+        {"brief", no_argument, 0, 'b'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"lba", required_argument, 0, 'l'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"raw", no_argument, 0, 'r'},
+        {"readonly", no_argument, 0, 'R'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_get_lba_status  [--brief] [--help] [--hex] "
+            "[--lba=LBA]\n"
+            "                          [--maxlen=LEN] [--raw] [--readonly] "
+            "[--verbose]\n"
+            "                          [--version] DEVICE\n"
+            "  where:\n"
+            "    --brief|-b        a descriptor per line: "
+            "<lba_hex blocks_hex p_status>\n"
+            "                      use twice ('-bb') for given LBA "
+            "provisioning status\n"
+            "    --help|-h         print out usage message\n"
+            "    --hex|-H          output in hexadecimal\n"
+            "    --lba=LBA|-l LBA    starting LBA (logical block address) "
+            "(def: 0)\n"
+            "    --maxlen=LEN|-m LEN    max response length (allocation "
+            "length in cdb)\n"
+            "                           (def: 0 -> %d bytes)\n",
+            DEF_GLBAS_BUFF_LEN );
+    pr2serr("    --raw|-r          output in binary\n"
+            "    --readonly|-R     open DEVICE read-only (def: read-write)\n"
+            "    --verbose|-v      increase verbosity\n"
+            "    --version|-V      print version string and exit\n\n"
+            "Performs a SCSI GET LBA STATUS command (SBC-3)\n"
+            );
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* Decodes given LBA status descriptor passing back the starting LBA,
+ * the number of blocks and returns the provisioning status, -1 for error.
+ */
+static int
+decode_lba_status_desc(const unsigned char * ucp, uint64_t * slbap,
+                       uint32_t * blocksp)
+{
+    uint32_t blocks;
+    uint64_t ull;
+
+    if (NULL == ucp)
+        return -1;
+    ull = sg_get_unaligned_be64(ucp + 0);
+    blocks = sg_get_unaligned_be32(ucp + 8);
+    if (slbap)
+        *slbap = ull;
+    if (blocksp)
+        *blocksp = blocks;
+    return ucp[12] & 0xf;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, j, res, c, rlen, num_descs;
+    int do_brief = 0;
+    int do_hex = 0;
+    int64_t ll;
+    uint64_t lba = 0;
+    uint64_t d_lba = 0;
+    uint32_t d_blocks = 0;
+    int maxlen = DEF_GLBAS_BUFF_LEN;
+    int do_raw = 0;
+    int o_readonly = 0;
+    int verbose = 0;
+    const char * device_name = NULL;
+    const unsigned char * ucp;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "bhHl:m:rRvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            ++do_brief;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'l':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            lba = (uint64_t)ll;
+            break;
+        case 'm':
+            maxlen = sg_get_num(optarg);
+            if ((maxlen < 0) || (maxlen > MAX_GLBAS_BUFF_LEN)) {
+                pr2serr("argument to '--maxlen' should be %d or less\n",
+                        MAX_GLBAS_BUFF_LEN);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 'R':
+            ++o_readonly;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (maxlen > DEF_GLBAS_BUFF_LEN) {
+        glbasBuffp = (unsigned char *)calloc(maxlen, 1);
+        if (NULL == glbasBuffp) {
+            pr2serr("unable to allocate %d bytes on heap\n", maxlen);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            ret = SG_LIB_FILE_ERROR;
+            goto free_buff;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        ret = SG_LIB_FILE_ERROR;
+        goto free_buff;
+    }
+
+    res = sg_ll_get_lba_status(sg_fd, lba, glbasBuffp, maxlen, 1,
+                               verbose);
+    ret = res;
+    if (0 == res) {
+        /* in sbc3r25 offset for calculating the 'parameter data length'
+         * (rlen variable below) was reduced from 8 to 4. */
+        if (maxlen >= 4)
+            rlen = sg_get_unaligned_be32(glbasBuffp + 0) + 4;
+        else
+            rlen = maxlen;
+        k = (rlen > maxlen) ? maxlen : rlen;
+        if (do_raw) {
+            dStrRaw((const char *)glbasBuffp, k);
+            goto the_end;
+        }
+        if (do_hex) {
+            dStrHex((const char *)glbasBuffp, k, 1);
+            goto the_end;
+        }
+        if (maxlen < 4) {
+            if (verbose)
+                pr2serr("Exiting because allocation length (maxlen) less "
+                        "than 4\n");
+            goto the_end;
+        }
+        if ((verbose > 1) || (verbose && (rlen > maxlen))) {
+            pr2serr("response length %d bytes\n", rlen);
+            if (rlen > maxlen)
+                pr2serr("  ... which is greater than maxlen (allocation "
+                        "length %d), truncation\n", maxlen);
+        }
+        if (rlen > maxlen)
+            rlen = maxlen;
+
+        if (do_brief > 1) {
+            if (rlen < 24) {
+                pr2serr("Need maxlen and response length to be at least 24, "
+                        "have %d bytes\n", rlen);
+                ret = SG_LIB_CAT_OTHER;
+                goto the_end;
+            }
+            res = decode_lba_status_desc(glbasBuffp + 8, &d_lba, &d_blocks);
+            if ((res < 0) || (res > 15)) {
+                pr2serr("first LBA status descriptor returned %d ??\n", res);
+                ret = SG_LIB_CAT_OTHER;
+                goto the_end;
+            }
+            if ((lba < d_lba) || (lba >= (d_lba + d_blocks))) {
+                pr2serr("given LBA not in range of first descriptor:\n"
+                        "  descriptor LBA: 0x");
+                for (j = 0; j < 8; ++j)
+                    pr2serr("%02x", glbasBuffp[8 + j]);
+                pr2serr("  blocks: 0x%x  p_status: %d\n",
+                        (unsigned int)d_blocks, res);
+                ret = SG_LIB_CAT_OTHER;
+                goto the_end;
+            }
+            printf("%d\n", res);
+            goto the_end;
+        }
+
+        if (rlen < 24) {
+            printf("No complete LBA status descriptors available\n");
+            goto the_end;
+        }
+        num_descs = (rlen - 8) / 16;
+        if (verbose)
+            pr2serr("%d complete LBA status descriptors found\n", num_descs);
+        for (ucp = glbasBuffp + 8, k = 0; k < num_descs; ucp += 16, ++k) {
+            res = decode_lba_status_desc(ucp, &d_lba, &d_blocks);
+            if ((res < 0) || (res > 15))
+                pr2serr("descriptor %d: bad LBA status descriptor returned "
+                        "%d\n", k + 1, res);
+            if (do_brief) {
+                printf("0x");
+                for (j = 0; j < 8; ++j)
+                    printf("%02x", ucp[j]);
+                printf("  0x%x  %d\n", (unsigned int)d_blocks, res);
+            } else {
+                printf("descriptor LBA: 0x");
+                for (j = 0; j < 8; ++j)
+                    printf("%02x", ucp[j]);
+                printf("  blocks: %u", (unsigned int)d_blocks);
+                switch (res) {
+                case 0:
+                    printf("  mapped (or unknown)\n");
+                    break;
+                case 1:
+                    printf("  deallocated\n");
+                    break;
+                case 2:
+                    printf("  anchored\n");
+                    break;
+                default:
+                    printf("  Provisioning status: %d\n", res);
+                    break;
+                }
+            }
+        }
+        if ((num_descs * 16) + 8 < rlen)
+            pr2serr("incomplete trailing LBA status descriptors found\n");
+    } else if (SG_LIB_CAT_INVALID_OP == res)
+        pr2serr("Get LBA Status command not supported\n");
+    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+        pr2serr("Get LBA Status command: bad field in cdb\n");
+    else {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Get LBA Status command: %s\n", b);
+    }
+
+the_end:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            ret = SG_LIB_FILE_ERROR;
+    }
+free_buff:
+    if (glbasBuffp && (glbasBuffp != glbasBuff))
+        free(glbasBuffp);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_ident.c b/sg3_utils/src/sg_ident.c
new file mode 100644
index 0000000..3f64156
--- /dev/null
+++ b/sg3_utils/src/sg_ident.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2005-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues these SCSI commands: REPORT IDENTIFYING INFORMATION
+ * and SET IDENTIFYING INFORMATION. These commands were called REPORT
+ * DEVICE IDENTIFIER and SET DEVICE IDENTIFIER prior to spc4r07.
+ */
+
+static const char * version_str = "1.14 20151219";
+
+#define ME "sg_ident: "
+
+#define REPORT_ID_INFO_SANITY_LEN 512
+
+
+static struct option long_options[] = {
+        {"ascii", 0, 0, 'A'},
+        {"clear", 0, 0, 'C'},
+        {"help", 0, 0, 'h'},
+        {"itype", 1, 0, 'i'},
+        {"raw", 0, 0, 'r'},
+        {"set", 0, 0, 'S'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void decode_ii(const unsigned char * iip, int ii_len, int itype,
+                      int ascii, int raw, int verbose)
+{
+    int k;
+
+    if (raw) {
+        if (ii_len > 0) {
+            int n;
+
+            if (sg_set_binary_mode(STDOUT_FILENO) < 0)
+                perror("sg_set_binary_mode");
+#if 0
+            n = fwrite(iip, 1, ii_len, stdout);
+#else
+            n = write(STDOUT_FILENO, iip, ii_len);
+#endif
+            if (verbose && (n < 1))
+                pr2serr("unable to write to stdout\n");
+        }
+        return;
+    }
+    if (0x7f == itype) {  /* list of available information types */
+        for (k = 0; k < (ii_len - 3); k += 4)
+            printf("  Information type: %d, Maximum information length: "
+                   "%d bytes\n", iip[k], sg_get_unaligned_be16(iip + 2));
+    } else {        /* single element */
+        if (verbose)
+            printf("Information:\n");
+        if (ii_len > 0) {
+            if (ascii)
+                printf("%.*s\n", ii_len, (const char *)iip);
+            else
+                dStrHex((const char *)iip, ii_len, 0);
+        }
+    }
+}
+
+static void usage()
+{
+    pr2serr("Usage: sg_ident   [--ascii] [--clear] [--help] [--itype=IT] "
+            "[--raw] [--set]\n"
+            "                  [--verbose] [--version] DEVICE\n"
+            "  where:\n"
+            "    --ascii|-A      report identifying information as ASCII "
+            "(or UTF8) string\n"
+            "    --clear|-C      clear (set to zero length) identifying "
+            "information\n"
+            "    --help|-h       print out usage message\n"
+            "    --itype=IT|-i IT    specify identifying information type "
+            "(def: 0)\n"
+            "    --raw|-r        output identifying information to "
+            "stdout\n"
+            "    --set|-S        invoke set identifying information with "
+            "data from stdin\n"
+            "    --verbose|-v    increase verbosity of output\n"
+            "    --version|-V    print version string and exit\n\n"
+            "Performs a SCSI REPORT (or SET) IDENTIFYING INFORMATION "
+            "command\n");
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, res, c, ii_len;
+    unsigned char rdi_buff[REPORT_ID_INFO_SANITY_LEN + 4];
+    char b[80];
+    unsigned char * ucp = NULL;
+    int ascii = 0;
+    int do_clear = 0;
+    int itype = 0;
+    int raw = 0;
+    int do_set = 0;
+    int verbose = 0;
+    const char * device_name = NULL;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "AChi:rSvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'A':
+            ascii = 1;
+            break;
+        case 'C':
+            do_clear = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'i':
+           itype = sg_get_num(optarg);
+           if ((itype < 0) || (itype > 127)) {
+                pr2serr("argument to '--itype' should be in range 0 to 127\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'r':
+            raw = 1;
+            break;
+        case 'S':
+            do_set = 1;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (do_set && do_clear) {
+        pr2serr("only one of '--clear' and '--set' can be given\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (ascii && raw) {
+        pr2serr("only one of '--ascii' and '--raw' can be given\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((do_set || do_clear) && (raw || ascii)) {
+        pr2serr("'--set' cannot be used with either '--ascii' or '--raw'\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    memset(rdi_buff, 0x0, sizeof(rdi_buff));
+    if (do_set || do_clear) {
+        if (do_set) {
+            res = fread(rdi_buff, 1, REPORT_ID_INFO_SANITY_LEN + 2, stdin);
+            if (res <= 0) {
+                pr2serr("no data read from stdin; to clear identifying "
+                        "information use '--clear' instead\n");
+                ret = -1;
+                goto err_out;
+            } else if (res > REPORT_ID_INFO_SANITY_LEN) {
+                pr2serr("SPC-4 limits information length to 512 bytes\n");
+                ret = -1;
+                goto err_out;
+            }
+            ii_len = res;
+            res = sg_ll_set_id_info(sg_fd, itype, rdi_buff, ii_len, 1,
+                                    verbose);
+        } else    /* do_clear */
+            res = sg_ll_set_id_info(sg_fd, itype, rdi_buff, 0, 1, verbose);
+        if (res) {
+            ret = res;
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("Set identifying information: %s\n", b);
+            if (0 == verbose)
+                pr2serr("    try '-v' for more information\n");
+        }
+    } else {    /* do report identifying information */
+        res = sg_ll_report_id_info(sg_fd, itype, rdi_buff, 4, 1, verbose);
+        if (0 == res) {
+            ii_len = sg_get_unaligned_be32(rdi_buff + 0);
+            if ((! raw) && (verbose > 0))
+                printf("Reported identifying information length = %d\n",
+                       ii_len);
+            if (0 == ii_len) {
+                if (verbose > 1)
+                    pr2serr("    This implies the device has an empty "
+                            "information field\n");
+                goto err_out;
+            }
+            if (ii_len > REPORT_ID_INFO_SANITY_LEN) {
+                pr2serr("    That length (%d) seems too long for an "
+                        "information\n", ii_len);
+                ret = -1;
+                goto err_out;
+            }
+            ucp = rdi_buff;
+            res = sg_ll_report_id_info(sg_fd, itype, ucp, ii_len + 4, 1,
+                                       verbose);
+            if (0 == res) {
+                ii_len = sg_get_unaligned_be32(ucp + 0);
+                decode_ii(ucp + 4, ii_len, itype, ascii, raw, verbose);
+            } else
+                ret = res;
+        } else
+            ret = res;
+        if (ret) {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("Report identifying information: %s\n", b);
+            if (0 == verbose)
+                pr2serr("    try '-v' for more information\n");
+        }
+    }
+
+err_out:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_inq.c b/sg3_utils/src/sg_inq.c
new file mode 100644
index 0000000..e4bcfd3
--- /dev/null
+++ b/sg3_utils/src/sg_inq.c
@@ -0,0 +1,4253 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+*  Copyright (C) 2000-2016 D. Gilbert
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This program outputs information provided by a SCSI INQUIRY command.
+   It is mainly based on the SCSI SPC-4 document at http://www.t10.org .
+
+   Acknowledgment:
+      - Martin Schwenke <martin at meltin dot net> added the raw switch and
+        other improvements [20020814]
+      - Lars Marowsky-Bree <lmb at suse dot de> contributed Unit Path Report
+        VPD page decoding for EMC CLARiiON devices [20041016]
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef SG_LIB_LINUX
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/hdreg.h>
+#endif
+
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pt.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.57 20160208";    /* SPC-5 rev 08 */
+
+/* INQUIRY notes:
+ * It is recommended that the initial allocation length given to a
+ * standard INQUIRY is 36 (bytes), especially if this is the first
+ * SCSI command sent to a logical unit. This is compliant with SCSI-2
+ * and another major operating system. There are devices out there
+ * that use one of the SCSI commands sets and lock up if they receive
+ * an allocation length other than 36. This technique is sometimes
+ * referred to as a "36 byte INQUIRY".
+ *
+ * A "standard" INQUIRY is one that has the EVPD and the CmdDt bits
+ * clear.
+ *
+ * When doing device discovery on a SCSI transport (e.g. bus scanning)
+ * the first SCSI command sent to a device should be a standard (36
+ * byte) INQUIRY.
+ *
+ * The allocation length field in the INQUIRY command was changed
+ * from 1 to 2 bytes in SPC-3, revision 9, 17 September 2002.
+ * Be careful using allocation lengths greater than 252 bytes, especially
+ * if the lower byte is 0x0 (e.g. a 512 byte allocation length may
+ * not be a good arbitrary choice (as 512 == 0x200) ).
+ *
+ * From SPC-3 revision 16 the CmdDt bit in an INQUIRY is obsolete. There
+ * is now a REPORT SUPPORTED OPERATION CODES command that yields similar
+ * information [MAINTENANCE IN, service action = 0xc]; see sg_opcodes.
+ */
+
+
+/* Following VPD pages are in ascending page number order */
+#define VPD_SUPPORTED_VPDS 0x0
+#define VPD_UNIT_SERIAL_NUM 0x80
+#define VPD_DEVICE_ID  0x83
+#define VPD_SOFTW_INF_ID 0x84
+#define VPD_MAN_NET_ADDR  0x85
+#define VPD_EXT_INQ  0x86
+#define VPD_MODE_PG_POLICY  0x87
+#define VPD_SCSI_PORTS  0x88
+#define VPD_ATA_INFO  0x89
+#define VPD_POWER_CONDITION  0x8a
+#define VPD_DEVICE_CONSTITUENTS 0x8b
+#define VPD_CFA_PROFILE_INFO  0x8c
+#define VPD_POWER_CONSUMPTION  0x8d
+#define VPD_3PARTY_COPY  0x8f
+#define VPD_PROTO_LU 0x90
+#define VPD_PROTO_PORT 0x91
+#define VPD_BLOCK_LIMITS 0xb0
+#define VPD_BLOCK_DEV_CHARS 0xb1
+#define VPD_MAN_ASS_SN 0xb1
+#define VPD_LB_PROVISIONING 0xb2
+#define VPD_REFERRALS 0xb3
+#define VPD_UPR_EMC 0xc0
+#define VPD_RDAC_VERS 0xc2
+#define VPD_RDAC_VAC 0xc9
+
+/* values for selection one or more associations (2**vpd_assoc),
+   except _AS_IS */
+#define VPD_DI_SEL_LU 1
+#define VPD_DI_SEL_TPORT 2
+#define VPD_DI_SEL_TARGET 4
+#define VPD_DI_SEL_AS_IS 32
+
+#define DEF_ALLOC_LEN 252
+#define SAFE_STD_INQ_RESP_LEN 36
+#define MX_ALLOC_LEN (0xc000 + 0x80)
+#define VPD_ATA_INFO_LEN  572
+
+#define SENSE_BUFF_LEN  64       /* Arbitrary, could be larger */
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+#define DEF_PT_TIMEOUT  60       /* 60 seconds */
+
+
+static unsigned char rsp_buff[MX_ALLOC_LEN + 1];
+static char xtra_buff[MX_ALLOC_LEN + 1];
+static char usn_buff[MX_ALLOC_LEN + 1];
+
+static const char * find_version_descriptor_str(int value);
+static void decode_dev_ids(const char * leadin, unsigned char * buff,
+                           int len, int do_hex);
+static void decode_transport_id(const char * leadin, unsigned char * ucp,
+                                int len);
+
+#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
+static int try_ata_identify(int ata_fd, int do_hex, int do_raw,
+                            int do_verbose);
+#endif
+
+/* This structure is a duplicate of one of the same name in sg_vpd_vendor.c .
+   Take care that both have the same fields (and types). */
+struct svpd_values_name_t {
+    int value;
+    int subvalue;
+    int pdt;         /* peripheral device type id, -1 is the default */
+                     /* (all or not applicable) value */
+    int vendor;      /* vendor flag */
+    const char * acron;
+    const char * name;
+};
+
+static struct svpd_values_name_t vpd_pg[] = {
+    {VPD_ATA_INFO, 0, -1, 0, "ai", "ATA information (SAT)"},
+    {VPD_BLOCK_DEV_CHARS, 0, 0, 0, "bdc",
+     "Block device characteristics (SBC)"},
+    {VPD_BLOCK_LIMITS, 0, 0, 0, "bl", "Block limits (SBC)"},
+    {VPD_DEVICE_ID, 0, -1, 0, "di", "Device identification"},
+#if 0
+    {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, 0, "di_asis", "Like 'di' "
+     "but designators ordered as found"},
+    {VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, 0, "di_lu", "Device identification, "
+     "lu only"},
+    {VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, 0, "di_port", "Device "
+     "identification, target port only"},
+    {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, 0, "di_target", "Device "
+     "identification, target device only"},
+#endif
+    {VPD_EXT_INQ, 0, -1, 0, "ei", "Extended inquiry data"},
+    {VPD_LB_PROVISIONING, 0, 0, 0, "lbpv", "Logical block provisioning "
+     "(SBC)"},
+    {VPD_MAN_NET_ADDR, 0, -1, 0, "mna", "Management network addresses"},
+    {VPD_MODE_PG_POLICY, 0, -1, 0, "mpp", "Mode page policy"},
+    {VPD_POWER_CONDITION, 0, -1, 0, "po", "Power condition"},
+    {VPD_POWER_CONSUMPTION, 0, -1, 0, "psm", "Power consumption"},
+    {VPD_PROTO_LU, 0, 0x0, 0, "pslu", "Protocol-specific logical unit "
+     "information"},
+    {VPD_PROTO_PORT, 0, 0x0, 0, "pspo", "Protocol-specific port information"},
+    {VPD_REFERRALS, 0, 0, 0, "ref", "Referrals (SBC)"},
+    {VPD_SOFTW_INF_ID, 0, -1, 0, "sii", "Software interface identification"},
+    {VPD_UNIT_SERIAL_NUM, 0, -1, 0, "sn", "Unit serial number"},
+    {VPD_SCSI_PORTS, 0, -1, 0, "sp", "SCSI ports"},
+    {VPD_SUPPORTED_VPDS, 0, -1, 0, "sv", "Supported VPD pages"},
+    {VPD_3PARTY_COPY, 0, -1, 0, "tpc", "Third party copy"},
+    /* Following are vendor specific */
+    {VPD_RDAC_VAC, 0, -1, 1, "rdac_vac", "RDAC volume access control (RDAC)"},
+    {VPD_RDAC_VERS, 0, -1, 1, "rdac_vers", "RDAC software version (RDAC)"},
+    {VPD_UPR_EMC, 0, -1, 1, "upr", "Unit path report (EMC)"},
+    {0, 0, 0, 0, NULL, NULL},
+};
+
+static struct option long_options[] = {
+#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
+        {"ata", no_argument, 0, 'a'},
+#endif
+        {"block", required_argument, 0, 'B'},
+        {"cmddt", no_argument, 0, 'c'},
+        {"descriptors", no_argument, 0, 'd'},
+        {"export", no_argument, 0, 'u'},
+        {"extended", no_argument, 0, 'x'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"id", no_argument, 0, 'i'},
+        {"inhex", required_argument, 0, 'I'},
+        {"len", required_argument, 0, 'l'},
+        {"maxlen", required_argument, 0, 'm'},
+#ifdef SG_SCSI_STRINGS
+        {"new", no_argument, 0, 'N'},
+        {"old", no_argument, 0, 'O'},
+#endif
+        {"page", required_argument, 0, 'p'},
+        {"raw", no_argument, 0, 'r'},
+        {"vendor", no_argument, 0, 's'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {"vpd", no_argument, 0, 'e'},
+        {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_ata;
+    int do_block;
+    int do_cmddt;
+    int do_descriptors;
+    int do_export;
+    int do_help;
+    int do_hex;
+    int do_raw;
+    int do_vendor;
+    int do_verbose;
+    int do_version;
+    int do_decode;
+    int do_vpd;
+    int resp_len;
+    int page_num;
+    int page_pdt;
+    int num_pages;
+    int num_opcodes;
+    int p_given;
+    const char * page_arg;
+    const char * device_name;
+    const char * inhex_fn;
+#ifdef SG_SCSI_STRINGS
+    int opt_new;
+#endif
+};
+
+
+static void
+usage()
+{
+#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
+    pr2serr("Usage: sg_inq [--ata] [--block=0|1] [--cmddt] [--descriptors] "
+            "[--export]\n"
+            "              [--extended] [--help] [--hex] [--id] [--inhex=FN] "
+            "[--len=LEN]\n"
+            "              [--maxlen=LEN] [--page=PG] [--raw] [--vendor] "
+            "[--verbose]\n"
+            "              [--version] [--vpd] DEVICE\n"
+            "  where:\n"
+            "    --ata|-a        treat DEVICE as (directly attached) ATA "
+            "device\n");
+#else
+    pr2serr("Usage: sg_inq [--block=0|1] [--cmddt] [--descriptors] "
+            "[--export]\n"
+            "              [--extended] [--help] [--hex] [--id] [--inhex=FN] "
+            "[--len=LEN]\n"
+            "              [--maxlen=LEN] [--page=PG] [--raw] [--verbose] "
+            "[--version]\n"
+            "              [--vpd] DEVICE\n"
+            "  where:\n");
+#endif
+    pr2serr("    --block=0|1     0-> open(non-blocking); 1-> "
+            "open(blocking)\n"
+            "      -B 0|1        (def: depends on OS; Linux pt: 0)\n"
+            "    --cmddt|-c      command support data mode (set opcode "
+            "with '--page=PG')\n"
+            "                    use twice for list of supported "
+            "commands; obsolete\n"
+            "    --descriptors|-d    fetch and decode version descriptors\n"
+            "    --export|-u     SCSI_IDENT_<assoc>_<type>=<ident> output "
+            "format.\n"
+            "                    Defaults to device id page (0x83) if --page "
+            "not given,\n"
+            "                    only supported for VPD pages 0x80 and 0x83\n"
+            "    --extended|-E|-x    decode extended INQUIRY data VPD page "
+            "(0x86)\n"
+            "    --help|-h       print usage message then exit\n"
+            "    --hex|-H        output response in hex\n"
+            "    --id|-i         decode device identification VPD page "
+            "(0x83)\n"
+            "    --inhex=FN|-I FN    read ASCII hex from file FN instead of "
+            "DEVICE;\n"
+            "                        if used with --raw then read binary "
+            "from FN\n"
+            "    --len=LEN|-l LEN    requested response length (def: 0 "
+            "-> fetch 36\n"
+            "                        bytes first, then fetch again as "
+            "indicated)\n"
+            "    --maxlen=LEN|-m LEN    same as '--len='\n"
+            "    --page=PG|-p PG     Vital Product Data (VPD) page number "
+            "or\n"
+            "                        abbreviation (opcode number if "
+            "'--cmddt' given)\n"
+            "    --raw|-r        output response in binary (to stdout)\n"
+            "    --vendor|-s     show vendor specific fields in std "
+            "inquiry\n"
+            "    --verbose|-v    increase verbosity\n"
+            "    --version|-V    print version string then exit\n"
+            "    --vpd|-e        vital product data (set page with "
+            "'--page=PG')\n\n"
+            "Performs a SCSI INQUIRY command on DEVICE or decodes INQUIRY "
+            "response\nheld in file FN. If no options given then does a "
+            "'standard' INQUIRY.\nCan list VPD pages with '--vpd' or "
+            "'--page=PG' option. sg_vpd and\nsdparm decode more VPD pages "
+            "than this utility.\n");
+}
+
+#ifdef SG_SCSI_STRINGS
+static void
+usage_old()
+{
+#ifdef SG_LIB_LINUX
+    pr2serr("Usage:  sg_inq [-a] [-A] [-b] [-B=0|1] [-c] [-cl] [-d] [-e] "
+            "[-h]\n"
+            "               [-H] [-i] [I=FN] [-l=LEN] [-m] [-M] "
+            "[-o=OPCODE_PG]\n"
+            "               [-p=VPD_PG] [-P] [-r] [-s] [-u] [-U] [-v] [-V] "
+            "[-x]\n"
+            "               [-36] [-?] DEVICE\n"
+            "  where:\n"
+            "    -a    decode ATA information VPD page (0x89)\n"
+            "    -A    treat <device> as (directly attached) ATA device\n");
+#else
+    pr2serr("Usage:  sg_inq [-a] [-b] [-B 0|1] [-c] [-cl] [-d] [-e] [-h] "
+            "[-H]\n"
+            "               [-i] [-l=LEN] [-m] [-M] [-o=OPCODE_PG] "
+            "[-p=VPD_PG]\n"
+            "               [-P] [-r] [-s] [-u] [-v] [-V] [-x] [-36] "
+            "[-?]\n"
+            "               DEVICE\n"
+            "  where:\n"
+            "    -a    decode ATA information VPD page (0x89)\n");
+
+#endif  /* SG_LIB_LINUX */
+    pr2serr("    -b    decode Block limits VPD page (0xb0) (SBC)\n"
+            "    -B=0|1    0-> open(non-blocking); 1->open(blocking)\n"
+            "    -c    set CmdDt mode (use -o for opcode) [obsolete]\n"
+            "    -cl   list supported commands using CmdDt mode [obsolete]\n"
+            "    -d    decode: version descriptors or VPD page\n"
+            "    -e    set VPD mode (use -p for page code)\n"
+            "    -h    output in hex (ASCII to the right)\n"
+            "    -H    output in hex (ASCII to the right) [same as '-h']\n"
+            "    -i    decode device identification VPD page (0x83)\n"
+            "    -I=FN    use ASCII hex in file FN instead of DEVICE\n"
+            "    -l=LEN    requested response length (def: 0 "
+            "-> fetch 36\n"
+            "                    bytes first, then fetch again as "
+            "indicated)\n"
+            "    -m    decode management network addresses VPD page "
+            "(0x85)\n"
+            "    -M    decode mode page policy VPD page (0x87)\n"
+            "    -o=OPCODE_PG    opcode or page code in hex (def: 0)\n"
+            "    -p=VPD_PG    vpd page code in hex (def: 0)\n"
+            "    -P    decode Unit Path Report VPD page (0xc0) (EMC)\n"
+            "    -r    output response in binary ('-rr': output for hdparm)\n"
+            "    -s    decode SCSI Ports VPD page (0x88)\n"
+            "    -u    SCSI_IDENT_<assoc>_<type>=<ident> output format\n"
+            "    -v    verbose (output cdb and, if non-zero, resid)\n"
+            "    -V    output version string\n"
+            "    -x    decode extended INQUIRY data VPD page (0x86)\n"
+            "    -36   perform standard INQUIRY with a 36 byte response\n"
+            "    -?    output this usage message\n\n"
+            "If no options given then does a standard SCSI INQUIRY\n");
+}
+
+static void
+usage_for(const struct opts_t * op)
+{
+    if (op->opt_new)
+        usage();
+    else
+        usage_old();
+}
+
+#else  /* SG_SCSI_STRINGS */
+
+static void
+usage_for(const struct opts_t * op)
+{
+    op = op;
+    usage();
+}
+
+#endif /* SG_SCSI_STRINGS */
+
+/* Processes command line options according to new option format. Returns
+ * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
+static int
+cl_new_process(struct opts_t * op, int argc, char * argv[])
+{
+    int c, n;
+
+    while (1) {
+        int option_index = 0;
+
+#ifdef SG_LIB_LINUX
+#ifdef SG_SCSI_STRINGS
+        c = getopt_long(argc, argv, "aB:cdeEhHiI:l:m:NOp:rsuvVx",
+                        long_options, &option_index);
+#else
+        c = getopt_long(argc, argv, "B:cdeEhHiI:l:m:p:rsuvVx", long_options,
+                        &option_index);
+#endif /* SG_SCSI_STRINGS */
+#else  /* SG_LIB_LINUX */
+#ifdef SG_SCSI_STRINGS
+        c = getopt_long(argc, argv, "B:cdeEhHiI:l:m:NOp:rsuvVx", long_options,
+                        &option_index);
+#else
+        c = getopt_long(argc, argv, "B:cdeEhHiI:l:m:p:rsuvVx", long_options,
+                        &option_index);
+#endif /* SG_SCSI_STRINGS */
+#endif /* SG_LIB_LINUX */
+        if (c == -1)
+            break;
+
+        switch (c) {
+#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
+        case 'a':
+            ++op->do_ata;
+            break;
+#endif
+        case 'B':
+            if ('-' == optarg[0])
+                n = -1;
+            else {
+                n = sg_get_num(optarg);
+                if ((n < 0) || (n > 1)) {
+                    pr2serr("bad argument to '--block=' want 0 or 1\n");
+                    usage_for(op);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+            op->do_block = n;
+            break;
+        case 'c':
+            ++op->do_cmddt;
+            break;
+        case 'd':
+            ++op->do_descriptors;
+            break;
+        case 'e':
+            ++op->do_vpd;
+            break;
+        case 'E':
+        case 'x':
+            ++op->do_decode;
+            ++op->do_vpd;
+            op->page_num = VPD_EXT_INQ;
+            break;
+        case 'h':
+            ++op->do_help;
+            break;
+        case '?':
+            if (! op->do_help)
+                ++op->do_help;
+            break;
+        case 'H':
+            ++op->do_hex;
+            break;
+        case 'i':
+            ++op->do_decode;
+            ++op->do_vpd;
+            op->page_num = VPD_DEVICE_ID;
+            break;
+        case 'I':
+            op->inhex_fn = optarg;
+            break;
+        case 'l':
+        case 'm':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 65532)) {
+                pr2serr("bad argument to '--len='\n");
+                usage_for(op);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->resp_len = n;
+            break;
+#ifdef SG_SCSI_STRINGS
+        case 'N':
+            break;      /* ignore */
+        case 'O':
+            op->opt_new = 0;
+            return 0;
+#endif
+        case 'p':
+            op->page_arg = optarg;
+            ++op->p_given;
+            break;
+        case 'r':
+            ++op->do_raw;
+            break;
+        case 's':
+            ++op->do_vendor;
+            break;
+        case 'u':
+            ++op->do_export;
+            break;
+        case 'v':
+            ++op->do_verbose;
+            break;
+        case 'V':
+            ++op->do_version;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (op->do_help)
+                break;
+            usage_for(op);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage_for(op);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+#ifdef SG_SCSI_STRINGS
+/* Processes command line options according to old option format. Returns
+ * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
+static int
+cl_old_process(struct opts_t * op, int argc, char * argv[])
+{
+    int k, jmp_out, plen, num, n;
+    const char * cp;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case '3':
+                    if ('6' == *(cp + 1)) {
+                        op->resp_len = 36;
+                        --plen;
+                        ++cp;
+                    } else
+                        jmp_out = 1;
+                    break;
+                case 'a':
+                    op->page_num = VPD_ATA_INFO;
+                    ++op->do_vpd;
+                    ++op->num_pages;
+                    break;
+#ifdef SG_LIB_LINUX
+                case 'A':
+                    ++op->do_ata;
+                    break;
+#endif
+                case 'b':
+                    op->page_num = VPD_BLOCK_LIMITS;
+                    ++op->do_vpd;
+                    ++op->num_pages;
+                    break;
+                case 'c':
+                    ++op->do_cmddt;
+                    if ('l' == *(cp + 1)) {
+                        ++op->do_cmddt;
+                        --plen;
+                        ++cp;
+                    }
+                    break;
+                case 'd':
+                    ++op->do_descriptors;
+                    ++op->do_decode;
+                    break;
+                case 'e':
+                    ++op->do_vpd;
+                    break;
+                case 'h':
+                case 'H':
+                    ++op->do_hex;
+                    break;
+                case 'i':
+                    op->page_num = VPD_DEVICE_ID;
+                    ++op->do_vpd;
+                    ++op->num_pages;
+                    break;
+                case 'm':
+                    op->page_num = VPD_MAN_NET_ADDR;
+                    ++op->do_vpd;
+                    ++op->num_pages;
+                    break;
+                case 'M':
+                    op->page_num = VPD_MODE_PG_POLICY;
+                    ++op->do_vpd;
+                    ++op->num_pages;
+                    break;
+                case 'N':
+                    op->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case 'P':
+                    op->page_num = VPD_UPR_EMC;
+                    ++op->do_vpd;
+                    ++op->num_pages;
+                    break;
+                case 'r':
+                    ++op->do_raw;
+                    break;
+                case 's':
+                    op->page_num = VPD_SCSI_PORTS;
+                    ++op->do_vpd;
+                    ++op->num_pages;
+                    break;
+                case 'u':
+                    ++op->do_export;
+                    break;
+                case 'v':
+                    ++op->do_verbose;
+                    break;
+                case 'V':
+                    ++op->do_version;
+                    break;
+                case 'x':
+                    op->page_num = VPD_EXT_INQ;
+                    ++op->do_vpd;
+                    ++op->num_pages;
+                    break;
+                case '?':
+                    if (! op->do_help)
+                        ++op->do_help;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            else if (0 == strncmp("B=", cp, 2)) {
+                num = sscanf(cp + 2, "%d", &n);
+                if ((1 != num) || (n < 0) || (n > 1)) {
+                    pr2serr("'B=' option expects 0 or 1\n");
+                    usage_for(op);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->do_block = n;
+            } else if (0 == strncmp("I=", cp, 2))
+                op->inhex_fn = cp + 2;
+            else if (0 == strncmp("l=", cp, 2)) {
+                num = sscanf(cp + 2, "%d", &n);
+                if ((1 != num) || (n < 1)) {
+                    pr2serr("Inappropriate value after 'l=' option\n");
+                    usage_for(op);
+                    return SG_LIB_SYNTAX_ERROR;
+                } else if (n > MX_ALLOC_LEN) {
+                    pr2serr("value after 'l=' option too large\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->resp_len = n;
+            } else if (0 == strncmp("o=", cp, 2)) {
+                op->page_arg = cp + 2;
+                ++op->num_opcodes;
+            } else if (0 == strncmp("p=", cp, 2)) {
+                op->page_arg = cp + 2;
+                ++op->p_given;
+            } else if (0 == strncmp("-old", cp, 4))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage_for(op);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == op->device_name)
+            op->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not expecting: %s\n",
+                    op->device_name, cp);
+            usage_for(op);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+/* Process command line options. First check using new option format unless
+ * the SG3_UTILS_OLD_OPTS environment variable is defined which causes the
+ * old option format to be checked first. Both new and old format can be
+ * countermanded by a '-O' and '-N' options respectively. As soon as either
+ * of these options is detected (when processing the other format), processing
+ * stops and is restarted using the other format. Clear? */
+static int
+cl_process(struct opts_t * op, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        op->opt_new = 0;
+        res = cl_old_process(op, argc, argv);
+        if ((0 == res) && op->opt_new)
+            res = cl_new_process(op, argc, argv);
+    } else {
+        op->opt_new = 1;
+        res = cl_new_process(op, argc, argv);
+        if ((0 == res) && (0 == op->opt_new))
+            res = cl_old_process(op, argc, argv);
+    }
+    return res;
+}
+
+#else  /* SG_SCSI_STRINGS */
+
+static int
+cl_process(struct opts_t * op, int argc, char * argv[])
+{
+    return cl_new_process(op, argc, argv);
+}
+
+#endif  /* SG_SCSI_STRINGS */
+
+
+/* Read ASCII hex bytes or binary from fname (a file named '-' taken as
+ * stdin). If reading ASCII hex then there should be either one entry per
+ * line or a comma, space or tab separated list of bytes. If no_space is
+ * set then a string of ACSII hex digits is expected, 2 per byte. Everything
+ * from and including a '#' on a line is ignored. Returns 0 if ok, or 1 if
+ * error. */
+static int
+f2hex_arr(const char * fname, int as_binary, int no_space,
+          unsigned char * mp_arr, int * mp_arr_len, int max_arr_len)
+{
+    int fn_len, in_len, k, j, m, split_line, fd;
+    bool has_stdin;
+    unsigned int h;
+    const char * lcp;
+    FILE * fp;
+    char line[512];
+    char carry_over[4];
+    int off = 0;
+
+    if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len))
+        return 1;
+    fn_len = strlen(fname);
+    if (0 == fn_len)
+        return 1;
+    has_stdin = ((1 == fn_len) && ('-' == fname[0]));  /* read from stdin */
+    if (as_binary) {
+        if (has_stdin) {
+            fd = STDIN_FILENO;
+                if (sg_set_binary_mode(STDIN_FILENO) < 0)
+                    perror("sg_set_binary_mode");
+        } else {
+            fd = open(fname, O_RDONLY);
+            if (fd < 0) {
+                pr2serr("unable to open binary file %s: %s\n", fname,
+                         safe_strerror(errno));
+                return 1;
+            } else if (sg_set_binary_mode(fd) < 0)
+                perror("sg_set_binary_mode");
+        }
+        k = read(fd, mp_arr, max_arr_len);
+        if (k <= 0) {
+            if (0 == k)
+                pr2serr("read 0 bytes from binary file %s\n", fname);
+            else
+                pr2serr("read from binary file %s: %s\n", fname,
+                        safe_strerror(errno));
+            if (! has_stdin)
+                close(fd);
+            return 1;
+        }
+        *mp_arr_len = k;
+        if (! has_stdin)
+            close(fd);
+        return 0;
+    } else {    /* So read the file as ASCII hex */
+        if (has_stdin)
+            fp = stdin;
+        else {
+            fp = fopen(fname, "r");
+            if (NULL == fp) {
+                pr2serr("Unable to open %s for reading\n", fname);
+                return 1;
+            }
+        }
+    }
+
+    carry_over[0] = 0;
+    for (j = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+                split_line = 0;
+            } else
+                split_line = 1;
+        }
+        if (in_len < 1) {
+            carry_over[0] = 0;
+            continue;
+        }
+        if (carry_over[0]) {
+            if (isxdigit(line[0])) {
+                carry_over[1] = line[0];
+                carry_over[2] = '\0';
+                if (1 == sscanf(carry_over, "%x", &h))
+                    mp_arr[off - 1] = h;       /* back up and overwrite */
+                else {
+                    pr2serr("%s: carry_over error ['%s'] around line %d\n",
+                            __func__, carry_over, j + 1);
+                    goto bad;
+                }
+                lcp = line + 1;
+                --in_len;
+            } else
+                lcp = line;
+            carry_over[0] = 0;
+        } else
+            lcp = line;
+
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+        if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) {
+            pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
+                    j + 1, m + k + 1);
+            goto bad;
+        }
+        if (no_space) {
+            for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
+                 ++k, lcp += 2) {
+                if (1 != sscanf(lcp, "%2x", &h)) {
+                    pr2serr("%s: bad hex number in line %d, pos %d\n",
+                            __func__, j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+                if ((off + k) >= max_arr_len) {
+                    pr2serr("%s: array length exceeded\n", __func__);
+                    goto bad;
+                }
+                mp_arr[off + k] = h;
+            }
+            if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
+                carry_over[0] = *lcp;
+            off += k;
+        } else {
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("%s: hex number larger than 0xff in line %d, "
+                                "pos %d\n", __func__, j + 1,
+                                (int)(lcp - line + 1));
+                        goto bad;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("%s: array length exceeded\n", __func__);
+                        goto bad;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if (('#' == *lcp) || ('\r' == *lcp)) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("%s: error in line %d, at pos %d\n", __func__,
+                            j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+            }
+            off += (k + 1);
+        }
+    }
+    *mp_arr_len = off;
+    if (stdin != fp)
+        fclose(fp);
+    return 0;
+bad:
+    if (stdin != fp)
+        fclose(fp);
+    return 1;
+}
+
+
+/* Local version of sg_ll_inquiry() [found in libsgutils] that additionally
+ * passes back resid. Same return values as sg_ll_inquiry() (0 is good). */
+static int
+pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len,
+           int * residp, int noisy, int verbose)
+{
+    int res, ret, k, sense_cat, resid;
+    unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    unsigned char * up;
+    struct sg_pt_base * ptvp;
+
+    if (evpd)
+        inqCmdBlk[1] |= 1;
+    inqCmdBlk[2] = (unsigned char)pg_op;
+    /* 16 bit allocation length (was 8) is a recent SPC-3 addition */
+    sg_put_unaligned_be16((uint16_t)mx_resp_len, inqCmdBlk + 3);
+    if (verbose) {
+        pr2serr("    inquiry cdb: ");
+        for (k = 0; k < INQUIRY_CMDLEN; ++k)
+            pr2serr("%02x ", inqCmdBlk[k]);
+        pr2serr("\n");
+    }
+    if (resp && (mx_resp_len > 0)) {
+        up = (unsigned char *)resp;
+        up[0] = 0x7f;   /* defensive prefill */
+        if (mx_resp_len > 4)
+            up[4] = 0;
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("inquiry: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, inqCmdBlk, sizeof(inqCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b,
+                               noisy, verbose, &sense_cat);
+    resid = get_scsi_pt_resid(ptvp);
+    if (residp)
+        *residp = resid;
+    destruct_scsi_pt_obj(ptvp);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else if (ret < 4) {
+        if (verbose)
+            pr2serr("inquiry: got too few bytes (%d)\n", ret);
+        ret = SG_LIB_CAT_MALFORMED;
+    } else
+        ret = 0;
+
+    if (resid > 0) {
+        if (resid > mx_resp_len) {
+            pr2serr("INQUIRY resid (%d) should never exceed requested "
+                    "len=%d\n", resid, mx_resp_len);
+            return ret ? ret : SG_LIB_CAT_MALFORMED;
+        }
+        /* zero unfilled section of response buffer */
+        memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
+    }
+    return ret;
+}
+
+static const struct svpd_values_name_t *
+sdp_find_vpd_by_acron(const char * ap)
+{
+    const struct svpd_values_name_t * vnp;
+
+    for (vnp = vpd_pg; vnp->acron; ++vnp) {
+        if (0 == strcmp(vnp->acron, ap))
+            return vnp;
+    }
+    return NULL;
+}
+
+static void
+enumerate_vpds()
+{
+    const struct svpd_values_name_t * vnp;
+
+    for (vnp = vpd_pg; vnp->acron; ++vnp) {
+        if (vnp->name)
+            printf("  %-10s 0x%02x      %s\n", vnp->acron, vnp->value,
+                   vnp->name);
+    }
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* Strip initial and trailing whitespaces; convert one or repeated
+ * whitespaces to a single "_"; convert non-printable characters to "."
+ * and if there are no valid (i.e. printable) characters return 0.
+ * Process 'str' in place (i.e. it's input and output) and return the
+ * length of the output, excluding the trailing '\0'. To cover any
+ * potential unicode string an intermediate zero is skipped; two
+ * consecutive zeroes indicate a string termination.
+ */
+static int
+encode_whitespaces(unsigned char *str, int inlen)
+{
+    int k, res;
+    int j = 0;
+    int valid = 0;
+    int outlen = inlen, zeroes = 0;
+
+    /* Skip initial whitespaces */
+    while (isblank(str[j]))
+        j++;
+    /* Skip possible unicode prefix characters */
+    while (str[j] < 0x20)
+        j++;
+
+    k = j;
+    /* Strip trailing whitespaces */
+    while ((outlen > k) &&
+           (isblank(str[outlen - 1]) || ('\0' == str[outlen - 1]))) {
+        str[outlen - 1] = '\0';
+        outlen--;
+    }
+    for (res = 0; k < outlen; ++k) {
+        if (isblank(str[k])) {
+            if ((res > 0) && ('_' != str[res - 1])) {
+                str[res++] = '_';
+                valid++;
+            }
+            zeroes = 0;
+        } else if (! isprint(str[k])) {
+            if (str[k] == 0x00) {
+                /* Stop on more than one consecutive zero */
+                if (zeroes)
+                    break;
+                zeroes++;
+                continue;
+            }
+            str[res++] = '.';
+            zeroes = 0;
+        } else {
+            str[res++] = str[k];
+            valid++;
+            zeroes = 0;
+        }
+    }
+    if (! valid)
+        res = 0;
+    if (res < inlen)
+        str[res] = '\0';
+    return res;
+}
+
+static int
+encode_unicode(unsigned char *str, int inlen)
+{
+    int k = 0, res;
+    int zeroes = 0;
+
+    for (res = 0; k < inlen; ++k) {
+        if (str[k] == 0x00) {
+            if (zeroes) {
+                str[res++] = '\0';
+                break;
+            }
+            zeroes++;
+        } else {
+            zeroes = 0;
+            if (isprint(str[k]))
+                str[res++] = str[k];
+            else
+                str[res++] = ' ';
+        }
+    }
+
+    return res;
+}
+
+static int
+encode_string(char *out, const unsigned char *in, int inlen)
+{
+    int i, j = 0;
+
+    for (i = 0; (i < inlen); ++i) {
+        if (isblank(in[i]) || !isprint(in[i])) {
+            sprintf(&out[j], "\\x%02x", in[i]);
+            j += 4;
+        } else {
+            out[j] = in[i];
+            j++;
+        }
+    }
+    out[j] = '\0';
+    return j;
+}
+
+struct vpd_name {
+    int number;
+    int peri_type;
+    const char * name;
+};
+
+/* In numerical order */
+static struct vpd_name vpd_name_arr[] = {
+    {VPD_SUPPORTED_VPDS, 0, "Supported VPD pages"},             /* 0x0 */
+    {VPD_UNIT_SERIAL_NUM, 0, "Unit serial number"},             /* 0x80 */
+    {0x81, 0, "Implemented operating definitions (obsolete)"},
+    {0x82, 0, "ASCII implemented operating definition (obsolete)"},
+    {VPD_DEVICE_ID, 0, "Device identification"},
+    {VPD_SOFTW_INF_ID, 0, "Software interface identification"},
+    {VPD_MAN_NET_ADDR, 0, "Management network addresses"},
+    {VPD_EXT_INQ, 0, "Extended INQUIRY data"},
+    {VPD_MODE_PG_POLICY, 0, "Mode page policy"},
+    {VPD_SCSI_PORTS, 0, "SCSI ports"},
+    {VPD_ATA_INFO, 0, "ATA information"},
+    {VPD_POWER_CONDITION, 0, "Power condition"},
+    {VPD_DEVICE_CONSTITUENTS, 0, "Device constituents"},
+    {VPD_CFA_PROFILE_INFO, 0, "CFA profile information"},       /* 0x8c */
+    {VPD_POWER_CONSUMPTION, 0, "Power consumption"},            /* 0x8d */
+    {VPD_3PARTY_COPY, 0, "Third party copy"},                   /* 0x8f */
+    /* 0xb0 to 0xbf are per peripheral device type */
+    {VPD_BLOCK_LIMITS, 0, "Block limits (sbc2)"},               /* 0xb0 */
+    {VPD_BLOCK_DEV_CHARS, 0, "Block device characteristics (sbc3)"},
+    {VPD_LB_PROVISIONING, 0, "Logical block provisioning (sbc3)"},
+    {VPD_REFERRALS, 0, "Referrals (sbc3)"},
+    {0xb0, PDT_TAPE, "Sequential access device capabilities (ssc3)"},
+    {0xb2, PDT_TAPE, "TapeAlert supported flags (ssc3)"},
+    {0xb0, PDT_OSD, "OSD information (osd)"},
+    {0xb1, PDT_OSD, "Security token (osd)"},
+    /* 0xc0 to 0xff are vendor specific */
+    {0xc0, 0, "vendor: Firmware numbers (seagate); Unit path report (EMC)"},
+    {0xc1, 0, "vendor: Date code (seagate)"},
+    {0xc2, 0, "vendor: Jumper settings (seagate); Software version (RDAC)"},
+    {0xc3, 0, "vendor: Device behavior (seagate)"},
+    {0xc9, 0, "Volume Access Control (RDAC)"},
+};
+
+static const char *
+get_vpd_page_str(int vpd_page_num, int scsi_ptype)
+{
+    int k;
+    int vpd_name_arr_sz =
+        (int)(sizeof(vpd_name_arr) / sizeof(vpd_name_arr[0]));
+
+    if ((vpd_page_num >= 0xb0) && (vpd_page_num < 0xc0)) {
+        /* peripheral device type relevant for 0xb0..0xbf range */
+        for (k = 0; k < vpd_name_arr_sz; ++k) {
+            if ((vpd_name_arr[k].number == vpd_page_num) &&
+                (vpd_name_arr[k].peri_type == scsi_ptype))
+                break;
+        }
+        if (k < vpd_name_arr_sz)
+            return vpd_name_arr[k].name;
+        for (k = 0; k < vpd_name_arr_sz; ++k) {
+            if ((vpd_name_arr[k].number == vpd_page_num) &&
+                (vpd_name_arr[k].peri_type == 0))
+                break;
+        }
+        if (k < vpd_name_arr_sz)
+            return vpd_name_arr[k].name;
+        else
+            return NULL;
+    } else {
+        /* rest of 0x0..0xff range doesn't depend on peripheral type */
+        for (k = 0; k < vpd_name_arr_sz; ++k) {
+            if (vpd_name_arr[k].number == vpd_page_num)
+                break;
+        }
+        if (k < vpd_name_arr_sz)
+            return vpd_name_arr[k].name;
+        else
+            return NULL;
+    }
+}
+
+static void
+decode_supported_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int vpd, k, rlen, pdt;
+    const char * cp;
+
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("Supported VPD pages VPD page length too short=%d\n", len);
+        return;
+    }
+    pdt = 0x1f & buff[0];
+    rlen = buff[3] + 4;
+    if (rlen > len)
+        pr2serr("Supported VPD pages VPD page truncated, indicates %d, got "
+                "%d\n", rlen, len);
+    else
+        len = rlen;
+    printf("   Supported VPD pages:\n");
+    for (k = 0; k < len - 4; ++k) {
+        vpd = buff[4 + k];
+        cp = get_vpd_page_str(vpd, pdt);
+        if (cp)
+            printf("     0x%x\t%s\n", vpd, cp);
+        else
+            printf("     0x%x\n", vpd);
+    }
+}
+
+/* ASCII Information VPD pages (page numbers: 0x1 to 0x7f) */
+static void
+decode_ascii_inf(unsigned char * buff, int len, int do_hex)
+{
+    int al, k, bump;
+    unsigned char * ucp;
+    unsigned char * p;
+
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("ASCII information VPD page length too short=%d\n", len);
+        return;
+    }
+    if (4 == len)
+        return;
+    al = buff[4];
+    if ((al + 5) > len)
+        al = len - 5;
+    for (k = 0, ucp = buff + 5; k < al; k += bump, ucp += bump) {
+        p = (unsigned char *)memchr(ucp, 0, al - k);
+        if (! p) {
+            printf("  %.*s\n", al - k, (const char *)ucp);
+            break;
+        }
+        printf("  %s\n", (const char *)ucp);
+        bump = (p - ucp) + 1;
+    }
+    ucp = buff + 5 + al;
+    if (ucp < (buff + len)) {
+        printf("Vendor specific information in hex:\n");
+        dStrHex((const char *)ucp, len - (al + 5), 0);
+    }
+}
+
+static void
+decode_id_vpd(unsigned char * buff, int len, int do_hex)
+{
+    if (len < 4) {
+        pr2serr("Device identification VPD page length too "
+                "short=%d\n", len);
+        return;
+    }
+    decode_dev_ids("Device identification", buff + 4, len - 4, do_hex);
+}
+
+static const char * assoc_arr[] =
+{
+    "addressed logical unit",
+    "target port",      /* that received request; unless SCSI ports VPD */
+    "target device that contains addressed lu",
+    "reserved [0x3]",
+};
+
+static const char * network_service_type_arr[] =
+{
+    "unspecified",
+    "storage configuration service",
+    "diagnostics",
+    "status",
+    "logging",
+    "code download",
+    "copy service",
+    "administrative configuration service",
+    "[0x8]", "[0x9]", "[0xa]", "[0xb]", "[0xc]", "[0xd]",
+    "[0xe]", "[0xf]", "[0x10]", "[0x11]", "[0x12]", "[0x13]", "[0x14]",
+    "[0x15]", "[0x16]", "[0x17]", "[0x18]", "[0x19]", "[0x1a]",
+    "[0x1b]", "[0x1c]", "[0x1d]", "[0x1e]", "[0x1f]",
+};
+
+/* VPD_MAN_NET_ADDR */
+static void
+decode_net_man_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, bump, na_len;
+    unsigned char * ucp;
+
+    if (len < 4) {
+        pr2serr("Management network addresses VPD page length too short=%d\n",
+                len);
+        return;
+    }
+    if (do_hex > 2) {
+        dStrHex((const char *)buff, len, -1);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        printf("  %s, Service type: %s\n",
+               assoc_arr[(ucp[0] >> 5) & 0x3],
+               network_service_type_arr[ucp[0] & 0x1f]);
+        na_len = sg_get_unaligned_be16(ucp + 2);
+        bump = 4 + na_len;
+        if ((k + bump) > len) {
+            pr2serr("Management network addresses VPD page, short "
+                    "descriptor length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (na_len > 0) {
+            if (do_hex) {
+                printf("    Network address:\n");
+                dStrHex((const char *)(ucp + 4), na_len, 0);
+            } else
+                printf("    %s\n", ucp + 4);
+        }
+    }
+}
+
+static const char * mode_page_policy_arr[] =
+{
+    "shared",
+    "per target port",
+    "per initiator port",
+    "per I_T nexus",
+};
+
+/* VPD_MODE_PG_POLICY */
+static void
+decode_mode_policy_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, bump;
+    unsigned char * ucp;
+
+    if (len < 4) {
+        pr2serr("Mode page policy VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex > 2) {
+        dStrHex((const char *)buff, len, -1);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        bump = 4;
+        if ((k + bump) > len) {
+            pr2serr("Mode page policy VPD page, short "
+                    "descriptor length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (do_hex)
+            dStrHex((const char *)ucp, 4, (1 == do_hex) ? 1 : -1);
+        else {
+            printf("  Policy page code: 0x%x", (ucp[0] & 0x3f));
+            if (ucp[1])
+                printf(",  subpage code: 0x%x\n", ucp[1]);
+            else
+                printf("\n");
+            printf("    MLUS=%d,  Policy: %s\n", !!(ucp[2] & 0x80),
+                   mode_page_policy_arr[ucp[2] & 0x3]);
+        }
+    }
+}
+
+/* VPD_SCSI_PORTS */
+static void
+decode_scsi_ports_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, bump, rel_port, ip_tid_len, tpd_len;
+    unsigned char * ucp;
+
+    if (len < 4) {
+        pr2serr("SCSI Ports VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex > 2) {
+        dStrHex((const char *)buff, len, -1);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        rel_port = sg_get_unaligned_be16(ucp + 2);
+        printf("Relative port=%d\n", rel_port);
+        ip_tid_len = sg_get_unaligned_be16(ucp + 6);
+        bump = 8 + ip_tid_len;
+        if ((k + bump) > len) {
+            pr2serr("SCSI Ports VPD page, short descriptor "
+                    "length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (ip_tid_len > 0) {
+            if (do_hex) {
+                printf(" Initiator port transport id:\n");
+                dStrHex((const char *)(ucp + 8), ip_tid_len,
+                        (1 == do_hex) ? 1 : -1);
+            } else
+                decode_transport_id(" ", ucp + 8, ip_tid_len);
+        }
+        tpd_len = sg_get_unaligned_be16(ucp + bump + 2);
+        if ((k + bump + tpd_len + 4) > len) {
+            pr2serr("SCSI Ports VPD page, short descriptor(tgt) "
+                    "length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (tpd_len > 0) {
+            printf(" Target port descriptor(s):\n");
+            if (do_hex)
+                dStrHex((const char *)(ucp + bump + 4), tpd_len,
+                        (1 == do_hex) ? 1 : -1);
+            else
+                decode_dev_ids("SCSI Ports", ucp + bump + 4, tpd_len,
+                               do_hex);
+        }
+        bump += tpd_len + 4;
+    }
+}
+
+/* These are target port, device server (i.e. target) and LU identifiers */
+static void
+decode_dev_ids(const char * leadin, unsigned char * buff, int len, int do_hex)
+{
+    int u, j, m, id_len, p_id, c_set, piv, assoc, desig_type, i_len;
+    int off, ci_off, c_id, d_id, naa, vsi, k;
+    uint64_t vsei;
+    uint64_t id_ext;
+    const unsigned char * ucp;
+    const unsigned char * ip;
+    char b[64];
+    const char * cp;
+
+    if (buff[2] != 0) {
+        /*
+         * Reference the 3rd byte of the first Identification descriptor
+         * of a page 83 reply to determine whether the reply is compliant
+         * with SCSI-2 or SPC-2/3 specifications.  A zero value in the
+         * 3rd byte indicates an SPC-2/3 conformant reply ( the field is
+         * reserved ).  This byte will be non-zero for a SCSI-2
+         * conformant page 83 reply from these EMC Symmetrix models since
+         * the 7th byte of the reply corresponds to the 4th and 5th
+         * nibbles of the 6-byte OUI for EMC, that is, 0x006048.
+         */
+        i_len = len;
+        ip = ucp = buff;
+        c_set = 1;
+        assoc = 0;
+        piv = 0;
+        p_id = 0xf;
+        desig_type = 3;
+        j = 1;
+        off = 16;
+        printf("  Pre-SPC descriptor, descriptor length: %d\n", i_len);
+        goto decode;
+    }
+
+    for (j = 1, off = -1;
+         (u = sg_vpd_dev_id_iter(buff, len, &off, -1, -1, -1)) == 0;
+         ++j) {
+        ucp = buff + off;
+        i_len = ucp[3];
+        id_len = i_len + 4;
+        printf("  Designation descriptor number %d, "
+               "descriptor length: %d\n", j, id_len);
+        if ((off + id_len) > len) {
+            pr2serr("%s VPD page error: designator length longer "
+                    "than\n     remaining response length=%d\n", leadin,
+                    (len - off));
+            return;
+        }
+        ip = ucp + 4;
+        p_id = ((ucp[0] >> 4) & 0xf);   /* protocol identifier */
+        c_set = (ucp[0] & 0xf);         /* code set */
+        piv = ((ucp[1] & 0x80) ? 1 : 0); /* protocol identifier valid */
+        assoc = ((ucp[1] >> 4) & 0x3);
+        desig_type = (ucp[1] & 0xf);
+  decode:
+        if (piv && ((1 == assoc) || (2 == assoc)))
+            printf("    transport: %s\n",
+                   sg_get_trans_proto_str(p_id, sizeof(b), b));
+        cp = sg_get_desig_type_str(desig_type);
+        printf("    designator_type: %s,  ", cp ? cp : "-");
+        cp = sg_get_desig_code_set_str(c_set);
+        printf("code_set: %s\n", cp ? cp : "-");
+        cp = sg_get_desig_assoc_str(assoc);
+        printf("    associated with the %s\n", cp ? cp : "-");
+        if (do_hex) {
+            printf("    designator header(hex): %.2x %.2x %.2x %.2x\n",
+                   ucp[0], ucp[1], ucp[2], ucp[3]);
+            printf("    designator:\n");
+            dStrHex((const char *)ip, i_len, 0);
+            continue;
+        }
+        switch (desig_type) {
+        case 0: /* vendor specific */
+            k = 0;
+            if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
+                for (k = 0; (k < i_len) && isprint(ip[k]); ++k)
+                    ;
+                if (k >= i_len)
+                    k = 1;
+            }
+            if (k)
+                printf("      vendor specific: %.*s\n", i_len, ip);
+            else {
+                printf("      vendor specific:\n");
+                dStrHex((const char *)ip, i_len, -1);
+            }
+            break;
+        case 1: /* T10 vendor identification */
+            printf("      vendor id: %.8s\n", ip);
+            if (i_len > 8) {
+                if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
+                    printf("      vendor specific: %.*s\n", i_len - 8, ip + 8);
+                } else {
+                    printf("      vendor specific: 0x");
+                    for (m = 8; m < i_len; ++m)
+                        printf("%02x", (unsigned int)ip[m]);
+                    printf("\n");
+                }
+            }
+            break;
+        case 2: /* EUI-64 based */
+            printf("      EUI-64 based %d byte identifier\n", i_len);
+            if (1 != c_set) {
+                pr2serr("      << expected binary code_set (1)>>\n");
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            ci_off = 0;
+            if (16 == i_len) {
+                ci_off = 8;
+                id_ext = sg_get_unaligned_be64(ip);
+                printf("      Identifier extension: 0x%" PRIx64 "\n", id_ext);
+            } else if ((8 != i_len) && (12 != i_len)) {
+                pr2serr("      << can only decode 8, 12 and 16 "
+                        "byte ids>>\n");
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            c_id = sg_get_unaligned_be24(ip + ci_off);
+            printf("      IEEE Company_id: 0x%x\n", c_id);
+            vsei = sg_get_unaligned_be48(ip + ci_off + 3);
+            printf("      Vendor Specific Extension Identifier: 0x%" PRIx64
+                   "\n", vsei);
+            if (12 == i_len) {
+                d_id = sg_get_unaligned_be32(ip + 8);
+                printf("      Directory ID: 0x%x\n", d_id);
+            }
+            printf("      [0x");
+            for (m = 0; m < i_len; ++m)
+                printf("%02x", (unsigned int)ip[m]);
+            printf("]\n");
+            break;
+        case 3: /* NAA <n> */
+            naa = (ip[0] >> 4) & 0xff;
+            if (1 != c_set) {
+                pr2serr("      << expected binary code_set (1), got %d for "
+                        "NAA=%d>>\n", c_set, naa);
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            switch (naa) {
+            case 2:     /* NAA 2: IEEE Extended */
+                if (8 != i_len) {
+                    pr2serr("      << unexpected NAA 2 identifier "
+                            "length: 0x%x>>\n", i_len);
+                    dStrHexErr((const char *)ip, i_len, -1);
+                    break;
+                }
+                d_id = (((ip[0] & 0xf) << 8) | ip[1]);
+                c_id = sg_get_unaligned_be24(ip + 2);
+                vsi = sg_get_unaligned_be24(ip + 5);
+                printf("      NAA 2, vendor specific identifier A: 0x%x\n",
+                       d_id);
+                printf("      IEEE Company_id: 0x%x\n", c_id);
+                printf("      vendor specific identifier B: 0x%x\n", vsi);
+                printf("      [0x");
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("]\n");
+                break;
+            case 3:     /* NAA 3: Locally assigned */
+                if (8 != i_len) {
+                    pr2serr("      << unexpected NAA 3 identifier "
+                            "length: 0x%x>>\n", i_len);
+                    dStrHexErr((const char *)ip, i_len, -1);
+                    break;
+                }
+                printf("      NAA 3, Locally assigned:\n");
+                printf("      [0x");
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("]\n");
+                break;
+            case 5:     /* NAA 5: IEEE Registered */
+                if (8 != i_len) {
+                    pr2serr("      << unexpected NAA 5 identifier "
+                            "length: 0x%x>>\n", i_len);
+                    dStrHexErr((const char *)ip, i_len, -1);
+                    break;
+                }
+                c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
+                        (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
+                vsei = ip[3] & 0xf;
+                for (m = 1; m < 5; ++m) {
+                    vsei <<= 8;
+                    vsei |= ip[3 + m];
+                }
+                printf("      NAA 5, IEEE Company_id: 0x%x\n", c_id);
+                printf("      Vendor Specific Identifier: 0x%" PRIx64
+                       "\n", vsei);
+                printf("      [0x");
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("]\n");
+                break;
+            case 6:     /* NAA 6: IEEE Registered extended */
+                if (16 != i_len) {
+                    pr2serr("      << unexpected NAA 6 identifier "
+                            "length: 0x%x>>\n", i_len);
+                    dStrHexErr((const char *)ip, i_len, 0);
+                    break;
+                }
+                c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
+                        (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
+                vsei = ip[3] & 0xf;
+                for (m = 1; m < 5; ++m) {
+                    vsei <<= 8;
+                    vsei |= ip[3 + m];
+                }
+                printf("      NAA 6, IEEE Company_id: 0x%x\n", c_id);
+                printf("      Vendor Specific Identifier: 0x%" PRIx64 "\n",
+                       vsei);
+                vsei = sg_get_unaligned_be64(ip + 8);
+                printf("      Vendor Specific Identifier Extension: "
+                       "0x%" PRIx64 "\n", vsei);
+                printf("      [0x");
+                for (m = 0; m < 16; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("]\n");
+                break;
+            default:
+                pr2serr("      << bad NAA nibble , expect 2, 3, 5 or 6, "
+                        "got %d>>\n", naa);
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            break;
+        case 4: /* Relative target port */
+            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+                pr2serr("      << expected binary code_set, target "
+                        "port association, length 4>>\n");
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            d_id = sg_get_unaligned_be16(ip + 2);
+            printf("      Relative target port: 0x%x\n", d_id);
+            break;
+        case 5: /* (primary) Target port group */
+            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+                pr2serr("      << expected binary code_set, target "
+                        "port association, length 4>>\n");
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            d_id = sg_get_unaligned_be16(ip + 2);
+            printf("      Target port group: 0x%x\n", d_id);
+            break;
+        case 6: /* Logical unit group */
+            if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
+                pr2serr("      << expected binary code_set, logical "
+                        "unit association, length 4>>\n");
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            d_id = sg_get_unaligned_be16(ip + 2);
+            printf("      Logical unit group: 0x%x\n", d_id);
+            break;
+        case 7: /* MD5 logical unit identifier */
+            if ((1 != c_set) || (0 != assoc)) {
+                pr2serr("      << expected binary code_set, logical "
+                        "unit association>>\n");
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            printf("      MD5 logical unit identifier:\n");
+            dStrHex((const char *)ip, i_len, -1);
+            break;
+        case 8: /* SCSI name string */
+            if (3 != c_set) {
+                pr2serr("      << expected UTF-8 code_set>>\n");
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            printf("      SCSI name string:\n");
+            /* does %s print out UTF-8 ok??
+             * Seems to depend on the locale. Looks ok here with my
+             * locale setting: en_AU.UTF-8
+             */
+            printf("      %s\n", (const char *)ip);
+            break;
+        case 9: /* Protocol specific port identifier */
+            /* added in spc4r36, PIV must be set, proto_id indicates */
+            /* whether UAS (USB) or SOP (PCIe) or ... */
+            if (! piv)
+                printf("      >>>> Protocol specific port identifier "
+                       "expects protocol\n"
+                       "           identifier to be valid and it is not\n");
+            if (TPROTO_UAS == p_id) {
+                printf("      USB device address: 0x%x\n", 0x7f & ip[0]);
+                printf("      USB interface number: 0x%x\n", ip[2]);
+            } else if (TPROTO_SOP == p_id) {
+                printf("      PCIe routing ID, bus number: 0x%x\n", ip[0]);
+                printf("          function number: 0x%x\n", ip[1]);
+                printf("          [or device number: 0x%x, function number: "
+                       "0x%x]\n", (0x1f & (ip[1] >> 3)), 0x7 & ip[1]);
+            } else
+                printf("      >>>> unexpected protocol indentifier: %s\n"
+                       "           with Protocol specific port "
+                       "identifier\n",
+                       sg_get_trans_proto_str(p_id, sizeof(b), b));
+            break;
+        case 0xa: /* UUID identifier [spc5r08] */
+            if (1 != c_set) {
+                pr2serr("      << expected binary code_set >>\n");
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            if ((1 != ((ip[0] >> 4) & 0xf)) || (18 != i_len)) {
+                pr2serr("      << expected locally assigned UUID, 16 bytes "
+                        "long >>\n");
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            printf("      Locally assigned UUID: ");
+            for (m = 0; m < 16; ++m) {
+                if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
+                    printf("-");
+                printf("%02x", (unsigned int)ip[2 + m]);
+            }
+            printf("\n");
+                break;
+        default: /* reserved */
+            pr2serr("      reserved designator=0x%x\n", desig_type);
+            dStrHexErr((const char *)ip, i_len, -1);
+            break;
+        }
+    }
+    if (-2 == u)
+        pr2serr("%s VPD page error: around offset=%d\n", leadin, off);
+}
+
+static void
+export_dev_ids(unsigned char * buff, int len, int verbose)
+{
+    int u, j, m, id_len, c_set, assoc, desig_type, i_len;
+    int off, d_id, naa, k, p_id;
+    unsigned char * ucp;
+    unsigned char * ip;
+    const char * assoc_str;
+
+    if (buff[2] != 0) {
+        /*
+         * Cf decode_dev_ids() for details
+         */
+        i_len = len;
+        ip = buff;
+        c_set = 1;
+        assoc = 0;
+        p_id = 0xf;
+        desig_type = 3;
+        j = 1;
+        off = 16;
+        goto decode;
+    }
+
+    for (j = 1, off = -1;
+         (u = sg_vpd_dev_id_iter(buff, len, &off, -1, -1, -1)) == 0;
+         ++j) {
+        ucp = buff + off;
+        i_len = ucp[3];
+        id_len = i_len + 4;
+        if ((off + id_len) > len) {
+            if (verbose)
+                pr2serr("Device Identification VPD page error: designator "
+                        "length longer than\n     remaining response "
+                        "length=%d\n", (len - off));
+            return;
+        }
+        ip = ucp + 4;
+        p_id = ((ucp[0] >> 4) & 0xf);   /* protocol identifier */
+        c_set = (ucp[0] & 0xf);
+        assoc = ((ucp[1] >> 4) & 0x3);
+        desig_type = (ucp[1] & 0xf);
+  decode:
+        switch (assoc) {
+            case 0:
+                assoc_str = "LUN";
+                break;
+            case 1:
+                assoc_str = "PORT";
+                break;
+            case 2:
+                assoc_str = "TARGET";
+                break;
+            default:
+                if (verbose)
+                    pr2serr("    Invalid association %d\n", assoc);
+                return;
+        }
+        switch (desig_type) {
+        case 0: /* vendor specific */
+            if (i_len == 0 || i_len > 128)
+                break;
+            if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
+                k = encode_whitespaces(ip, i_len);
+                /* udev-conformant character encoding */
+                if (k > 0) {
+                    printf("SCSI_IDENT_%s_VENDOR=", assoc_str);
+                    for (m = 0; m < k; ++m) {
+                        if ((ip[m] >= '0' && ip[m] <= '9') ||
+                            (ip[m] >= 'A' && ip[m] <= 'Z') ||
+                            (ip[m] >= 'a' && ip[m] <= 'z') ||
+                            strchr("#+-.:=@_", ip[m]) != NULL)
+                            printf("%c", ip[m]);
+                        else
+                            printf("\\x%02x", ip[m]);
+                    }
+                    printf("\n");
+                }
+            } else {
+                printf("SCSI_IDENT_%s_VENDOR=", assoc_str);
+                for (m = 0; m < i_len; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            }
+            break;
+        case 1: /* T10 vendor identification */
+            printf("SCSI_IDENT_%s_T10=", assoc_str);
+            if ((2 == c_set) || (3 == c_set)) {
+                k = encode_whitespaces(ip, i_len);
+                /* udev-conformant character encoding */
+                for (m = 0; m < k; ++m) {
+                    if ((ip[m] >= '0' && ip[m] <= '9') ||
+                        (ip[m] >= 'A' && ip[m] <= 'Z') ||
+                        (ip[m] >= 'a' && ip[m] <= 'z') ||
+                        strchr("#+-.:=@_", ip[m]) != NULL)
+                        printf("%c", ip[m]);
+                    else
+                        printf("\\x%02x", ip[m]);
+                }
+                printf("\n");
+                if (!memcmp(ip, "ATA_", 4)) {
+                    printf("SCSI_IDENT_%s_ATA=%.*s\n", assoc_str,
+                           k - 4, ip + 4);
+                }
+            } else {
+                for (m = 0; m < i_len; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            }
+            break;
+        case 2: /* EUI-64 based */
+            if (1 != c_set) {
+                if (verbose) {
+                    pr2serr("      << expected binary code_set (1)>>\n");
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            printf("SCSI_IDENT_%s_EUI64=", assoc_str);
+            for (m = 0; m < i_len; ++m)
+                printf("%02x", (unsigned int)ip[m]);
+            printf("\n");
+            break;
+        case 3: /* NAA */
+            if (1 != c_set) {
+                if (verbose) {
+                    pr2serr("      << expected binary code_set (1)>>\n");
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            /*
+             * Unfortunately, there are some (broken) implementations
+             * which return _several_ NAA descriptors.
+             * So add a suffix to differentiate between them.
+             */
+            naa = (ip[0] >> 4) & 0xff;
+            if ((naa < 2) || (naa > 6) || (4 == naa)) {
+                if (verbose) {
+                    pr2serr("      << unexpected naa [0x%x]>>\n", naa);
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            if (6 != naa) {
+                const char *suffix;
+
+                if (8 != i_len) {
+                    if (verbose) {
+                        pr2serr("      << unexpected NAA %d identifier "
+                                "length: 0x%x>>\n", naa, i_len);
+                        dStrHexErr((const char *)ip, i_len, 0);
+                    }
+                    break;
+                }
+                if (naa != 2 && naa != 3 && naa != 5) {
+                    if (verbose) {
+                        pr2serr("      << unexpected NAA format %d>>\n", naa);
+                        dStrHexErr((const char *)ip, i_len, 0);
+                    }
+                    break;
+                }
+                switch (naa) {
+                    case 5:
+                        suffix="REG";
+                        break;
+                    case 2:
+                        suffix="EXT";
+                        break;
+                    case 3:
+                    default:
+                        suffix="LOCAL";
+                        break;
+                }
+                printf("SCSI_IDENT_%s_NAA_%s=", assoc_str, suffix);
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            } else {      /* NAA IEEE Registered extended */
+                if (16 != i_len) {
+                    if (verbose) {
+                        pr2serr("      << unexpected NAA 6 identifier "
+                                "length: 0x%x>>\n", i_len);
+                        dStrHexErr((const char *)ip, i_len, 0);
+                    }
+                    break;
+                }
+                printf("SCSI_IDENT_%s_NAA_REGEXT=", assoc_str);
+                for (m = 0; m < 16; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            }
+            break;
+        case 4: /* Relative target port */
+            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+                if (verbose) {
+                    pr2serr("      << expected binary code_set, target "
+                            "port association, length 4>>\n");
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            d_id = sg_get_unaligned_be16(ip + 2);
+            printf("SCSI_IDENT_%s_RELATIVE=%d\n", assoc_str, d_id);
+            break;
+        case 5: /* (primary) Target port group */
+            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+                if (verbose) {
+                    pr2serr("      << expected binary code_set, target "
+                            "port association, length 4>>\n");
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            d_id = sg_get_unaligned_be16(ip + 2);
+            printf("SCSI_IDENT_%s_TARGET_PORT_GROUP=0x%x\n", assoc_str, d_id);
+            break;
+        case 6: /* Logical unit group */
+            if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
+                if (verbose) {
+                    pr2serr("      << expected binary code_set, logical "
+                            "unit association, length 4>>\n");
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            d_id = sg_get_unaligned_be16(ip + 2);
+            printf("SCSI_IDENT_%s_LOGICAL_UNIT_GROUP=0x%x\n", assoc_str, d_id);
+            break;
+        case 7: /* MD5 logical unit identifier */
+            if ((1 != c_set) || (0 != assoc)) {
+                if (verbose) {
+                    pr2serr("      << expected binary code_set, logical "
+                            "unit association>>\n");
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            printf("SCSI_IDENT_%s_MD5=", assoc_str);
+            dStrHex((const char *)ip, i_len, -1);
+            break;
+        case 8: /* SCSI name string */
+            if (3 != c_set) {
+                if (verbose) {
+                    pr2serr("      << expected UTF-8 code_set>>\n");
+                    dStrHexErr((const char *)ip, i_len, -1);
+                }
+                break;
+            }
+            if (! (strncmp((const char *)ip, "eui.", 4) ||
+                   strncmp((const char *)ip, "EUI.", 4) ||
+                   strncmp((const char *)ip, "naa.", 4) ||
+                   strncmp((const char *)ip, "NAA.", 4) ||
+                   strncmp((const char *)ip, "iqn.", 4))) {
+                if (verbose) {
+                    pr2serr("      << expected name string prefix>>\n");
+                    dStrHexErr((const char *)ip, i_len, -1);
+                }
+                break;
+            }
+
+            printf("SCSI_IDENT_%s_NAME=%.*s\n", assoc_str, i_len,
+                   (const char *)ip);
+            break;
+        case 9: /*  Protocol specific port identifier */
+            if (TPROTO_UAS == p_id) {
+                if ((4 != i_len) || (1 != assoc)) {
+                    if (verbose) {
+                        pr2serr("      << UAS (USB) expected target "
+                                "port association>>\n");
+                        dStrHexErr((const char *)ip, i_len, 0);
+                    }
+                    break;
+                }
+                printf("SCSI_IDENT_%s_UAS_DEVICE_ADDRESS=0x%x\n", assoc_str,
+                       ip[0] & 0x7f);
+                printf("SCSI_IDENT_%s_UAS_INTERFACE_NUMBER=0x%x\n", assoc_str,
+                       ip[2]);
+            } else if (TPROTO_SOP == p_id) {
+                if ((4 != i_len) && (8 != i_len)) {   /* spc4r36h confused */
+                    if (verbose) {
+                        pr2serr("      << SOP (PCIe) descriptor "
+                                "length=%d >>\n", i_len);
+                        dStrHexErr((const char *)ip, i_len, 0);
+                    }
+                    break;
+                }
+                printf("SCSI_IDENT_%s_SOP_ROUTING_ID=0x%x\n", assoc_str,
+                       sg_get_unaligned_be16(ip + 0));
+            } else {
+                pr2serr("      << Protocol specific port identifier "
+                        "protocol_id=0x%x>>\n", p_id);
+            }
+            break;
+        case 0xa: /* UUID based */
+            if (1 != c_set) {
+                if (verbose) {
+                    pr2serr("      << expected binary code_set (1)>>\n");
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            if (i_len < 18) {
+                if (verbose) {
+                    pr2serr("      << short UUID field expected 18 or more, "
+                            "got %d >>\n", i_len);
+                    dStrHexErr((const char *)ip, i_len, 0);
+                }
+                break;
+            }
+            printf("SCSI_IDENT_%s_UUID=", assoc_str);
+            for (m = 2; m < i_len; ++m) {
+                if ((6 == m) || (8 == m) || (10 == m) || (12 == m))
+                    printf("-%02x", (unsigned int)ip[m]);
+                else
+                    printf("%02x", (unsigned int)ip[m]);
+            }
+            printf("\n");
+            break;
+        default: /* reserved */
+            if (verbose) {
+                pr2serr("      reserved designator=0x%x\n", desig_type);
+                dStrHexErr((const char *)ip, i_len, -1);
+            }
+            break;
+        }
+    }
+    if (-2 == u && verbose)
+        pr2serr("Device identification VPD page error: "
+                "around offset=%d\n", off);
+}
+
+/* Transport IDs are initiator port identifiers, typically other than the
+   initiator port issuing a SCSI command. */
+static void
+decode_transport_id(const char * leadin, unsigned char * ucp, int len)
+{
+    int format_code, proto_id, num, k;
+    uint64_t ull;
+    int bump;
+
+    for (k = 0, bump = 24; k < len; k += bump, ucp += bump) {
+        if ((len < 24) || (0 != (len % 4)))
+            printf("%sTransport Id short or not multiple of 4 "
+                   "[length=%d]:\n", leadin, len);
+        else
+            printf("%sTransport Id of initiator:\n", leadin);
+        format_code = ((ucp[0] >> 6) & 0x3);
+        proto_id = (ucp[0] & 0xf);
+        switch (proto_id) {
+        case TPROTO_FCP:
+            printf("%s  FCP-2 World Wide Name:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 8, -1);
+            bump = 24;
+            break;
+        case TPROTO_SPI:
+            printf("%s  Parallel SCSI initiator SCSI address: 0x%x\n",
+                   leadin, sg_get_unaligned_be16(ucp + 2));
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            printf("%s  relative port number (of corresponding target): "
+                   "0x%x\n", leadin, sg_get_unaligned_be16(ucp + 6));
+            bump = 24;
+            break;
+        case TPROTO_SSA: /* SSA */
+            printf("%s  SSA (transport id not defined):\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            bump = 24;
+            break;
+        case TPROTO_1394:
+            printf("%s  IEEE 1394 EUI-64 name:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 8, -1);
+            bump = 24;
+            break;
+        case TPROTO_SRP:
+            printf("%s  RDMA initiator port identifier:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 16, -1);
+            bump = 24;
+            break;
+        case TPROTO_ISCSI:
+            printf("%s  iSCSI ", leadin);
+            num = sg_get_unaligned_be16(ucp + 2);
+            if (0 == format_code)
+                printf("name: %.*s\n", num, &ucp[4]);
+            else if (1 == format_code)
+                printf("world wide unique port id: %.*s\n", num, &ucp[4]);
+            else {
+                printf("  [Unexpected format code: %d]\n", format_code);
+                dStrHex((const char *)ucp, num + 4, -1);
+            }
+            bump = (((num + 4) < 24) ? 24 : num + 4);
+            break;
+        case TPROTO_SAS:
+            ull = sg_get_unaligned_be64(ucp + 4);
+            printf("%s  SAS address: 0x%" PRIx64 "\n", leadin, ull);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            bump = 24;
+            break;
+        case TPROTO_ADT:
+            printf("%s  ADT:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            bump = 24;
+            break;
+        case TPROTO_ATA:
+            printf("%s  ATAPI:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            bump = 24;
+            break;
+        case TPROTO_UAS:
+            printf("%s  UAS:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            bump = 24;
+            break;
+        case TPROTO_SOP:
+            printf("%s  SOP ", leadin);
+            num = sg_get_unaligned_be16(ucp + 2);
+            if (0 == format_code)
+                printf("Routing ID: 0x%x\n", num);
+            else {
+                printf("  [Unexpected format code: %d]\n", format_code);
+                dStrHex((const char *)ucp, 24, -1);
+            }
+            bump = 24;
+            break;
+        case TPROTO_NONE:
+            pr2serr("%s  No specified protocol\n", leadin);
+            /* dStrHexErr((const char *)ucp, ((len > 24) ? 24 : len), 0); */
+            bump = 24;
+            break;
+        default:
+            pr2serr("%s  unknown protocol id=0x%x  "
+                    "format_code=%d\n", leadin, proto_id, format_code);
+            dStrHexErr((const char *)ucp, ((len > 24) ? 24 : len), 0);
+            bump = 24;
+            break;
+        }
+    }
+}
+
+/* VPD_EXT_INQ   Extended Inquiry */
+static void
+decode_x_inq_vpd(unsigned char * buff, int len, int do_hex)
+{
+    if (len < 7) {
+        pr2serr("Extended INQUIRY data VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    printf("  ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d "
+           "REF_CHK=%d\n", ((buff[4] >> 6) & 0x3), ((buff[4] >> 3) & 0x7),
+           !!(buff[4] & 0x4), !!(buff[4] & 0x2), !!(buff[4] & 0x1));
+    printf("  UASK_SUP=%d GROUP_SUP=%d PRIOR_SUP=%d HEADSUP=%d ORDSUP=%d "
+           "SIMPSUP=%d\n", !!(buff[5] & 0x20), !!(buff[5] & 0x10),
+           !!(buff[5] & 0x8), !!(buff[5] & 0x4), !!(buff[5] & 0x2),
+           !!(buff[5] & 0x1));
+    /* CRD_SUP made obsolete in spc5r04 */
+    printf("  WU_SUP=%d [CRD_SUP=%d] NV_SUP=%d V_SUP=%d\n",
+           !!(buff[6] & 0x8), !!(buff[6] & 0x4), !!(buff[6] & 0x2),
+           !!(buff[6] & 0x1));
+    printf("  P_I_I_SUP=%d LUICLR=%d R_SUP=%d CBCS=%d\n",
+           !!(buff[7] & 0x10), !!(buff[7] & 0x1), !!(buff[8] & 0x10),
+           !!(buff[8] & 0x1));
+    printf("  Multi I_T nexus microcode download=%d\n", buff[9] & 0xf);
+    printf("  Extended self-test completion minutes=%d\n",
+           sg_get_unaligned_be16(buff + 10));     /* spc4r27 */
+    printf("  POA_SUP=%d HRA_SUP=%d VSA_SUP=%d\n",      /* spc4r32 */
+           !!(buff[12] & 0x80), !!(buff[12] & 0x40), !!(buff[12] & 0x20));
+    printf("  Maximum supported sense data length=%d\n",
+           buff[13]); /* spc4r34 */
+}
+
+/* VPD_SOFTW_INF_ID */
+static void
+decode_softw_inf_id(unsigned char * buff, int len, int do_hex)
+{
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    len -= 4;
+    buff += 4;
+    for ( ; len > 5; len -= 6, buff += 6)
+        printf("    IEEE Company_id: 0x%06x, vendor specific extension "
+               "id: 0x%06x\n", sg_get_unaligned_be24(buff + 0),
+               sg_get_unaligned_be24(buff + 3));
+}
+
+/* VPD_ATA_INFO */
+static void
+decode_ata_info_vpd(unsigned char * buff, int len, int do_hex)
+{
+    char b[80];
+    int is_be, num;
+
+    if (len < 36) {
+        pr2serr("ATA information VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex && (2 != do_hex)) {
+        dStrHex((const char *)buff, len, (3 == do_hex) ? 0 : -1);
+        return;
+    }
+    memcpy(b, buff + 8, 8);
+    b[8] = '\0';
+    printf("  SAT Vendor identification: %s\n", b);
+    memcpy(b, buff + 16, 16);
+    b[16] = '\0';
+    printf("  SAT Product identification: %s\n", b);
+    memcpy(b, buff + 32, 4);
+    b[4] = '\0';
+    printf("  SAT Product revision level: %s\n", b);
+    if (len < 56)
+        return;
+    printf("  Signature (Device to host FIS):\n");
+    dStrHex((const char *)buff + 36, 20, 1);
+    if (len < 60)
+        return;
+    is_be = sg_is_big_endian();
+    if ((0xec == buff[56]) || (0xa1 == buff[56])) {
+        printf("  ATA command IDENTIFY %sDEVICE response summary:\n",
+               ((0xa1 == buff[56]) ? "PACKET " : ""));
+        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20,
+                               is_be, b);
+        b[num] = '\0';
+        printf("    model: %s\n", b);
+        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10,
+                               is_be, b);
+        b[num] = '\0';
+        printf("    serial number: %s\n", b);
+        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4,
+                               is_be, b);
+        b[num] = '\0';
+        printf("    firmware revision: %s\n", b);
+        printf("  response in hex:\n");
+    } else
+        printf("  ATA command 0x%x got following response:\n",
+               (unsigned int)buff[56]);
+    if (len < 572)
+        return;
+    if (2 == do_hex)
+        dStrHex((const char *)(buff + 60), 512, 0);
+    else
+        dWordHex((const unsigned short *)(buff + 60), 256, 0,
+                 sg_is_big_endian());
+}
+
+/* VPD_POWER_CONDITION */
+static void
+decode_power_condition(unsigned char * buff, int len, int do_hex)
+{
+    if (len < 18) {
+        pr2serr("Power condition VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    printf("  Standby_y=%d Standby_z=%d Idle_c=%d Idle_b=%d Idle_a=%d\n",
+           !!(buff[4] & 0x2), !!(buff[4] & 0x1),
+           !!(buff[5] & 0x4), !!(buff[5] & 0x2), !!(buff[5] & 0x1));
+    printf("  Stopped condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 6));
+    printf("  Standby_z condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 8));
+    printf("  Standby_y condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 10));
+    printf("  Idle_a condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 12));
+    printf("  Idle_b condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 14));
+    printf("  Idle_c condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 16));
+}
+
+/* VPD_BLOCK_LIMITS sbc */
+/* Sequential access device characteristics,  ssc+smc */
+/* OSD information, osd */
+static void
+decode_b0_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int pdt;
+    unsigned int u;
+
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    pdt = 0x1f & buff[0];
+    switch (pdt) {
+        case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
+            if (len < 16) {
+                pr2serr("Block limits VPD page length too short=%d\n", len);
+                return;
+            }
+            printf("  Maximum compare and write length: %u blocks\n",
+                   buff[5]);
+            u = sg_get_unaligned_be16(buff + 6);
+            printf("  Optimal transfer length granularity: %u blocks\n", u);
+            u = sg_get_unaligned_be32(buff + 8);
+             printf("  Maximum transfer length: %u blocks\n", u);
+            u = sg_get_unaligned_be32(buff + 12);
+            printf("  Optimal transfer length: %u blocks\n", u);
+            if (len > 19) {     /* added in sbc3r09 */
+                u = sg_get_unaligned_be32(buff + 16);
+                printf("  Maximum prefetch transfer length: %u blocks\n", u);
+            }
+            if (len > 27) {     /* added in sbc3r18 */
+                u = sg_get_unaligned_be32(buff + 20);
+                printf("  Maximum unmap LBA count: %u\n", u);
+                u = sg_get_unaligned_be32(buff + 24);
+                printf("  Maximum unmap block descriptor count: %u\n", u);
+            }
+            if (len > 35) {     /* added in sbc3r19 */
+                u = sg_get_unaligned_be32(buff + 28);
+                printf("  Optimal unmap granularity: %u\n", u);
+                printf("  Unmap granularity alignment valid: %u\n",
+                       !!(buff[32] & 0x80));
+                u = 0x7fffffff & sg_get_unaligned_be32(buff + 32);
+                printf("  Unmap granularity alignment: %u\n", u);
+            }
+            if (len > 43) {     /* added in sbc3r26 */
+                printf("  Maximum write same length: 0x%" PRIx64 " blocks\n",
+                       sg_get_unaligned_be64(buff + 36));
+            }
+            if (len > 44) {     /* added in sbc4r02 */
+                u = sg_get_unaligned_be32(buff + 44);
+                printf("  Maximum atomic transfer length: %u\n", u);
+                u = sg_get_unaligned_be32(buff + 48);
+                printf("  Atomic alignment: %u\n", u);
+                u = sg_get_unaligned_be32(buff + 52);
+                printf("  Atomic transfer length granularity: %u\n", u);
+            }
+            break;
+        case PDT_TAPE: case PDT_MCHANGER:
+            printf("  WORM=%d\n", !!(buff[4] & 0x1));
+            break;
+        case PDT_OSD:
+        default:
+            printf("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+            dStrHex((const char *)buff, len, 0);
+            break;
+    }
+}
+
+/* VPD_BLOCK_DEV_CHARS sbc */
+/* VPD_MAN_ASS_SN ssc */
+static void
+decode_b1_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int pdt;
+    unsigned int u;
+
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    pdt = 0x1f & buff[0];
+    switch (pdt) {
+        case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
+            if (len < 64) {
+                pr2serr("Block device characteristics VPD page length too "
+                        "short=%d\n", len);
+                return;
+            }
+            u = sg_get_unaligned_be16(buff + 4);
+            if (0 == u)
+                printf("  Medium rotation rate is not reported\n");
+            else if (1 == u)
+                printf("  Non-rotating medium (e.g. solid state)\n");
+            else if ((u < 0x401) || (0xffff == u))
+                printf("  Reserved [0x%x]\n", u);
+            else
+                printf("  Nominal rotation rate: %d rpm\n", u);
+            printf("  Product type=%d\n", buff[6]);
+            printf("  WABEREQ=%d\n", (buff[7] >> 6) & 0x3);
+            printf("  WACEREQ=%d\n", (buff[7] >> 4) & 0x3);
+            u = buff[7] & 0xf;
+            printf("  Nominal form factor ");
+            switch(u) {
+            case 0:
+                printf("is not reported\n");
+                break;
+            case 1:
+                printf("5.25 inches\n");
+                break;
+            case 2:
+                printf("3.5 inches\n");
+                break;
+            case 3:
+                printf("2.5 inches\n");
+                break;
+            case 4:
+                printf("1.8 inches\n");
+                break;
+            case 5:
+                printf("less then 1.8 inches\n");
+                break;
+            default:
+                printf("reserved [%u]\n", u);
+                break;
+            }
+            printf("  ZONED=%d\n", (buff[8] >> 4) & 0x3);   /* sbc4r04 */
+            printf("  FUAB=%d\n", buff[8] & 0x2);
+            printf("  VBULS=%d\n", buff[8] & 0x1);
+            break;
+        case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC:
+            printf("  Manufacturer-assigned serial number: %.*s\n",
+                   len - 4, buff + 4);
+            break;
+        default:
+            printf("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+            dStrHex((const char *)buff, len, 0);
+            break;
+    }
+}
+
+/* VPD_REFERRALS sbc */
+static void
+decode_b3_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int pdt;
+    unsigned int s, m;
+
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    pdt = 0x1f & buff[0];
+    switch (pdt) {
+        case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
+            if (len < 0x10) {
+                pr2serr("Referrals VPD page length too short=%d\n", len);
+                return;
+            }
+            s = sg_get_unaligned_be32(buff + 8);
+            m = sg_get_unaligned_be32(buff + 12);
+            if (0 == s)
+                printf("  Single user data segment\n");
+            else if (0 == m)
+                printf("  Segment size specified by user data segment "
+                       "descriptor\n");
+            else
+                printf("  Segment size: %u, segment multiplier: %u\n", s, m);
+            break;
+        default:
+            printf("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+            dStrHex((const char *)buff, len, 0);
+            break;
+    }
+}
+
+static const char * lun_state_arr[] =
+{
+    "LUN not bound or LUN_Z report",
+    "LUN bound, but not owned by this SP",
+    "LUN bound and owned by this SP",
+};
+
+static const char * ip_mgmt_arr[] =
+{
+    "No IP access",
+    "Reserved (undefined)",
+    "via IPv4",
+    "via IPv6",
+};
+
+static const char * sp_arr[] =
+{
+    "SP A",
+    "SP B",
+};
+
+static const char * lun_op_arr[] =
+{
+    "Normal operations",
+    "I/O Operations being rejected, SP reboot or NDU in progress",
+};
+
+static const char * failover_mode_arr[] =
+{
+    "Legacy mode 0",
+    "Unknown mode (1)",
+    "Unknown mode (2)",
+    "Unknown mode (3)",
+    "Active/Passive (PNR) mode 1",
+    "Unknown mode (5)",
+    "Active/Active (ALUA) mode 4",
+    "Unknown mode (7)",
+    "Legacy mode 2",
+    "Unknown mode (9)",
+    "Unknown mode (10)",
+    "Unknown mode (11)",
+    "Unknown mode (12)",
+    "Unknown mode (13)",
+    "AIX Active/Passive (PAR) mode 3",
+    "Unknown mode (15)",
+};
+
+static void
+decode_upr_vpd_c0_emc(unsigned char * buff, int len, int do_hex)
+{
+    int k, ip_mgmt, vpp80, lun_z;
+
+    if (len < 3) {
+        pr2serr("EMC upr VPD page [0xc0]: length too short=%d\n", len);
+        return;
+    }
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
+        return;
+    }
+    if (buff[9] != 0x00) {
+        pr2serr("Unsupported page revision %d, decoding not possible.\n",
+                buff[9]);
+        return;
+    }
+    printf("  LUN WWN: ");
+    for (k = 0; k < 16; ++k)
+        printf("%02x", buff[10 + k]);
+    printf("\n");
+    printf("  Array Serial Number: ");
+    dStrRaw((const char *)&buff[50], buff[49]);
+    printf("\n");
+
+    printf("  LUN State: ");
+    if (buff[4] > 0x02)
+           printf("Unknown (%x)\n", buff[4]);
+    else
+           printf("%s\n", lun_state_arr[buff[4]]);
+
+    printf("  This path connects to: ");
+    if (buff[8] > 0x01)
+           printf("Unknown SP (%x)", buff[8]);
+    else
+           printf("%s", sp_arr[buff[8]]);
+    printf(", Port Number: %u\n", buff[7]);
+
+    printf("  Default Owner: ");
+    if (buff[5] > 0x01)
+           printf("Unknown (%x)\n", buff[5]);
+    else
+           printf("%s\n", sp_arr[buff[5]]);
+
+    printf("  NO_ATF: %s, Access Logix: %s\n",
+                   buff[6] & 0x80 ? "set" : "not set",
+                   buff[6] & 0x40 ? "supported" : "not supported");
+
+    ip_mgmt = (buff[6] >> 4) & 0x3;
+
+    printf("  SP IP Management Mode: %s\n", ip_mgmt_arr[ip_mgmt]);
+    if (ip_mgmt == 2)
+        printf("  SP IPv4 address: %u.%u.%u.%u\n",
+               buff[44], buff[45], buff[46], buff[47]);
+    else {
+        printf("  SP IPv6 address: ");
+        for (k = 0; k < 16; ++k)
+            printf("%02x", buff[32 + k]);
+        printf("\n");
+    }
+
+    vpp80 = buff[30] & 0x08;
+    lun_z = buff[30] & 0x04;
+
+    printf("  System Type: %x, Failover mode: %s\n",
+           buff[27], failover_mode_arr[buff[28] & 0x0f]);
+
+    printf("  Inquiry VPP 0x80 returns: %s, Arraycommpath: %s\n",
+                   vpp80 ? "array serial#" : "LUN serial#",
+                   lun_z ? "Set to 1" : "Unknown");
+
+    printf("  Lun operations: %s\n",
+               buff[48] > 1 ? "undefined" : lun_op_arr[buff[48]]);
+
+    return;
+}
+
+static void
+decode_rdac_vpd_c2(unsigned char * buff, int len, int do_hex)
+{
+    if (len < 3) {
+        pr2serr("Software Version VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
+        return;
+    }
+    if (buff[4] != 's' && buff[5] != 'w' && buff[6] != 'r') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding "
+                "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    printf("  Software Version: %02x.%02x.%02x\n", buff[8], buff[9], buff[10]);
+    printf("  Software Date: %02d/%02d/%02d\n", buff[11], buff[12], buff[13]);
+    printf("  Features:");
+    if (buff[14] & 0x01)
+        printf(" Dual Active,");
+    if (buff[14] & 0x02)
+        printf(" Series 3,");
+    if (buff[14] & 0x04)
+        printf(" Multiple Sub-enclosures,");
+    if (buff[14] & 0x08)
+        printf(" DCE/DRM/DSS/DVE,");
+    if (buff[14] & 0x10)
+        printf(" Asymmetric Logical Unit Access,");
+    printf("\n");
+    printf("  Max. #of LUNS: %d\n", buff[15]);
+    return;
+}
+
+static void
+decode_rdac_vpd_c9_rtpg_data(unsigned char aas, unsigned char vendor)
+{
+    printf("  Asymmetric Access State:");
+    switch(aas & 0x0F) {
+        case 0x0:
+            printf(" Active/Optimized");
+            break;
+        case 0x1:
+            printf(" Active/Non-Optimized");
+            break;
+        case 0x2:
+            printf(" Standby");
+            break;
+        case 0x3:
+            printf(" Unavailable");
+            break;
+        case 0xE:
+            printf(" Offline");
+            break;
+        case 0xF:
+            printf(" Transitioning");
+            break;
+        default:
+            printf(" (unknown)");
+            break;
+    }
+    printf("\n");
+
+    printf("  Vendor Specific Field:");
+    switch(vendor) {
+        case 0x01:
+            printf(" Operating normally");
+            break;
+        case 0x02:
+            printf(" Non-responsive to queries");
+            break;
+        case 0x03:
+            printf(" Controller being held in reset");
+            break;
+        case 0x04:
+            printf(" Performing controller firmware download (1st "
+                   "controller)");
+            break;
+        case 0x05:
+            printf(" Performing controller firmware download (2nd "
+                   "controller)");
+            break;
+        case 0x06:
+            printf(" Quiesced as a result of an administrative request");
+            break;
+        case 0x07:
+            printf(" Service mode as a result of an administrative request");
+            break;
+        case 0xFF:
+            printf(" Details are not available");
+            break;
+        default:
+            printf(" (unknown)");
+            break;
+    }
+    printf("\n");
+}
+
+static void
+decode_rdac_vpd_c9(unsigned char * buff, int len, int do_hex)
+{
+    if (len < 3) {
+        pr2serr("Volume Access Control VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
+        return;
+    }
+    if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding "
+                "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    if (buff[7] != '1') {
+        pr2serr("Invalid page version '%c' (should be 1)\n", buff[7]);
+    }
+    if ( (buff[8] & 0xE0) == 0xE0 ) {
+        printf("  IOShipping (ALUA): Enabled\n");
+    } else {
+        printf("  AVT:");
+        if (buff[8] & 0x80) {
+            printf(" Enabled");
+            if (buff[8] & 0x40)
+                printf(" (Allow reads on sector 0)");
+            printf("\n");
+        } else {
+            printf(" Disabled\n");
+        }
+    }
+    printf("  Volume Access via: ");
+    if (buff[8] & 0x01)
+        printf("primary controller\n");
+    else
+        printf("alternate controller\n");
+
+    if (buff[8] & 0x08) {
+        printf("  Path priority: %d ", buff[15] & 0xf);
+        switch(buff[15] & 0xf) {
+            case 0x1:
+                printf("(preferred path)\n");
+                break;
+            case 0x2:
+                printf("(secondary path)\n");
+                break;
+            default:
+                printf("(unknown)\n");
+                break;
+        }
+
+        printf("  Preferred Path Auto Changeable:");
+        switch(buff[14] & 0x3C) {
+            case 0x14:
+                printf(" No (User Disabled and Host Type Restricted)\n");
+                break;
+            case 0x18:
+                printf(" No (User Disabled)\n");
+                break;
+            case 0x24:
+                printf(" No (Host Type Restricted)\n");
+                break;
+            case 0x28:
+                printf(" Yes\n");
+                break;
+            default:
+                printf(" (Unknown)\n");
+                break;
+        }
+
+        printf("  Implicit Failback:");
+        switch(buff[14] & 0x03) {
+            case 0x1:
+                printf(" Disabled\n");
+                break;
+            case 0x2:
+                printf(" Enabled\n");
+                break;
+            default:
+                printf(" (Unknown)\n");
+                break;
+        }
+    } else {
+        printf("  Path priority: %d ", buff[9] & 0xf);
+        switch(buff[9] & 0xf) {
+            case 0x1:
+                printf("(preferred path)\n");
+                break;
+            case 0x2:
+                printf("(secondary path)\n");
+                break;
+            default:
+                printf("(unknown)\n");
+                break;
+        }
+    }
+
+    if (buff[8] & 0x80) {
+        printf(" Target Port Group Data (This controller):\n");
+        decode_rdac_vpd_c9_rtpg_data(buff[10], buff[11]);
+
+        printf(" Target Port Group Data (Alternate controller):\n");
+        decode_rdac_vpd_c9_rtpg_data(buff[12], buff[13]);
+    }
+
+    return;
+}
+
+extern const char * sg_ansi_version_arr[];
+
+static const char *
+get_ansi_version_str(int version, char * buff, int buff_len)
+{
+    version &= 0xf;
+    buff[buff_len - 1] = '\0';
+    strncpy(buff, sg_ansi_version_arr[version], buff_len - 1);
+    return buff;
+}
+
+static int
+std_inq_response(const struct opts_t * op, int act_len)
+{
+    int len, pqual, peri_type, ansi_version, k, j;
+    const char * cp;
+    int vdesc_arr[8];
+    char buff[48];
+    const unsigned char * rp;
+
+    rp = rsp_buff;
+    memset(vdesc_arr, 0, sizeof(vdesc_arr));
+    len = rp[4] + 5;
+
+    if (op->do_raw) {
+        dStrRaw((const char *)rp, act_len);
+        return 0;
+    } else if (op->do_hex) {
+        /* with -H, print with address, -HH without */
+        dStrHex((const char *)rp, act_len, ((1 == op->do_hex) ? 0 : -1));
+        return 0;
+    }
+    pqual = (rp[0] & 0xe0) >> 5;
+    if (! op->do_raw && ! op->do_export) {
+        if (0 == pqual)
+            printf("standard INQUIRY:\n");
+        else if (1 == pqual)
+            printf("standard INQUIRY: [qualifier indicates no connected "
+                   "LU]\n");
+        else if (3 == pqual)
+            printf("standard INQUIRY: [qualifier indicates not capable "
+                   "of supporting LU]\n");
+        else
+            printf("standard INQUIRY: [reserved or vendor specific "
+                   "qualifier [%d]]\n", pqual);
+    }
+    len = rp[4] + 5;
+    /* N.B. rp[2] full byte is 'version' in SPC-2,3,4 but in SPC
+     * [spc-r11a (1997)] bits 6,7: ISO/IEC version; bits 3-5: ECMA
+     * version; bits 0-2: SCSI version */
+    ansi_version = rp[2] & 0x7;       /* Only take SCSI version */
+    peri_type = rp[0] & 0x1f;
+    if (op->do_export) {
+        printf("SCSI_TPGS=%d\n", (rp[5] & 0x30) >> 4);
+        cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
+        if (strlen(cp) > 0)
+            printf("SCSI_TYPE=%s\n", cp);
+    } else {
+        printf("  PQual=%d  Device_type=%d  RMB=%d  LU_CONG=%d  "
+               "version=0x%02x ", pqual, peri_type, !!(rp[1] & 0x80),
+               !!(rp[1] & 0x40), (unsigned int)rp[2]);
+        printf(" [%s]\n", get_ansi_version_str(ansi_version, buff,
+                                               sizeof(buff)));
+        printf("  [AERC=%d]  [TrmTsk=%d]  NormACA=%d  HiSUP=%d "
+               " Resp_data_format=%d\n  SCCS=%d  ", !!(rp[3] & 0x80),
+               !!(rp[3] & 0x40), !!(rp[3] & 0x20), !!(rp[3] & 0x10),
+               rp[3] & 0x0f, !!(rp[5] & 0x80));
+        printf("ACC=%d  TPGS=%d  3PC=%d  Protect=%d ", !!(rp[5] & 0x40),
+               ((rp[5] & 0x30) >> 4), !!(rp[5] & 0x08), !!(rp[5] & 0x01));
+        printf(" [BQue=%d]\n  EncServ=%d  ", !!(rp[6] & 0x80),
+               !!(rp[6] & 0x40));
+        if (rp[6] & 0x10)
+            printf("MultiP=1 (VS=%d)  ", !!(rp[6] & 0x20));
+        else
+            printf("MultiP=0  ");
+        printf("[MChngr=%d]  [ACKREQQ=%d]  Addr16=%d\n  [RelAdr=%d]  ",
+               !!(rp[6] & 0x08), !!(rp[6] & 0x04), !!(rp[6] & 0x01),
+               !!(rp[7] & 0x80));
+        printf("WBus16=%d  Sync=%d  [Linked=%d]  [TranDis=%d]  ",
+               !!(rp[7] & 0x20), !!(rp[7] & 0x10), !!(rp[7] & 0x08),
+               !!(rp[7] & 0x04));
+        printf("CmdQue=%d\n", !!(rp[7] & 0x02));
+        if (act_len > 56)
+            printf("  [SPI: Clocking=0x%x  QAS=%d  IUS=%d]\n",
+                   (rp[56] & 0x0c) >> 2, !!(rp[56] & 0x2), !!(rp[56] & 0x1));
+        if (act_len >= len)
+            printf("    length=%d (0x%x)", len, len);
+        else
+            printf("    length=%d (0x%x), but only fetched %d bytes", len,
+                   len, act_len);
+        if ((ansi_version >= 2) && (len < SAFE_STD_INQ_RESP_LEN))
+            printf("\n  [for SCSI>=2, len>=36 is expected]");
+        cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
+        if (strlen(cp) > 0)
+            printf("   Peripheral device type: %s\n", cp);
+    }
+    if (act_len <= 8) {
+        if (! op->do_export)
+            printf(" Inquiry response length=%d, no vendor, product or "
+                   "revision data\n", act_len);
+    } else {
+        int i;
+
+        memcpy(xtra_buff, &rp[8], 8);
+        xtra_buff[8] = '\0';
+        /* Fixup any tab characters */
+        for (i = 0; i < 8; ++i)
+            if (xtra_buff[i] == 0x09)
+                xtra_buff[i] = ' ';
+        if (op->do_export) {
+            len = encode_whitespaces((unsigned char *)xtra_buff, 8);
+            if (len > 0) {
+                printf("SCSI_VENDOR=%s\n", xtra_buff);
+                encode_string(xtra_buff, &rp[8], 8);
+                printf("SCSI_VENDOR_ENC=%s\n", xtra_buff);
+            }
+        } else
+            printf(" Vendor identification: %s\n", xtra_buff);
+        if (act_len <= 16) {
+            if (! op->do_export)
+                printf(" Product identification: <none>\n");
+        } else {
+            memcpy(xtra_buff, &rp[16], 16);
+            xtra_buff[16] = '\0';
+            if (op->do_export) {
+                len = encode_whitespaces((unsigned char *)xtra_buff, 16);
+                if (len > 0) {
+                    printf("SCSI_MODEL=%s\n", xtra_buff);
+                    encode_string(xtra_buff, &rp[16], 16);
+                    printf("SCSI_MODEL_ENC=%s\n", xtra_buff);
+                }
+            } else
+                printf(" Product identification: %s\n", xtra_buff);
+        }
+        if (act_len <= 32) {
+            if (!op->do_export)
+                printf(" Product revision level: <none>\n");
+        } else {
+            memcpy(xtra_buff, &rp[32], 4);
+            xtra_buff[4] = '\0';
+            if (op->do_export) {
+                len = encode_whitespaces((unsigned char *)xtra_buff, 4);
+                if (len > 0)
+                    printf("SCSI_REVISION=%s\n", xtra_buff);
+            } else
+                printf(" Product revision level: %s\n", xtra_buff);
+        }
+        if (op->do_vendor && (act_len > 36) && ('\0' != rp[36]) &&
+            (' ' != rp[36])) {
+            memcpy(xtra_buff, &rp[36], act_len < 56 ? act_len - 36 :
+                   20);
+            if (op->do_export) {
+                len = encode_whitespaces((unsigned char *)xtra_buff, 20);
+                if (len > 0)
+                    printf("VENDOR_SPECIFIC=%s\n", xtra_buff);
+            } else
+                printf(" Vendor specific: %s\n", xtra_buff);
+        }
+        if (op->do_descriptors) {
+            for (j = 0, k = 58; ((j < 8) && ((k + 1) < act_len));
+                 k +=2, ++j)
+                vdesc_arr[j] = sg_get_unaligned_be16(rp + k);
+        }
+        if ((op->do_vendor > 1) && (act_len > 96)) {
+            memcpy(xtra_buff, &rp[96], act_len - 96);
+            if (op->do_export) {
+                len = encode_whitespaces((unsigned char *)xtra_buff,
+                                         act_len - 96);
+                if (len > 0)
+                    printf("VENDOR_SPECIFIC=%s\n", xtra_buff);
+            } else
+                printf(" Vendor specific: %s\n", xtra_buff);
+        }
+    }
+    if (! op->do_export) {
+        if ((0 == op->resp_len) && usn_buff[0])
+            printf(" Unit serial number: %s\n", usn_buff);
+        if (op->do_descriptors) {
+            if (0 == vdesc_arr[0])
+                printf("\n  No version descriptors available\n");
+            else {
+                printf("\n  Version descriptors:\n");
+                for (k = 0; k < 8; ++k) {
+                    if (0 == vdesc_arr[k])
+                        break;
+                    cp = find_version_descriptor_str(vdesc_arr[k]);
+                    if (cp)
+                        printf("    %s\n", cp);
+                    else
+                        printf("    [unrecognised version descriptor "
+                               "code: 0x%x]\n", vdesc_arr[k]);
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+/* When sg_fd >= 0 fetch VPD page from device; mxlen is command line
+ * --maxlen=LEN option (def: 0) or -1 for a VPD page with a short length
+ * (1 byte). When sg_fd < 0 then mxlen bytes have been read from
+ * --inhex=FN file. Returns 0 for success. */
+static int
+vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page,
+                        int mxlen, int vb, int * rlenp)
+{
+    int res, resid, rlen, len, n;
+
+    if (sg_fd < 0) {
+        len = sg_get_unaligned_be16(rp + 2) + 4;
+        if (vb && (len > mxlen))
+            pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN "
+                    "file (%d)\n",  len , mxlen);
+        if (rlenp)
+            *rlenp = (len < mxlen) ? len : mxlen;
+        return 0;
+    }
+    if (mxlen > MX_ALLOC_LEN) {
+        pr2serr("--maxlen=LEN too long: %d > %d\n", mxlen, MX_ALLOC_LEN);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    n = (mxlen > 0) ? mxlen : DEF_ALLOC_LEN;
+    res = pt_inquiry(sg_fd, 1, page, rp, n, &resid, 1, vb);
+    if (res)
+        return res;
+    rlen = n - resid;
+    if (rlen < 4) {
+        pr2serr("VPD response too short (len=%d)\n", rlen);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    if (page != rp[1]) {
+        pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
+                "response\n");
+        return SG_LIB_CAT_MALFORMED;
+    } else if ((0x80 == page) && (0x2 == rp[2]) && (0x2 == rp[3])) {
+        /* could be a Unit Serial number VPD page with a very long
+         * length of 4+514 bytes; more likely standard response for
+         * SCSI-2, RMB=1 and a response_data_format of 0x2. */
+        pr2serr("invalid Unit Serial Number VPD response; probably a "
+                "STANDARD INQUIRY response\n");
+        return SG_LIB_CAT_MALFORMED;
+    }
+    if (mxlen < 0)
+        len = rp[3] + 4;
+    else
+        len = sg_get_unaligned_be16(rp + 2) + 4;
+    if (len <= rlen) {
+        if (rlenp)
+            *rlenp = len;
+        return 0;
+    } else if (mxlen) {
+        if (rlenp)
+            *rlenp = rlen;
+        return 0;
+    }
+    if (len > MX_ALLOC_LEN) {
+        pr2serr("response length too long: %d > %d\n", len, MX_ALLOC_LEN);
+        return SG_LIB_CAT_MALFORMED;
+    } else {
+        res = pt_inquiry(sg_fd, 1, page, rp, len, &resid, 1, vb);
+        if (res)
+            return res;
+        rlen = len - resid;
+        /* assume it is well behaved: hence page and len still same */
+        if (rlenp)
+            *rlenp = rlen;
+        return 0;
+    }
+}
+
+/* Returns 0 if Unit Serial Number VPD page contents found, else see
+ * sg_ll_inquiry() return values */
+static int
+fetch_unit_serial_num(int sg_fd, char * obuff, int obuff_len, int verbose)
+{
+    int len, k, res;
+    unsigned char b[DEF_ALLOC_LEN];
+
+    res = 0;
+    memset(b, 0xff, 4); /* guard against empty response */
+    res = vpd_fetch_page_from_dev(sg_fd, b, VPD_UNIT_SERIAL_NUM, -1, verbose,
+                                  &len);
+    if ((0 == res) && (len > 3)) {
+        len -= 4;
+        len = (len < (obuff_len - 1)) ? len : (obuff_len - 1);
+        if (len > 0) {
+            /* replace non-printable ASCII characters with space */
+            for (k = 0; k < len; ++k)
+                obuff[k] = isprint(b[4 + k]) ? b[4 + k] : ' ';
+            obuff[len] = '\0';
+            return 0;
+        } else {
+            if (verbose > 2)
+                pr2serr("fetch_unit_serial_num: bad sn VPD page\n");
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else {
+        if (verbose > 2)
+            pr2serr("fetch_unit_serial_num: no supported VPDs page\n");
+        return SG_LIB_CAT_MALFORMED;
+    }
+    return res;
+}
+
+
+/* Process a standard INQUIRY response. Returns 0 if successful */
+static int
+std_inq_process(int sg_fd, const struct opts_t * op, int inhex_len)
+{
+    int res, len, rlen, act_len;
+    char buff[48];
+    int verb, resid;
+
+    if (sg_fd < 0)
+        return std_inq_response(op, inhex_len);
+    rlen = (op->resp_len > 0) ? op->resp_len : SAFE_STD_INQ_RESP_LEN;
+    verb = op->do_verbose;
+    res = pt_inquiry(sg_fd, 0, 0, rsp_buff, rlen, &resid, 0, verb);
+    if (0 == res) {
+        len = rsp_buff[4] + 5;
+        if ((len > SAFE_STD_INQ_RESP_LEN) && (len < 256) &&
+            (0 == op->resp_len)) {
+            rlen = len;
+            memset(rsp_buff, 0, rlen);
+            if (pt_inquiry(sg_fd, 0, 0, rsp_buff, rlen, &resid, 1, verb)) {
+                pr2serr("second INQUIRY (%d byte) failed\n", len);
+                return SG_LIB_CAT_OTHER;
+            }
+            if (len != (rsp_buff[4] + 5)) {
+                pr2serr("strange, consecutive INQUIRYs yield different "
+                        "'additional lengths'\n");
+                res = SG_LIB_CAT_MALFORMED;
+                len = rsp_buff[4] + 5;
+            }
+        }
+        if (op->resp_len > 0)
+            act_len = rlen;
+        else
+            act_len = (rlen < len) ? rlen : len;
+        /* don't use more than HBA's resid says was transferred from LU */
+        if (act_len > (rlen - resid))
+            act_len = rlen - resid;
+        if (act_len < SAFE_STD_INQ_RESP_LEN)
+            rsp_buff[act_len] = '\0';
+        if ((! op->do_export) && (0 == op->resp_len)) {
+            if (fetch_unit_serial_num(sg_fd, usn_buff, sizeof(usn_buff),
+                                      op->do_verbose))
+                usn_buff[0] = '\0';
+        }
+        return std_inq_response(op, act_len);
+    } else if (res < 0) { /* could be an ATA device */
+#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
+        /* Try an ATA Identify Device command */
+        res = try_ata_identify(sg_fd, op->do_hex, op->do_raw,
+                               op->do_verbose);
+        if (0 != res) {
+            pr2serr("Both SCSI INQUIRY and fetching ATA information "
+                    "failed on %s\n", op->device_name);
+            return SG_LIB_CAT_OTHER;
+        }
+#else
+        pr2serr("SCSI INQUIRY failed on %s, res=%d\n",
+                op->device_name, res);
+        return res;
+#endif
+    } else {
+        char b[80];
+
+        pr2serr("    inquiry: failed requesting %d byte response: ", rlen);
+        if (resid && verb)
+            snprintf(buff, sizeof(buff), " [resid=%d]", resid);
+        else
+            buff[0] = '\0';
+        sg_get_category_sense_str(res, sizeof(b), b, verb);
+        pr2serr("%s%s\n", b, buff);
+        return res;
+    }
+    return 0;
+}
+
+#ifdef SG_SCSI_STRINGS
+/* Returns 0 if successful */
+static int
+cmddt_process(int sg_fd, const struct opts_t * op)
+{
+    int k, j, num, len, peri_type, reserved_cmddt, support_num, res;
+    char op_name[128];
+
+    memset(rsp_buff, 0, DEF_ALLOC_LEN);
+    if (op->do_cmddt > 1) {
+        printf("Supported command list:\n");
+        for (k = 0; k < 256; ++k) {
+            res = sg_ll_inquiry(sg_fd, 1, 0, k, rsp_buff, DEF_ALLOC_LEN,
+                                1, op->do_verbose);
+            if (0 == res) {
+                peri_type = rsp_buff[0] & 0x1f;
+                support_num = rsp_buff[1] & 7;
+                reserved_cmddt = rsp_buff[4];
+                if ((3 == support_num) || (5 == support_num)) {
+                    num = rsp_buff[5];
+                    for (j = 0; j < num; ++j)
+                        printf(" %.2x", (int)rsp_buff[6 + j]);
+                    if (5 == support_num)
+                        printf("  [vendor specific manner (5)]");
+                    sg_get_opcode_name((unsigned char)k, peri_type,
+                                       sizeof(op_name) - 1, op_name);
+                    op_name[sizeof(op_name) - 1] = '\0';
+                    printf("  %s\n", op_name);
+                } else if ((4 == support_num) || (6 == support_num))
+                    printf("  opcode=0x%.2x vendor specific (%d)\n",
+                           k, support_num);
+                else if ((0 == support_num) && (reserved_cmddt > 0)) {
+                    printf("  opcode=0x%.2x ignored cmddt bit, "
+                           "given standard INQUIRY response, stop\n", k);
+                    break;
+                }
+            } else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+                break;
+            else {
+                pr2serr("CmdDt INQUIRY on opcode=0x%.2x: failed\n", k);
+                break;
+            }
+        }
+    }
+    else {
+        res = sg_ll_inquiry(sg_fd, 1, 0, op->page_num, rsp_buff,
+                            DEF_ALLOC_LEN, 1, op->do_verbose);
+        if (0 == res) {
+            peri_type = rsp_buff[0] & 0x1f;
+            if (! op->do_raw) {
+                printf("CmdDt INQUIRY, opcode=0x%.2x:  [", op->page_num);
+                sg_get_opcode_name((unsigned char)op->page_num, peri_type,
+                                   sizeof(op_name) - 1, op_name);
+                op_name[sizeof(op_name) - 1] = '\0';
+                printf("%s]\n", op_name);
+            }
+            len = rsp_buff[5] + 6;
+            reserved_cmddt = rsp_buff[4];
+            if (op->do_hex)
+                dStrHex((const char *)rsp_buff, len,
+                        (1 == op->do_hex) ? 0 : -1);
+            else if (op->do_raw)
+                dStrRaw((const char *)rsp_buff, len);
+            else {
+                const char * desc_p;
+                int prnt_cmd = 0;
+
+                support_num = rsp_buff[1] & 7;
+                num = rsp_buff[5];
+                switch (support_num) {
+                case 0:
+                    if (0 == reserved_cmddt)
+                        desc_p = "no data available";
+                    else
+                        desc_p = "ignored cmddt bit, standard INQUIRY "
+                                 "response";
+                    break;
+                case 1: desc_p = "not supported"; break;
+                case 2: desc_p = "reserved (2)"; break;
+                case 3: desc_p = "supported as per standard";
+                        prnt_cmd = 1;
+                        break;
+                case 4: desc_p = "vendor specific (4)"; break;
+                case 5: desc_p = "supported in vendor specific way";
+                        prnt_cmd = 1;
+                        break;
+                case 6: desc_p = "vendor specific (6)"; break;
+                case 7: desc_p = "reserved (7)"; break;
+                default: desc_p = "impossible value > 7"; break;
+                }
+                if (prnt_cmd) {
+                    printf("  Support field: %s [", desc_p);
+                    for (j = 0; j < num; ++j)
+                        printf(" %.2x", (int)rsp_buff[6 + j]);
+                    printf(" ]\n");
+                } else
+                    printf("  Support field: %s\n", desc_p);
+            }
+        } else if (SG_LIB_CAT_ILLEGAL_REQ != res) {
+            if (! op->do_raw) {
+                printf("CmdDt INQUIRY, opcode=0x%.2x:  [", op->page_num);
+                sg_get_opcode_name((unsigned char)op->page_num, 0,
+                                   sizeof(op_name) - 1, op_name);
+                op_name[sizeof(op_name) - 1] = '\0';
+                printf("%s]\n", op_name);
+            }
+            pr2serr("CmdDt INQUIRY on opcode=0x%.2x: failed\n", op->page_num);
+        }
+    }
+    return res;
+}
+
+#else /* SG_SCSI_STRINGS */
+
+/* Returns 0. */
+static int
+cmddt_process(int sg_fd, const struct opts_t * op)
+{
+    sg_fd = sg_fd;
+    op = op;
+    pr2serr("'--cmddt' not implemented, use sg_opcodes\n");
+    return 0;
+}
+
+#endif /* SG_SCSI_STRINGS */
+
+
+/* Returns 0 if successful */
+static int
+vpd_mainly_hex(int sg_fd, const struct opts_t * op, int inhex_len)
+{
+    int res, len;
+    char b[128];
+    const char * cp;
+    unsigned char * rp;
+
+    rp = rsp_buff;
+    if ((! op->do_raw) && (op->do_hex < 2))
+        printf("VPD INQUIRY, page code=0x%.2x:\n", op->page_num);
+    if (sg_fd < 0) {
+        len = sg_get_unaligned_be16(rp + 2) + 4;
+        if (op->do_verbose && (len > inhex_len))
+            pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN "
+                    "file (%d)\n",  len , inhex_len);
+        res = 0;
+    } else {
+        memset(rp, 0, DEF_ALLOC_LEN);
+        res = vpd_fetch_page_from_dev(sg_fd, rp, op->page_num, op->resp_len,
+                                      op->do_verbose, &len);
+    }
+    if (0 == res) {
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else {
+            if (0 == op->page_num)
+                decode_supported_vpd(rp, len, op->do_hex);
+            else {
+                if (op->do_verbose) {
+                    cp = sg_get_pdt_str(rp[0] & 0x1f, sizeof(b), b);
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5, cp);
+                }
+                dStrHex((const char *)rp, len, ((1 == op->do_hex) ? 0 : -1));
+            }
+        }
+    } else {
+        if (SG_LIB_CAT_ILLEGAL_REQ == res)
+            pr2serr("    inquiry: field in cdb illegal (page not "
+                    "supported)\n");
+        else {
+            sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose);
+            pr2serr("    inquiry: %s\n", b);
+        }
+    }
+    return res;
+}
+
+/* Returns 0 if successful */
+static int
+vpd_decode(int sg_fd, const struct opts_t * op, int inhex_len)
+{
+    int len, pdt, pn, vb, mxlen;
+    int res = 0;
+    unsigned char * rp;
+
+    pn = op->page_num;
+    rp = rsp_buff;
+    vb = op->do_verbose;
+    if (sg_fd >= 0)
+        mxlen = op->resp_len;
+    else
+        mxlen = inhex_len;
+    switch (pn) {
+    case VPD_SUPPORTED_VPDS:
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: Supported VPD pages page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else if (op->do_hex)
+            dStrHex((const char *)rp, len,
+                    (1 == op->do_hex) ? 0 : -1);
+        else
+            decode_supported_vpd(rp, len, 0x1f & rp[0]);
+        break;
+    case VPD_UNIT_SERIAL_NUM:
+        if (! op->do_raw && ! op->do_export && (op->do_hex < 2))
+            printf("VPD INQUIRY: Unit serial number page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else if (op->do_hex)
+            dStrHex((const char *)rp, len,
+                    (1 == op->do_hex) ? 0 : -1);
+        else {
+            char obuff[DEF_ALLOC_LEN];
+            int k, m;
+
+            memset(obuff, 0, sizeof(obuff));
+            len -= 4;
+            if (len >= (int)sizeof(obuff))
+                len = sizeof(obuff) - 1;
+            memcpy(obuff, rp + 4, len);
+            if (op->do_export) {
+                k = encode_whitespaces((unsigned char *)obuff, len);
+                if (k > 0) {
+                    printf("SCSI_IDENT_SERIAL=");
+                    /* udev-conformant character encoding */
+                    for (m = 0; m < k; ++m) {
+                        if ((obuff[m] >= '0' && obuff[m] <= '9') ||
+                            (obuff[m] >= 'A' && obuff[m] <= 'Z') ||
+                            (obuff[m] >= 'a' && obuff[m] <= 'z') ||
+                            strchr("#+-.:=@_", obuff[m]) != NULL)
+                            printf("%c", obuff[m]);
+                        else
+                            printf("\\x%02x", obuff[m]);
+                    }
+                    printf("\n");
+                }
+            } else {
+                k = encode_unicode((unsigned char *)obuff, len);
+                if (k > 0)
+                    printf("  Unit serial number: %s\n", obuff);
+            }
+        }
+        break;
+    case VPD_DEVICE_ID:
+        if (! op->do_raw && ! op->do_export && (op->do_hex < 3))
+            printf("VPD INQUIRY: Device Identification page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else if (op->do_hex > 2)
+            dStrHex((const char *)rp, len, -1);
+        else if (op->do_export)
+            export_dev_ids(rp + 4, len - 4, op->do_verbose);
+        else
+            decode_id_vpd(rp, len, op->do_hex);
+        break;
+    case VPD_SOFTW_INF_ID:
+        if (! op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: Software interface identification page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_softw_inf_id(rp, len, op->do_hex);
+        break;
+    case VPD_MAN_NET_ADDR:
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: Management network addresses page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_net_man_vpd(rp, len, op->do_hex);
+        break;
+    case VPD_MODE_PG_POLICY:
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: Mode page policy\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_mode_policy_vpd(rp, len, op->do_hex);
+        break;
+    case VPD_EXT_INQ:
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: extended INQUIRY data page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_x_inq_vpd(rp, len, op->do_hex);
+        break;
+    case VPD_ATA_INFO:
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: ATA information page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        /* format output for 'hdparm --Istdin' with '-rr' or '-HHH' */
+        if ((2 == op->do_raw) || (3 == op->do_hex))
+            dWordHex((const unsigned short *)(rp + 60), 256, -2,
+                     sg_is_big_endian());
+        else if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_ata_info_vpd(rp, len, op->do_hex);
+        break;
+    case VPD_POWER_CONDITION:
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: Power condition page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_power_condition(rp, len, op->do_hex);
+        break;
+    case 0xb0:  /* VPD pages in B0h to BFh range depend on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (! op->do_raw && (op->do_hex < 2)) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
+                    printf("VPD INQUIRY: Block limits page (SBC)\n");
+                    break;
+                case PDT_TAPE: case PDT_MCHANGER:
+                    printf("VPD INQUIRY: Sequential access device "
+                           "capabilities (SSC)\n");
+                    break;
+                case PDT_OSD:
+                    printf("VPD INQUIRY: OSD information (OSD)\n");
+                    break;
+                default:
+                    printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb0, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else
+                decode_b0_vpd(rp, len, op->do_hex);
+        } else if (! op->do_raw)
+            pr2serr("VPD INQUIRY: page=0xb0\n");
+        break;
+    case 0xb1:  /* VPD pages in B0h to BFh range depend on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (! op->do_raw && (op->do_hex < 2)) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
+                    printf("VPD INQUIRY: Block device characteristcis page "
+                           "(SBC)\n");
+                    break;
+                case PDT_TAPE: case PDT_MCHANGER:
+                    printf("Manufactured assigned serial number VPD page "
+                           "(SSC):\n");
+                    break;
+                case PDT_OSD:
+                    printf("Security token VPD page (OSD):\n");
+                    break;
+                case PDT_ADC:
+                    printf("Manufactured assigned serial number VPD page "
+                           "(ADC):\n");
+                    break;
+                default:
+                    printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb1, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else
+                decode_b1_vpd(rp, len, op->do_hex);
+        } else if (! op->do_raw)
+            pr2serr("VPD INQUIRY: page=0xb1\n");
+        break;
+    case 0xb2:  /* VPD pages in B0h to BFh range depend on pdt */
+        if (!op->do_raw && (op->do_hex < 2))
+            pr2serr(" Only hex output supported. sg_vpd decodes the B2h "
+                    "page.\n");
+        return vpd_mainly_hex(sg_fd, op, inhex_len);
+    case 0xb3:  /* VPD pages in B0h to BFh range depend on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (! op->do_raw && (op->do_hex < 2)) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL:
+                    printf("VPD INQUIRY: Referrals VPD page (SBC)\n");
+                    break;
+                default:
+                    printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb3, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else
+                decode_b3_vpd(rp, len, op->do_hex);
+        } else if (! op->do_raw)
+            pr2serr("VPD INQUIRY: page=0xb3\n");
+        break;
+    case VPD_UPR_EMC:   /* 0xc0 */
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: Unit Path Report Page (EMC)\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, -1, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_upr_vpd_c0_emc(rp, len, op->do_hex);
+        break;
+    case VPD_RDAC_VERS:         /* 0xc2 */
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: Software Version (RDAC)\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, -1, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_rdac_vpd_c2(rp, len, op->do_hex);
+        break;
+    case VPD_RDAC_VAC:          /* 0xc9 */
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: Volume Access Control (RDAC)\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, -1, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_rdac_vpd_c9(rp, len, op->do_hex);
+        break;
+    case VPD_SCSI_PORTS:
+        if (!op->do_raw && (op->do_hex < 2))
+            printf("VPD INQUIRY: SCSI Ports page\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+        if (res)
+            break;
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else
+            decode_scsi_ports_vpd(rp, len, op->do_hex);
+        break;
+    default:
+        if ((pn > 0) && (pn < 0x80)) {
+            if (!op->do_raw && (op->do_hex < 2))
+                printf("VPD INQUIRY: ASCII information page, FRU code=0x%x\n",
+                       pn);
+            res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len);
+            if (0 == res) {
+                if (op->do_raw)
+                    dStrRaw((const char *)rp, len);
+                else
+                    decode_ascii_inf(rp, len, op->do_hex);
+            }
+        } else {
+            if (op->do_hex < 2)
+                pr2serr(" Only hex output supported. sg_vpd and sdparm "
+                        "decode more VPD pages.\n");
+            return vpd_mainly_hex(sg_fd, op, inhex_len);
+        }
+    }
+    if (res) {
+        char b[80];
+
+        if (SG_LIB_CAT_ILLEGAL_REQ == res)
+            pr2serr("    inquiry: field in cdb illegal (page not "
+                    "supported)\n");
+        else {
+            sg_get_category_sense_str(res, sizeof(b), b, vb);
+            pr2serr("    inquiry: %s\n", b);
+        }
+    }
+    return res;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, n;
+    int ret = 0;
+    int inhex_len = 0;
+    const struct svpd_values_name_t * vnp;
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->page_num = -1;
+    op->page_pdt = -1;
+    op->do_block = -1;         /* use default for OS */
+    res = cl_process(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        usage_for(op);
+        if (op->do_help > 1) {
+            pr2serr("\n>>> Available VPD page abbreviations:\n");
+            enumerate_vpds();
+        }
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+    if (op->page_arg) {
+        if (op->page_num >= 0) {
+            pr2serr("Given '-p' option and another option that "
+                    "implies a page\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (isalpha(op->page_arg[0])) {
+            vnp = sdp_find_vpd_by_acron(op->page_arg);
+            if (NULL == vnp) {
+#ifdef SG_SCSI_STRINGS
+                if (op->opt_new)
+                    pr2serr("abbreviation %s given to '--page=' "
+                            "not recognized\n", op->page_arg);
+                else
+                    pr2serr("abbreviation %s given to '-p=' "
+                            "not recognized\n", op->page_arg);
+#else
+                pr2serr("abbreviation %s given to '--page=' "
+                        "not recognized\n", op->page_arg);
+#endif
+                pr2serr(">>> Available abbreviations:\n");
+                enumerate_vpds();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if ((1 != op->do_hex) && (0 == op->do_raw))
+                ++op->do_decode;
+            op->page_num = vnp->value;
+            op->page_pdt = vnp->pdt;
+        } else if ('-' == op->page_arg[0]) {
+            op->page_num = -2;  /* request standard INQUIRY response */
+        } else {
+#ifdef SG_SCSI_STRINGS
+            if (op->opt_new) {
+                n = sg_get_num(op->page_arg);
+                if ((n < 0) || (n > 255)) {
+                    pr2serr("Bad argument to '--page=', "
+                            "expecting 0 to 255 inclusive\n");
+                    usage_for(op);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                if ((1 != op->do_hex) && (0 == op->do_raw))
+                    ++op->do_decode;
+            } else {
+                int num;
+                unsigned int u;
+
+                num = sscanf(op->page_arg, "%x", &u);
+                if ((1 != num) || (u > 255)) {
+                    pr2serr("Inappropriate value after '-o=' "
+                            "or '-p=' option\n");
+                    usage_for(op);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                n = u;
+            }
+#else
+            n = sg_get_num(op->page_arg);
+            if ((n < 0) || (n > 255)) {
+                pr2serr("Bad argument to '--page=', "
+                        "expecting 0 to 255 inclusive\n");
+                usage_for(op);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if ((1 != op->do_hex) && (0 == op->do_raw))
+                ++op->do_decode;
+#endif /* SG_SCSI_STRINGS */
+            op->page_num = n;
+        }
+    }
+    if (op->inhex_fn) {
+        if (op->device_name) {
+            pr2serr("Cannot have both a DEVICE and --inhex= option\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->do_cmddt) {
+            pr2serr("Don't support --cmddt with --inhex= option\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (f2hex_arr(op->inhex_fn, op->do_raw, 0, rsp_buff, &inhex_len,
+                      sizeof(rsp_buff)))
+            return SG_LIB_FILE_ERROR;
+        op->do_raw = 0;         /* don't want raw on output with --inhex= */
+        if (-1 == op->page_num) {       /* may be able to deduce VPD page */
+            if (op->page_pdt < 0)
+                op->page_pdt = 0x1f & rsp_buff[0];
+            if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) {
+                if (op->do_verbose)
+                    pr2serr("Guessing from --inhex= this is a standard "
+                            "INQUIRY\n");
+            } else if (rsp_buff[2] <= 2) {
+                if (op->do_verbose)
+                    pr2serr("Guessing from --inhex this is VPD page 0x%x\n",
+                            rsp_buff[1]);
+                op->page_num = rsp_buff[1];
+                ++op->do_vpd;
+                if ((1 != op->do_hex) && (0 == op->do_raw))
+                    ++op->do_decode;
+            } else {
+                if (op->do_verbose)
+                    pr2serr("page number unclear from --inhex, hope it's a "
+                            "standard INQUIRY\n");
+            }
+        }
+    } else if (0 == op->device_name) {
+        pr2serr("No DEVICE argument given\n");
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (-2 == op->page_num) /* from --page=-<num> to force standard INQUIRY */
+        op->page_num = -1;  /* now past guessing, set to normal indication */
+
+    if (op->do_export) {
+        if (op->page_num != -1) {
+            if (op->page_num != VPD_DEVICE_ID &&
+                op->page_num != VPD_UNIT_SERIAL_NUM) {
+                pr2serr("Option '--export' only supported "
+                        "for VPD pages 0x80 and 0x83\n");
+                usage_for(op);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++op->do_decode;
+            ++op->do_vpd;
+        }
+    }
+
+    if ((0 == op->do_cmddt) && (op->page_num >= 0) && op->p_given)
+        ++op->do_vpd;
+
+    if (op->do_raw && op->do_hex) {
+        pr2serr("Can't do hex and raw at the same time\n");
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_vpd && op->do_cmddt) {
+#ifdef SG_SCSI_STRINGS
+        if (op->opt_new)
+            pr2serr("Can't use '--cmddt' with VPD pages\n");
+        else
+            pr2serr("Can't have both '-e' and '-c' (or '-cl')\n");
+#else
+        pr2serr("Can't use '--cmddt' with VPD pages\n");
+#endif
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (((op->do_vpd || op->do_cmddt)) && (op->page_num < 0))
+        op->page_num = 0;
+    if (op->num_pages > 1) {
+        pr2serr("Can only fetch one page (VPD or Cmd) at a time\n");
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_descriptors) {
+        if ((op->resp_len > 0) && (op->resp_len < 60)) {
+            pr2serr("version descriptors need INQUIRY response "
+                    "length >= 60 bytes\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->do_vpd || op->do_cmddt) {
+            pr2serr("version descriptors require standard INQUIRY\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (op->num_pages && op->do_ata) {
+        pr2serr("Can't use '-A' with an explicit decode VPD page option\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (op->do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+    if (op->inhex_fn) {
+        if (op->do_vpd) {
+            if (op->do_decode)
+                return vpd_decode(-1, op, inhex_len);
+            else
+                return vpd_mainly_hex(-1, op, inhex_len);
+        } else
+            return std_inq_process(-1, op, inhex_len);
+    }
+
+#if defined(O_NONBLOCK) && defined(O_RDONLY)
+    if (op->do_block >= 0) {
+        n = O_RDONLY | (op->do_block ? 0 : O_NONBLOCK);
+        if ((sg_fd = sg_cmds_open_flags(op->device_name, n,
+                                        op->do_verbose)) < 0) {
+            pr2serr("sg_inq: error opening file: %s: %s\n",
+                    op->device_name, safe_strerror(-sg_fd));
+            return SG_LIB_FILE_ERROR;
+        }
+
+    } else {
+        if ((sg_fd = sg_cmds_open_device(op->device_name, 1 /* ro */,
+                                         op->do_verbose)) < 0) {
+            pr2serr("sg_inq: error opening file: %s: %s\n",
+                    op->device_name, safe_strerror(-sg_fd));
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+#else
+    if ((sg_fd = sg_cmds_open_device(op->device_name, 1 /* ro */,
+                                     op->do_verbose)) < 0) {
+        pr2serr("sg_inq: error opening file: %s: %s\n",
+                op->device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+#endif
+    memset(rsp_buff, 0, sizeof(rsp_buff));
+
+#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
+    if (op->do_ata) {
+        res = try_ata_identify(sg_fd, op->do_hex, op->do_raw,
+                               op->do_verbose);
+        if (0 != res) {
+            pr2serr("fetching ATA information failed on %s\n",
+                    op->device_name);
+            ret = SG_LIB_CAT_OTHER;
+        } else
+            ret = 0;
+        goto err_out;
+    }
+#endif
+
+    if ((! op->do_cmddt) && (! op->do_vpd)) {
+        /* So it's a standard INQUIRY, try ATA IDENTIFY if that fails */
+        ret = std_inq_process(sg_fd, op, -1);
+        if (ret)
+            goto err_out;
+    } else if (op->do_cmddt) {
+        if (op->page_num < 0)
+            op->page_num = 0;
+        ret = cmddt_process(sg_fd, op);
+        if (ret)
+            goto err_out;
+    } else if (op->do_vpd) {
+        if (op->do_decode) {
+            ret = vpd_decode(sg_fd, op, -1);
+            if (ret)
+                goto err_out;
+        } else {
+            ret = vpd_mainly_hex(sg_fd, op, -1);
+            if (ret)
+                goto err_out;
+        }
+    }
+
+err_out:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
+
+
+#if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS)
+/* Following code permits ATA IDENTIFY commands to be performed on
+   ATA non "Packet Interface" devices (e.g. ATA disks).
+   GPL-ed code borrowed from smartmontools (smartmontools.sf.net).
+   Copyright (C) 2002-4 Bruce Allen
+                <smartmontools-support@lists.sourceforge.net>
+ */
+#ifndef ATA_IDENTIFY_DEVICE
+#define ATA_IDENTIFY_DEVICE 0xec
+#define ATA_IDENTIFY_PACKET_DEVICE 0xa1
+#endif
+#ifndef HDIO_DRIVE_CMD
+#define HDIO_DRIVE_CMD    0x031f
+#endif
+
+/* Needed parts of the ATA DRIVE IDENTIFY Structure. Those labeled
+ * word* are NOT used.
+ */
+struct ata_identify_device {
+  unsigned short words000_009[10];
+  unsigned char  serial_no[20];
+  unsigned short words020_022[3];
+  unsigned char  fw_rev[8];
+  unsigned char  model[40];
+  unsigned short words047_079[33];
+  unsigned short major_rev_num;
+  unsigned short minor_rev_num;
+  unsigned short command_set_1;
+  unsigned short command_set_2;
+  unsigned short command_set_extension;
+  unsigned short cfs_enable_1;
+  unsigned short word086;
+  unsigned short csf_default;
+  unsigned short words088_255[168];
+};
+
+#define ATA_IDENTIFY_BUFF_SZ  sizeof(struct ata_identify_device)
+#define HDIO_DRIVE_CMD_OFFSET 4
+
+static int
+ata_command_interface(int device, char *data, int * atapi_flag, int verbose)
+{
+    unsigned char buff[ATA_IDENTIFY_BUFF_SZ + HDIO_DRIVE_CMD_OFFSET];
+    unsigned short get_ident[256];
+
+    if (atapi_flag)
+        *atapi_flag = 0;
+    memset(buff, 0, sizeof(buff));
+    if (ioctl(device, HDIO_GET_IDENTITY, &get_ident) < 0) {
+        if (ENOTTY == errno) {
+            if (verbose > 1)
+                pr2serr("HDIO_GET_IDENTITY failed with ENOTTY, "
+                        "try HDIO_DRIVE_CMD ioctl ...\n");
+            buff[0] = ATA_IDENTIFY_DEVICE;
+            buff[3] = 1;
+            if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
+                if (verbose)
+                    pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) "
+                            "ioctl failed:\n\t%s [%d]\n",
+                            safe_strerror(errno), errno);
+                return errno;
+            }
+            memcpy(data, buff + HDIO_DRIVE_CMD_OFFSET, ATA_IDENTIFY_BUFF_SZ);
+            return 0;
+        } else {
+            if (verbose)
+                pr2serr("HDIO_GET_IDENTITY ioctl failed:\n"
+                        "\t%s [%d]\n", safe_strerror(errno), errno);
+            return errno;
+        }
+    } else if (verbose > 1)
+        pr2serr("HDIO_GET_IDENTITY succeeded\n");
+    if (0x2 == ((get_ident[0] >> 14) &0x3)) {   /* ATAPI device */
+        if (verbose > 1)
+            pr2serr("assume ATAPI device from HDIO_GET_IDENTITY response\n");
+        memset(buff, 0, sizeof(buff));
+        buff[0] = ATA_IDENTIFY_PACKET_DEVICE;
+        buff[3] = 1;
+        if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
+            if (verbose)
+                pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_PACKET_DEVICE) "
+                        "ioctl failed:\n\t%s [%d]\n", safe_strerror(errno),
+                        errno);
+            buff[0] = ATA_IDENTIFY_DEVICE;
+            buff[3] = 1;
+            if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
+                if (verbose)
+                    pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) "
+                            "ioctl failed:\n\t%s [%d]\n", safe_strerror(errno),
+                            errno);
+                return errno;
+            }
+        } else if (atapi_flag) {
+            *atapi_flag = 1;
+            if (verbose > 1)
+                pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) succeeded\n");
+        }
+    } else {    /* assume non-packet device */
+        buff[0] = ATA_IDENTIFY_DEVICE;
+        buff[3] = 1;
+        if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
+            if (verbose)
+                pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) ioctl failed:"
+                        "\n\t%s [%d]\n", safe_strerror(errno), errno);
+            return errno;
+        } else if (verbose > 1)
+            pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) succeeded\n");
+    }
+    /* if the command returns data, copy it back */
+    memcpy(data, buff + HDIO_DRIVE_CMD_OFFSET, ATA_IDENTIFY_BUFF_SZ);
+    return 0;
+}
+
+/* Returns 0 if successful, else errno of error */
+static int
+try_ata_identify(int ata_fd, int do_hex, int do_raw, int verbose)
+{
+    struct ata_identify_device ata_ident;
+    char model[64];
+    char serial[64];
+    char firm[64];
+    int res, atapi;
+
+    memset(&ata_ident, 0, sizeof(ata_ident));
+    res = ata_command_interface(ata_fd, (char *)&ata_ident, &atapi, verbose);
+    if (res)
+        return res;
+    if ((2 == do_raw) || (3 == do_hex))
+        dWordHex((const unsigned short *)&ata_ident, 256, -2,
+                 sg_is_big_endian());
+    else if (do_raw)
+        dStrRaw((const char *)&ata_ident, 512);
+    else {
+        if (do_hex) {
+            if (atapi)
+                printf("ATA IDENTIFY PACKET DEVICE response ");
+            else
+                printf("ATA IDENTIFY DEVICE response ");
+            if (do_hex > 1) {
+                printf("(512 bytes):\n");
+                dStrHex((const char *)&ata_ident, 512, 0);
+            } else {
+                printf("(256 words):\n");
+                dWordHex((const unsigned short *)&ata_ident, 256, 0,
+                         sg_is_big_endian());
+            }
+        } else {
+            printf("%s device: model, serial number and firmware revision:\n",
+                   (atapi ? "ATAPI" : "ATA"));
+            res = sg_ata_get_chars((const unsigned short *)ata_ident.model,
+                                   0, 20, sg_is_big_endian(), model);
+            model[res] = '\0';
+            res = sg_ata_get_chars((const unsigned short *)ata_ident.serial_no,
+                                   0, 10, sg_is_big_endian(), serial);
+            serial[res] = '\0';
+            res = sg_ata_get_chars((const unsigned short *)ata_ident.fw_rev,
+                                   0, 4, sg_is_big_endian(), firm);
+            firm[res] = '\0';
+            printf("  %s %s %s\n", model, serial, firm);
+            if (verbose) {
+                if (atapi)
+                    printf("ATA IDENTIFY PACKET DEVICE response "
+                           "(256 words):\n");
+                else
+                    printf("ATA IDENTIFY DEVICE response (256 words):\n");
+                dWordHex((const unsigned short *)&ata_ident, 256, 0,
+                         sg_is_big_endian());
+            }
+        }
+    }
+    return 0;
+}
+#endif
+
+/* If this structure is changed then the structure of the same name in
+ * sg_inq_data,c should also be changed
+ */
+struct sg_version_descriptor {
+    int value;
+    const char * name;
+};
+
+extern struct sg_version_descriptor sg_version_descriptor_arr[];
+
+
+static const char *
+find_version_descriptor_str(int value)
+{
+    int k;
+    const struct sg_version_descriptor * vdp;
+
+    for (k = 0; ((vdp = sg_version_descriptor_arr + k) && vdp->name); ++k) {
+        if (value == vdp->value)
+            return vdp->name;
+        if (value < vdp->value)
+            break;
+    }
+    return NULL;
+}
diff --git a/sg3_utils/src/sg_inq_data.c b/sg3_utils/src/sg_inq_data.c
new file mode 100644
index 0000000..a0a0f7f
--- /dev/null
+++ b/sg3_utils/src/sg_inq_data.c
@@ -0,0 +1,535 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+*  Copyright (C) 2000-2016 D. Gilbert
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This is an auxiliary file holding data tables for the sg_inq utility.
+   It is mainly based on the SCSI SPC-4 document at http://www.t10.org .
+
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+/* Assume index is less than 16 */
+const char * sg_ansi_version_arr[] =
+{
+    "no conformance claimed",
+    "SCSI-1",           /* obsolete, ANSI X3.131-1986 */
+    "SCSI-2",           /* obsolete, ANSI X3.131-1994 */
+    "SPC",              /* withdrawn, ANSI INCITS 301-1997 */
+    "SPC-2",            /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */
+    "SPC-3",            /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */
+    "SPC-4",            /* ANSI INCITS 513-2015 */
+    "SPC-5",
+    "ecma=1, [8h]",
+    "ecma=1, [9h]",
+    "ecma=1, [Ah]",
+    "ecma=1, [Bh]",
+    "reserved [Ch]",
+    "reserved [Dh]",
+    "reserved [Eh]",
+    "reserved [Fh]",
+};
+
+
+/* Copy of structure found in sg_inq.c */
+struct sg_version_descriptor {
+    int value;
+    const char * name;
+};
+
+/* table from SPC-5 revision 08 [sorted numerically (from Annex E.9)] */
+/* Can also be obtained from : http://www.t10.org/lists/stds.txt 20160125 */
+
+
+#ifdef SG_SCSI_STRINGS
+struct sg_version_descriptor sg_version_descriptor_arr[] = {
+    {0x0, "Version Descriptor not supported or No standard identified"},
+    {0x20, "SAM (no version claimed)"},
+    {0x3b, "SAM T10/0994-D revision 18"},
+    {0x3c, "SAM ANSI INCITS 270-1996"},
+    {0x40, "SAM-2 (no version claimed)"},
+    {0x54, "SAM-2 T10/1157-D revision 23"},
+    {0x55, "SAM-2 T10/1157-D revision 24"},
+    {0x5c, "SAM-2 ANSI INCITS 366-2003"},
+    {0x5e, "SAM-2 ISO/IEC 14776-412"},
+    {0x60, "SAM-3 (no version claimed)"},
+    {0x62, "SAM-3 T10/1561-D revision 7"},
+    {0x75, "SAM-3 T10/1561-D revision 13"},
+    {0x76, "SAM-3 T10/1561-D revision 14"},
+    {0x77, "SAM-3 ANSI INCITS 402-2005"},
+    {0x80, "SAM-4 (no version claimed)"},
+    {0x87, "SAM-4 T10/1683-D revision 13"},
+    {0x8b, "SAM-4 T10/1683-D revision 14"},
+    {0x90, "SAM-4 ANSI INCITS 447-2008"},
+    {0x92, "SAM-4 ISO/IEC 14776-414"},
+    {0xa0, "SAM-5 (no version claimed)"},
+    {0xa2, "SAM-5 T10/2104-D revision 4"},
+    {0xa4, "SAM-5 T10/2104-D revision 20"},
+    {0xa6, "SAM-5 T10/2104-D revision 21"},
+    {0xc0, "SAM-6 (no version claimed)"},
+    {0x120, "SPC (no version claimed)"},
+    {0x13b, "SPC T10/0995-D revision 11a"},
+    {0x13c, "SPC ANSI INCITS 301-1997"},
+    {0x140, "MMC (no version claimed)"},
+    {0x15b, "MMC T10/1048-D revision 10a"},
+    {0x15c, "MMC ANSI INCITS 304-1997"},
+    {0x160, "SCC (no version claimed)"},
+    {0x17b, "SCC T10/1047-D revision 06c"},
+    {0x17c, "SCC ANSI INCITS 276-1997"},
+    {0x180, "SBC (no version claimed)"},
+    {0x19b, "SBC T10/0996-D revision 08c"},
+    {0x19c, "SBC ANSI INCITS 306-1998"},
+    {0x1a0, "SMC (no version claimed)"},
+    {0x1bb, "SMC T10/0999-D revision 10a"},
+    {0x1bc, "SMC ANSI INCITS 314-1998"},
+    {0x1be, "SMC ISO/IEC 14776-351"},
+    {0x1c0, "SES (no version claimed)"},
+    {0x1db, "SES T10/1212-D revision 08b"},
+    {0x1dc, "SES ANSI INCITS 305-1998"},
+    {0x1dd, "SES T10/1212-D revision 08b w/ Amendment ANSI "
+            "INCITS.305/AM1:2000"},
+    {0x1de, "SES ANSI INCITS 305-1998 w/ Amendment ANSI "
+            "INCITS.305/AM1:2000"},
+    {0x1e0, "SCC-2 (no version claimed}"},
+    {0x1fb, "SCC-2 T10/1125-D revision 04"},
+    {0x1fc, "SCC-2 ANSI INCITS 318-1998"},
+    {0x200, "SSC (no version claimed)"},
+    {0x201, "SSC T10/0997-D revision 17"},
+    {0x207, "SSC T10/0997-D revision 22"},
+    {0x21c, "SSC ANSI INCITS 335-2000"},
+    {0x220, "RBC (no version claimed)"},
+    {0x238, "RBC T10/1240-D revision 10a"},
+    {0x23c, "RBC ANSI INCITS 330-2000"},
+    {0x240, "MMC-2 (no version claimed)"},
+    {0x255, "MMC-2 T10/1228-D revision 11"},
+    {0x25b, "MMC-2 T10/1228-D revision 11a"},
+    {0x25c, "MMC-2 ANSI INCITS 333-2000"},
+    {0x260, "SPC-2 (no version claimed)"},
+    {0x267, "SPC-2 T10/1236-D revision 12"},
+    {0x269, "SPC-2 T10/1236-D revision 18"},
+    {0x275, "SPC-2 T10/1236-D revision 19"},
+    {0x276, "SPC-2 T10/1236-D revision 20"},
+    {0x277, "SPC-2 ANSI INCITS 351-2001"},
+    {0x278, "SPC-2 ISO/IEC 14776-452"},
+    {0x280, "OCRW (no version claimed)"},
+    {0x29e, "OCRW ISO/IEC 14776-381"},
+    {0x2a0, "MMC-3 (no version claimed)"},
+    {0x2b5, "MMC-3 T10/1363-D revision 9"},
+    {0x2b6, "MMC-3 T10/1363-D revision 10g"},
+    {0x2b8, "MMC-3 ANSI INCITS 360-2002"},
+    {0x2e0, "SMC-2 (no version claimed)"},
+    {0x2f5, "SMC-2 T10/1383-D revision 5"},
+    {0x2fc, "SMC-2 T10/1383-D revision 6"},
+    {0x2fd, "SMC-2 T10/1383-D revision 7"},
+    {0x2fe, "SMC-2 ANSI INCITS 382-2004"},
+    {0x300, "SPC-3 (no version claimed)"},
+    {0x301, "SPC-3 T10/1416-D revision 7"},
+    {0x307, "SPC-3 T10/1416-D revision 21"},
+    {0x30f, "SPC-3 T10/1416-D revision 22"},
+    {0x312, "SPC-3 T10/1416-D revision 23"},
+    {0x314, "SPC-3 ANSI INCITS 408-2005"},
+    {0x316, "SPC-3 ISO/IEC 14776-453"},
+    {0x320, "SBC-2 (no version claimed)"},
+    {0x322, "SBC-2 T10/1417-D revision 5a"},
+    {0x324, "SBC-2 T10/1417-D revision 15"},
+    {0x33b, "SBC-2 T10/1417-D revision 16"},
+    {0x33d, "SBC-2 ANSI INCITS 405-2005"},
+    {0x33e, "SBC-2 ISO/IEC 14776-322"},
+    {0x340, "OSD (no version claimed)"},
+    {0x341, "OSD T10/1355-D revision 0"},
+    {0x342, "OSD T10/1355-D revision 7a"},
+    {0x343, "OSD T10/1355-D revision 8"},
+    {0x344, "OSD T10/1355-D revision 9"},
+    {0x355, "OSD T10/1355-D revision 10"},
+    {0x356, "OSD ANSI INCITS 400-2004"},
+    {0x360, "SSC-2 (no version claimed)"},
+    {0x374, "SSC-2 T10/1434-D revision 7"},
+    {0x375, "SSC-2 T10/1434-D revision 9"},
+    {0x37d, "SSC-2 ANSI INCITS 380-2003"},
+    {0x380, "BCC (no version claimed)"},
+    {0x3a0, "MMC-4 (no version claimed)"},
+    {0x3b0, "MMC-4 T10/1545-D revision 5"},     /* dropped in spc4r09 */
+    {0x3b1, "MMC-4 T10/1545-D revision 5a"},
+    {0x3bd, "MMC-4 T10/1545-D revision 3"},
+    {0x3be, "MMC-4 T10/1545-D revision 3d"},
+    {0x3bf, "MMC-4 ANSI INCITS 401-2005"},
+    {0x3c0, "ADC (no version claimed)"},
+    {0x3d5, "ADC T10/1558-D revision 6"},
+    {0x3d6, "ADC T10/1558-D revision 7"},
+    {0x3d7, "ADC ANSI INCITS 403-2005"},
+    {0x3e0, "SES-2 (no version claimed)"},
+    {0x3e1, "SES-2 T10/1559-D revision 16"},
+    {0x3e7, "SES-2 T10/1559-D revision 19"},
+    {0x3eb, "SES-2 T10/1559-D revision 20"},
+    {0x3f0, "SES-2 ANSI INCITS 448-2008"},
+    {0x3f2, "SES-2 ISO/IEC 14776-372"},
+    {0x400, "SSC-3 (no version claimed)"},
+    {0x403, "SSC-3 T10/1611-D revision 04a"},
+    {0x407, "SSC-3 T10/1611-D revision 05"},
+    {0x409, "SSC-3 ANSI INCITS 467-2011"},
+    {0x40b, "SSC-3 ISO/IEC 14776-333:2013"},
+    {0x420, "MMC-5 (no version claimed)"},
+    {0x42f, "MMC-5 T10/1675-D revision 03"},
+    {0x431, "MMC-5 T10/1675-D revision 03b"},
+    {0x432, "MMC-5 T10/1675-D revision 04"},
+    {0x434, "MMC-5 ANSI INCITS 430-2007"},
+    {0x440, "OSD-2 (no version claimed)"},
+    {0x444, "OSD-2 T10/1729-D revision 4"},
+    {0x446, "OSD-2 T10/1729-D revision 5"},
+    {0x448, "OSD-2 ANSI INCITS 458-2011"},
+    {0x460, "SPC-4 (no version claimed)"},
+    {0x461, "SPC-4 T10/BSR INCITS 513 revision 16"},
+    {0x462, "SPC-4 T10/BSR INCITS 513 revision 18"},
+    {0x463, "SPC-4 T10/BSR INCITS 513 revision 23"},
+    {0x466, "SPC-4 T10/BSR INCITS 513 revision 36"},
+    {0x468, "SPC-4 T10/BSR INCITS 513 revision 37"},
+    {0x469, "SPC-4 T10/BSR INCITS 513 revision 37a"},
+    {0x46c, "SPC-4 ANSI INCITS 513-2015"},
+    {0x480, "SMC-3 (no version claimed)"},
+    {0x482, "SMC-3 T10/1730-D revision 15"},
+    {0x482, "SMC-3 T10/1730-D revision 16"},
+    {0x486, "SMC-3 ANSI INCITS 484-2012"},
+    {0x4a0, "ADC-2 (no version claimed)"},
+    {0x4a7, "ADC-2 T10/1741-D revision 7"},
+    {0x4aa, "ADC-2 T10/1741-D revision 8"},
+    {0x4ac, "ADC-2 ANSI INCITS 441-2008"},
+    {0x4c0, "SBC-3 (no version claimed)"},
+    {0x4c3, "SBC-3 T10/BSR INCITS 514 revision 35"},
+    {0x4c5, "SBC-3 T10/BSR INCITS 514 revision 36"},
+    {0x4c8, "SBC-3 ANSI INCITS 514-2014"},
+    {0x4e0, "MMC-6 (no version claimed)"},
+    {0x4e3, "MMC-6 T10/1836-D revision 2b"},
+    {0x4e5, "MMC-6 T10/1836-D revision 02g"},
+    {0x4e6, "MMC-6 ANSI INCITS 468-2010"},
+    {0x4e7, "MMC-6 ANSI INCITS 468-2010 + MMC-6/AM1 ANSI INCITS "
+            "468-2010/AM 1"},
+    {0x500, "ADC-3 (no version claimed)"},
+    {0x502, "ADC-3 T10/1895-D revision 04"},
+    {0x504, "ADC-3 T10/1895-D revision 05"},
+    {0x506, "ADC-3 T10/1895-D revision 05a"},
+    {0x50a, "ADC-3 ANSI INCITS 497-2012"},
+    {0x520, "SSC-4 (no version claimed)"},
+    {0x523, "SSC-4 T10/BSR INCITS 516 revision 2"},
+    {0x525, "SSC-4 T10/BSR INCITS 516 revision 3"},
+    {0x527, "SSC-4 SSC-4 ANSI INCITS 516-2013"},
+    {0x560, "OSD-3 (no version claimed)"},
+    {0x580, "SES-3 (no version claimed)"},
+    {0x5a0, "SSC-5 (no version claimed)"},
+    {0x5c0, "SPC-5 (no version claimed)"},
+    {0x5e0, "SFSC (no version claimed)"},
+    {0x5e3, "SFSC BSR INCITS 501 revision 01"},
+    {0x5e5, "SFSC BSR INCITS 501 revision 02"},
+    {0x600, "SBC-4 (no version claimed)"},
+    {0x620, "ZBC (no version claimed)"},
+    {0x622, "ZBC BSR INCITS 536 revision 02"},
+    {0x624, "ZBC BSR INCITS 536 revision 05"},
+    {0x640, "ADC-4 (no version claimed)"},
+    {0x660, "ZBC-2 (no version claimed)"},
+    {0x820, "SSA-TL2 (no version claimed)"},
+    {0x83b, "SSA-TL2 T10/1147-D revision 05b"},
+    {0x83c, "SSA-TL2 ANSI INCITS 308-1998"},
+    {0x840, "SSA-TL1 (no version claimed)"},
+    {0x85b, "SSA-TL1 T10/0989-D revision 10b"},
+    {0x85c, "SSA-TL1 ANSI INCITS 295-1996"},
+    {0x860, "SSA-S3P (no version claimed)"},
+    {0x87b, "SSA-S3P T10/1051-D revision 05b"},
+    {0x87c, "SSA-S3P ANSI INCITS 309-1998"},
+    {0x880, "SSA-S2P (no version claimed)"},
+    {0x89b, "SSA-S2P T10/1121-D revision 07b"},
+    {0x89c, "SSA-S2P ANSI INCITS 294-1996"},
+    {0x8a0, "SIP (no version claimed)"},
+    {0x8bb, "SIP T10/0856-D revision 10"},
+    {0x8bc, "SIP ANSI INCITS 292-1997"},
+    {0x8c0, "FCP (no version claimed)"},
+    {0x8db, "FCP T10/0856-D revision 12"},
+    {0x8dc, "FCP ANSI INCITS 269-1996"},
+    {0x8e0, "SBP-2 (no version claimed)"},
+    {0x8fb, "SBP-2 T10/1155-D revision 04"},
+    {0x8fc, "SBP-2 ANSI INCITS 325-1999"},
+    {0x900, "FCP-2 (no version claimed)"},
+    {0x901, "FCP-2 T10/1144-D revision 4"},
+    {0x915, "FCP-2 T10/1144-D revision 7"},
+    {0x916, "FCP-2 T10/1144-D revision 7a"},
+    {0x917, "FCP-2 ANSI INCITS 350-2003"},
+    {0x918, "FCP-2 T10/1144-D revision 8"},
+    {0x920, "SST (no version claimed)"},
+    {0x935, "SST T10/1380-D revision 8b"},
+    {0x940, "SRP (no version claimed)"},
+    {0x954, "SRP T10/1415-D revision 10"},
+    {0x955, "SRP T10/1415-D revision 16a"},
+    {0x95c, "SRP ANSI INCITS 365-2002"},
+    {0x960, "iSCSI (no version claimed)"},
+    {0x961, "iSCSI RFC 7143"},
+    {0x962, "iSCSI RFC 7144"},
+    /* 0x960 up to 0x97f for iSCSI use */
+    {0x980, "SBP-3 (no version claimed)"},
+    {0x982, "SBP-3 T10/1467-D revision 1f"},
+    {0x994, "SBP-3 T10/1467-D revision 3"},
+    {0x99a, "SBP-3 T10/1467-D revision 4"},
+    {0x99b, "SBP-3 T10/1467-D revision 5"},
+    {0x99c, "SBP-3 ANSI INCITS 375-2004"},
+    {0x9a0, "SRP-2 (no version claimed)"},
+    {0x9c0, "ADP (no version claimed)"},
+    {0x9e0, "ADT (no version claimed)"},
+    {0x9f9, "ADT T10/1557-D revision 11"},
+    {0x9fa, "ADT T10/1557-D revision 14"},
+    {0x9fd, "ADT ANSI INCITS 406-2005"},
+    {0xa00, "FCP-3 (no version claimed)"},
+    {0xa07, "FCP-3 T10/1560-D revision 3f"},
+    {0xa0f, "FCP-3 T10/1560-D revision 4"},
+    {0xa11, "FCP-3 ANSI INCITS 416-2006"},
+    {0xa1c, "FCP-3 ISO/IEC 14776-223"},
+    {0xa20, "ADT-2 (no version claimed)"},
+    {0xa22, "ADT-2 T10/1742-D revision 06"},
+    {0xa27, "ADT-2 T10/1742-D revision 08"},
+    {0xa28, "ADT-2 T10/1742-D revision 09"},
+    {0xa2b, "ADT-2 ANSI INCITS 472-2011"},
+    {0xa40, "FCP-4 (no version claimed)"},
+    {0xa42, "FCP-4 T10/1828-D revision 01"},
+    {0xa44, "FCP-4 T10/1828-D revision 02"},
+    {0xa45, "FCP-4 T10/1828-D revision 02b"},
+    {0xa46, "FCP-4 ANSI INCITS 481-2012"},
+    {0xa60, "ADT-3 (no version claimed)"},
+    {0xaa0, "SPI (no version claimed)"},
+    {0xab9, "SPI T10/0855-D revision 15a"},
+    {0xaba, "SPI ANSI INCITS 253-1995"},
+    {0xabb, "SPI T10/0855-D revision 15a with SPI Amnd revision 3a"},
+    {0xabc, "SPI ANSI INCITS 253-1995 with SPI Amnd ANSI INCITS "
+            "253/AM1:1998"},
+    {0xac0, "Fast-20 (no version claimed)"},
+    {0xadb, "Fast-20 T10/1071-D revision 06"},
+    {0xadc, "Fast-20 ANSI INCITS 277-1996"},
+    {0xae0, "SPI-2 (no version claimed)"},
+    {0xafb, "SPI-2 T10/1142-D revision 20b"},
+    {0xafc, "SPI-2 ANSI INCITS 302-1999"},
+    {0xb00, "SPI-3 (no version claimed)"},
+    {0xb18, "SPI-3 T10/1302-D revision 10"},
+    {0xb19, "SPI-3 T10/1302-D revision 13a"},
+    {0xb1a, "SPI-3 T10/1302-D revision 14"},
+    {0xb1c, "SPI-3 ANSI INCITS 336-2000"},
+    {0xb20, "EPI (no version claimed)"},
+    {0xb3b, "EPI T10/1134-D revision 16"},
+    {0xb3c, "EPI ANSI INCITS TR-23 1999"},
+    {0xb40, "SPI-4 (no version claimed)"},
+    {0xb54, "SPI-4 T10/1365-D revision 7"},
+    {0xb55, "SPI-4 T10/1365-D revision 9"},
+    {0xb56, "SPI-4 ANSI INCITS 362-2002"},
+    {0xb59, "SPI-4 T10/1365-D revision 10"},
+    {0xb60, "SPI-5 (no version claimed)"},
+    {0xb79, "SPI-5 T10/1525-D revision 3"},
+    {0xb7a, "SPI-5 T10/1525-D revision 5"},
+    {0xb7b, "SPI-5 T10/1525-D revision 6"},
+    {0xb7c, "SPI-5 ANSI INCITS 367-2004"},
+    {0xbe0, "SAS (no version claimed)"},
+    {0xbe1, "SAS T10/1562-D revision 01"},
+    {0xbf5, "SAS T10/1562-D revision 03"},
+    {0xbfa, "SAS T10/1562-D revision 04"},
+    {0xbfb, "SAS T10/1562-D revision 04"},
+    {0xbfc, "SAS T10/1562-D revision 05"},
+    {0xbfd, "SAS ANSI INCITS 376-2003"},
+    {0xc00, "SAS-1.1 (no version claimed)"},
+    {0xc07, "SAS-1.1 T10/1602-D revision 9"},
+    {0xc0f, "SAS-1.1 T10/1602-D revision 10"},
+    {0xc11, "SAS-1.1 ANSI INCITS 417-2006"},
+    {0xc12, "SAS-1.1 ISO/IEC 14776-151"},
+    {0xc20, "SAS-2 (no version claimed)"},
+    {0xc23, "SAS-2 T10/1760-D revision 14"},
+    {0xc27, "SAS-2 T10/1760-D revision 15"},
+    {0xc28, "SAS-2 T10/1760-D revision 16"},
+    {0xc2a, "SAS-2 ANSI INCITS 457-2010"},
+    {0xc40, "SAS-2.1 (no version claimed)"},
+    {0xc48, "SAS-2.1 T10/2125-D revision 04"},
+    {0xc4a, "SAS-2.1 T10/2125-D revision 06"},
+    {0xc4b, "SAS-2.1 T10/2125-D revision 07"},
+    {0xc4e, "SAS-2.1 ANSI INCITS 478-2011"},
+    {0xc4f, "SAS-2.1 ANSI INCITS 478-2011 w/ Amnd 1 ANSI INCITS "
+            "478/AM1-2014"},
+    {0xc52, "SAS-2.1 ISO/IEC 14776-153"},
+    {0xc60, "SAS-3 (no version claimed)"},
+    {0xc63, "SAS-3 T10/BSR INCITS 519 revision 05a"},
+    {0xc65, "SAS-3 T10/BSR INCITS 519 revision 06"},
+    {0xc68, "SAS-3 ANSI INCITS 519-2014"},
+    {0xc80, "SAS-4 (no version claimed)"},
+    {0xd20, "FC-PH (no version claimed)"},
+    {0xd3b, "FC-PH ANSI INCITS 230-1994"},
+    {0xd3c, "FC-PH ANSI INCITS 230-1994 with Amnd 1 ANSI INCITS "
+            "230/AM1:1996"},
+    {0xd40, "FC-AL (no version claimed)"},
+    {0xd5c, "FC-AL ANSI INCITS 272-1996"},
+    {0xd60, "FC-AL-2 (no version claimed)"},
+    {0xd61, "FC-AL-2 T11/1133-D revision 7.0"},
+    {0xd63, "FC-AL-2 ANSI INCITS 332-1999 with AM1-2003 & AM2-2006"},
+    {0xd64, "FC-AL-2 ANSI INCITS 332-1999 with Amnd 2 AM2-2006"},
+    {0xd65, "FC-AL-2 ISO/IEC 14165-122 with AM1 & AM2"},
+    {0xd7c, "FC-AL-2 ANSI INCITS 332-1999"},
+    {0xd7d, "FC-AL-2 ANSI INCITS 332-1999 with Amnd 1 AM1:2002"},
+    {0xd80, "FC-PH-3 (no version claimed)"},
+    {0xd9c, "FC-PH-3 ANSI INCITS 303-1998"},
+    {0xda0, "FC-FS (no version claimed)"},
+    {0xdb7, "FC-FS T11/1331-D revision 1.2"},
+    {0xdb8, "FC-FS T11/1331-D revision 1.7"},
+    {0xdbc, "FC-FS ANSI INCITS 373-2003"},
+    {0xdbd, "FC-FS ISO/IEC 14165-251"},
+    {0xdc0, "FC-PI (no version claimed)"},
+    {0xddc, "FC-PI ANSI INCITS 352-2002"},
+    {0xde0, "FC-PI-2 (no version claimed)"},
+    {0xde2, "FC-PI-2 T11/1506-D revision 5.0"},
+    {0xde4, "FC-PI-2 ANSI INCITS 404-2006"},
+    {0xe00, "FC-FS-2 (no version claimed)"},
+    {0xe02, "FC-FS-2 ANSI INCITS 242-2007"},
+    {0xe03, "FC-FS-2 ANSI INCITS 242-2007 with AM1 ANSI INCITS 242/AM1-2007"},
+    {0xe20, "FC-LS (no version claimed)"},
+    {0xe21, "FC-LS T11/1620-D revision 1.62"},
+    {0xe29, "FC-LS ANSI INCITS 433-2007"},
+    {0xe40, "FC-SP (no version claimed)"},
+    {0xe42, "FC-SP T11/1570-D revision 1.6"},
+    {0xe45, "FC-SP ANSI INCITS 426-2007"},
+    {0xe60, "FC-PI-3 (no version claimed)"},
+    {0xe62, "FC-PI-3 T11/1625-D revision 2.0"},
+    {0xe68, "FC-PI-3 T11/1625-D revision 2.1"},
+    {0xe6a, "FC-PI-3 T11/1625-D revision 4.0"},
+    {0xe6e, "FC-PI-3 ANSI INCITS 460-2011"},
+    {0xe80, "FC-PI-4 (no version claimed)"},
+    {0xe82, "FC-PI-4 T11/1647-D revision 8.0"},
+    {0xea0, "FC 10GFC (no version claimed)"},
+    {0xea2, "FC 10GFC ANSI INCITS 364-2003"},
+    {0xea3, "FC 10GFC ISO/IEC 14165-116"},
+    {0xea5, "FC 10GFC ISO/IEC 14165-116 with AM1"},
+    {0xea6, "FC 10GFC ANSI INCITS 364-2003 with AM1 ANSI INCITS 364/AM1-2007"},
+    {0xec0, "FC-SP-2 (no version claimed)"},
+    {0xee0, "FC-FS-3 (no version claimed)"},
+    {0xee2, "FC-FS-3 T11/1861-D revision 0.9"},
+    {0xee7, "FC-FS-3 T11/1861-D revision 1.0"},
+    {0xee9, "FC-FS-3 T11/1861-D revision 1.10"},
+    {0xeeb, "FC-FS-3 ANSI INCITS 470-2011"},
+    {0xf00, "FC-LS-2 (no version claimed)"},
+    {0xf03, "FC-LS-2 T11/2103-D revision 2.11"},
+    {0xf05, "FC-LS-2 T11/2103-D revision 2.21"},
+    {0xf07, "FC-LS-2 ANSI INCITS 477-2011"},
+    {0xf20, "FC-PI-5 (no version claimed)"},
+    {0xf27, "FC-PI-5 T11/2118-D revision 2.00"},
+    {0xf28, "FC-PI-5 T11/2118-D revision 3.00"},
+    {0xf2a, "FC-PI-5 T11/2118-D revision 6.00"},
+    {0xf2b, "FC-PI-5 T11/2118-D revision 6.10"},
+    {0xf2e, "FC-PI-5 ANSI INCITS 479-2011"},
+    {0xf40, "FC-PI-6 (no version claimed)"},
+    {0xf60, "FC-FS-4 (no version claimed)"},
+    {0xf80, "FC-LS-3 (no version claimed)"},
+    {0x12a0, "FC-SCM (no version claimed)"},
+    {0x12a3, "FC-SCM T11/1824DT revision 1.0"},
+    {0x12a5, "FC-SCM T11/1824DT revision 1.1"},
+    {0x12a7, "FC-SCM T11/1824DT revision 1.4"},
+    {0x12aa, "FC-SCM INCITS TR-47 2012"},
+    {0x12c0, "FC-DA-2 (no version claimed)"},
+    {0x12c3, "FC-DA-2 T11/1870DT revision 1.04"},
+    {0x12c5, "FC-DA-2 T11/1870DT revision 1.06"},
+    {0x12c9, "FC-DA-2 INCITS TR-49 2012"},
+    {0x12e0, "FC-DA (no version claimed)"},
+    {0x12e2, "FC-DA T11/1513-DT revision 3.1"},
+    {0x12e8, "FC-DA ANSI INCITS TR-36 2004"},
+    {0x12e9, "FC-DA ISO/IEC 14165-341"},
+    {0x1300, "FC-Tape (no version claimed)"},
+    {0x1301, "FC-Tape T11/1315-D revision 1.16"},
+    {0x131b, "FC-Tape T11/1315-D revision 1.17"},
+    {0x131c, "FC-Tape ANSI INCITS TR-24 1999"},
+    {0x1320, "FC-FLA (no version claimed)"},
+    {0x133b, "FC-FLA T11/1235-D revision 7"},
+    {0x133c, "FC-FLA ANSI INCITS TR-20 1998"},
+    {0x1340, "FC-PLDA (no version claimed)"},
+    {0x135b, "FC-PLDA T11/1162-D revision 2.1"},
+    {0x135c, "FC-PLDA ANSI INCITS TR-19 1998"},
+    {0x1360, "SSA-PH2 (no version claimed)"},
+    {0x137b, "SSA-PH2 T10/1145-D revision 09c"},
+    {0x137c, "SSA-PH2 ANSI INCITS 293-1996"},
+    {0x1380, "SSA-PH3 (no version claimed)"},
+    {0x139b, "SSA-PH3 T10/1146-D revision 05b"},
+    {0x139c, "SSA-PH3 ANSI INCITS 307-1998"},
+    {0x14a0, "IEEE 1394 (no version claimed)"},
+    {0x14bd, "ANSI IEEE 1394:1995"},
+    {0x14c0, "IEEE 1394a (no version claimed)"},
+    {0x14e0, "IEEE 1394b (no version claimed)"},
+    {0x15e0, "ATA/ATAPI-6 (no version claimed)"},
+    {0x15fd, "ATA/ATAPI-6 ANSI INCITS 361-2002"},
+    {0x1600, "ATA/ATAPI-7 (no version claimed)"},
+    {0x1602, "ATA/ATAPI-7 T13/1532-D revision 3"},
+    {0x161c, "ATA/ATAPI-7 ANSI INCITS 397-2005"},
+    {0x161e, "ATA/ATAPI-7 ISO/IEC 24739"},
+    {0x1620, "ATA/ATAPI-8 ATA-AAM Architecture model (no version claimed)"},
+    {0x1621, "ATA/ATAPI-8 ATA-PT Parallel transport (no version claimed)"},
+    {0x1622, "ATA/ATAPI-8 ATA-AST Serial transport (no version claimed)"},
+    {0x1623, "ATA/ATAPI-8 ATA-ACS ATA/ATAPI command set (no version "
+             "claimed)"},
+    {0x1628, "ATA/ATAPI-8 ATA-AAM ANSI INCITS 451-2008"},
+    {0x162a, "ATA/ATAPI-8 ATA8-ACS ANSI INCITS 452-2009 w/ Amendment 1"},
+    {0x1721, "ACS-2 (no version claimed)"},
+    {0x1722, "ACS-2 ANSI INCITS 482-2013"},
+    {0x1726, "ACS-3 (no version claimed)"},
+    {0x1728, "Universal Serial Bus Specification, Revision 1.1"},
+    {0x1729, "Universal Serial Bus Specification, Revision 2.0"},
+    {0x1730, "USB Mass Storage Class Bulk-Only Transport, Revision 1.0"},
+    {0x1740, "UAS (no version claimed)"},    /* USB attached SCSI */
+    {0x1743, "UAS T10/2095-D revision 02"},
+    {0x1747, "UAS T10/2095-D revision 04"},
+    {0x1748, "UAS ANSI INCITS 471-2010"},
+    {0x1749, "UAS ISO/IEC 14776-251:2014"},
+    {0x1780, "UAS-2 (no version claimed)"},
+    {0x1ea0, "SAT (no version claimed)"},
+    {0x1ea7, "SAT T10/1711-D rev 8"},
+    {0x1eab, "SAT T10/1711-D rev 9"},
+    {0x1ead, "SAT ANSI INCITS 431-2007"},
+    {0x1ec0, "SAT-2 (no version claimed)"},
+    {0x1ec4, "SAT-2 T10/1826-D revision 06"},
+    {0x1ec8, "SAT-2 T10/1826-D revision 09"},
+    {0x1eca, "SAT-2 ANSI INCITS 465-2010"},
+    {0x1ee0, "SAT-3 (no version claimed)"},
+    {0x1ee2, "SAT-3 T10/BSR INCITS 517 revision 4"},
+    {0x1ee4, "SAT-3 T10/BSR INCITS 517 revision 7"},
+    {0x1ee8, "SAT-3 ANSI INCITS 517-2015"},
+    {0x1f00, "SAT-4 (no version claimed)"},
+    {0x20a0, "SPL (no version claimed)"},
+    {0x20a3, "SPL T10/2124-D revision 6a"},
+    {0x20a5, "SPL T10/2124-D revision 7"},
+    {0x20a7, "SPL ANSI INCITS 476-2011"},
+    {0x20a8, "SPL ANSI INCITS 476-2011 + SPL AM1 INCITS 476/AM1 2012"},
+    {0x20aa, "SPL ISO/IEC 14776-261:2012"},
+    {0x20c0, "SPL-2 (no version claimed)"},
+    {0x20c2, "SPL-2 T10/BSR INCITS 505 revision 4"},
+    {0x20c4, "SPL-2 T10/BSR INCITS 505 revision 5"},
+    {0x20c8, "SPL-2 ANSI INCITS 505-2013"},
+    {0x20e0, "SPL-3 (no version claimed)"},
+    {0x20e4, "SPL-3 T10/BSR INCITS 492 revision 6"},
+    {0x20e6, "SPL-3 T10/BSR INCITS 492 revision 7"},
+    {0x20e8, "SPL-3 ANSI INCITS 492-2015"},
+    {0x2100, "SPL-4 (no version claimed)"},
+    {0x21e0, "SOP (no version claimed)"},
+    {0x21e4, "SOP T10/BSR INCITS 489 revision 4"},
+    {0x21e6, "SOP T10/BSR INCITS 489 revision 5"},
+    {0x21e8, "SOP ANSI INCITS 489-2014"},
+    {0x2200, "PQI (no version claimed)"},
+    {0x2204, "PQI T10/BSR INCITS 490 revision 6"},
+    {0x2206, "PQI T10/BSR INCITS 490 revision 7"},
+    {0x2208, "PQI ANSI INCITS 490-2014"},
+    {0x2220, "SOP-2 (no draft published)"},
+    {0x2240, "PQI-2 (no version claimed)"},
+    {0x2242, "PQI-2 T10/BSR INCITS 507 revision 01"},
+    {0xffc0, "IEEE 1667 (no version claimed)"},
+    {0xffc1, "IEEE 1667-2006"},
+    {0xffc2, "IEEE 1667-2009"},
+    {0xffff, NULL},
+};
+#else
+struct sg_version_descriptor sg_version_descriptor_arr[] = {
+    {0xffff, NULL},
+};
+#endif
diff --git a/sg3_utils/src/sg_logs.c b/sg3_utils/src/sg_logs.c
new file mode 100644
index 0000000..a2534d8
--- /dev/null
+++ b/sg3_utils/src/sg_logs.c
@@ -0,0 +1,6284 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *  Copyright (C) 2000-2016 D. Gilbert
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ * This program outputs information provided by a SCSI LOG SENSE command
+ * and in some cases issues a LOG SELECT command.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.39 20160203";    /* spc5r08 + sbc4r10 */
+
+#define MX_ALLOC_LEN (0xfffc)
+#define SHORT_RESP_LEN 128
+
+#define SUPP_PAGES_LPAGE 0x0
+#define BUFF_OVER_UNDER_LPAGE 0x1
+#define WRITE_ERR_LPAGE 0x2
+#define READ_ERR_LPAGE 0x3
+#define READ_REV_ERR_LPAGE 0x4
+#define VERIFY_ERR_LPAGE 0x5
+#define NON_MEDIUM_LPAGE 0x6
+#define LAST_N_ERR_LPAGE 0x7
+#define FORMAT_STATUS_LPAGE 0x8
+#define LAST_N_DEFERRED_LPAGE 0xb
+#define LB_PROV_LPAGE 0xc
+#define TEMPERATURE_LPAGE 0xd
+#define START_STOP_LPAGE 0xe
+#define APP_CLIENT_LPAGE 0xf
+#define SELF_TEST_LPAGE 0x10
+#define SOLID_STATE_MEDIA_LPAGE 0x11
+#define BACKGROUND_SCAN_LPAGE 0x15
+#define SAT_ATA_RESULTS_LPAGE 0x16
+#define PROTO_SPECIFIC_LPAGE 0x18
+#define STATS_LPAGE 0x19
+#define PCT_LPAGE 0x1a
+#define TAPE_ALERT_LPAGE 0x2e
+#define IE_LPAGE 0x2f
+#define NOT_SPG_SUBPG 0x0
+#define SUPP_SPGS_SUBPG 0xff
+#define LOW_GRP_STATS_SUBPG 0x1
+#define PENDING_DEFECTS_SUBPG 0x1
+#define BACKGROUND_OP_SUBPG 0x2
+#define HIGH_GRP_STATS_SUBPG 0x1f
+#define CACHE_STATS_SUBPG 0x20
+#define ENV_REPORTING_SUBPG 0x1
+#define UTILIZATION_SUBPG 0x1
+#define ENV_LIMITS_SUBPG 0x2
+#define LPS_MISALIGNMENT_SUBPG 0x3
+
+#define VENDOR_M 0x1000
+#define LTO5_M 0x2000
+
+#define PCB_STR_LEN 128
+
+#define LOG_SENSE_PROBE_ALLOC_LEN 4
+
+static uint8_t rsp_buff[MX_ALLOC_LEN + 4];
+
+static struct option long_options[] = {
+        {"All", no_argument, 0, 'A'},   /* equivalent to '-aa' */
+        {"all", no_argument, 0, 'a'},
+        {"brief", no_argument, 0, 'b'},
+        {"control", required_argument, 0, 'c'},
+        {"enumerate", no_argument, 0, 'e'},
+        {"enum_vendor", no_argument, 0, 'E'},
+        {"filter", required_argument, 0, 'f'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"in", required_argument, 0, 'i'},
+        {"list", no_argument, 0, 'l'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"name", no_argument, 0, 'n'},
+        {"new", no_argument, 0, 'N'},
+        {"no_inq", no_argument, 0, 'x'},
+        {"old", no_argument, 0, 'O'},
+        {"page", required_argument, 0, 'p'},
+        {"paramp", required_argument, 0, 'P'},
+        {"pcb", no_argument, 0, 'q'},
+        {"ppc", no_argument, 0, 'Q'},
+        {"raw", no_argument, 0, 'r'},
+        {"readonly", no_argument, 0, 'X'},
+        {"reset", no_argument, 0, 'R'},
+        {"sp", no_argument, 0, 's'},
+        {"select", no_argument, 0, 'S'},
+        {"temperature", no_argument, 0, 't'},
+        {"transport", no_argument, 0, 'T'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_all;
+    int do_brief;
+    int do_enumerate;
+    int do_enum_vendor;
+    int do_help;
+    int do_hex;
+    int do_list;
+    int do_name;
+    int do_pcb;
+    int do_ppc;
+    int do_raw;
+    int o_readonly;
+    int do_pcreset;
+    int do_select;
+    int do_sp;
+    int do_temperature;
+    int do_transport;
+    int verbose;
+    int do_version;
+    int filter;
+    int filter_given;
+    int page_control;
+    int maxlen;
+    int pg_code;
+    int subpg_code;
+    int paramp;
+    int opt_new;
+    int no_inq;
+    int dev_pdt;
+    const char * device_name;
+    const char * in_fn;
+    const char * pg_arg;
+    const struct log_elem * lep;
+};
+
+
+struct log_elem {
+    int pg_code;
+    int subpg_code;     /* only unless subpg_high>0 then this is only */
+    int subpg_high;     /* when >0 this is high end of subpage range */
+    int pdt;            /* -1 for all */
+    int flags;          /* bit mask; only VENDOR_M to start with */
+    const char * name;
+    const char * acron;
+    bool (*show_pagep)(const uint8_t * resp, int len,
+                       const struct opts_t * op);
+                        /* Returns true if done */
+};
+
+static bool show_supported_pgs_page(const uint8_t * resp, int len,
+                                    const struct opts_t * op);
+static bool show_supported_pgs_sub_page(const uint8_t * resp, int len,
+                                        const struct opts_t * op);
+static bool show_buffer_over_under_run_page(const uint8_t * resp, int len,
+                                            const struct opts_t * op);
+static bool show_error_counter_page(const uint8_t * resp, int len,
+                                    const struct opts_t * op);
+static bool show_non_medium_error_page(const uint8_t * resp, int len,
+                                       const struct opts_t * op);
+static bool show_last_n_error_page(const uint8_t * resp, int len,
+                                   const struct opts_t * op);
+static bool show_format_status_page(const uint8_t * resp, int len,
+                                    const struct opts_t * op);
+static bool show_last_n_deferred_error_page(const uint8_t * resp, int len,
+                                            const struct opts_t * op);
+static bool show_lb_provisioning_page(const uint8_t * resp, int len,
+                                      const struct opts_t * op);
+static bool show_sequential_access_page(const uint8_t * resp, int len,
+                                        const struct opts_t * op);
+static bool show_temperature_page(const uint8_t * resp, int len,
+                                  const struct opts_t * op);
+static bool show_start_stop_page(const uint8_t * resp, int len,
+                                 const struct opts_t * op);
+static bool show_utilization_page(const uint8_t * resp, int len,
+                                  const struct opts_t * op);
+static bool show_app_client_page(const uint8_t * resp, int len,
+                                 const struct opts_t * op);
+static bool show_self_test_page(const uint8_t * resp, int len,
+                                const struct opts_t * op);
+static bool show_solid_state_media_page(const uint8_t * resp, int len,
+                                        const struct opts_t * op);
+static bool show_device_stats_page(const uint8_t * resp, int len,
+                                   const struct opts_t * op);
+static bool show_media_stats_page(const uint8_t * resp, int len,
+                                  const struct opts_t * op);
+static bool show_dt_device_status_page(const uint8_t * resp, int len,
+                                       const struct opts_t * op);
+static bool show_background_scan_results_page(const uint8_t * resp, int len,
+                                              const struct opts_t * op);
+static bool show_pending_defects_page(const uint8_t * resp, int len,
+                                      const struct opts_t * op);
+static bool show_background_op_page(const uint8_t * resp, int len,
+                                    const struct opts_t * op);
+static bool show_lps_misalignment_page(const uint8_t * resp, int len,
+                                       const struct opts_t * op);
+static bool show_element_stats_page(const uint8_t * resp, int len,
+                                    const struct opts_t * op);
+static bool show_ata_pt_results_page(const uint8_t * resp, int len,
+                                     const struct opts_t * op);
+static bool show_tape_diag_data_page(const uint8_t * resp, int len,
+                                     const struct opts_t * op);
+static bool show_mchanger_diag_data_page(const uint8_t * resp, int len,
+                                         const struct opts_t * op);
+static bool show_non_volatile_cache_page(const uint8_t * resp, int len,
+                                         const struct opts_t * op);
+static bool show_volume_stats_page(const uint8_t * resp, int len,
+                                   const struct opts_t * op);
+static bool show_protocol_specific_page(const uint8_t * resp, int len,
+                                        const struct opts_t * op);
+static bool show_stats_perform_page(const uint8_t * resp, int len,
+                                    const struct opts_t * op);
+static bool show_cache_stats_page(const uint8_t * resp, int len,
+                                  const struct opts_t * op);
+static bool show_power_condition_transitions_page(const uint8_t * resp,
+                                 int len, const struct opts_t * op);
+static bool show_environmental_reporting_page(const uint8_t * resp, int len,
+                                              const struct opts_t * op);
+static bool show_environmental_limits_page(const uint8_t * resp, int len,
+                                           const struct opts_t * op);
+static bool show_data_compression_page(const uint8_t * resp, int len,
+                                       const struct opts_t * op);
+static bool show_tape_alert_ssc_page(const uint8_t * resp, int len,
+                                     const struct opts_t * op);
+static bool show_ie_page(const uint8_t * resp, int len,
+                         const struct opts_t * op);
+static bool show_tape_usage_page(const uint8_t * resp, int len,
+                                 const struct opts_t * op);
+static bool show_tape_capacity_page(const uint8_t * resp, int len,
+                                     const struct opts_t * op);
+static bool show_seagate_cache_page(const uint8_t * resp, int len,
+                                    const struct opts_t * op);
+static bool show_seagate_factory_page(const uint8_t * resp, int len,
+                                      const struct opts_t * op);
+
+/* elements in page_number/subpage_number order */
+static struct log_elem log_arr[] = {
+    {SUPP_PAGES_LPAGE, 0, 0, -1, 0, "Supported log pages", "sp",
+     show_supported_pgs_page},          /* 0, 0 */
+    {SUPP_PAGES_LPAGE, SUPP_SPGS_SUBPG, 0, -1, 0, "Supported log pages and "
+     "subpages", "ssp", show_supported_pgs_sub_page}, /* 0, 0xff */
+    {BUFF_OVER_UNDER_LPAGE, 0, 0, -1, 0, "Buffer over-run/under-run", "bou",
+     show_buffer_over_under_run_page},  /* 0x1, 0x0 */
+    {WRITE_ERR_LPAGE, 0, 0, -1, 0, "Write error", "we",
+     show_error_counter_page},          /* 0x2, 0x0 */
+    {READ_ERR_LPAGE, 0, 0, -1, 0, "Read error", "re",
+     show_error_counter_page},          /* 0x3, 0x0 */
+    {READ_REV_ERR_LPAGE, 0, 0, -1, 0, "Read reverse error", "rre",
+     show_error_counter_page},          /* 0x4, 0x0 */
+    {VERIFY_ERR_LPAGE, 0, 0, -1, 0, "Verify error", "ve",
+     show_error_counter_page},          /* 0x5, 0x0 */
+    {NON_MEDIUM_LPAGE, 0, 0, -1, 0, "Non medium", "nm",
+     show_non_medium_error_page},       /* 0x6, 0x0 */
+    {LAST_N_ERR_LPAGE, 0, 0, -1, 0, "Last n error", "lne",
+     show_last_n_error_page},           /* 0x7, 0x0 */
+    {FORMAT_STATUS_LPAGE, 0, 0, 0, 0, "Format status", "fs",
+     show_format_status_page},          /* 0x8, 0x0  SBC */
+    {LAST_N_DEFERRED_LPAGE, 0, 0, -1, 0, "Last n deferred error", "lnd",
+     show_last_n_deferred_error_page},  /* 0xb, 0x0 */
+    {LB_PROV_LPAGE, 0, 0, 0, 0, "Logical block provisioning", "lbp",
+     show_lb_provisioning_page},        /* 0xc, 0x0  SBC */
+    {0xc, 0, 0, PDT_TAPE, 0, "Sequential access device", "sad",
+     show_sequential_access_page},      /* 0xc, 0x0  SSC */
+    {TEMPERATURE_LPAGE, 0, 0, -1, 0, "Temperature", "temp",
+     show_temperature_page},            /* 0xd, 0x0 */
+    {TEMPERATURE_LPAGE, ENV_REPORTING_SUBPG, 0, -1, 0,  /* 0xd, 0x1 */
+     "Environmental reporting", "enr", show_environmental_reporting_page},
+    {TEMPERATURE_LPAGE, ENV_LIMITS_SUBPG, 0, -1, 0,     /* 0xd, 0x2 */
+     "Environmental limits", "enl", show_environmental_limits_page},
+    {START_STOP_LPAGE, 0, 0, -1, 0, "Start-stop cycle counter", "sscc",
+     show_start_stop_page},             /* 0xe, 0x0 */
+    {START_STOP_LPAGE, UTILIZATION_SUBPG, 0, 0, 0, "Utilization", "util",
+     show_utilization_page},            /* 0xe, 0x1 SBC */    /* sbc4r04 */
+    {APP_CLIENT_LPAGE, 0, 0, -1, 0, "Application client", "ac",
+     show_app_client_page},             /* 0xf, 0x0 */
+    {SELF_TEST_LPAGE, 0, 0, -1, 0, "Self test results", "str",
+     show_self_test_page},              /* 0x10, 0x0 */
+    {SOLID_STATE_MEDIA_LPAGE, 0, 0, 0, 0, "Solid state media", "ssm",
+     show_solid_state_media_page},      /* 0x11, 0x0  SBC */
+    {0x11, 0, 0, PDT_TAPE, 0, "DT Device status", "dtds",
+     show_dt_device_status_page},       /* 0x11, 0x0  SSC,ADC */
+    {0x12, 0, 0, PDT_TAPE, 0, "Tape alert response", "tar",
+     NULL},                             /* 0x12, 0x0  SSC,ADC */
+    {0x13, 0, 0, PDT_TAPE, 0, "Requested recovery", "rr",
+     NULL},                             /* 0x13, 0x0  SSC,ADC */
+    {0x14, 0, 0, PDT_TAPE, 0, "Device statistics", "ds",
+     show_device_stats_page},           /* 0x14, 0x0  SSC,ADC */
+    {0x14, 0, 0, PDT_MCHANGER, 0, "Media changer statistics", "mcs",
+     show_media_stats_page},            /* 0x14, 0x0  SMC */
+    {BACKGROUND_SCAN_LPAGE, 0, 0, 0, 0, "Background scan results", "bsr",
+     show_background_scan_results_page}, /* 0x15, 0x0  SBC */
+    {BACKGROUND_SCAN_LPAGE, BACKGROUND_OP_SUBPG, 0, 0, 0,
+     "Background operation", "bop", show_background_op_page},
+                                        /* 0x15, 0x2  SBC */
+    {BACKGROUND_SCAN_LPAGE, LPS_MISALIGNMENT_SUBPG, 0, 0, 0,
+     "LPS misalignment", "lps", show_lps_misalignment_page},
+                                        /* 0x15, 0x3  SBC-4 */
+    {0x15, 0, 0, PDT_MCHANGER, 0, "Element statistics", "els",
+     show_element_stats_page},          /* 0x15, 0x0  SMC */
+    {0x15, 0, 0, PDT_ADC, 0, "Service buffers information", "sbi",
+     NULL},                             /* 0x15, 0x0  ADC */
+    {BACKGROUND_SCAN_LPAGE, PENDING_DEFECTS_SUBPG, 0, 0, 0,
+     "Pending defects", "pd", show_pending_defects_page}, /* 0x15, 0x1  SBC */
+    {SAT_ATA_RESULTS_LPAGE, 0, 0, 0, 0, "ATA pass-through results", "aptr",
+     show_ata_pt_results_page},         /* 0x16, 0x0  SAT */
+    {0x16, 0, 0, PDT_TAPE, 0, "Tape diagnostic data", "tdd",
+     show_tape_diag_data_page},         /* 0x16, 0x0  SSC */
+    {0x16, 0, 0, PDT_MCHANGER, 0, "Media changer diagnostic data", "mcdd",
+     show_mchanger_diag_data_page},     /* 0x16, 0x0  SMC */
+    {0x17, 0, 0, 0, 0, "Non volatile cache", "nvc",
+     show_non_volatile_cache_page},     /* 0x17, 0x0  SBC */
+    {0x17, 0, 0, PDT_TAPE, 0, "Volume statistics", "vs",
+     show_volume_stats_page},           /* 0x17, 0x0  SSC */
+    {PROTO_SPECIFIC_LPAGE, 0, 0, -1, 0, "Protocol specific port", "psp",
+     show_protocol_specific_page},      /* 0x18, 0x0  */
+    {STATS_LPAGE, 0, 0, -1, 0, "General Statistics and Performance", "gsp",
+     show_stats_perform_page},          /* 0x19, 0x0  */
+    {STATS_LPAGE, 0x1, 0x1f, -1, 0, "Group Statistics and Performance", "grsp",
+     show_stats_perform_page},          /* 0x19, 0x1...0x1f  */
+    {STATS_LPAGE, 0x20, 0, -1, 0, "Cache memory statistics", "cms",
+     show_cache_stats_page},            /* 0x19, 0x20  */
+    {PCT_LPAGE, 0, 0, -1, 0, "Power condition transitions", "pct",
+     show_power_condition_transitions_page}, /* 0x1a, 0  */
+    {0x1b, 0, 0, PDT_TAPE, 0, "Data compression", "dc",
+     show_data_compression_page},       /* 0x1b, 0  SSC */
+    {0x2d, 0, 0, PDT_TAPE, 0, "Current service information", "csi",
+     NULL},                             /* 0x2d, 0  SSC */
+    {TAPE_ALERT_LPAGE, 0, 0, PDT_TAPE, 0, "Tape alert", "ta",
+     show_tape_alert_ssc_page},         /* 0x2e, 0  SSC */
+    {IE_LPAGE, 0, 0, -1, 0, "Informational exceptions", "ie",
+     show_ie_page},                     /* 0x2f, 0  */
+/* vendor specific */
+    {0x30, 0, 0, PDT_DISK, VENDOR_M, "Performance counters (Hitachi)",
+     "pc_hi", NULL},                             /* 0x30, 0  SBC */
+    {0x30, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Tape usage (lto-5, 6)", "tu_",
+     show_tape_usage_page},             /* 0x30, 0  SSC */
+    {0x31, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Tape capacity (lto-5, 6)",
+     "tc_", show_tape_capacity_page},   /* 0x31, 0  SSC */
+    {0x32, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Data compression (lto-5)",
+     "dc_", show_data_compression_page}, /* 0x32, 0  SSC; redirect to 0x1b */
+    {0x33, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Write errors (lto-5)", "we_",
+     NULL},                             /* 0x33, 0  SSC */
+    {0x34, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Read forward errors (lto-5)",
+     "rfe_", NULL},                             /* 0x34, 0  SSC */
+    {0x35, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "DT Device Error (lto-5, 6)",
+     "dtde_", NULL},                             /* 0x35, 0  SSC */
+    {0x37, 0, 0, PDT_DISK, VENDOR_M, "Cache (seagate)", "c_se",
+     show_seagate_cache_page},          /* 0x37, 0  SBC */
+    {0x37, 0, 0, PDT_DISK, VENDOR_M, "Miscellaneous (hitachi)", "mi_hi",
+     NULL},                             /* 0x37, 0  SBC */
+    {0x37, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Performance characteristics "
+     "(lto-5)", "pc_", NULL},                             /* 0x37, 0  SSC */
+    {0x38, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Blocks/bytes transferred "
+     "(lto-5)", "bbt_", NULL},                             /* 0x38, 0  SSC */
+    {0x39, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Host port 0 interface errors "
+     "(lto-5)", "hp0_", NULL},                             /* 0x39, 0  SSC */
+    {0x3a, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Drive control verification "
+     "(lto-5)", "dcv_", NULL},                             /* 0x3a, 0  SSC */
+    {0x3b, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Host port 1 interface errors "
+     "(lto-5)", "hp1_", NULL},                             /* 0x3b, 0  SSC */
+    {0x3c, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Drive usage information "
+     "(lto-5)", "dui_", NULL},                             /* 0x3c, 0  SSC */
+    {0x3d, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Subsystem statistics (lto-5)",
+     "ss_", NULL},                             /* 0x3d, 0  SSC */
+    {0x3e, 0, 0, PDT_DISK, VENDOR_M, "Factory (seagate)", "f_se",
+     show_seagate_factory_page},        /* 0x3e, 0  SBC */
+    {0x3e, 0, 0, PDT_DISK, VENDOR_M, "Factory (hitachi)", "f_hi",
+     NULL},                             /* 0x3e, 0  SBC */
+    {0x3e, 0, 0, PDT_TAPE, VENDOR_M | LTO5_M, "Device Status (lto-5, 6)",
+     "ds_", NULL},                             /* 0x3e, 0  SSC */
+
+    {-1, -1, -1, -1, -1, NULL, "zzzzz", NULL},           /* end sentinel */
+};
+
+#ifdef SG_LIB_WIN32
+static int win32_spt_init_state = 0;
+static int win32_spt_curr_state = 0;
+#endif
+
+
+static void
+usage(int hval)
+{
+    if (1 == hval) {
+        pr2serr(
+           "Usage: sg_logs [-All] [--all] [--brief] [--control=PC] "
+           "[--enumerate]\n"
+           "               [--enum_vendor] [--filter=FL] [--help] [--hex] "
+           "[--in=FN]\n"
+           "               [--list] [--no_inq] [--maxlen=LEN] [--name] "
+           "[--page=PG]\n"
+           "               [--paramp=PP] [--pcb] [--ppc] [--raw] "
+           "[--readonly]\n"
+           "               [--reset] [--select] [--sp] [--temperature] "
+           "[--transport]\n"
+           "               [--verbose] [--version] DEVICE\n"
+           "  where the main options are:\n"
+           "    --All|-A        fetch and decode all log pages and "
+           "subpages\n"
+           "    --all|-a        fetch and decode all log pages, but not "
+           "subpages; use\n"
+           "                    twice to fetch and decode all log pages "
+           "and subpages\n"
+           "    --brief|-b      shorten the output of some log pages\n"
+           "    --enumerate|-e    enumerate known pages, ignore DEVICE. "
+           "Sort order,\n"
+           "                      '-e': all by acronym; '-ee': non-vendor "
+           "by acronym;\n"
+           "                      '-eee': all numerically; '-eeee': "
+           "non-v numerically\n"
+           "    --enum_vendor|-E    enumerate known specific vendor pages "
+           "only\n"
+           "    --filter=FL|-f FL    FL is parameter code to display (def: "
+           "all);\n"
+           "                         with '-e' then FL>=0 enumerate that "
+           "pdt + spc\n"
+           "                         FL=-1 all (default), FL=-2 spc only\n"
+           "    --help|-h       print usage message then exit. Use twice "
+           "for more help\n"
+           "    --hex|-H        output response in hex (default: decode if "
+           "known)\n"
+           "    --in=FN|-i FN    FN is a filename containing a log page "
+           "in ASCII hex\n"
+           "                     or binary if --raw also given.\n"
+           "    --page=PG|-p PG    PG is either log page acronym, PGN or "
+           "PGN,SPGN\n"
+           "                       where (S)PGN is a (sub) page number\n");
+        pr2serr(
+           "    --raw|-r        either output response in binary to stdout "
+           "or, if\n"
+           "                    '--in=FN' is given, FN is decoded as "
+           "binary\n"
+           "    --temperature|-t    decode temperature (log page 0xd or "
+           "0x2f)\n"
+           "    --transport|-T    decode transport (protocol specific port "
+           "0x18) page\n"
+           "    --verbose|-v    increase verbosity\n\n"
+           "Performs a SCSI LOG SENSE (or LOG SELECT) command and decodes "
+           "the response.\nIf only DEVICE is given then '-p sp' (supported "
+           "pages) is assumed. Use\n'-e' to see known pages and their "
+           "acronyms. For more help use '-hh'.\n");
+    } else if (hval > 1) {
+        pr2serr(
+           "  where sg_logs' lesser used options are:\n"
+           "    --control=PC|-c PC    page control(PC) (default: 1)\n"
+           "                          0: current threshhold, 1: current "
+           "cumulative\n"
+           "                          2: default threshhold, 3: default "
+           "cumulative\n"
+           "    --list|-l       list supported log page names (equivalent to "
+           "'-p sp')\n"
+           "                    use twice to list supported log page and "
+           "subpage names\n"
+           "    --maxlen=LEN|-m LEN    max response length (def: 0 "
+           "-> everything)\n"
+           "                           when > 1 will request LEN bytes\n"
+           "    --name|-n       decode some pages into multiple name=value "
+           "lines\n"
+           "    --no_inq|-x     no initial INQUIRY output (twice: no "
+           "INQUIRY call)\n"
+           "    --old|-O        use old interface (use as first option)\n"
+           "    --paramp=PP|-P PP    parameter pointer (decimal) (def: 0)\n"
+           "    --pcb|-q        show parameter control bytes in decoded "
+           "output\n"
+           "    --ppc|-Q        set the Parameter Pointer Control (PPC) bit "
+           "(def: 0)\n"
+           "    --readonly|-X    open DEVICE read-only (def: first "
+           "read-write then if\n"
+           "                     fails try open again read-only)\n"
+           "    --reset|-R      reset log parameters (takes PC and SP into "
+           "account)\n"
+           "                    (uses PCR bit in LOG SELECT)\n"
+           "    --select|-S     perform LOG SELECT (def: LOG SENSE)\n"
+           "    --sp|-s         set the Saving Parameters (SP) bit (def: "
+           "0)\n"
+           "    --version|-V    output version string then exit\n\n"
+           "If DEVICE and --select are given, a LOG SELECT command will be "
+           "issued. If\nDEVICE is not given and '--in=FN' is given then FN "
+           "will decoded as if it\nwere a log page. Pages defined in SPC "
+           "are common to all device types.\n");
+    }
+}
+
+static void
+usage_old()
+{
+    printf("Usage: sg_logs [-a] [-A] [-b] [-c=PC] [-e] [-E] [-f=FL] [-h] "
+           "[-H]\n"
+           "               [-i=FN] [-l] [-L] [-m=LEN] [-n] [-p=PG] "
+           "[-paramp=PP]\n"
+           "               [-pcb] [-ppc] [-r] [-select] [-sp] [-t] [-T] "
+           "[-v] [-V]\n"
+           "               [-x] [-X] [-?] DEVICE\n"
+           "  where:\n"
+           "    -a     fetch and decode all log pages\n"
+           "    -A     fetch and decode all log pages and subpages\n"
+           "    -b     shorten the output of some log pages\n"
+           "    -c=PC    page control(PC) (default: 1)\n"
+           "                  0: current threshhold, 1: current cumulative\n"
+           "                  2: default threshhold, 3: default cumulative\n"
+           "    -e     enumerate known log pages\n"
+           "    -E     enumerate known vendor specific log pages only\n"
+           "    -f=FL    filter match parameter code or pdt\n"
+           "    -h     output in hex (default: decode if known)\n"
+           "    -H     output in hex (same as '-h')\n"
+           "    -i=FN    FN is a filename containing a log page "
+           "in ASCII hex.\n"
+           "    -l     list supported log page names (equivalent to "
+           "'-p=0')\n"
+           "    -L     list supported log page and subpages names "
+           "(equivalent to\n"
+           "           '-p=0,ff')\n"
+           "    -m=LEN   max response length (decimal) (def: 0 "
+           "-> everything)\n"
+           "    -n       decode some pages into multiple name=value "
+           "lines\n"
+           "    -p=PG    PG is an acronym (def: 'sp')\n"
+           "    -p=PGN    page code in hex (def: 0)\n"
+           "    -p=PGN,SPGN    page and subpage codes in hex, (defs: 0,0)\n"
+           "    -paramp=PP   (in hex) (def: 0)\n"
+           "    -pcb   show parameter control bytes in decoded "
+           "output\n");
+    printf("    -ppc   set the Parameter Pointer Control (PPC) bit "
+           "(def: 0)\n"
+           "    -r     reset log parameters (takes PC and SP into "
+           "account)\n"
+           "           (uses PCR bit in LOG SELECT)\n"
+           "    -select  perform LOG SELECT (def: LOG SENSE)\n"
+           "    -sp    set the Saving Parameters (SP) bit (def: 0)\n"
+           "    -t     outputs temperature log page (0xd)\n"
+           "    -T     outputs transport (protocol specific port) log "
+           "page (0x18)\n"
+           "    -v     increase verbosity\n"
+           "    -V     output version string\n"
+           "    -x     no initial INQUIRY output (twice: no INQUIRY call)\n"
+           "    -X     open DEVICE read-only (def: first read-write then "
+           "if fails\n"
+           "           try open again with read-only)\n"
+           "    -?     output this usage message\n\n"
+           "Performs a SCSI LOG SENSE (or LOG SELECT) command\n");
+}
+
+static int
+asort_comp(const void * lp, const void * rp)
+{
+    const struct log_elem * const * lepp =
+                (const struct log_elem * const *)lp;
+    const struct log_elem * const * repp =
+                (const struct log_elem * const *)rp;
+
+    return strcmp((*lepp)->acron, (*repp)->acron);
+}
+
+static void
+enumerate_helper(const struct log_elem * lep, int pos,
+                 const struct opts_t * op)
+{
+    char b[80];
+    char bb[80];
+    const char * cp;
+
+    if (0 == pos) {
+        if (1 == op->verbose) {
+            printf("acronym   pg[,spg]        name\n");
+            printf("===============================================\n");
+        } else if (2 == op->verbose) {
+            printf("acronym   pg[,spg]        pdt   name\n");
+            printf("===================================================\n");
+        }
+    }
+    if ((op->do_enum_vendor > 0) && !(VENDOR_M & lep->flags))
+        return;
+    if ((0 == (op->do_enumerate % 2)) && (VENDOR_M & lep->flags))
+        return;     /* if do_enumerate is even then skip vendor pages */
+    else if ((! op->filter_given) || (-1 == op->filter))
+        ;           /* otherwise enumerate all lpages if no --filter= */
+    else if (-2 == op->filter) {   /* skip non-SPC pages */
+        if (lep->pdt >= 0)
+            return;
+    } else if (-10 == op->filter) {   /* skip non-disk like pages */
+        if (sg_lib_pdt_decay(lep->pdt) != 0)
+            return;
+    } else if (-11 == op->filter) {   /* skip tape like device pages */
+        if (sg_lib_pdt_decay(lep->pdt) != 1)
+            return;
+    } else if ((op->filter >= 0) && (op->filter <= 0x1f)) {
+        if ((lep->pdt >= 0) && (lep->pdt != op->filter) &&
+            (lep->pdt != sg_lib_pdt_decay(op->filter)))
+            return;
+    }
+    if (lep->subpg_high > 0)
+        snprintf(b, sizeof(b), "0x%x,0x%x->0x%x", lep->pg_code,
+                 lep->subpg_code, lep->subpg_high);
+    else if (lep->subpg_code > 0)
+        snprintf(b, sizeof(b), "0x%x,0x%x", lep->pg_code,
+                 lep->subpg_code);
+    else
+        snprintf(b, sizeof(b), "0x%x", lep->pg_code);
+    snprintf(bb, sizeof(bb), "%-16s", b);
+    cp = (op->verbose && (! lep->show_pagep)) ? " [hex only]" : "";
+    if (op->verbose > 1) {
+        if (lep->pdt < 0)
+            printf("  %-8s%s-     %s%s\n", lep->acron, bb, lep->name, cp);
+        else
+            printf("  %-8s%s0x%02x  %s%s\n", lep->acron, bb, lep->pdt,
+                   lep->name, cp);
+    } else
+        printf("  %-8s%s%s%s\n", lep->acron, bb, lep->name, cp);
+}
+
+static void
+enumerate_pages(const struct opts_t * op)
+{
+    int k, j;
+    struct log_elem * lep;
+    struct log_elem ** lepp;
+    struct log_elem ** lep_arr;
+
+    if (op->do_enumerate < 3) { /* -e, -ee: sort by acronym */
+        for (k = 0, lep = log_arr; lep->pg_code >=0; ++lep, ++k)
+            ;
+        ++k;
+        lep_arr = (struct log_elem **)calloc(k, sizeof(struct log_elem *));
+        if (NULL == lep_arr) {
+            pr2serr("%s: out of memory\n", __func__);
+            return;
+        }
+        for (k = 0, lep = log_arr; lep->pg_code >=0; ++lep, ++k)
+            lep_arr[k] = lep;
+        lep_arr[k++] = lep;     /* put sentinel on end */
+        qsort(lep_arr, k, sizeof(struct log_elem *), asort_comp);
+        printf("Known log pages in acronym order:\n");
+        for (lepp = lep_arr, j = 0; (*lepp)->pg_code >=0; ++lepp, ++j)
+            enumerate_helper(*lepp, j, op);
+        free(lep_arr);
+    } else {    /* -eee, -eeee numeric sort (as per table) */
+        printf("Known log pages in numerical order:\n");
+        for (lep = log_arr, j = 0; lep->pg_code >=0; ++lep, ++j)
+            enumerate_helper(lep, j, op);
+    }
+}
+
+static const struct log_elem *
+acron_search(const char * acron)
+{
+    const struct log_elem * lep;
+
+    for (lep = log_arr; lep->pg_code >=0; ++lep) {
+        if (0 == strcmp(acron, lep->acron))
+            return lep;
+    }
+    return NULL;
+}
+
+static const struct log_elem *
+pg_subpg_pdt_search(int pg_code, int subpg_code, int pdt)
+{
+    const struct log_elem * lep;
+    int d_pdt;
+
+    d_pdt = sg_lib_pdt_decay(pdt);
+    for (lep = log_arr; lep->pg_code >=0; ++lep) {
+        if (pg_code == lep->pg_code) {
+            if (subpg_code == lep->subpg_code) {
+                if ((lep->pdt < 0) || (pdt == lep->pdt) || (pdt < 0))
+                    return lep;
+                else if (d_pdt == lep->pdt)
+                    return lep;
+                else if (pdt == sg_lib_pdt_decay(lep->pdt))
+                    return lep;
+            } else if ((lep->subpg_high > 0) &&
+                     (subpg_code > lep->subpg_code) &&
+                     (subpg_code <= lep->subpg_high))
+                return lep;
+        }
+    }
+    return NULL;
+}
+
+static void
+usage_for(int hval, const struct opts_t * op)
+{
+    if (op->opt_new)
+        usage(hval);
+    else
+        usage_old();
+}
+
+/* Processes command line options according to new option format. Returns
+ * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
+static int
+process_cl_new(struct opts_t * op, int argc, char * argv[])
+{
+    int c, n;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "aAbc:eEf:hHi:lLm:nNOp:P:qQrRsStTvVxX",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            ++op->do_all;
+            break;
+        case 'A':    /* not documented: compatibility with old interface */
+            op->do_all += 2;
+            break;
+        case 'b':
+            ++op->do_brief;
+            break;
+        case 'c':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 3)) {
+                pr2serr("bad argument to '--control='\n");
+                usage(2);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->page_control = n;
+            break;
+        case 'e':
+            ++op->do_enumerate;
+            break;
+        case 'E':
+            ++op->do_enum_vendor;
+            break;
+        case 'f':
+            if ('-' == optarg[0]) {
+                n = sg_get_num(optarg + 1);
+                if ((n < 0) || (n > 0x30)) {
+                    pr2serr("bad negated argument to '--filter='\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->filter = -n;
+            } else {
+                n = sg_get_num(optarg);
+                if ((n < 0) || (n > 0xffff)) {
+                    pr2serr("bad argument to '--filter='\n");
+                    usage(1);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->filter = n;
+            }
+            ++op->filter_given;
+            break;
+        case 'h':
+        case '?':
+            ++op->do_help;
+            break;
+        case 'H':
+            ++op->do_hex;
+            break;
+        case 'i':
+            op->in_fn = optarg;
+            break;
+        case 'l':
+            ++op->do_list;
+            break;
+        case 'L':
+            op->do_list += 2;
+            break;
+        case 'm':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (1 == n) || (n > 0xffff)) {
+                pr2serr("bad argument to '--maxlen=', from 2 to 65535 "
+                        "(inclusive) expected\n");
+                usage(2);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->maxlen = n;
+            break;
+        case 'n':
+            ++op->do_name;
+            break;
+        case 'N':
+            break;      /* ignore */
+        case 'O':
+            op->opt_new = 0;
+            return 0;
+        case 'p':
+            op->pg_arg = optarg;
+            break;
+        case 'P':
+            n = sg_get_num(optarg);
+            if (n < 0) {
+                pr2serr("bad argument to '--paramp='\n");
+                usage(2);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->paramp = n;
+            break;
+        case 'q':
+            ++op->do_pcb;
+            break;
+        case 'Q':       /* N.B. PPC bit obsoleted in SPC-4 rev 18 */
+            ++op->do_ppc;
+            break;
+        case 'r':
+            ++op->do_raw;
+            break;
+        case 'R':
+            ++op->do_pcreset;
+            ++op->do_select;
+            break;
+        case 's':
+            ++op->do_sp;
+            break;
+        case 'S':
+            ++op->do_select;
+            break;
+        case 't':
+            ++op->do_temperature;
+            break;
+        case 'T':
+            ++op->do_transport;
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            ++op->do_version;
+            break;
+        case 'x':
+            ++op->no_inq;
+            break;
+        case 'X':
+            ++op->o_readonly;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (op->do_help)
+                break;
+            usage(1);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage(1);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+/* Processes command line options according to old option format. Returns
+ * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
+static int
+process_cl_old(struct opts_t * op, int argc, char * argv[])
+{
+    int k, jmp_out, plen, num, n;
+    unsigned int u, uu;
+    const char * cp;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case 'a':
+                    ++op->do_all;
+                    break;
+                case 'A':
+                    op->do_all += 2;
+                    break;
+                case 'b':
+                    ++op->do_brief;
+                    break;
+                case 'e':
+                    ++op->do_enumerate;
+                    break;
+                case 'E':
+                    ++op->do_enum_vendor;
+                    break;
+                case 'h':
+                case 'H':
+                    ++op->do_hex;
+                    break;
+                case 'l':
+                    ++op->do_list;
+                    break;
+                case 'L':
+                    op->do_list += 2;
+                    break;
+                case 'n':
+                    ++op->do_name;
+                    break;
+                case 'N':
+                    op->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case 'r':
+                    op->do_pcreset = 1;
+                    op->do_select = 1;
+                    break;
+                case 't':
+                    ++op->do_temperature;
+                    break;
+                case 'T':
+                    ++op->do_transport;
+                    break;
+                case 'v':
+                    ++op->verbose;
+                    break;
+                case 'V':
+                    ++op->do_version;
+                    break;
+                case 'x':
+                    ++op->no_inq;
+                    break;
+                case 'X':
+                    ++op->o_readonly;
+                    break;
+                case '?':
+                    ++op->do_help;
+                    break;
+                case '-':
+                    ++cp;
+                    jmp_out = 1;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            if (0 == strncmp("c=", cp, 2)) {
+                num = sscanf(cp + 2, "%6x", &u);
+                if ((1 != num) || (u > 3)) {
+                    pr2serr("Bad page control after '-c=' option [0..3]\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->page_control = u;
+            } else if (0 == strncmp("f=", cp, 2)) {
+                n = sg_get_num(cp + 2);
+                if ((n < 0) || (n > 0xffff)) {
+                    pr2serr("Bad argument after '-f=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->filter = n;
+                ++op->filter_given;
+            } else if (0 == strncmp("i=", cp, 2))
+                op->in_fn = cp + 2;
+            else if (0 == strncmp("m=", cp, 2)) {
+                num = sscanf(cp + 2, "%8d", &n);
+                if ((1 != num) || (n < 0) || (n > MX_ALLOC_LEN)) {
+                    pr2serr("Bad maximum response length after '-m=' "
+                            "option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->maxlen = n;
+            } else if (0 == strncmp("p=", cp, 2)) {
+                const char * ccp = cp + 2;
+                char * xp;
+                const struct log_elem * lep;
+                char b[80];
+
+                if (isalpha(ccp[0])) {
+                    if (strlen(ccp) >= (sizeof(b) - 1)) {
+                        pr2serr("argument to '-p=' is too long\n");
+                        return SG_LIB_SYNTAX_ERROR;
+                    }
+                    strcpy(b, ccp);
+                    xp = (char *)strchr(b, ',');
+                    if (xp)
+                        *xp = '\0';
+                    lep = acron_search(b);
+                    if (NULL == lep) {
+                        pr2serr("bad argument to '--page=' no acronyn match "
+                                "to '%s'\n", b);
+                        pr2serr("  Try using '-e' or'-ee' to see available "
+                                "acronyns\n");
+                        return SG_LIB_SYNTAX_ERROR;
+                    }
+                    op->lep = lep;
+                    op->pg_code = lep->pg_code;
+                    if (xp) {
+                        n = sg_get_num_nomult(xp + 1);
+                        if ((n < 0) || (n > 255)) {
+                            pr2serr("Bad second value in argument to "
+                                    "'--page='\n");
+                            return SG_LIB_SYNTAX_ERROR;
+                        }
+                        op->subpg_code = n;
+                    } else
+                        op->subpg_code = lep->subpg_code;
+                } else {
+                    /* numeric arg: either 'pg_num' or 'pg_num,subpg_num' */
+                    if (NULL == strchr(cp + 2, ',')) {
+                        num = sscanf(cp + 2, "%6x", &u);
+                        if ((1 != num) || (u > 63)) {
+                            pr2serr("Bad page code value after '-p=' "
+                                    "option\n");
+                            usage_old();
+                            return SG_LIB_SYNTAX_ERROR;
+                        }
+                        op->pg_code = u;
+                    } else if (2 == sscanf(cp + 2, "%4x,%4x", &u, &uu)) {
+                        if (uu > 255) {
+                            pr2serr("Bad sub page code value after '-p=' "
+                                    "option\n");
+                            usage_old();
+                            return SG_LIB_SYNTAX_ERROR;
+                        }
+                        op->pg_code = u;
+                        op->subpg_code = uu;
+                    } else {
+                        pr2serr("Bad page code, subpage code sequence after "
+                                "'-p=' option\n");
+                        usage_old();
+                        return SG_LIB_SYNTAX_ERROR;
+                    }
+                }
+            } else if (0 == strncmp("paramp=", cp, 7)) {
+                num = sscanf(cp + 7, "%8x", &u);
+                if ((1 != num) || (u > 0xffff)) {
+                    pr2serr("Bad parameter pointer after '-paramp=' "
+                            "option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->paramp = u;
+            } else if (0 == strncmp("pcb", cp, 3))
+                op->do_pcb = 1;
+            else if (0 == strncmp("ppc", cp, 3))
+                op->do_ppc = 1;
+            else if (0 == strncmp("select", cp, 6))
+                op->do_select = 1;
+            else if (0 == strncmp("sp", cp, 2))
+                op->do_sp = 1;
+            else if (0 == strncmp("old", cp, 3))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage_old();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == op->device_name)
+            op->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not expecting: %s\n",
+                    op->device_name, cp);
+            usage_old();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+/* Process command line options. First check using new option format unless
+ * the SG3_UTILS_OLD_OPTS environment variable is defined which causes the
+ * old option format to be checked first. Both new and old format can be
+ * countermanded by a '-O' and '-N' options respectively. As soon as either
+ * of these options is detected (when processing the other format), processing
+ * stops and is restarted using the other format. Clear? */
+static int
+process_cl(struct opts_t * op, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        op->opt_new = 0;
+        res = process_cl_old(op, argc, argv);
+        if ((0 == res) && op->opt_new)
+            res = process_cl_new(op, argc, argv);
+    } else {
+        op->opt_new = 1;
+        res = process_cl_new(op, argc, argv);
+        if ((0 == res) && (0 == op->opt_new))
+            res = process_cl_old(op, argc, argv);
+    }
+    return res;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* Assumes an integral numbers of bytes pointed to by 'xp' of length
+ * 'num_bytes' given. [So the number of bits modulo 8 must be zero.]
+ * Returns true if all bytes are 0xff (which is the same as all bits
+ * being set), else returns false. */
+static bool
+all_bits_set(const uint8_t * xp, int num_bytes)
+{
+    for ( ; num_bytes > 0; ++xp, --num_bytes) {
+        if (0xff != *xp)
+            return false;
+    }
+    return true;
+}
+
+static char *
+num_or_unknown(const uint8_t * xp, int num_bytes, bool in_hex, char * b,
+               int blen)
+{
+    if (all_bits_set(xp, num_bytes))
+        snprintf(b, blen, "unknown");
+    else {
+        uint64_t num = sg_get_unaligned_be(num_bytes, xp);
+
+        if (in_hex)
+            snprintf(b, blen, "0x%" PRIx64, num);
+        else
+            snprintf(b, blen, "%" PRIu64, num);
+    }
+    return b;
+}
+
+/* Read ASCII hex bytes or binary from fname (a file named '-' taken as
+ * stdin). If reading ASCII hex then there should be either one entry per
+ * line or a comma, space or tab separated list of bytes. If no_space is
+ * set then a string of ACSII hex digits is expected, 2 per byte. Everything
+ * from and including a '#' on a line is ignored. Returns 0 if ok, or 1 if
+ * error. */
+static int
+f2hex_arr(const char * fname, int as_binary, int no_space,
+          uint8_t * mp_arr, int * mp_arr_len, int max_arr_len)
+{
+    int fn_len, in_len, k, j, m, split_line, fd, has_stdin;
+    unsigned int h;
+    const char * lcp;
+    FILE * fp;
+    char line[512];
+    char carry_over[4];
+    int off = 0;
+
+    if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len))
+        return 1;
+    fn_len = strlen(fname);
+    if (0 == fn_len)
+        return 1;
+    has_stdin = ((1 == fn_len) && ('-' == fname[0]));  /* read from stdin */
+    if (as_binary) {
+        if (has_stdin) {
+            fd = STDIN_FILENO;
+                if (sg_set_binary_mode(STDIN_FILENO) < 0)
+                    perror("sg_set_binary_mode");
+        } else {
+            fd = open(fname, O_RDONLY);
+            if (fd < 0) {
+                pr2serr("unable to open binary file %s: %s\n", fname,
+                         safe_strerror(errno));
+                return 1;
+            } else if (sg_set_binary_mode(fd) < 0)
+                perror("sg_set_binary_mode");
+        }
+        k = read(fd, mp_arr, max_arr_len);
+        if (k <= 0) {
+            if (0 == k)
+                pr2serr("read 0 bytes from binary file %s\n", fname);
+            else
+                pr2serr("read from binary file %s: %s\n", fname,
+                        safe_strerror(errno));
+            if (! has_stdin)
+                close(fd);
+            return 1;
+        }
+        *mp_arr_len = k;
+        if (! has_stdin)
+            close(fd);
+        return 0;
+    } else {    /* So read the file as ASCII hex */
+        if (has_stdin)
+            fp = stdin;
+        else {
+            fp = fopen(fname, "r");
+            if (NULL == fp) {
+                pr2serr("Unable to open %s for reading\n", fname);
+                return 1;
+            }
+        }
+    }
+
+    carry_over[0] = 0;
+    for (j = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+                split_line = 0;
+            } else
+                split_line = 1;
+        }
+        if (in_len < 1) {
+            carry_over[0] = 0;
+            continue;
+        }
+        if (carry_over[0]) {
+            if (isxdigit(line[0])) {
+                carry_over[1] = line[0];
+                carry_over[2] = '\0';
+                if (1 == sscanf(carry_over, "%4x", &h))
+                    mp_arr[off - 1] = h;       /* back up and overwrite */
+                else {
+                    pr2serr("%s: carry_over error ['%s'] around line %d\n",
+                            __func__, carry_over, j + 1);
+                    goto bad;
+                }
+                lcp = line + 1;
+                --in_len;
+            } else
+                lcp = line;
+            carry_over[0] = 0;
+        } else
+            lcp = line;
+
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+        if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) {
+            pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
+                    j + 1, m + k + 1);
+            goto bad;
+        }
+        if (no_space) {
+            for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
+                 ++k, lcp += 2) {
+                if (1 != sscanf(lcp, "%2x", &h)) {
+                    pr2serr("%s: bad hex number in line %d, pos %d\n",
+                            __func__, j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+                if ((off + k) >= max_arr_len) {
+                    pr2serr("%s: array length exceeded\n", __func__);
+                    goto bad;
+                }
+                mp_arr[off + k] = h;
+            }
+            if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
+                carry_over[0] = *lcp;
+            off += k;
+        } else {
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%4x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("%s: hex number larger than 0xff in line %d, "
+                                "pos %d\n", __func__, j + 1,
+                                (int)(lcp - line + 1));
+                        goto bad;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("%s: array length exceeded\n", __func__);
+                        goto bad;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if (('#' == *lcp) || ('\r' == *lcp)) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("%s: error in line %d, at pos %d\n", __func__,
+                            j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+            }
+            off += (k + 1);
+        }
+    }
+    *mp_arr_len = off;
+    if (stdin != fp)
+        fclose(fp);
+    return 0;
+bad:
+    if (stdin != fp)
+        fclose(fp);
+    return 1;
+}
+
+
+/* Call LOG SENSE twice: the first time ask for 4 byte response to determine
+   actual length of response; then a second time requesting the
+   min(actual_len, mx_resp_len) bytes. If the calculated length for the
+   second fetch is odd then it is incremented (perhaps should be made modulo
+   4 in the future for SAS). Returns 0 if ok, SG_LIB_CAT_INVALID_OP for
+   log_sense not supported, SG_LIB_CAT_ILLEGAL_REQ for bad field in log sense
+   command, SG_LIB_CAT_NOT_READY, SG_LIB_CAT_UNIT_ATTENTION,
+   SG_LIB_CAT_ABORTED_COMMAND and -1 for other errors. */
+static int
+do_logs(int sg_fd, uint8_t * resp, int mx_resp_len,
+        const struct opts_t * op)
+{
+    int actual_len, res, vb;
+
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+    if (0 == win32_spt_init_state) {
+        if (win32_spt_curr_state) {
+            if (mx_resp_len < 16384) {
+                scsi_pt_win32_direct(0);
+                win32_spt_curr_state = 0;
+            }
+        } else {
+            if (mx_resp_len >= 16384) {
+                scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT direct */);
+                win32_spt_curr_state = 1;
+            }
+        }
+    }
+#endif
+#endif
+    memset(resp, 0, mx_resp_len);
+    vb = op->verbose;
+    if (op->maxlen > 1)
+        actual_len = mx_resp_len;
+    else {
+        if ((res = sg_ll_log_sense(sg_fd, op->do_ppc, op->do_sp,
+                                   op->page_control, op->pg_code,
+                                   op->subpg_code, op->paramp,
+                                   resp, LOG_SENSE_PROBE_ALLOC_LEN,
+                                   1 /* noisy */, vb)))
+            return res;
+        actual_len = sg_get_unaligned_be16(resp + 2) + 4;
+        if ((0 == op->do_raw) && (vb > 1)) {
+            pr2serr("  Log sense (find length) response:\n");
+            dStrHexErr((const char *)resp, LOG_SENSE_PROBE_ALLOC_LEN, 1);
+            pr2serr("  hence calculated response length=%d\n", actual_len);
+        }
+        if (op->pg_code != (0x3f & resp[0])) {
+            if (vb)
+                pr2serr("Page code does not appear in first byte of "
+                        "response so it's suspect\n");
+            if (actual_len > 0x40) {
+                actual_len = 0x40;
+                if (vb)
+                    pr2serr("Trim response length to 64 bytes due to "
+                            "suspect response format\n");
+            }
+        }
+        /* Some HBAs don't like odd transfer lengths */
+        if (actual_len % 2)
+            actual_len += 1;
+        if (actual_len > mx_resp_len)
+            actual_len = mx_resp_len;
+    }
+    if ((res = sg_ll_log_sense(sg_fd, op->do_ppc, op->do_sp,
+                               op->page_control, op->pg_code,
+                               op->subpg_code, op->paramp,
+                               resp, actual_len, 1 /* noisy */, vb)))
+        return res;
+    if ((0 == op->do_raw) && (vb > 1)) {
+        pr2serr("  Log sense response:\n");
+        dStrHexErr((const char *)resp, actual_len, 1);
+    }
+    return 0;
+}
+
+/* DS made obsolete in spc4r03; TMC and ETC made obsolete in spc5r03. */
+static void
+get_pcb_str(int pcb, char * outp, int maxoutlen)
+{
+    char buff[PCB_STR_LEN];
+    int n;
+
+    n = sprintf(buff, "du=%d [ds=%d] tsd=%d [etc=%d] ", ((pcb & 0x80) ? 1 : 0),
+                ((pcb & 0x40) ? 1 : 0), ((pcb & 0x20) ? 1 : 0),
+                ((pcb & 0x10) ? 1 : 0));
+    if (pcb & 0x10)
+        n += sprintf(buff + n, "[tmc=%d] ", ((pcb & 0xc) >> 2));
+#if 1
+    n += sprintf(buff + n, "format+linking=%d  [0x%.2x]", pcb & 3,
+                 pcb);
+#else
+    if (pcb & 0x1)
+        n += sprintf(buff + n, "lbin=%d ", ((pcb & 0x2) >> 1));
+    n += sprintf(buff + n, "lp=%d  [0x%.2x]", pcb & 0x1, pcb);
+#endif
+    if (outp && (n < maxoutlen)) {
+        memcpy(outp, buff, n);
+        outp[n] = '\0';
+    } else if (outp && (maxoutlen > 0))
+        outp[0] = '\0';
+}
+
+/* SUPP_PAGES_LPAGE [0x0,0x0] */
+static bool
+show_supported_pgs_page(const uint8_t * resp, int len,
+                        const struct opts_t * op)
+{
+    int num, k, pg_code;
+    const uint8_t * ucp;
+    const struct log_elem * lep;
+    char b[64];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Supported log pages  [0x0]:\n");  /* introduced: SPC-2 */
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    for (k = 0; k < num; ++k) {
+        pg_code = ucp[k];
+        snprintf(b, sizeof(b) - 1, "    0x%02x        ", pg_code);
+        lep = pg_subpg_pdt_search(pg_code, 0, op->dev_pdt);
+        if (lep) {
+            if (op->do_brief > 1)
+                printf("    %s\n", lep->name);
+            else if (op->do_brief)
+                printf("%s%s\n", b, lep->name);
+            else
+                printf("%s%s [%s]\n", b, lep->name, lep->acron);
+        } else
+            printf("%s\n", b);
+    }
+    return true;
+}
+
+/* SUPP_PAGES_LPAGE,SUPP_SPGS_SUBPG [0x0,0xff] or all subpages of a given
+ * page code: [<pg_code>,0xff] */
+static bool
+show_supported_pgs_sub_page(const uint8_t * resp, int len,
+                            const struct opts_t * op)
+{
+    int num, k, pg_code, subpg_code;
+    const uint8_t * ucp;
+    const struct log_elem * lep;
+    char b[64];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        if (op->pg_code > 0)
+            printf("Supported subpages  [0x%x, 0xff]:\n", op->pg_code);
+        else
+            printf("Supported log pages and subpages  [0x0, 0xff]:\n");
+    }
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    for (k = 0; k < num; k += 2) {
+        pg_code = ucp[k];
+        subpg_code = ucp[k + 1];
+        if (NOT_SPG_SUBPG == subpg_code)
+            snprintf(b, sizeof(b) - 1, "    0x%02x        ", pg_code);
+        else
+            snprintf(b, sizeof(b) - 1, "    0x%02x,0x%02x   ", pg_code,
+                     subpg_code);
+        lep = pg_subpg_pdt_search(pg_code, subpg_code, op->dev_pdt);
+        if (lep) {
+            if (op->do_brief > 1)
+                printf("    %s\n", lep->name);
+            else if (op->do_brief)
+                printf("%s%s\n", b, lep->name);
+            else
+                printf("%s%s [%s]\n", b, lep->name, lep->acron);
+        } else
+            printf("%s\n", b);
+    }
+    return true;
+}
+
+/* BUFF_OVER_UNDER_LPAGE [0x1]  introduced: SPC-2 */
+static bool
+show_buffer_over_under_run_page(const uint8_t * resp, int len,
+                                const struct opts_t * op)
+{
+    int num, pl, pcb, pc;
+    uint64_t count;
+    const uint8_t * ucp;
+    const char * cp;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Buffer over-run/under-run page  [0x1]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        cp = NULL;
+        pl = ucp[3] + 4;
+        count = (pl > 4) ? sg_get_unaligned_be(pl - 4, ucp + 4) : 0;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0x0:
+            cp = "under-run";
+            break;
+        case 0x1:
+            cp = "over-run";
+            break;
+        case 0x2:
+            cp = "transport under-run";
+            break;
+        case 0x3:
+            cp = "transport over-run";
+            break;
+        case 0x4:
+            cp = "transfer too slow, under-run";
+            break;
+        case 0x5:
+            cp = "transfer too slow, over-run";
+            break;
+        case 0x20:
+            cp = "command, under-run";
+            break;
+        case 0x21:
+            cp = "command, over-run";
+            break;
+        case 0x22:
+            cp = "command, transport under-run";
+            break;
+        case 0x23:
+            cp = "command, transport over-run";
+            break;
+        case 0x24:
+            cp = "command, transfer too slow, under-run";
+            break;
+        case 0x25:
+            cp = "command, transfer too slow, over-run";
+            break;
+        case 0x40:
+            cp = "I_T nexus, under-run";
+            break;
+        case 0x41:
+            cp = "I_T nexus, over-run";
+            break;
+        case 0x42:
+            cp = "I_T nexus, transport under-run";
+            break;
+        case 0x43:
+            cp = "I_T nexus, transport over-run";
+            break;
+        case 0x44:
+            cp = "I_T nexus, transfer too slow, under-run";
+            break;
+        case 0x45:
+            cp = "I_T nexus, transfer too slow, over-run";
+            break;
+        case 0x80:
+            cp = "time, under-run";
+            break;
+        case 0x81:
+            cp = "time, over-run";
+            break;
+        case 0x82:
+            cp = "time, transport under-run";
+            break;
+        case 0x83:
+            cp = "time, transport over-run";
+            break;
+        case 0x84:
+            cp = "time, transfer too slow, under-run";
+            break;
+        case 0x85:
+            cp = "time, transfer too slow, over-run";
+            break;
+        default:
+            printf("  undefined parameter code [0x%x], count = %" PRIu64 "",
+                   pc, count);
+            break;
+        }
+        if (cp)
+            printf("  %s = %" PRIu64 "", cp, count);
+
+        if (op->do_pcb) {
+            pcb = ucp[2];
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* WRITE_ERR_LPAGE; READ_ERR_LPAGE; READ_REV_ERR_LPAGE; VERIFY_ERR_LPAGE */
+/* [0x2, 0x3, 0x4, 0x5]  introduced: SPC-3 */
+static bool
+show_error_counter_page(const uint8_t * resp, int len,
+                        const struct opts_t * op)
+{
+    int num, pl, pc, pcb, pg_code;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    pg_code = resp[0] & 0x3f;
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        switch(pg_code) {
+        case WRITE_ERR_LPAGE:
+            printf("Write error counter page  [0x%x]\n", pg_code);
+            break;
+        case READ_ERR_LPAGE:
+            printf("Read error counter page  [0x%x]\n", pg_code);
+            break;
+        case READ_REV_ERR_LPAGE:
+            printf("Read Reverse error counter page  [0x%x]\n",
+                   pg_code);
+            break;
+        case VERIFY_ERR_LPAGE:
+            printf("Verify error counter page  [0x%x]\n", pg_code);
+            break;
+        default:
+            pr2serr("expecting error counter page, got page = 0x%x\n",
+                    resp[0]);
+            return false;
+        }
+    }
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0: printf("  Errors corrected without substantial delay"); break;
+        case 1: printf("  Errors corrected with possible delays"); break;
+        case 2: printf("  Total rewrites or rereads"); break;
+        case 3: printf("  Total errors corrected"); break;
+        case 4: printf("  Total times correction algorithm processed"); break;
+        case 5: printf("  Total bytes processed"); break;
+        case 6: printf("  Total uncorrected errors"); break;
+        case 0x8009: printf("  Track following errors [Hitachi]"); break;
+        case 0x8015: printf("  Positioning errors [Hitachi]"); break;
+        default: printf("  Reserved or vendor specific [0x%x]", pc); break;
+        }
+        printf(" = %" PRIu64 "", sg_get_unaligned_be(pl - 4, ucp + 4));
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* NON_MEDIUM_LPAGE [0x6]  introduced: SPC-2 */
+static bool
+show_non_medium_error_page(const uint8_t * resp, int len,
+                           const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Non-medium error page  [0x6]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0:
+            printf("  Non-medium error count");
+            break;
+        default:
+            if (pc <= 0x7fff)
+                printf("  Reserved [0x%x]", pc);
+            else
+                printf("  Vendor specific [0x%x]", pc);
+            break;
+        }
+        printf(" = %" PRIu64 "", sg_get_unaligned_be(pl - 4, ucp + 4));
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* PCT_LPAGE [0x1a]  introduced: SPC-4 */
+static bool
+show_power_condition_transitions_page(const uint8_t * resp, int len,
+                                      const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Power condition transitions page  [0x1a]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0:
+            printf("  Accumulated transitions to active"); break;
+        case 1:
+            printf("  Accumulated transitions to idle_a"); break;
+        case 2:
+            printf("  Accumulated transitions to idle_b"); break;
+        case 3:
+            printf("  Accumulated transitions to idle_c"); break;
+        case 8:
+            printf("  Accumulated transitions to standby_z"); break;
+        case 9:
+            printf("  Accumulated transitions to standby_y"); break;
+        default:
+            printf("  Reserved [0x%x]", pc);
+        }
+        printf(" = %" PRIu64 "", sg_get_unaligned_be(pl - 4, ucp + 4));
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+static char *
+temperature_str(int8_t t, bool reporting, char * b, int blen)
+{
+    if (-128 == t) {
+        if (reporting)
+            snprintf(b, blen, "not available");
+        else
+            snprintf(b, blen, "no limit");
+    } else
+        snprintf(b, blen, "%d C", t);
+    return b;
+}
+
+static char *
+humidity_str(uint8_t h, bool reporting, char * b, int blen)
+{
+    if (255 == h) {
+        if (reporting)
+            snprintf(b, blen, "not available");
+        else
+            snprintf(b, blen, "no limit");
+    } else if (h <= 100)
+        snprintf(b, blen, "%u %%", h);
+    else
+        snprintf(b, blen, "reserved value [%u]", h);
+    return b;
+}
+
+/* ENV_REPORTING_SUBPG [0xd,0x1]  introduced: SPC-5 (rev 02) */
+static bool
+show_environmental_reporting_page(const uint8_t * resp, int len,
+                                  const struct opts_t * op)
+{
+    int num, pl, pc, pcb, blen;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+    char b[32];
+
+    blen = sizeof(b);
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Environmental reporting page  [0xd,0x1]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        if (pc < 0x100) {
+            if (pl < 12)  {
+                printf("  <<expect parameter 0x%x to be at least 12 bytes "
+                       "long, got %d, skip>>\n", pc, pl);
+                goto skip;
+            }
+            printf("  Temperature: %s\n",
+                   temperature_str(ucp[5], true, b, blen));
+            printf("  Lifetime maximum temperature: %s\n",
+                   temperature_str(ucp[6], true, b, blen));
+            printf("  Lifetime minimum temperature: %s\n",
+                   temperature_str(ucp[7], true, b, blen));
+            printf("  Maximum temperature since power on: %s\n",
+                   temperature_str(ucp[8], true, b, blen));
+            printf("  Minimum temperature since power on: %s\n",
+                   temperature_str(ucp[9], true, b, blen));
+        } else if (pc < 0x200) {
+            printf("  Relative humidity: %s\n",
+                   humidity_str(ucp[5], true, b, blen));
+            printf("  Lifetime maximum relative humidity: %s\n",
+                   humidity_str(ucp[6], true, b, blen));
+            printf("  Lifetime minimum relative humidity: %s\n",
+                   humidity_str(ucp[7], true, b, blen));
+            printf("  Maximum relative humidity since power on: %s\n",
+                   humidity_str(ucp[8], true, b, blen));
+            printf("  Minimum relative humidity since power on: %s\n",
+                   humidity_str(ucp[9], true, b, blen));
+        } else
+            printf("  <<unexpect parameter code 0x%x\n", pc);
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* ENV_LIMITS_SUBPG [0xd,0x2]  introduced: SPC-5 (rev 02) */
+static bool
+show_environmental_limits_page(const uint8_t * resp, int len,
+                               const struct opts_t * op)
+{
+    int num, pl, pc, pcb, blen;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+    char b[32];
+
+    blen = sizeof(b);
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Environmental limits page  [0xd,0x2]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        if (pc < 0x100) {
+            if (pl < 12)  {
+                printf("  <<expect parameter 0x%x to be at least 12 bytes "
+                       "long, got %d, skip>>\n", pc, pl);
+                goto skip;
+            }
+            printf("  High critical temperature limit trigger: %s\n",
+                   temperature_str(ucp[4], false, b, blen));
+            printf("  High critical temperature limit reset: %s\n",
+                   temperature_str(ucp[5], false, b, blen));
+            printf("  Low critical temperature limit reset: %s\n",
+                   temperature_str(ucp[6], false, b, blen));
+            printf("  Low critical temperature limit trigger: %s\n",
+                   temperature_str(ucp[7], false, b, blen));
+            printf("  High operating temperature limit trigger: %s\n",
+                   temperature_str(ucp[8], false, b, blen));
+            printf("  High operating temperature limit reset: %s\n",
+                   temperature_str(ucp[9], false, b, blen));
+            printf("  Low operating temperature limit reset: %s\n",
+                   temperature_str(ucp[10], false, b, blen));
+            printf("  Low operating temperature limit trigger: %s\n",
+                   temperature_str(ucp[11], false, b, blen));
+        } else if (pc < 0x200) {
+            printf("  High critical relative humidity limit trigger: %s\n",
+                   humidity_str(ucp[4], false, b, blen));
+            printf("  High critical relative humidity limit reset: %s\n",
+                   humidity_str(ucp[5], false, b, blen));
+            printf("  Low critical relative humidity limit reset: %s\n",
+                   humidity_str(ucp[6], false, b, blen));
+            printf("  Low critical relative humidity limit trigger: %s\n",
+                   humidity_str(ucp[7], false, b, blen));
+            printf("  High operating relative humidity limit trigger: %s\n",
+                   humidity_str(ucp[8], false, b, blen));
+            printf("  High operating relative humidity limit reset: %s\n",
+                   humidity_str(ucp[9], false, b, blen));
+            printf("  Low operating relative humidity limit reset: %s\n",
+                   humidity_str(ucp[10], false, b, blen));
+            printf("  Low operating relative humidity limit trigger: %s\n",
+                   humidity_str(ucp[11], false, b, blen));
+        } else
+            printf("  <<unexpect parameter code 0x%x\n", pc);
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* Tape usage: Vendor specific (LTO-5 and LTO-6): 0x30 */
+static bool
+show_tape_usage_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int k, num, extra, pc, pcb;
+    unsigned int n;
+    uint64_t ull;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        pr2serr("badly formed tape usage page\n");
+        return false;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Tape usage page  (LTO-5 and LTO-6 specific) [0x30]\n");
+    for (k = num; k > 0; k -= extra, ucp += extra) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        extra = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, extra);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, extra,
+                        ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        ull = n = 0;
+        switch (ucp[3]) {
+        case 2:
+            n = sg_get_unaligned_be16(ucp + 4);
+            break;
+        case 4:
+            n = sg_get_unaligned_be32(ucp + 4);
+            break;
+        case 8:
+            ull = sg_get_unaligned_be64(ucp + 4);
+            break;
+        }
+        switch (pc) {
+        case 0x01:
+            if (extra == 8)
+                printf("  Thread count: %u", n);
+            break;
+        case 0x02:
+            if (extra == 12)
+                printf("  Total data sets written: %" PRIu64, ull);
+            break;
+        case 0x03:
+            if (extra == 8)
+                printf("  Total write retries: %u", n);
+            break;
+        case 0x04:
+            if (extra == 6)
+                printf("  Total unrecovered write errors: %u", n);
+            break;
+        case 0x05:
+            if (extra == 6)
+                printf("  Total suspended writes: %u", n);
+            break;
+        case 0x06:
+            if (extra == 6)
+                printf("  Total fatal suspended writes: %u", n);
+            break;
+        case 0x07:
+            if (extra == 12)
+                printf("  Total data sets read: %" PRIu64, ull);
+            break;
+        case 0x08:
+            if (extra == 8)
+                printf("  Total read retries: %u", n);
+            break;
+        case 0x09:
+            if (extra == 6)
+                printf("  Total unrecovered read errors: %u", n);
+            break;
+        case 0x0a:
+            if (extra == 6)
+                printf("  Total suspended reads: %u", n);
+            break;
+        case 0x0b:
+            if (extra == 6)
+                printf("  Total fatal suspended reads: %u", n);
+            break;
+        default:
+            printf("  unknown parameter code = 0x%x, contents in "
+                   "hex:\n", pc);
+            dStrHex((const char *)ucp, extra, 1);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* Tape capacity: vendor specific (LTO-5 and LTO-6 ?): 0x31 */
+static bool
+show_tape_capacity_page(const uint8_t * resp, int len,
+                        const struct opts_t * op)
+{
+    int k, num, extra, pc, pcb;
+    unsigned int n;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        pr2serr("badly formed tape capacity page\n");
+        return false;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Tape capacity page  (LTO-5 and LTO-6 specific) [0x31]\n");
+    for (k = num; k > 0; k -= extra, ucp += extra) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        extra = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, extra);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, extra,
+                        ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        if (extra != 8)
+            continue;
+        n = sg_get_unaligned_be32(ucp + 4);
+        switch (pc) {
+        case 0x01:
+            printf("  Main partition remaining capacity (in MiB): %u", n);
+            break;
+        case 0x02:
+            printf("  Alternate partition remaining capacity (in MiB): %u", n);
+            break;
+        case 0x03:
+            printf("  Main partition maximum capacity (in MiB): %u", n);
+            break;
+        case 0x04:
+            printf("  Alternate partition maximum capacity (in MiB): %u", n);
+            break;
+        default:
+            printf("  unknown parameter code = 0x%x, contents in "
+                    "hex:\n", pc);
+            dStrHex((const char *)ucp, extra, 1);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* Data compression: originally vendor specific 0x32 (LTO-5), then
+ * ssc-4 standardizes it at 0x1b */
+static bool
+show_data_compression_page(const uint8_t * resp, int len,
+                           const struct opts_t * op)
+{
+    int k, j, pl, num, extra, pc, pcb, pg_code;
+    uint64_t n;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    pg_code = resp[0] & 0x3f;
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        pr2serr("badly formed data compression page\n");
+        return false;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        if (0x1b == pg_code)
+            printf("Data compression page  (ssc-4) [0x1b]\n");
+        else
+            printf("Data compression page  (LTO-5 specific) [0x%x]\n",
+                   pg_code);
+    }
+    for (k = num; k > 0; k -= extra, ucp += extra) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3];
+        extra = pl + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, extra);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, extra,
+                        ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        if ((0 == pl) || (pl > 8)) {
+            printf("badly formed data compression log parameter\n");
+            printf("  parameter code = 0x%x, contents in hex:\n", pc);
+            dStrHex((const char *)ucp, extra, 1);
+            goto skip_para;
+        }
+        /* variable length integer, max length 8 bytes */
+        for (j = 0, n = 0; j < pl; ++j) {
+            if (j > 0)
+                n <<= 8;
+            n |= ucp[4 + j];
+        }
+        switch (pc) {
+        case 0x00:
+            printf("  Read compression ratio x100: %" PRIu64 , n);
+            break;
+        case 0x01:
+            printf("  Write compression ratio x100: %" PRIu64 , n);
+            break;
+        case 0x02:
+            printf("  Megabytes transferred to server: %" PRIu64 , n);
+            break;
+        case 0x03:
+            printf("  Bytes transferred to server: %" PRIu64 , n);
+            break;
+        case 0x04:
+            printf("  Megabytes read from tape: %" PRIu64 , n);
+            break;
+        case 0x05:
+            printf("  Bytes read from tape: %" PRIu64 , n);
+            break;
+        case 0x06:
+            printf("  Megabytes transferred from server: %" PRIu64 , n);
+            break;
+        case 0x07:
+            printf("  Bytes transferred from server: %" PRIu64 , n);
+            break;
+        case 0x08:
+            printf("  Megabytes written to tape: %" PRIu64 , n);
+            break;
+        case 0x09:
+            printf("  Bytes written to tape: %" PRIu64 , n);
+            break;
+        case 0x100:
+            printf("  Data compression enabled: 0x%" PRIx64, n);
+            break;
+        default:
+            printf("  unknown parameter code = 0x%x, contents in "
+                    "hex:\n", pc);
+            dStrHex((const char *)ucp, extra, 1);
+            break;
+        }
+skip_para:
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* LAST_N_ERR_LPAGE [0x7]  introduced: SPC-2 */
+static bool
+show_last_n_error_page(const uint8_t * resp, int len,
+                       const struct opts_t * op)
+{
+    int k, num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        printf("No error events logged\n");
+        return true;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Last n error events page  [0x7]\n");
+    for (k = num; k > 0; k -= pl, ucp += pl) {
+        if (k < 3) {
+            printf("short Last n error events page\n");
+            return false;
+        }
+        pl = ucp[3] + 4;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        printf("  Error event %d:\n", pc);
+        if (pl > 4) {
+            if ((pcb & 0x1) && (pcb & 0x2)) {
+                printf("    [binary]:\n");
+                dStrHex((const char *)ucp + 4, pl - 4, 1);
+            } else if (pcb & 0x1)
+                printf("    %.*s\n", pl - 4, (const char *)(ucp + 4));
+            else {
+                printf("    [data counter?? (LP bit should be set)]:\n");
+                dStrHex((const char *)ucp + 4, pl - 4, 1);
+            }
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("        <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* LAST_N_DEFERRED_LPAGE [0xb]  introduced: SPC-2 */
+static bool
+show_last_n_deferred_error_page(const uint8_t * resp, int len,
+                                const struct opts_t * op)
+{
+    int k, num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        printf("No deferred errors logged\n");
+        return true;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Last n deferred errors page  [0xb]\n");
+    for (k = num; k > 0; k -= pl, ucp += pl) {
+        if (k < 3) {
+            printf("short Last n deferred errors page\n");
+            return true;
+        }
+        pl = ucp[3] + 4;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        printf("  Deferred error %d:\n", pc);
+        dStrHex((const char *)ucp + 4, pl - 4, 1);
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("        <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+static const char * self_test_code[] = {
+    "default", "background short", "background extended", "reserved",
+    "aborted background", "foreground short", "foreground extended",
+    "reserved"};
+
+static const char * self_test_result[] = {
+    "completed without error",
+    "aborted by SEND DIAGNOSTIC",
+    "aborted other than by SEND DIAGNOSTIC",
+    "unknown error, unable to complete",
+    "self test completed with failure in test segment (which one unknown)",
+    "first segment in self test failed",
+    "second segment in self test failed",
+    "another segment in self test failed",
+    "reserved", "reserved", "reserved", "reserved", "reserved", "reserved",
+    "reserved",
+    "self test in progress"};
+
+/* SELF_TEST_LPAGE [0x10]  introduced: SPC-3 */
+static bool
+show_self_test_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int k, num, n, res, pc, pl, pcb;
+    unsigned int v;
+    const uint8_t * ucp;
+    uint64_t ull;
+    char pcb_str[PCB_STR_LEN];
+    char b[80];
+
+    num = len - 4;
+    if (num < 0x190) {
+        pr2serr("short self-test results page [length 0x%x rather than "
+                "0x190 bytes]\n", num);
+        return true;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Self-test results page  [0x10]\n");
+    for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        n = sg_get_unaligned_be16(ucp + 6);
+        if ((0 == n) && (0 == ucp[4]))
+            break;
+        printf("  Parameter code = %d, accumulated power-on hours = %d\n",
+               pc, n);
+        printf("    self-test code: %s [%d]\n",
+               self_test_code[(ucp[4] >> 5) & 0x7], (ucp[4] >> 5) & 0x7);
+        res = ucp[4] & 0xf;
+        printf("    self-test result: %s [%d]\n", self_test_result[res], res);
+        if (ucp[5])
+            printf("    self-test number = %d\n", (int)ucp[5]);
+        if (! all_bits_set(ucp + 8, 8)) {
+            ull = sg_get_unaligned_be64(ucp + 8);
+            if ((res > 0) && ( res < 0xf))
+                printf("    address of first error = 0x%" PRIx64 "\n", ull);
+        }
+        v = ucp[16] & 0xf;
+        if (v) {
+            printf("    sense key = 0x%x [%s] , asc = 0x%x, ascq = 0x%x",
+                   v, sg_get_sense_key_str(v, sizeof(b), b), ucp[17],
+                   ucp[18]);
+            if (ucp[17] || ucp[18])
+                printf("      [%s]\n", sg_get_asc_ascq_str(ucp[17], ucp[18],
+                       sizeof(b), b));
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* TEMPERATURE_LPAGE [0xd]  introduced: SPC-3 */
+static bool
+show_temperature_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int k, num, extra, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        pr2serr("badly formed Temperature page\n");
+        return false;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        if (! op->do_temperature)
+            printf("Temperature page  [0xd]\n");
+    }
+    for (k = num; k > 0; k -= extra, ucp += extra) {
+        if (k < 3) {
+            pr2serr("short Temperature page\n");
+            return true;
+        }
+        extra = ucp[3] + 4;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, extra);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, extra,
+                        ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0:
+            if ((extra > 5) && (k > 5)) {
+                if (ucp[5] < 0xff)
+                    printf("  Current temperature = %d C", ucp[5]);
+                else
+                    printf("  Current temperature = <not available>");
+            }
+            break;
+        case 1:
+            if ((extra > 5) && (k > 5)) {
+                if (ucp[5] < 0xff)
+                    printf("  Reference temperature = %d C", ucp[5]);
+                else
+                    printf("  Reference temperature = <not available>");
+            }
+            break;
+        default:
+            if (! op->do_temperature) {
+                printf("  unknown parameter code = 0x%x, contents in "
+                       "hex:\n", pc);
+                dStrHex((const char *)ucp, extra, 1);
+            } else
+                continue;
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* START_STOP_LPAGE [0xe]  introduced: SPC-3 */
+static bool
+show_start_stop_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int k, num, extra, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        pr2serr("badly formed Start-stop cycle counter page\n");
+        return false;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Start-stop cycle counter page  [0xe]\n");
+    for (k = num; k > 0; k -= extra, ucp += extra) {
+        if (k < 3) {
+            pr2serr("short Start-stop cycle counter page\n");
+            return true;
+        }
+        extra = ucp[3] + 4;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, extra);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, extra,
+                        ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 1:
+            if (10 == extra)
+                printf("  Date of manufacture, year: %.4s, week: %.2s",
+                       &ucp[4], &ucp[8]);
+            else if (op->verbose) {
+                pr2serr("  Date of manufacture parameter length strange: "
+                        "%d\n", extra - 4);
+                dStrHexErr((const char *)ucp, extra, 1);
+            }
+            break;
+        case 2:
+            if (10 == extra)
+                printf("  Accounting date, year: %.4s, week: %.2s",
+                       &ucp[4], &ucp[8]);
+            else if (op->verbose) {
+                pr2serr("  Accounting date parameter length strange: %d\n",
+                        extra - 4);
+                dStrHexErr((const char *)ucp, extra, 1);
+            }
+            break;
+        case 3:
+            if (extra > 7) {
+                if (all_bits_set(ucp + 4, 4))
+                    printf("  Specified cycle count over device lifetime "
+                           "= -1");
+                else
+                    printf("  Specified cycle count over device lifetime "
+                           "= %u", sg_get_unaligned_be32(ucp + 4));
+            }
+            break;
+        case 4:
+            if (extra > 7) {
+                if (all_bits_set(ucp + 4, 4))
+                    printf("  Accumulated start-stop cycles = -1");
+                else
+                    printf("  Accumulated start-stop cycles = %u",
+                           sg_get_unaligned_be32(ucp + 4));
+            }
+            break;
+        case 5:
+            if (extra > 7) {
+                if (all_bits_set(ucp + 4, 4))
+                    printf("  Specified load-unload count over device "
+                           "lifetime = -1");
+                else
+                    printf("  Specified load-unload count over device "
+                           "lifetime = %u", sg_get_unaligned_be32(ucp + 4));
+            }
+            break;
+        case 6:
+            if (extra > 7) {
+                if (all_bits_set(ucp + 4, 4))
+                    printf("  Accumulated load-unload cycles = -1");
+                else
+                    printf("  Accumulated load-unload cycles = %u",
+                           sg_get_unaligned_be32(ucp + 4));
+            }
+            break;
+        default:
+            printf("  unknown parameter code = 0x%x, contents in "
+                   "hex:\n", pc);
+            dStrHex((const char *)ucp, extra, 1);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* APP_CLIENT_LPAGE [0xf]  introduced: SPC-3 */
+static bool
+show_app_client_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int k, num, extra, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        pr2serr("badly formed Application Client page\n");
+        return false;
+    }
+    if (op->verbose || ((op->do_raw == 0) && (op->do_hex == 0)))
+        printf("Application client page  [0xf]\n");
+    if (0 == op->filter_given) {
+        if ((len > 128) && (0 == op->do_hex)) {
+            dStrHex((const char *)resp, 64, 1);
+            printf(" .....  [truncated after 64 of %d bytes (use '-H' to "
+                   "see the rest)]\n", len);
+        }
+        else
+            dStrHex((const char *)resp, len, 1);
+        return true;
+    }
+    /* only here if filter_given set */
+    for (k = num; k > 0; k -= extra, ucp += extra) {
+        if (k < 3) {
+            pr2serr("short Application client page\n");
+            return true;
+        }
+        extra = ucp[3] + 4;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        if (op->filter != pc)
+            continue;
+        if (op->do_raw)
+            dStrRaw((const char *)ucp, extra);
+        else if (0 == op->do_hex)
+            dStrHex((const char *)ucp, extra, 0);
+        else if (1 == op->do_hex)
+            dStrHex((const char *)ucp, extra, 1);
+        else
+            dStrHex((const char *)ucp, extra, -1);
+
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        break;
+    }
+    return true;
+}
+
+/* IE_LPAGE [0x2f]  introduced: SPC-3 */
+static bool
+show_ie_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int k, num, extra, pc, pcb, full;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+    char b[256];
+
+    full = ! op->do_temperature;
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    if (num < 4) {
+        pr2serr("badly formed Informational Exceptions page\n");
+        return false;
+    }
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        if (full)
+            printf("Informational Exceptions page  [0x2f]\n");
+    }
+    for (k = num; k > 0; k -= extra, ucp += extra) {
+        if (k < 3) {
+            printf("short Informational Exceptions page\n");
+            return false;
+        }
+        extra = ucp[3] + 4;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, extra);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, extra,
+                        ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0:
+            if (extra > 5) {
+                if (full) {
+                    printf("  IE asc = 0x%x, ascq = 0x%x", ucp[4], ucp[5]);
+                    if (ucp[4] || ucp[5])
+                        if(sg_get_asc_ascq_str(ucp[4], ucp[5], sizeof(b), b))
+                            printf("\n    [%s]", b);
+                }
+                if (extra > 6) {
+                    if (ucp[6] < 0xff)
+                        printf("\n  Current temperature = %d C", ucp[6]);
+                    else
+                        printf("\n  Current temperature = <not available>");
+                    if (extra > 7) {
+                        if (ucp[7] < 0xff)
+                            printf("\n  Threshold temperature = %d C  [IBM "
+                                   "extension]", ucp[7]);
+                        else
+                            printf("\n  Threshold temperature = <not "
+                                   "available>");
+                     }
+                }
+            }
+            break;
+        default:
+            if (full) {
+                printf("  parameter code = 0x%x, contents in hex:\n", pc);
+                dStrHex((const char *)ucp, extra, 1);
+            }
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* called for SAS port of PROTO_SPECIFIC_LPAGE [0x18] */
+static void
+show_sas_phy_event_info(int pes, unsigned int val, unsigned int thresh_val)
+{
+    unsigned int u;
+
+    switch (pes) {
+    case 0:
+        printf("     No event\n");
+        break;
+    case 0x1:
+        printf("     Invalid word count: %u\n", val);
+        break;
+    case 0x2:
+        printf("     Running disparity error count: %u\n", val);
+        break;
+    case 0x3:
+        printf("     Loss of dword synchronization count: %u\n", val);
+        break;
+    case 0x4:
+        printf("     Phy reset problem count: %u\n", val);
+        break;
+    case 0x5:
+        printf("     Elasticity buffer overflow count: %u\n", val);
+        break;
+    case 0x6:
+        printf("     Received ERROR  count: %u\n", val);
+        break;
+    case 0x7:
+        printf("     Invalid SPL packet count: %u\n", val);
+        break;
+    case 0x8:
+        printf("     Loss of SPL packet synchronization count: %u\n", val);
+        break;
+    case 0x20:
+        printf("     Received address frame error count: %u\n", val);
+        break;
+    case 0x21:
+        printf("     Transmitted abandon-class OPEN_REJECT count: %u\n", val);
+        break;
+    case 0x22:
+        printf("     Received abandon-class OPEN_REJECT count: %u\n", val);
+        break;
+    case 0x23:
+        printf("     Transmitted retry-class OPEN_REJECT count: %u\n", val);
+        break;
+    case 0x24:
+        printf("     Received retry-class OPEN_REJECT count: %u\n", val);
+        break;
+    case 0x25:
+        printf("     Received AIP (WATING ON PARTIAL) count: %u\n", val);
+        break;
+    case 0x26:
+        printf("     Received AIP (WAITING ON CONNECTION) count: %u\n", val);
+        break;
+    case 0x27:
+        printf("     Transmitted BREAK count: %u\n", val);
+        break;
+    case 0x28:
+        printf("     Received BREAK count: %u\n", val);
+        break;
+    case 0x29:
+        printf("     Break timeout count: %u\n", val);
+        break;
+    case 0x2a:
+        printf("     Connection count: %u\n", val);
+        break;
+    case 0x2b:
+        printf("     Peak transmitted pathway blocked count: %u\n",
+               val & 0xff);
+        printf("         Peak value detector threshold: %u\n",
+               thresh_val & 0xff);
+        break;
+    case 0x2c:
+        u = val & 0xffff;
+        if (u < 0x8000)
+            printf("     Peak transmitted arbitration wait time (us): "
+                   "%u\n", u);
+        else
+            printf("     Peak transmitted arbitration wait time (ms): "
+                   "%u\n", 33 + (u - 0x8000));
+        u = thresh_val & 0xffff;
+        if (u < 0x8000)
+            printf("         Peak value detector threshold (us): %u\n",
+                   u);
+        else
+            printf("         Peak value detector threshold (ms): %u\n",
+                   33 + (u - 0x8000));
+        break;
+    case 0x2d:
+        printf("     Peak arbitration time (us): %u\n", val);
+        printf("         Peak value detector threshold: %u\n", thresh_val);
+        break;
+    case 0x2e:
+        printf("     Peak connection time (us): %u\n", val);
+        printf("         Peak value detector threshold: %u\n", thresh_val);
+        break;
+    case 0x2f:
+        printf("     Persistent connection count: %u\n", val);
+        break;
+    case 0x40:
+        printf("     Transmitted SSP frame count: %u\n", val);
+        break;
+    case 0x41:
+        printf("     Received SSP frame count: %u\n", val);
+        break;
+    case 0x42:
+        printf("     Transmitted SSP frame error count: %u\n", val);
+        break;
+    case 0x43:
+        printf("     Received SSP frame error count: %u\n", val);
+        break;
+    case 0x44:
+        printf("     Transmitted CREDIT_BLOCKED count: %u\n", val);
+        break;
+    case 0x45:
+        printf("     Received CREDIT_BLOCKED count: %u\n", val);
+        break;
+    case 0x50:
+        printf("     Transmitted SATA frame count: %u\n", val);
+        break;
+    case 0x51:
+        printf("     Received SATA frame count: %u\n", val);
+        break;
+    case 0x52:
+        printf("     SATA flow control buffer overflow count: %u\n", val);
+        break;
+    case 0x60:
+        printf("     Transmitted SMP frame count: %u\n", val);
+        break;
+    case 0x61:
+        printf("     Received SMP frame count: %u\n", val);
+        break;
+    case 0x63:
+        printf("     Received SMP frame error count: %u\n", val);
+        break;
+    default:
+        printf("     Unknown phy event source: %d, val=%u, thresh_val=%u\n",
+               pes, val, thresh_val);
+        break;
+    }
+}
+
+static const char * sas_link_rate_arr[16] = {
+    "phy enabled; unknown rate",
+    "phy disabled",
+    "phy enabled; speed negotiation failed",
+    "phy enabled; SATA spinup hold state",
+    "phy enabled; port selector",
+    "phy enabled; reset in progress",
+    "phy enabled; unsupported phy attached",
+    "reserved [0x7]",
+    "1.5 Gbps",                 /* 0x8 */
+    "3 Gbps",
+    "6 Gbps",
+    "12 Gbps",
+    "22.5 Gbps",
+    "reserved [0xd]",
+    "reserved [0xe]",
+    "reserved [0xf]",
+};
+
+static char *
+sas_negot_link_rate(int lrate, char * b, int blen)
+{
+    int mask = 0xf;
+
+    if (~mask & lrate)
+        snprintf(b, blen, "bad link_rate value=0x%x\n", lrate);
+    else
+        snprintf(b, blen, "%s", sas_link_rate_arr[lrate]);
+    return b;
+}
+
+/* helper for SAS port of PROTO_SPECIFIC_LPAGE [0x18] */
+static void
+show_sas_port_param(const uint8_t * ucp, int param_len,
+                    const struct opts_t * op)
+{
+    int j, m, nphys, pcb, t, sz, spld_len;
+    const uint8_t * vcp;
+    uint64_t ull;
+    unsigned int ui;
+    char pcb_str[PCB_STR_LEN];
+    char s[64];
+
+    sz = sizeof(s);
+    pcb = ucp[2];
+    t = sg_get_unaligned_be16(ucp + 0);
+    if (op->do_name)
+        printf("rel_target_port=%d\n", t);
+    else
+        printf("relative target port id = %d\n", t);
+    if (op->do_name)
+        printf("  gen_code=%d\n", ucp[6]);
+    else
+        printf("  generation code = %d\n", ucp[6]);
+    nphys = ucp[7];
+    if (op->do_name)
+        printf("  num_phys=%d\n", nphys);
+    else {
+        printf("  number of phys = %d", nphys);
+        if ((op->do_pcb) && (0 == op->do_name)) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+    }
+
+    for (j = 0, vcp = ucp + 8; j < (param_len - 8);
+         vcp += spld_len, j += spld_len) {
+        if (op->do_name)
+            printf("    phy_id=%d\n", vcp[1]);
+        else
+            printf("  phy identifier = %d\n", vcp[1]);
+        spld_len = vcp[3];
+        if (spld_len < 44)
+            spld_len = 48;      /* in SAS-1 and SAS-1.1 vcp[3]==0 */
+        else
+            spld_len += 4;
+        if (op->do_name) {
+            t = ((0x70 & vcp[4]) >> 4);
+            printf("      att_dev_type=%d\n", t);
+            printf("      att_iport_mask=0x%x\n", vcp[6]);
+            printf("      att_phy_id=%d\n", vcp[24]);
+            printf("      att_reason=0x%x\n", (vcp[4] & 0xf));
+            ull = sg_get_unaligned_be64(vcp + 16);
+            printf("      att_sas_addr=0x%" PRIx64 "\n", ull);
+            printf("      att_tport_mask=0x%x\n", vcp[7]);
+            ui = sg_get_unaligned_be32(vcp + 32);
+            printf("      inv_dwords=%u\n", ui);
+            ui = sg_get_unaligned_be32(vcp + 40);
+            printf("      loss_dword_sync=%u\n", ui);
+            printf("      neg_log_lrate=%d\n", 0xf & vcp[5]);
+            ui = sg_get_unaligned_be32(vcp + 44);
+            printf("      phy_reset_probs=%u\n", ui);
+            ui = sg_get_unaligned_be32(vcp + 36);
+            printf("      running_disparity=%u\n", ui);
+            printf("      reason=0x%x\n", (vcp[5] & 0xf0) >> 4);
+            ull = sg_get_unaligned_be64(vcp + 8);
+            printf("      sas_addr=0x%" PRIx64 "\n", ull);
+        } else {
+            t = ((0x70 & vcp[4]) >> 4);
+            /* attached SAS device type. In SAS-1.1 case 2 was an edge
+             * expander; in SAS-2 case 3 is marked as obsolete. */
+            switch (t) {
+            case 0: snprintf(s, sz, "no device attached"); break;
+            case 1: snprintf(s, sz, "SAS or SATA device"); break;
+            case 2: snprintf(s, sz, "expander device"); break;
+            case 3: snprintf(s, sz, "expander device (fanout)"); break;
+            default: snprintf(s, sz, "reserved [%d]", t); break;
+            }
+            /* the word 'SAS' in following added in spl4r01 */
+            printf("    attached SAS device type: %s\n", s);
+            t = 0xf & vcp[4];
+            switch (t) {
+            case 0: snprintf(s, sz, "unknown"); break;
+            case 1: snprintf(s, sz, "power on"); break;
+            case 2: snprintf(s, sz, "hard reset"); break;
+            case 3: snprintf(s, sz, "SMP phy control function"); break;
+            case 4: snprintf(s, sz, "loss of dword synchronization"); break;
+            case 5: snprintf(s, sz, "mux mix up"); break;
+            case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
+                break;
+            case 7: snprintf(s, sz, "break timeout timer expired"); break;
+            case 8: snprintf(s, sz, "phy test function stopped"); break;
+            case 9: snprintf(s, sz, "expander device reduced functionality");
+                 break;
+            default: snprintf(s, sz, "reserved [0x%x]", t); break;
+            }
+            printf("    attached reason: %s\n", s);
+            t = (vcp[5] & 0xf0) >> 4;
+            switch (t) {
+            case 0: snprintf(s, sz, "unknown"); break;
+            case 1: snprintf(s, sz, "power on"); break;
+            case 2: snprintf(s, sz, "hard reset"); break;
+            case 3: snprintf(s, sz, "SMP phy control function"); break;
+            case 4: snprintf(s, sz, "loss of dword synchronization"); break;
+            case 5: snprintf(s, sz, "mux mix up"); break;
+            case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
+                break;
+            case 7: snprintf(s, sz, "break timeout timer expired"); break;
+            case 8: snprintf(s, sz, "phy test function stopped"); break;
+            case 9: snprintf(s, sz, "expander device reduced functionality");
+                 break;
+            default: snprintf(s, sz, "reserved [0x%x]", t); break;
+            }
+            printf("    reason: %s\n", s);
+            printf("    negotiated logical link rate: %s\n",
+                   sas_negot_link_rate((0xf & vcp[5]), s, sz));
+            printf("    attached initiator port: ssp=%d stp=%d smp=%d\n",
+                   !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
+            printf("    attached target port: ssp=%d stp=%d smp=%d\n",
+                   !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
+            ull = sg_get_unaligned_be64(vcp + 8);
+            printf("    SAS address = 0x%" PRIx64 "\n", ull);
+            ull = sg_get_unaligned_be64(vcp + 16);
+            printf("    attached SAS address = 0x%" PRIx64 "\n", ull);
+            printf("    attached phy identifier = %d\n", vcp[24]);
+            ui = sg_get_unaligned_be32(vcp + 32);
+            printf("    Invalid DWORD count = %u\n", ui);
+            ui = sg_get_unaligned_be32(vcp + 36);
+            printf("    Running disparity error count = %u\n", ui);
+            ui = sg_get_unaligned_be32(vcp + 40);
+            printf("    Loss of DWORD synchronization = %u\n", ui);
+            ui = sg_get_unaligned_be32(vcp + 44);
+            printf("    Phy reset problem = %u\n", ui);
+        }
+        if (spld_len > 51) {
+            int num_ped, pes;
+            const uint8_t * xcp;
+            unsigned int pvdt;
+
+            num_ped = vcp[51];
+            if (op->verbose > 1)
+                printf("    <<Phy event descriptors: %d, spld_len: %d, "
+                       "calc_ped: %d>>\n", num_ped, spld_len,
+                       (spld_len - 52) / 12);
+            if (num_ped > 0) {
+                if (op->do_name) {
+                   printf("      phy_event_desc_num=%d\n", num_ped);
+                   return;      /* don't decode at this stage */
+                } else
+                   printf("    Phy event descriptors:\n");
+            }
+            xcp = vcp + 52;
+            for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
+                pes = xcp[3];
+                ui = sg_get_unaligned_be32(xcp + 4);
+                pvdt = sg_get_unaligned_be32(xcp + 8);
+                show_sas_phy_event_info(pes, ui, pvdt);
+            }
+        } else if (op->verbose)
+           printf("    <<No phy event descriptors>>\n");
+    }
+}
+
+/* PROTO_SPECIFIC_LPAGE [0x18] */
+static bool
+show_protocol_specific_page(const uint8_t * resp, int len,
+                            const struct opts_t * op)
+{
+    int k, num, pl, pc, pid;
+    const uint8_t * ucp;
+
+    num = len - 4;
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        if (op->do_name)
+            printf("log_page=0x%x\n", PROTO_SPECIFIC_LPAGE);
+    }
+    for (k = 0, ucp = resp + 4; k < num; ) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        pid = 0xf & ucp[4];
+        if (6 != pid) {
+            pr2serr("Protocol identifier: %d, only support SAS (SPL) which "
+                    "is 6\n", pid);
+            return false;   /* only decode SAS log page */
+        }
+        if ((0 == k) && (0 == op->do_name))
+            printf("Protocol Specific port page for SAS SSP  (sas-2) "
+                   "[0x18]\n");
+        show_sas_port_param(ucp, pl, op);
+        if (op->filter_given)
+            break;
+skip:
+        k += pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* Returns 1 if processed page, 0 otherwise */
+/* STATS_LPAGE [0x19], subpages: 0x0 to 0x1f  introduced: SPC-4 */
+static bool
+show_stats_perform_page(const uint8_t * resp, int len,
+                        const struct opts_t * op)
+{
+    int k, num, param_len, param_code, spf, subpg_code, extra;
+    int pcb, nam;
+    unsigned int ui;
+    const uint8_t * ucp;
+    const char * ccp;
+    uint64_t ull;
+    char pcb_str[PCB_STR_LEN];
+
+    nam = op->do_name;
+    num = len - 4;
+    ucp = resp + 4;
+    spf = !!(resp[0] & 0x40);
+    subpg_code = spf ? resp[1] : 0;
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        if (nam) {
+            printf("log_page=0x%x\n", STATS_LPAGE);
+            if (subpg_code > 0)
+                printf("log_subpage=0x%x\n", subpg_code);
+        } else {
+            if (0 == subpg_code)
+                printf("General Statistics and Performance  [0x19]\n");
+            else
+                printf("Group Statistics and Performance (%d)  "
+                       "[0x19,0x%x]\n", subpg_code, subpg_code);
+        }
+    }
+    if (subpg_code > 31)
+        return false;
+    if (0 == subpg_code) { /* General statistics and performance log page */
+        if (num < 0x5c)
+            return false;
+        for (k = num; k > 0; k -= extra, ucp += extra) {
+            if (k < 3)
+                return false;
+            param_len = ucp[3];
+            extra = param_len + 4;
+            param_code = sg_get_unaligned_be16(ucp + 0);
+            pcb = ucp[2];
+            if (op->filter_given) {
+                if (param_code != op->filter)
+                    continue;
+                if (op->do_raw) {
+                    dStrRaw((const char *)ucp, extra);
+                    break;
+                } else if (op->do_hex) {
+                    dStrHex((const char *)ucp, extra,
+                            ((1 == op->do_hex) ? 1 : -1));
+                    break;
+                }
+            }
+            switch (param_code) {
+            case 1:     /* Statistics and performance log parameter */
+                ccp = nam ? "parameter_code=1" : "Statistics and performance "
+                        "log parameter";
+                printf("%s\n", ccp);
+                ull = sg_get_unaligned_be64(ucp + 4);
+                ccp = nam ? "read_commands=" : "number of read commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 12);
+                ccp = nam ? "write_commands=" : "number of write commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 20);
+                ccp = nam ? "lb_received="
+                          : "number of logical blocks received = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 28);
+                ccp = nam ? "lb_transmitted="
+                          : "number of logical blocks transmitted = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 36);
+                ccp = nam ? "read_proc_intervals="
+                          : "read command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 44);
+                ccp = nam ? "write_proc_intervals="
+                          : "write command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 52);
+                ccp = nam ? "weight_rw_commands=" : "weighted number of "
+                                "read commands plus write commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 60);
+                ccp = nam ? "weight_rw_processing=" : "weighted read command "
+                                "processing plus write command processing = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                break;
+            case 2:     /* Idle time log parameter */
+                ccp = nam ? "parameter_code=2" : "Idle time log parameter";
+                printf("%s\n", ccp);
+                ull = sg_get_unaligned_be64(ucp + 4);
+                ccp = nam ? "idle_time_intervals=" : "idle time "
+                                "intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                break;
+            case 3:     /* Time interval log parameter for general stats */
+                ccp = nam ? "parameter_code=3" : "Time interval log "
+                        "parameter for general stats";
+                printf("%s\n", ccp);
+                ui = sg_get_unaligned_be32(ucp + 4);
+                ccp = nam ? "time_interval_neg_exp=" : "time interval "
+                                "negative exponent = ";
+                printf("  %s%u\n", ccp, ui);
+                ui = sg_get_unaligned_be32(ucp + 8);
+                ccp = nam ? "time_interval_int=" : "time interval "
+                                "integer = ";
+                printf("  %s%u\n", ccp, ui);
+                break;
+            case 4:     /* FUA statistics and performance log parameter */
+                ccp = nam ? "parameter_code=4" : "Force unit access "
+                        "statistics and performance log parameter ";
+                printf("%s\n", ccp);
+                ull = sg_get_unaligned_be64(ucp + 4);
+                ccp = nam ? "read_fua_commands=" : "number of read FUA "
+                                "commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 12);
+                ccp = nam ? "write_fua_commands=" : "number of write FUA "
+                                "commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 20);
+                ccp = nam ? "read_fua_nv_commands="
+                          : "number of read FUA_NV commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 28);
+                ccp = nam ? "write_fua_nv_commands="
+                          : "number of write FUA_NV commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 36);
+                ccp = nam ? "read_fua_proc_intervals="
+                          : "read FUA command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 44);
+                ccp = nam ? "write_fua_proc_intervals="
+                          : "write FUA command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 52);
+                ccp = nam ? "read_fua_nv_proc_intervals="
+                          : "read FUA_NV command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 60);
+                ccp = nam ? "write_fua_nv_proc_intervals="
+                          : "write FUA_NV command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                break;
+            case 6:     /* Time interval log parameter for cache stats */
+                ccp = nam ? "parameter_code=6" : "Time interval log "
+                        "parameter for cache stats";
+                printf("%s\n", ccp);
+                ull = sg_get_unaligned_be64(ucp + 4);
+                ccp = nam ? "time_interval_neg_exp=" : "time interval "
+                                "negative exponent = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 8);
+                ccp = nam ? "time_interval_int=" : "time interval "
+                                "integer = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                break;
+            default:
+                if (nam) {
+                    printf("parameter_code=%d\n", param_code);
+                    printf("  unknown=1\n");
+                } else
+                    pr2serr("show_performance...  unknown parameter code "
+                            "%d\n", param_code);
+                if (op->verbose)
+                    dStrHexErr((const char *)ucp, extra, 1);
+                break;
+            }
+            if ((op->do_pcb) && (0 == op->do_name)) {
+                get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+                printf("    <%s>\n", pcb_str);
+            }
+            if (op->filter_given)
+                break;
+        }
+    } else {    /* Group statistics and performance (n) log page */
+        if (num < 0x34)
+            return false;
+        for (k = num; k > 0; k -= extra, ucp += extra) {
+            if (k < 3)
+                return false;
+            param_len = ucp[3];
+            extra = param_len + 4;
+            param_code = sg_get_unaligned_be16(ucp + 0);
+            pcb = ucp[2];
+            if (op->filter_given) {
+                if (param_code != op->filter)
+                    continue;
+                if (op->do_raw) {
+                    dStrRaw((const char *)ucp, extra);
+                    break;
+                } else if (op->do_hex) {
+                    dStrHex((const char *)ucp, extra,
+                            ((1 == op->do_hex) ? 1 : -1));
+                    break;
+                }
+            }
+            switch (param_code) {
+            case 1:     /* Group n Statistics and performance log parameter */
+                if (nam)
+                    printf("parameter_code=1\n");
+                else
+                    printf("Group %d Statistics and performance log "
+                           "parameter\n", subpg_code);
+                ull = sg_get_unaligned_be64(ucp + 4);
+                ccp = nam ? "gn_read_commands=" : "group n number of read "
+                                "commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 12);
+                ccp = nam ? "gn_write_commands=" : "group n number of write "
+                                "commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 20);
+                ccp = nam ? "gn_lb_received="
+                          : "group n number of logical blocks received = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 28);
+                ccp = nam ? "gn_lb_transmitted="
+                          : "group n number of logical blocks transmitted = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 36);
+                ccp = nam ? "gn_read_proc_intervals="
+                          : "group n read command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 44);
+                ccp = nam ? "gn_write_proc_intervals="
+                          : "group n write command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                break;
+            case 4: /* Group n FUA statistics and performance log parameter */
+                ccp = nam ? "parameter_code=4" : "Group n force unit access "
+                        "statistics and performance log parameter";
+                printf("%s\n", ccp);
+                ull = sg_get_unaligned_be64(ucp + 4);
+                ccp = nam ? "gn_read_fua_commands="
+                          : "group n number of read FUA commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 12);
+                ccp = nam ? "gn_write_fua_commands="
+                          : "group n number of write FUA commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 20);
+                ccp = nam ? "gn_read_fua_nv_commands="
+                          : "group n number of read FUA_NV commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 28);
+                ccp = nam ? "gn_write_fua_nv_commands="
+                          : "group n number of write FUA_NV commands = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 36);
+                ccp = nam ? "gn_read_fua_proc_intervals="
+                          : "group n read FUA command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 44);
+                ccp = nam ? "gn_write_fua_proc_intervals=" : "group n write "
+                            "FUA command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 52);
+                ccp = nam ? "gn_read_fua_nv_proc_intervals=" : "group n "
+                            "read FUA_NV command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                ull = sg_get_unaligned_be64(ucp + 60);
+                ccp = nam ? "gn_write_fua_nv_proc_intervals=" : "group n "
+                            "write FUA_NV command processing intervals = ";
+                printf("  %s%" PRIu64 "\n", ccp, ull);
+                break;
+            default:
+                if (nam) {
+                    printf("parameter_code=%d\n", param_code);
+                    printf("  unknown=1\n");
+                } else
+                    pr2serr("show_performance...  unknown parameter code "
+                            "%d\n", param_code);
+                if (op->verbose)
+                    dStrHexErr((const char *)ucp, extra, 1);
+                break;
+            }
+            if ((op->do_pcb) && (0 == op->do_name)) {
+                get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+                printf("    <%s>\n", pcb_str);
+            }
+            if (op->filter_given)
+                break;
+        }
+    }
+    return true;
+}
+
+/* Returns 1 if processed page, 0 otherwise */
+/* STATS_LPAGE [0x19], CACHE_STATS_SUBPG [0x20]  introduced: SPC-4 */
+static bool
+show_cache_stats_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int k, num, pc, spf, subpg_code, extra;
+    int pcb, nam;
+    unsigned int ui;
+    const uint8_t * ucp;
+    const char * ccp;
+    uint64_t ull;
+    char pcb_str[PCB_STR_LEN];
+
+    nam = op->do_name;
+    num = len - 4;
+    ucp = resp + 4;
+    if (num < 4) {
+        pr2serr("badly formed Cache memory statistics page\n");
+        return false;
+    }
+    spf = !!(resp[0] & 0x40);
+    subpg_code = spf ? resp[1] : 0;
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        if (nam) {
+            printf("log_page=0x%x\n", STATS_LPAGE);
+            if (subpg_code > 0)
+                printf("log_subpage=0x%x\n", subpg_code);
+        } else
+            printf("Cache memory statistics page  [0x19,0x20]\n");
+    }
+
+    for (k = num; k > 0; k -= extra, ucp += extra) {
+        if (k < 3) {
+            pr2serr("short Cache memory statistics page\n");
+            return false;
+        }
+        if (8 != ucp[3]) {
+            printf("Cache memory statistics page parameter length not "
+                   "8\n");
+            return false;
+        }
+        extra = ucp[3] + 4;
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        if (op->filter_given) {
+            if (pc != op->filter)
+                continue;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, extra);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, extra,
+                        ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 1:     /* Read cache memory hits log parameter */
+            ccp = nam ? "parameter_code=1" :
+                        "Read cache memory hits log parameter";
+            printf("%s\n", ccp);
+            ull = sg_get_unaligned_be64(ucp + 4);
+            ccp = nam ? "read_cache_memory_hits=" :
+                        "read cache memory hits = ";
+            printf("  %s%" PRIu64 "\n", ccp, ull);
+            break;
+        case 2:     /* Reads to cache memory log parameter */
+            ccp = nam ? "parameter_code=2" :
+                        "Reads to cache memory log parameter";
+            printf("%s\n", ccp);
+            ull = sg_get_unaligned_be64(ucp + 4);
+            ccp = nam ? "reads_to_cache_memory=" :
+                        "reads to cache memory = ";
+            printf("  %s%" PRIu64 "\n", ccp, ull);
+            break;
+        case 3:     /* Write cache memory hits log parameter */
+            ccp = nam ? "parameter_code=3" :
+                        "Write cache memory hits log parameter";
+            printf("%s\n", ccp);
+            ull = sg_get_unaligned_be64(ucp + 4);
+            ccp = nam ? "write_cache_memory_hits=" :
+                        "write cache memory hits = ";
+            printf("  %s%" PRIu64 "\n", ccp, ull);
+            break;
+        case 4:     /* Writes from cache memory log parameter */
+            ccp = nam ? "parameter_code=4" :
+                        "Writes from cache memory log parameter";
+            printf("%s\n", ccp);
+            ull = sg_get_unaligned_be64(ucp + 4);
+            ccp = nam ? "writes_from_cache_memory=" :
+                        "writes from cache memory = ";
+            printf("  %s%" PRIu64 "\n", ccp, ull);
+            break;
+        case 5:     /* Time from last hard reset log parameter */
+            ccp = nam ? "parameter_code=5" :
+                        "Time from last hard reset log parameter";
+            printf("%s\n", ccp);
+            ull = sg_get_unaligned_be64(ucp + 4);
+            ccp = nam ? "time_from_last_hard_reset=" :
+                        "time from last hard reset = ";
+            printf("  %s%" PRIu64 "\n", ccp, ull);
+            break;
+        case 6:     /* Time interval log parameter for cache stats */
+            ccp = nam ? "parameter_code=6" :
+                        "Time interval log parameter";
+            printf("%s\n", ccp);
+            ui = sg_get_unaligned_be32(ucp + 4);
+            ccp = nam ? "time_interval_neg_exp=" : "time interval "
+                            "negative exponent = ";
+            printf("  %s%u\n", ccp, ui);
+            ui = sg_get_unaligned_be32(ucp + 8);
+            ccp = nam ? "time_interval_int=" : "time interval "
+                            "integer = ";
+            printf("  %s%u\n", ccp, ui);
+            break;
+        default:
+            if (nam) {
+                printf("parameter_code=%d\n", pc);
+                printf("  unknown=1\n");
+            } else
+                pr2serr("show_performance...  unknown parameter code %d\n",
+                        pc);
+            if (op->verbose)
+                dStrHexErr((const char *)ucp, extra, 1);
+            break;
+        }
+        if ((op->do_pcb) && (0 == op->do_name)) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("    <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+    }
+    return true;
+}
+
+/* FORMAT_STATUS_LPAGE [0x8]  introduced: SBC-2 */
+static bool
+show_format_status_page(const uint8_t * resp, int len,
+                        const struct opts_t * op)
+{
+    int k, j, num, pl, pc, pcb, all_ff, counter;
+    const uint8_t * ucp;
+    const uint8_t * xp;
+    uint64_t ull;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Format status page  [0x8]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        counter = 1;
+        switch (pc) {
+        case 0:
+            if (pl < 5)
+                printf("  Format data out: <empty>\n");
+            else {
+                for (all_ff = 1, j = 4; j < pl; ++j) {
+                    if (0xff != ucp[j]) {
+                        all_ff = 0;
+                        break;
+                    }
+                }
+                if (all_ff)
+                    printf("  Format data out: <not available>\n");
+                else {
+                    printf("  Format data out:\n");
+                    dStrHex((const char *)ucp + 4, pl - 4, 0);
+                }
+            }
+            counter = 0;
+            break;
+        case 1:
+            printf("  Grown defects during certification");
+            break;
+        case 2:
+            printf("  Total blocks reassigned during format");
+            break;
+        case 3:
+            printf("  Total new blocks reassigned");
+            break;
+        case 4:
+            printf("  Power on minutes since format");
+            break;
+        default:
+            printf("  Unknown Format status code = 0x%x\n", pc);
+            counter = 0;
+            dStrHex((const char *)ucp, pl, 0);
+            break;
+        }
+        if (counter) {
+            k = pl - 4;
+            xp = ucp + 4;
+            if (k > (int)sizeof(ull)) {
+                xp += (k - sizeof(ull));
+                k = sizeof(ull);
+            }
+            ull = 0;
+            for (all_ff = 0, j = 0; j < k; ++j) {
+                if (j > 0)
+                    ull <<= 8;
+                else
+                    all_ff = 1;
+                ull |= xp[j];
+                if (0xff != xp[j])
+                    all_ff = 0;
+            }
+            if (all_ff)
+                printf(" <not available>");
+            else
+                printf(" = %" PRIu64 "", ull);
+            if (op->do_pcb) {
+                get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+                printf("\n        <%s>\n", pcb_str);
+            } else
+                printf("\n");
+        } else {
+            if (op->do_pcb) {
+                get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+                printf("\n        <%s>\n", pcb_str);
+            }
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* Non-volatile cache page [0x17]  introduced: SBC-2 */
+static bool
+show_non_volatile_cache_page(const uint8_t * resp, int len,
+                             const struct opts_t * op)
+{
+    int j, num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Non-volatile cache page  [0x17]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0:
+            printf("  Remaining non-volatile time: ");
+            if (3 == ucp[4]) {
+                j = sg_get_unaligned_be24(ucp + 5);
+                switch (j) {
+                case 0:
+                    printf("0 (i.e. it is now volatile)\n");
+                    break;
+                case 1:
+                    printf("<unknown>\n");
+                    break;
+                case 0xffffff:
+                    printf("<indefinite>\n");
+                    break;
+                default:
+                    printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60));
+                    break;
+                }
+            } else
+                printf("<unexpected parameter length=%d>\n", ucp[4]);
+            break;
+        case 1:
+            printf("  Maximum non-volatile time: ");
+            if (3 == ucp[4]) {
+                j = sg_get_unaligned_be24(ucp + 5);
+                switch (j) {
+                case 0:
+                    printf("0 (i.e. it is now volatile)\n");
+                    break;
+                case 1:
+                    printf("<reserved>\n");
+                    break;
+                case 0xffffff:
+                    printf("<indefinite>\n");
+                    break;
+                default:
+                    printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60));
+                    break;
+                }
+            } else
+                printf("<unexpected parameter length=%d>\n", ucp[4]);
+            break;
+        default:
+            printf("  Unknown Format status code = 0x%x\n", pc);
+            dStrHex((const char *)ucp, pl, 0);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("        <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* LB_PROV_LPAGE [0xc]  introduced: SBC-3 */
+static bool
+show_lb_provisioning_page(const uint8_t * resp, int len,
+                          const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    const char * cp;
+    char str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Logical block provisioning page  [0xc]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0x1:
+            cp = "  Available LBA mapping threshold";
+            break;
+        case 0x2:
+            cp = "  Used LBA mapping threshold";
+            break;
+        case 0x3:
+            cp = "  Available provisioning resource percentage";
+            break;
+        case 0x100:
+            cp = "  De-duplicated LBA";
+            break;
+        case 0x101:
+            cp = "  Compressed LBA";
+            break;
+        case 0x102:
+            cp = "  Total efficiency LBA";
+            break;
+        default:
+            cp = NULL;
+            break;
+        }
+        if (cp) {
+            if ((pl < 8) || (num < 8)) {
+                if (num < 8)
+                    pr2serr("\n    truncated by response length, expected at "
+                            "least 8 bytes\n");
+                else
+                    pr2serr("\n    parameter length >= 8 expected, got %d\n",
+                            pl);
+                break;
+            }
+            if (0x3 == pc)
+                printf("  %s: %u %%\n", cp, sg_get_unaligned_be16(ucp + 4));
+            else {
+                printf("  %s resource count:", cp);
+                printf(" %u\n", sg_get_unaligned_be32(ucp + 4));
+            }
+            if (pl > 8) {
+                switch (ucp[8] & 0x3) {
+                case 0: cp = "not reported"; break;
+                case 1: cp = "dedicated to lu"; break;
+                case 2: cp = "not dedicated to lu"; break;
+                case 3: cp = "reserved"; break;
+                }
+                printf("    Scope: %s\n", cp);
+            }
+        } else if ((pc >= 0xfff0) && (pc <= 0xffff)) {
+            printf("  Vendor specific [0x%x]:", pc);
+            dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+        } else {
+            printf("  Reserved [parameter_code=0x%x]:\n", pc);
+            dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* UTILIZATION_SUBPG [0xe,0x1]  introduced: SBC-4 */
+static bool
+show_utilization_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int num, pl, pc, pcb, k;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Utilization page  [0xe,0x1]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0x0:
+            printf("  Workload utilization:");
+            if ((pl < 6) || (num < 6)) {
+                if (num < 6)
+                    pr2serr("\n    truncated by response length, expected "
+                            "at least 6 bytes\n");
+                else
+                    pr2serr("\n    parameter length >= 6 expected, got %d\n",
+                            pl);
+                break;
+            }
+            k = sg_get_unaligned_be16(ucp + 4);
+            printf(" %d.%02d %%\n", k / 100, k % 100);
+            break;
+        case 0x1:
+            printf("  Utilization usage rate based on date and time:");
+            if ((pl < 6) || (num < 6)) {
+                if (num < 6)
+                    pr2serr("\n    truncated by response length, expected "
+                            "at least 6 bytes\n");
+                else
+                    pr2serr("\n    parameter length >= 6 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf(" %d %%\n", ucp[4]);
+            break;
+        default:
+            printf("  Reserved [parameter_code=0x%x]:\n", pc);
+            dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* SOLID_STATE_MEDIA_LPAGE [0x11]  introduced: SBC-3 */
+static bool
+show_solid_state_media_page(const uint8_t * resp, int len,
+                            const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Solid state media page  [0x11]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0x1:
+            printf("  Percentage used endurance indicator:");
+            if ((pl < 8) || (num < 8)) {
+                if (num < 8)
+                    pr2serr("\n    truncated by response length, expected "
+                            "at least 8 bytes\n");
+                else
+                    pr2serr("\n    parameter length >= 8 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf(" %u %%\n", ucp[7]);
+            break;
+        default:
+            printf("  Reserved [parameter_code=0x%x]:\n", pc);
+            dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+static const char * dt_dev_activity[] = {
+    "No DT device activity",
+    "Cleaning operation in progress",
+    "Volume is being loaded",
+    "Volume is being unloaded",
+    "Other medium activity",
+    "Reading from medium",
+    "Writing to medium",
+    "Locating medium",
+    "Rewinding medium", /* 8 */
+    "Erasing volume",
+    "Formatting volume",
+    "Calibrating",
+    "Other DT device activity",
+    "Microcode update in progress",
+    "Reading encrypted from medium",
+    "Writing encrypted to medium",
+    "Diagnostic operation in progress", /* 10 */
+};
+
+/* DT device status [0x11] (ssc, adc) */
+static bool
+show_dt_device_status_page(const uint8_t * resp, int len,
+                           const struct opts_t * op)
+{
+    int num, pl, pc, pcb, j;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+    char b[64];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("DT device status page (ssc-3, adc-3) [0x11]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0x0:
+            printf("  Very high frequency data:\n");
+            if ((pl < 8) || (num < 8)) {
+                if (num < 8)
+                    pr2serr("    truncated by response length, expected at "
+                            "least 8 bytes\n");
+                else
+                    pr2serr("    parameter length >= 8 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf("  PAMR=%d HUI=%d MACC=%d CMPR=%d ", !!(0x80 & ucp[4]),
+                   !!(0x40 & ucp[4]), !!(0x20 & ucp[4]), !!(0x10 & ucp[4]));
+            printf("WRTP=%d CRQST=%d CRQRD=%d DINIT=%d\n", !!(0x8 & ucp[4]),
+                   !!(0x4 & ucp[4]), !!(0x2 & ucp[4]), !!(0x1 & ucp[4]));
+            printf("  INXTN=%d RAA=%d MPRSNT=%d ", !!(0x80 & ucp[5]),
+                   !!(0x20 & ucp[5]), !!(0x10 & ucp[5]));
+            printf("MSTD=%d MTHRD=%d MOUNTED=%d\n",
+                   !!(0x4 & ucp[5]), !!(0x2 & ucp[5]), !!(0x1 & ucp[5]));
+            printf("  DT device activity: ");
+            j = ucp[6];
+            if (j < (int)(sizeof(dt_dev_activity) /
+                          sizeof(dt_dev_activity[0])))
+                printf("%s\n", dt_dev_activity[j]);
+            else if (j < 0x80)
+                printf("Reserved [0x%x]\n", j);
+            else
+                printf("Vendor specific [0x%x]\n", j);
+            printf("  VS=%d TDDEC=%d EPP=%d ", !!(0x80 & ucp[7]),
+                   !!(0x20 & ucp[7]), !!(0x10 & ucp[7]));
+            printf("ESR=%d RRQST=%d INTFC=%d TAFC=%d\n", !!(0x8 & ucp[7]),
+                   !!(0x4 & ucp[7]), !!(0x2 & ucp[7]), !!(0x1 & ucp[7]));
+            break;
+        case 0x1:
+            printf("  Very high frequency polling delay: ");
+            if ((pl < 6) || (num < 6)) {
+                if (num < 6)
+                    pr2serr("\n    truncated by response length, expected at "
+                            "least 6 bytes\n");
+                else
+                    pr2serr("\n    parameter length >= 6 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf(" %d milliseconds\n", sg_get_unaligned_be16(ucp + 4));
+            break;
+        case 0x2:
+            printf("   DT device ADC data encryption control status (hex "
+                   "only now):\n");
+            if ((pl < 12) || (num < 12)) {
+                if (num < 12)
+                    pr2serr("    truncated by response length, expected at "
+                            "least 12 bytes\n");
+                else
+                    pr2serr("    parameter length >= 12 expected, got %d\n",
+                            pl);
+                break;
+            }
+            dStrHex((const char *)ucp + 4, 8, 1);
+            break;
+        case 0x3:
+            printf("   Key management error data (hex only now):\n");
+            if ((pl < 16) || (num < 16)) {
+                if (num < 16)
+                    pr2serr("    truncated by response length, expected at "
+                            "least 16 bytes\n");
+                else
+                    pr2serr("    parameter length >= 16 expected, got %d\n",
+                            pl);
+                break;
+            }
+            dStrHex((const char *)ucp + 4, 12, 1);
+            break;
+        default:
+            if ((pc >= 0x101) && (pc <= 0x1ff)) {
+                printf("  Primary port %d status:\n", pc - 0x100);
+                if (12 == ucp[3]) { /* if length of desc is 12, assume SAS */
+                    printf("    SAS: negotiated physical link rate: %s\n",
+                           sas_negot_link_rate((0xf & (ucp[4] >> 4)), b,
+                                               sizeof(b)));
+                    printf("    signal=%d, pic=%d, ", !!(0x2 & ucp[4]),
+                           !!(0x1 & ucp[4]));
+                    printf("hashed SAS addr: 0x%u\n",
+                            sg_get_unaligned_be24(ucp + 5));
+                    printf("    SAS addr: 0x%" PRIx64 "\n",
+                            sg_get_unaligned_be64(ucp + 8));
+                } else {
+                    printf("    non-SAS transport, in hex:\n");
+                    dStrHex((const char *)ucp + 4,
+                            ((pl < num) ? pl : num) - 4, 0);
+                }
+            } else if (pc >= 0x8000) {
+                printf("  Vendor specific [parameter_code=0x%x]:\n", pc);
+                dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+            } else {
+                printf("  Reserved [parameter_code=0x%x]:\n", pc);
+                dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+            }
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* SAT_ATA_RESULTS_LPAGE (SAT-2) [0x16] */
+static bool
+show_ata_pt_results_page(const uint8_t * resp, int len,
+                         const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    const uint8_t * dp;
+    char str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("ATA pass-through results page (sat-2) [0x16]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        if ((pc < 0xf) && (pl > 17)) {
+            int extend, sector_count;
+
+            dp = ucp + 4;
+            printf("  Log_index=0x%x (parameter_code=0x%x)\n", pc + 1, pc);
+            extend = dp[2] & 1;
+            sector_count = dp[5] + (extend ? (dp[4] << 8) : 0);
+            printf("    extend=%d  error=0x%x sector_count=0x%x\n", extend,
+                   dp[3], sector_count);
+            if (extend)
+                printf("    lba=0x%02x%02x%02x%02x%02x%02x\n", dp[10], dp[8],
+                       dp[6], dp[11], dp[9], dp[7]);
+            else
+                printf("    lba=0x%02x%02x%02x\n", dp[11], dp[9], dp[7]);
+            printf("    device=0x%x  status=0x%x\n", dp[12], dp[13]);
+        } else {
+            printf("  Reserved [parameter_code=0x%x]:\n", pc);
+            dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+static const char * bms_status[] = {
+    "no background scans active",
+    "background medium scan is active",
+    "background pre-scan is active",
+    "background scan halted due to fatal error",
+    "background scan halted due to a vendor specific pattern of error",
+    "background scan halted due to medium formatted without P-List",
+    "background scan halted - vendor specific cause",
+    "background scan halted due to temperature out of range",
+    "background scan enabled, none active (waiting for BMS interval timer "
+        "to expire)", /* 8 */
+    "background scan halted - scan results list full",
+    "background scan halted - pre-scan time limit timer expired" /* 10 */,
+};
+
+static const char * reassign_status[] = {
+    "Reassign status: Reserved [0x0]",
+    "Reassignment pending receipt of Reassign or Write command",
+    "Logical block successfully reassigned by device server",
+    "Reassign status: Reserved [0x3]",
+    "Reassignment by device server failed",
+    "Logical block recovered by device server via rewrite",
+    "Logical block reassigned by application client, has valid data",
+    "Logical block reassigned by application client, contains no valid data",
+    "Logical block unsuccessfully reassigned by application client", /* 8 */
+};
+
+/* Background scan results [0x15,0] for disk  introduced: SBC-3 */
+static bool
+show_background_scan_results_page(const uint8_t * resp, int len,
+                                  const struct opts_t * op)
+{
+    int j, m, num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Background scan results page  [0x15]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0:
+            printf("  Status parameters:\n");
+            if ((pl < 16) || (num < 16)) {
+                if (num < 16)
+                    pr2serr("    truncated by response length, expected at "
+                            "least 16 bytes\n");
+                else
+                    pr2serr("    parameter length >= 16 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf("    Accumulated power on minutes: ");
+            j = sg_get_unaligned_be32(ucp + 4);
+            printf("%d [h:m  %d:%d]\n", j, (j / 60), (j % 60));
+            printf("    Status: ");
+            j = ucp[9];
+            if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
+                printf("%s\n", bms_status[j]);
+            else
+                printf("unknown [0x%x] background scan status value\n", j);
+            j = sg_get_unaligned_be16(ucp + 10);
+            printf("    Number of background scans performed: %d\n", j);
+            j = sg_get_unaligned_be16(ucp + 12);
+#ifdef SG_LIB_MINGW
+            printf("    Background medium scan progress: %g %%\n",
+                   (double)(j * 100.0 / 65536.0));
+#else
+            printf("    Background medium scan progress: %.2f %%\n",
+                   (double)(j * 100.0 / 65536.0));
+#endif
+            j = sg_get_unaligned_be16(ucp + 14);
+            if (0 == j)
+                printf("    Number of background medium scans performed: 0 "
+                       "[not reported]\n");
+            else
+                printf("    Number of background medium scans performed: "
+                       "%d\n", j);
+            break;
+        default:
+            if (pc > 0x800) {
+                if ((pc >= 0x8000) && (pc <= 0xafff))
+                    printf("  Medium scan parameter # %d [0x%x], vendor "
+                           "specific\n", pc, pc);
+                else
+                    printf("  Medium scan parameter # %d [0x%x], "
+                           "reserved\n", pc, pc);
+                dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+                break;
+            } else
+                printf("  Medium scan parameter # %d [0x%x]\n", pc, pc);
+            if ((pl < 24) || (num < 24)) {
+                if (num < 24)
+                    pr2serr("    truncated by response length, expected at "
+                            "least 24 bytes\n");
+                else
+                    pr2serr("    parameter length >= 24 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf("    Power on minutes when error detected: ");
+            j = sg_get_unaligned_be32(ucp + 4);
+            printf("%d [%d:%d]\n", j, (j / 60), (j % 60));
+            j = (ucp[8] >> 4) & 0xf;
+            if (j <
+                (int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
+                printf("    %s\n", reassign_status[j]);
+            else
+                printf("    Reassign status: reserved [0x%x]\n", j);
+            printf("    sense key: %s  [sk,asc,ascq: 0x%x,0x%x,0x%x]\n",
+                   sg_get_sense_key_str(ucp[8] & 0xf, sizeof(str), str),
+                   ucp[8] & 0xf, ucp[9], ucp[10]);
+            if (ucp[9] || ucp[10])
+                printf("      %s\n", sg_get_asc_ascq_str(ucp[9], ucp[10],
+                                                         sizeof(str), str));
+            if (op->verbose) {
+                printf("    vendor bytes [11 -> 15]: ");
+                for (m = 0; m < 5; ++m)
+                    printf("0x%02x ", ucp[11 + m]);
+                printf("\n");
+            }
+            printf("    LBA (associated with medium error): 0x");
+            for (m = 0; m < 8; ++m)
+                printf("%02x", ucp[16 + m]);
+            printf("\n");
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* PENDING_DEFECTS_SUBPG [0x15,0x1]  introduced: SBC-4 */
+static bool
+show_pending_defects_page(const uint8_t * resp, int len,
+                          const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Pending defects page  [0x15,0x1]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0x0:
+            printf("  Pending defect count:");
+            if ((pl < 8) || (num < 8)) {
+                if (num < 8)
+                    pr2serr("\n    truncated by response length, expected "
+                            "at least 8 bytes\n");
+                else
+                    pr2serr("\n    parameter length >= 8 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf(" %u\n", sg_get_unaligned_be32(ucp + 4));
+            break;
+        default:
+            printf("  Pending defect: %d\n", pc);
+            if ((pl < 16) || (num < 16)) {
+                if (num < 16)
+                    pr2serr("\n    truncated by response length, expected "
+                            "at least 16 bytes\n");
+                else
+                    pr2serr("\n    parameter length >= 16 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf("    LBA: 0x%" PRIx64 " accumulated_power_on_hours: %u\n",
+                   sg_get_unaligned_be64(ucp + 8),
+                   sg_get_unaligned_be32(ucp + 4));
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* BACKGROUND_OP_SUBPG [0x15,0x2]  introduced: SBC-4 rev 7 */
+static bool
+show_background_op_page(const uint8_t * resp, int len,
+                        const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Background operation page  [0x15,0x2]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0x0:
+            printf("  Background operation:");
+            if ((pl < 8) || (num < 8)) {
+                if (num < 8)
+                    pr2serr("\n    truncated by response length, expected "
+                            "at least 8 bytes\n");
+                else
+                    pr2serr("\n    parameter length >= 8 expected, got %d\n",
+                            pl);
+                break;
+            }
+            printf(" BO_STATUS=%d\n", ucp[4]);
+            break;
+        default:
+            printf("  Reserved [parameter_code=0x%x]:\n", pc);
+            dStrHex((const char *)ucp, ((pl < num) ? pl : num), 0);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* LPS misalignment page [0x15,0x3]  introduced: SBC-4 rev 10 */
+static bool
+show_lps_misalignment_page(const uint8_t * resp, int len,
+                           const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("LPS misalignment page  [0x15,0x3]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0x0:
+            printf("  LPS misalignment count: ");
+            if (4 == ucp[3])
+                printf("max lpsm: %" PRIu16 ", count=%" PRIu16 "\n",
+                       sg_get_unaligned_be16(ucp + 4),
+                       sg_get_unaligned_be16(ucp + 6));
+            else
+                printf("<unexpected pc=0 parameter length=%d>\n", ucp[4]);
+            break;
+        default:
+            if (pc <= 0xf000) {
+                if (8 == ucp[3])
+                    printf("  LBA of misaligned block: 0x%" PRIx64 "\n",
+                           sg_get_unaligned_be64(ucp + 8));
+                else
+                    printf("<unexpected pc=0x%x parameter length=%d>\n",
+                           pc, ucp[4]);
+            } else {
+                printf("<unexpected pc=0x%x>\n", pc);
+                dStrHex((const char *)ucp, pl, 0);
+            }
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("        <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+
+/* Sequential access device page [0xc] for tape */
+static bool
+show_sequential_access_page(const uint8_t * resp, int len,
+                            const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    uint64_t ull, gbytes;
+    bool all_set;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Sequential access device page (ssc-3)\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        ull = sg_get_unaligned_be(pl - 4, ucp + 4);
+        all_set = all_bits_set(ucp + 4, pl - 4);
+        gbytes = ull / 1000000000;
+        switch (pc) {
+        case 0:
+            printf("  Data bytes received with WRITE commands: %" PRIu64
+                   " GB", gbytes);
+            if (op->verbose)
+                printf(" [%" PRIu64 " bytes]", ull);
+            printf("\n");
+            break;
+        case 1:
+            printf("  Data bytes written to media by WRITE commands: %" PRIu64
+                   " GB", gbytes);
+            if (op->verbose)
+                printf(" [%" PRIu64 " bytes]", ull);
+            printf("\n");
+            break;
+        case 2:
+            printf("  Data bytes read from media by READ commands: %" PRIu64
+                   " GB", gbytes);
+            if (op->verbose)
+                printf(" [%" PRIu64 " bytes]", ull);
+            printf("\n");
+            break;
+        case 3:
+            printf("  Data bytes transferred by READ commands: %" PRIu64
+                   " GB", gbytes);
+            if (op->verbose)
+                printf(" [%" PRIu64 " bytes]", ull);
+            printf("\n");
+            break;
+        case 4:
+            if (! all_set)
+                printf("  Native capacity from BOP to EOD: %" PRIu64 " MB\n",
+                       ull);
+            break;
+        case 5:
+            if (! all_set)
+                printf("  Native capacity from BOP to EW of current "
+                       "partition: %" PRIu64 " MB\n", ull);
+            break;
+        case 6:
+            if (! all_set)
+                printf("  Minimum native capacity from EW to EOP of current "
+                       "partition: %" PRIu64 " MB\n", ull);
+            break;
+        case 7:
+            if (! all_set)
+                printf("  Native capacity from BOP to current position: %"
+                       PRIu64 " MB\n", ull);
+            break;
+        case 8:
+            if (! all_set)
+                printf("  Maximum native capacity in device object buffer: %"
+                       PRIu64 " MB\n", ull);
+            break;
+        case 0x100:
+            if (ull > 0)
+                printf("  Cleaning action required\n");
+            else
+                printf("  Cleaning action not required (or completed)\n");
+            if (op->verbose)
+                printf("    cleaning value: %" PRIu64 "\n", ull);
+            break;
+        default:
+            if (pc >= 0x8000)
+                printf("  Vendor specific parameter [0x%x] value: %" PRIu64
+                       "\n", pc, ull);
+            else
+                printf("  Reserved parameter [0x%x] value: %" PRIu64 "\n",
+                       pc, ull);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("        <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* 0x14 for tape and ADC */
+static bool
+show_device_stats_page(const uint8_t * resp, int len,
+                       const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    uint64_t ull;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Device statistics page (ssc-3 and adc)\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        if (pc < 0x1000) {
+            ull = sg_get_unaligned_be(pl - 4, ucp + 4);
+            switch (pc) {
+            case 0:
+                printf("  Lifetime media loads: %" PRIu64 "\n", ull);
+                break;
+            case 1:
+                printf("  Lifetime cleaning operations: %" PRIu64 "\n", ull);
+                break;
+            case 2:
+                printf("  Lifetime power on hours: %" PRIu64 "\n", ull);
+                break;
+            case 3:
+                printf("  Lifetime media motion (head) hours: %" PRIu64 "\n",
+                       ull);
+                break;
+            case 4:
+                printf("  Lifetime metres of tape processed: %" PRIu64 "\n",
+                       ull);
+                break;
+            case 5:
+                printf("  Lifetime media motion (head) hours when "
+                       "incompatible media last loaded: %" PRIu64 "\n", ull);
+                break;
+            case 6:
+                printf("  Lifetime power on hours when last temperature "
+                       "condition occurred: %" PRIu64 "\n", ull);
+                break;
+            case 7:
+                printf("  Lifetime power on hours when last power "
+                       "consumption condition occurred: %" PRIu64 "\n", ull);
+                break;
+            case 8:
+                printf("  Media motion (head) hours since last successful "
+                       "cleaning operation: %" PRIu64 "\n", ull);
+                break;
+            case 9:
+                printf("  Media motion (head) hours since 2nd to last "
+                       "successful cleaning: %" PRIu64 "\n", ull);
+                break;
+            case 0xa:
+                printf("  Media motion (head) hours since 3rd to last "
+                       "successful cleaning: %" PRIu64 "\n", ull);
+                break;
+            case 0xb:
+                printf("  Lifetime power on hours when last operator "
+                       "initiated forced reset\n    and/or emergency "
+                       "eject occurred: %" PRIu64 "\n", ull);
+                break;
+            case 0xc:
+                printf("  Lifetime power cycles: %" PRIu64 "\n", ull);
+                break;
+            case 0xd:
+                printf("  Volume loads since last parameter reset: %" PRIu64
+                       "\n", ull);
+                break;
+            case 0xe:
+                printf("  Hard write errors: %" PRIu64 "\n", ull);
+                break;
+            case 0xf:
+                printf("  Hard read errors: %" PRIu64 "\n", ull);
+                break;
+            case 0x10:
+                printf("  Duty cycle sample time: %" PRIu64 " ms\n", ull);
+                break;
+            case 0x11:
+                printf("  Read duty cycle: %" PRIu64 "\n", ull);
+                break;
+            case 0x12:
+                printf("  Write duty cycle: %" PRIu64 "\n", ull);
+                break;
+            case 0x13:
+                printf("  Activity duty cycle: %" PRIu64 "\n", ull);
+                break;
+            case 0x14:
+                printf("  Volume not present duty cycle: %" PRIu64 "\n", ull);
+                break;
+            case 0x15:
+                printf("  Ready duty cycle: %" PRIu64 "\n", ull);
+                break;
+            case 0x16:
+                printf("  MBs transferred from app client in duty cycle "
+                       "sample time: %" PRIu64 "\n", ull);
+                break;
+            case 0x17:
+                printf("  MBs transferred to app client in duty cycle "
+                       "sample time: %" PRIu64 "\n", ull);
+                break;
+            case 0x40:
+                printf("  Drive manufacturer's serial number: %" PRIu64 "\n",
+                       ull);
+                break;
+            case 0x41:
+                printf("  Drive serial number: %" PRIu64 "\n", ull);
+                break;
+            case 0x80:
+                printf("  Medium removal prevented: %" PRIu64 "\n", ull);
+                break;
+            case 0x81:
+                printf("  Maximum recommended mechanism temperature "
+                       "exceeded: %" PRIu64 "\n", ull);
+                break;
+            case 0x1000:
+                printf("  Medium motion hours for each medium type: %" PRIu64
+                       "\n", ull);
+                break;
+            default:
+                printf("  Reserved parameter [0x%x] value: %" PRIu64 "\n", pc,
+                       ull);
+                break;
+            }
+        } else {
+            int k;
+            const uint8_t * p = ucp + 4;
+
+            switch (pc) {
+            case 0x1000:
+                printf("  Media motion (head) hours for each medium type:\n");
+                for (k = 0; ((pl - 4) - k) >= 8; k += 8, p += 8) {
+                    printf("    Density code: 0x%x, Medium type: 0x%x\n",
+                           p[2], p[3]);
+                    printf("      Medium motion hours: %u\n",
+                           sg_get_unaligned_be32(p + 4));
+                }
+                break;
+            default:
+                if (pc >= 0x8000)
+                    printf("  Vendor specific parameter [0x%x], dump in "
+                           "hex:\n", pc);
+                else
+                    printf("  Reserved parameter [0x%x], dump in hex:\n", pc);
+                dStrHex((const char *)ucp, pl, 0);
+                break;
+            }
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("        <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* 0x14 for media changer */
+static bool
+show_media_stats_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    uint64_t ull;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Media statistics page (smc-3)\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        ull = sg_get_unaligned_be(pl - 4, ucp + 4);
+        switch (pc) {
+        case 0:
+            printf("  Number of moves: %" PRIu64 "\n", ull);
+            break;
+        case 1:
+            printf("  Number of picks: %" PRIu64 "\n", ull);
+            break;
+        case 2:
+            printf("  Number of pick retries: %" PRIu64 "\n", ull);
+            break;
+        case 3:
+            printf("  Number of places: %" PRIu64 "\n", ull);
+            break;
+        case 4:
+            printf("  Number of place retries: %" PRIu64 "\n", ull);
+            break;
+        case 5:
+            printf("  Number of volume tags read by volume "
+                   "tag reader: %" PRIu64 "\n", ull);
+            break;
+        case 6:
+            printf("  Number of invalid volume tags returned by "
+                   "volume tag reader: %" PRIu64 "\n", ull);
+            break;
+        case 7:
+            printf("  Number of library door opens: %" PRIu64 "\n", ull);
+            break;
+        case 8:
+            printf("  Number of import/export door opens: %" PRIu64 "\n",
+                   ull);
+            break;
+        case 9:
+            printf("  Number of physical inventory scans: %" PRIu64 "\n",
+                   ull);
+            break;
+        case 0xa:
+            printf("  Number of medium transport unrecovered errors: "
+                   "%" PRIu64 "\n", ull);
+            break;
+        case 0xb:
+            printf("  Number of medium transport recovered errors: "
+                   "%" PRIu64 "\n", ull);
+            break;
+        case 0xc:
+            printf("  Number of medium transport X axis translation "
+                   "unrecovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0xd:
+            printf("  Number of medium transport X axis translation "
+                   "recovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0xe:
+            printf("  Number of medium transport Y axis translation "
+                   "unrecovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0xf:
+            printf("  Number of medium transport Y axis translation "
+                   "recovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0x10:
+            printf("  Number of medium transport Z axis translation "
+                   "unrecovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0x11:
+            printf("  Number of medium transport Z axis translation "
+                   "recovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0x12:
+            printf("  Number of medium transport rotational translation "
+                   "unrecovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0x13:
+            printf("  Number of medium transport rotational translation "
+                   "recovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0x14:
+            printf("  Number of medium transport inversion translation "
+                   "unrecovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0x15:
+            printf("  Number of medium transport inversion translation "
+                   "recovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0x16:
+            printf("  Number of medium transport auxiliary translation "
+                   "unrecovered errors: %" PRIu64 "\n", ull);
+            break;
+        case 0x17:
+            printf("  Number of medium transport auxiliary translation "
+                   "recovered errors: %" PRIu64 "\n", ull);
+            break;
+        default:
+            printf("  Reserved parameter [0x%x] value: %" PRIu64 "\n",
+                   pc, ull);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("        <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* 0x15 for media changer */
+static bool
+show_element_stats_page(const uint8_t * resp, int len,
+                        const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    unsigned int v;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Element statistics page (smc-3) [0x15]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        printf("  Element address: %d\n", pc);
+        v = sg_get_unaligned_be32(ucp + 4);
+        printf("    Number of places: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 8);
+        printf("    Number of place retries: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 12);
+        printf("    Number of picks: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 16);
+        printf("    Number of pick retries: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 20);
+        printf("    Number of determined volume identifiers: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 24);
+        printf("    Number of unreadable volume identifiers: %u\n", v);
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* 0x16 for tape */
+static bool
+show_tape_diag_data_page(const uint8_t * resp, int len,
+                         const struct opts_t * op)
+{
+    int k, num, pl, pc, pcb;
+    unsigned int v;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+    char b[80];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Tape diagnostics data page (ssc-3) [0x16]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        printf("  Parameter code: %d\n", pc);
+        printf("    Density code: 0x%x\n", ucp[6]);
+        printf("    Medium type: 0x%x\n", ucp[7]);
+        v = sg_get_unaligned_be32(ucp + 8);
+        printf("    Lifetime media motion hours: %u\n", v);
+        printf("    Repeat: %d\n", !!(ucp[13] & 0x80));
+        v = ucp[13] & 0xf;
+        printf("    Sense key: 0x%x [%s]\n", v,
+               sg_get_sense_key_str(v, sizeof(b), b));
+        printf("    Additional sense code: 0x%x\n", ucp[14]);
+        printf("    Additional sense code qualifier: 0x%x\n", ucp[15]);
+        if (ucp[14] || ucp[15])
+            printf("      [%s]\n", sg_get_asc_ascq_str(ucp[14], ucp[15],
+                   sizeof(b), b));
+        v = sg_get_unaligned_be32(ucp + 16);
+        printf("    Vendor specific code qualifier: 0x%x\n", v);
+        v = sg_get_unaligned_be32(ucp + 20);
+        printf("    Product revision level: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 24);
+        printf("    Hours since last clean: %u\n", v);
+        printf("    Operation code: 0x%x\n", ucp[28]);
+        printf("    Service action: 0x%x\n", ucp[29] & 0xf);
+        // Check Medium id number for all zeros
+        // ssc4r03.pdf does not define this field, why? xxxxxx
+        for (k = 32; k < 64; ++k) {
+            if(ucp[k])
+                break;
+        }
+        if (64 == k)
+            printf("    Medium id number is 32 bytes of zero\n");
+        else {
+            printf("    Medium id number (in hex):\n");
+            dStrHex((const char *)(ucp + 32), 32, 0);
+        }
+        printf("    Timestamp origin: 0x%x\n", ucp[64] & 0xf);
+        // Check Timestamp for all zeros
+        for (k = 66; k < 72; ++k) {
+            if(ucp[k])
+                break;
+        }
+        if (72 == k)
+            printf("    Timestamp is all zeros:\n");
+        else {
+            printf("    Timestamp:\n");
+            dStrHex((const char *)(ucp + 66), 6, 1);
+        }
+        if (pl > 72) {
+            printf("    Vendor specific:\n");
+            dStrHex((const char *)(ucp + 72), pl - 72, 0);
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* 0x16 for media changer */
+static bool
+show_mchanger_diag_data_page(const uint8_t * resp, int len,
+                             const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    unsigned int v;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+    char b[80];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Media changer diagnostics data page (smc-3) [0x16]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        printf("  Parameter code: %d\n", pc);
+        printf("    Repeat: %d\n", !!(ucp[5] & 0x80));
+        v = ucp[5] & 0xf;
+        printf("    Sense key: 0x%x [%s]\n", v,
+               sg_get_sense_key_str(v, sizeof(b), b));
+        printf("    Additional sense code: 0x%x\n", ucp[6]);
+        printf("    Additional sense code qualifier: 0x%x\n", ucp[7]);
+        if (ucp[6] || ucp[7])
+            printf("      [%s]\n", sg_get_asc_ascq_str(ucp[6], ucp[7],
+                   sizeof(b), b));
+        v = sg_get_unaligned_be32(ucp + 8);
+        printf("    Vendor specific code qualifier: 0x%x\n", v);
+        v = sg_get_unaligned_be32(ucp + 12);
+        printf("    Product revision level: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 16);
+        printf("    Number of moves: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 20);
+        printf("    Number of pick: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 24);
+        printf("    Number of pick retries: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 28);
+        printf("    Number of places: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 32);
+        printf("    Number of place retries: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 36);
+        printf("    Number of determined volume identifiers: %u\n", v);
+        v = sg_get_unaligned_be32(ucp + 40);
+        printf("    Number of unreadable volume identifiers: %u\n", v);
+        printf("    Operation code: 0x%x\n", ucp[44]);
+        printf("    Service action: 0x%x\n", ucp[45] & 0xf);
+        printf("    Media changer error type: 0x%x\n", ucp[46]);
+        printf("    MTAV: %d\n", !!(ucp[47] & 0x8));
+        printf("    IAV: %d\n", !!(ucp[47] & 0x4));
+        printf("    LSAV: %d\n", !!(ucp[47] & 0x2));
+        printf("    DAV: %d\n", !!(ucp[47] & 0x1));
+        v = sg_get_unaligned_be16(ucp + 48);
+        printf("    Medium transport address: 0x%x\n", v);
+        v = sg_get_unaligned_be16(ucp + 50);
+        printf("    Intial address: 0x%x\n", v);
+        v = sg_get_unaligned_be16(ucp + 52);
+        printf("    Last successful address: 0x%x\n", v);
+        v = sg_get_unaligned_be16(ucp + 54);
+        printf("    Destination address: 0x%x\n", v);
+        if (pl > 91) {
+            printf("    Volume tag information:\n");
+            dStrHex((const char *)(ucp + 56), 36, 0);
+        }
+        if (pl > 99) {
+            printf("    Timestamp origin: 0x%x\n", ucp[92] & 0xf);
+            printf("    Timestamp:\n");
+            dStrHex((const char *)(ucp + 94), 6, 1);
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* Helper for show_volume_stats_page() */
+static void
+volume_stats_partition(const uint8_t * xp, int len, bool in_hex)
+{
+    int dl, pn, k;
+    bool all_ffs, ffs_last_fe;
+    uint8_t uc;
+    uint64_t ull;
+
+    while (len > 3) {
+        dl = xp[0] + 1;
+        if (dl < 3)
+            return;
+        pn = sg_get_unaligned_be16(xp + 2);
+        for (k = 0, all_ffs = false, ffs_last_fe = false; k < (dl - 4); ++k) {
+            uc = xp[4 + k];
+            if (uc < 0xfe)
+                break;
+            if ((k < (dl - 5)) && (0xfe == uc))
+                break;
+            if (k == (dl - 5)) {
+                if (0xff == uc)
+                    all_ffs = true;
+                else if (0xfe == uc)
+                    ffs_last_fe = true;
+            }
+        }
+        if ((! all_ffs) && (! ffs_last_fe)) {
+            ull = sg_get_unaligned_be(dl - 4, xp + 4);
+            if (in_hex)
+                printf("    partition number: %d, partition record data "
+                       "counter: 0x%" PRIx64 "\n", pn, ull);
+            else
+                printf("    partition number: %d, partition record data "
+                       "counter: %" PRIu64 "\n", pn, ull);
+        } else if (all_ffs)
+            printf("    partition number: %d, partition record data "
+                   "counter is all 0xFFs\n", pn);
+        else
+            printf("    partition number: %d, partition record data "
+                   "counter is all 0xFFs apart\n    from a trailing "
+                   "0xFE\n", pn);
+        xp += dl;
+        len -= dl;
+    }
+}
+
+/* Volume Statistics log page (ssc-4) [0x17, 0x1-0xf] */
+static bool
+show_volume_stats_page(const uint8_t * resp, int len,
+                       const struct opts_t * op)
+{
+    int num, pl, pc, pcb, spf, subpg_code;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+    char b[64];
+
+    spf = !!(resp[0] & 0x40);
+    subpg_code = spf ? resp[1] : 0;
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex))) {
+        if (0 == subpg_code)
+            printf("Volume statistics page (ssc-4) but subpage=0, abnormal: "
+                   "treat like subpage=1\n");
+        else if (subpg_code < 0x10)
+            printf("Volume statistics page (ssc-4), subpage=%d\n",
+                   subpg_code);
+        else {
+            printf("Volume statistics page (ssc-4), subpage=%d; Reserved, "
+                   "skip\n", subpg_code);
+            return false;
+        }
+    }
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+
+        switch (pc) {
+        case 0:
+            printf("  Page valid: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 1:
+            printf("  Thread count: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 2:
+            printf("  Total data sets written: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 3:
+            printf("  Total write retries: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 4:
+            printf("  Total unrecovered write errors: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 5:
+            printf("  Total suspended writes: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 6:
+            printf("  Total fatal suspended writes: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 7:
+            printf("  Total data sets read: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 8:
+            printf("  Total read retries: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 9:
+            printf("  Total unrecovered read errors: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0xa:
+            printf("  Total suspended reads: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0xb:
+            printf("  Total fatal suspended reads: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0xc:
+            printf("  Last mount unrecovered write errors: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0xd:
+            printf("  Last mount unrecovered read errors: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0xe:
+            printf("  Last mount megabytes written: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0xf:
+            printf("  Last mount megabytes read: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x10:
+            printf("  Lifetime megabytes written: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x11:
+            printf("  Lifetime megabytes read: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x12:
+            printf("  Last load write compression ratio: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x13:
+            printf("  Last load read compression ratio: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x14:
+            printf("  Medium mount time: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x15:
+            printf("  Medium ready time: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x16:
+            printf("  Total native capacity [MB]: %s\n",
+                   num_or_unknown(ucp + 4, pl - 4, false, b, sizeof(b)));
+            break;
+        case 0x17:
+            printf("  Total used native capacity [MB]: %s\n",
+                   num_or_unknown(ucp + 4, pl - 4, false, b, sizeof(b)));
+            break;
+        case 0x40:
+            printf("  Volume serial number: %.*s\n", pl - 4, ucp + 4);
+            break;
+        case 0x41:
+            printf("  Tape lot identifier: %.*s\n", pl - 4, ucp + 4);
+            break;
+        case 0x42:
+            printf("  Volume barcode: %.*s\n", pl - 4, ucp + 4);
+            break;
+        case 0x43:
+            printf("  Volume manufacturer: %.*s\n", pl - 4, ucp + 4);
+            break;
+        case 0x44:
+            printf("  Volume license code: %.*s\n", pl - 4, ucp + 4);
+            break;
+        case 0x45:
+            printf("  Volume personality: %.*s\n", pl - 4, ucp + 4);
+            break;
+        case 0x80:
+            printf("  Write protect: %s\n",
+                   num_or_unknown(ucp + 4, pl - 4, false, b, sizeof(b)));
+            break;
+        case 0x81:
+            printf("  WORM: %s\n",
+                   num_or_unknown(ucp + 4, pl - 4, false, b, sizeof(b)));
+            break;
+        case 0x82:
+            printf("  Maximum recommended tape path temperature exceeded: "
+                   "%s\n", num_or_unknown(ucp + 4, pl - 4, false, b,
+                                          sizeof(b)));
+            break;
+        case 0x100:
+            printf("  Volume write mounts: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x101:
+            printf("  Beginning of medium passes: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x102:
+            printf("  Middle of medium passes: %" PRIu64 "\n",
+                   sg_get_unaligned_be(pl - 4, ucp + 4));
+            break;
+        case 0x200:
+            printf("  Logical position of first encrypted logical object:\n");
+            volume_stats_partition(ucp + 4, pl - 4, true);
+            break;
+        case 0x201:
+            printf("  Logical position of first unencrypted logical object "
+                   "after first\n  encrypted logical object:\n");
+            volume_stats_partition(ucp + 4, pl - 4, true);
+            break;
+        case 0x202:
+            printf("  Native capacity partition(s) [MB]:\n");
+            volume_stats_partition(ucp + 4, pl - 4, false);
+            break;
+        case 0x203:
+            printf("  Used native capacity partition(s) [MB]:\n");
+            volume_stats_partition(ucp + 4, pl - 4, false);
+            break;
+        case 0x204:
+            printf("  Remaining native capacity partition(s) [MB]:\n");
+            volume_stats_partition(ucp + 4, pl - 4, false);
+            break;
+        case 0x300:
+            printf("  Mount history, payload in hex:\n");
+            // xxxxxxxx TODO
+            dStrHex((const char *)(ucp + 4), pl - 4, 0);
+            break;
+
+        default:
+            if (pc >= 0xf000)
+                printf("  Vendor specific parameter code (0x%x), payload "
+                       "in hex\n", pc);
+            else
+                printf("  Reserved parameter code (0x%x), payload in hex\n",
+                       pc);
+            dStrHex((const char *)(ucp + 4), pl - 4, 0);
+            break;
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("        <%s>\n", pcb_str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+static const char * tape_alert_strs[] = {
+    "<parameter code 0, unknown>",              /* 0x0 */
+    "Read warning",
+    "Write warning",
+    "Hard error",
+    "Media",
+    "Read failure",
+    "Write failure",
+    "Media life",
+    "Not data grade",                           /* 0x8 */
+    "Write protect",
+    "No removal",
+    "Cleaning media",
+    "Unsupported format",
+    "Recoverable mechanical cartridge failure",
+    "Unrecoverable mechanical cartridge failure",
+    "Memory chip in cartridge failure",
+    "Forced eject",                             /* 0x10 */
+    "Read only format",
+    "Tape directory corrupted on load",
+    "Nearing media life",
+    "Cleaning required",
+    "Cleaning requested",
+    "Expired cleaning media",
+    "Invalid cleaning tape",
+    "Retension requested",                      /* 0x18 */
+    "Dual port interface error",
+    "Cooling fan failing",
+    "Power supply failure",
+    "Power consumption",
+    "Drive maintenance",
+    "Hardware A",
+    "Hardware B",
+    "Interface",                                /* 0x20 */
+    "Eject media",
+    "Microcode update fail",
+    "Drive humidity",
+    "Drive temperature",
+    "Drive voltage",
+    "Predictive failure",
+    "Diagnostics required",
+    "Obsolete (28h)",                           /* 0x28 */
+    "Obsolete (29h)",
+    "Obsolete (2Ah)",
+    "Obsolete (2Bh)",
+    "Obsolete (2Ch)",
+    "Obsolete (2Dh)",
+    "Obsolete (2Eh)",
+    "Reserved (2Fh)",
+    "Reserved (30h)",                           /* 0x30 */
+    "Reserved (31h)",
+    "Lost statistics",
+    "Tape directory invalid at unload",
+    "Tape system area write failure",
+    "Tape system area read failure",
+    "No start of data",
+    "Loading failure",
+    "Unrecoverable unload failure",             /* 0x38 */
+    "Automation interface failure",
+    "Firmware failure",
+    "WORM medium - integrity check failed",
+    "WORM medium - overwrite attempted",
+};
+
+/* TAPE_ALERT_LPAGE [0x2e] */
+static bool
+show_tape_alert_ssc_page(const uint8_t * resp, int len,
+                         const struct opts_t * op)
+{
+    int num, pl, pc, pcb, flag;
+    const uint8_t * ucp;
+    char str[PCB_STR_LEN];
+
+    /* N.B. the Tape alert log page for smc-3 is different */
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Tape alert page (ssc-3) [0x2e]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        flag = ucp[4] & 1;
+        if (op->verbose && (0 == op->do_brief) && flag)
+            printf("  >>>> ");
+        if ((0 == op->do_brief) || op->verbose || flag) {
+            if (pc < (int)(sizeof(tape_alert_strs) /
+                           sizeof(tape_alert_strs[0])))
+                printf("  %s: %d\n", tape_alert_strs[pc], flag);
+            else
+                printf("  Reserved parameter code 0x%x, flag: %d\n", pc,
+                       flag);
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, str, sizeof(str));
+            printf("        <%s>\n", str);
+        }
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* 0x37 */
+static bool
+show_seagate_cache_page(const uint8_t * resp, int len,
+                        const struct opts_t * op)
+{
+    int num, pl, pc, pcb;
+    const uint8_t * ucp;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Seagate cache page [0x37]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        switch (pc) {
+        case 0: printf("  Blocks sent to initiator"); break;
+        case 1: printf("  Blocks received from initiator"); break;
+        case 2:
+            printf("  Blocks read from cache and sent to initiator");
+            break;
+        case 3:
+            printf("  Number of read and write commands whose size "
+                   "<= segment size");
+            break;
+        case 4:
+            printf("  Number of read and write commands whose size "
+                   "> segment size"); break;
+        default: printf("  Unknown Seagate parameter code = 0x%x", pc); break;
+        }
+        printf(" = %" PRIu64 "", sg_get_unaligned_be(pl - 4, ucp + 4));
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+/* 0x3e */
+static bool
+show_seagate_factory_page(const uint8_t * resp, int len,
+                          const struct opts_t * op)
+{
+    int num, pl, pc, pcb, valid;
+    const uint8_t * ucp;
+    uint64_t ull;
+    char pcb_str[PCB_STR_LEN];
+
+    if (op->verbose || ((0 == op->do_raw) && (0 == op->do_hex)))
+        printf("Seagate/Hitachi factory page [0x3e]\n");
+    num = len - 4;
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(ucp + 0);
+        pcb = ucp[2];
+        pl = ucp[3] + 4;
+        if (op->filter_given) {
+            if (pc != op->filter)
+                goto skip;
+            if (op->do_raw) {
+                dStrRaw((const char *)ucp, pl);
+                break;
+            } else if (op->do_hex) {
+                dStrHex((const char *)ucp, pl, ((1 == op->do_hex) ? 1 : -1));
+                break;
+            }
+        }
+        valid = 1;
+        switch (pc) {
+        case 0: printf("  number of hours powered up"); break;
+        case 8: printf("  number of minutes until next internal SMART test");
+            break;
+        default:
+            valid = 0;
+            printf("  Unknown Seagate/Hitachi parameter code = 0x%x", pc);
+            break;
+        }
+        if (valid) {
+            ull = sg_get_unaligned_be(pl - 4, ucp + 4);
+            if (0 == pc)
+                printf(" = %.2f", ((double)ull) / 60.0 );
+            else
+                printf(" = %" PRIu64 "", ull);
+        }
+        if (op->do_pcb) {
+            get_pcb_str(pcb, pcb_str, sizeof(pcb_str));
+            printf("\n        <%s>\n", pcb_str);
+        } else
+            printf("\n");
+        if (op->filter_given)
+            break;
+skip:
+        num -= pl;
+        ucp += pl;
+    }
+    return true;
+}
+
+static void
+show_ascii_page(const uint8_t * resp, int len, const struct opts_t * op)
+{
+    int pg_code, subpg_code, spf;
+    bool done = false;
+    const struct log_elem * lep;
+
+    if (len < 3) {
+        pr2serr("%s: response has bad length: %d\n", __func__, len);
+        return;
+    }
+    spf = !!(resp[0] & 0x40);
+    pg_code = resp[0] & 0x3f;
+    subpg_code = spf ? resp[1] : 0;
+    if ((SUPP_SPGS_SUBPG == subpg_code) && (SUPP_PAGES_LPAGE != pg_code)) {
+        done = show_supported_pgs_sub_page(resp, len, op);
+        if (done)
+            return;
+    }
+    lep = pg_subpg_pdt_search(pg_code, subpg_code, op->dev_pdt);
+    if (lep && lep->show_pagep)
+        done = (*lep->show_pagep)(resp, len, op);
+
+    if (! done) {
+        if (spf)
+            printf("Unable to decode page = 0x%x, subpage = 0x%x, here is "
+                   "hex:\n", pg_code, subpg_code);
+        else
+            printf("Unable to decode page = 0x%x, here is hex:\n", pg_code);
+        if (len > 128) {
+            dStrHex((const char *)resp, 64, 1);
+            printf(" .....  [truncated after 64 of %d bytes (use '-H' to "
+                   "see the rest)]\n", len);
+        }
+        else
+            dStrHex((const char *)resp, len, 1);
+    }
+}
+
+static int
+fetchTemperature(int sg_fd, uint8_t * resp, int max_len, struct opts_t * op)
+{
+    int len;
+    int res = 0;
+
+    op->pg_code = TEMPERATURE_LPAGE;
+    op->subpg_code = NOT_SPG_SUBPG;
+    res = do_logs(sg_fd, resp, max_len, op);
+    if (0 == res) {
+        len = sg_get_unaligned_be16(resp + 2) + 4;
+        if (op->do_raw)
+            dStrRaw((const char *)resp, len);
+        else if (op->do_hex)
+            dStrHex((const char *)resp, len, (1 == op->do_hex));
+        else
+            show_temperature_page(resp, len, op);
+    } else if (SG_LIB_CAT_NOT_READY == res)
+        pr2serr("Device not ready\n");
+    else {
+        op->pg_code = IE_LPAGE;
+        res = do_logs(sg_fd, resp, max_len, op);
+        if (0 == res) {
+            len = sg_get_unaligned_be16(resp + 2) + 4;
+            if (op->do_raw)
+                dStrRaw((const char *)resp, len);
+            else if (op->do_hex)
+                dStrHex((const char *)resp, len, (1 == op->do_hex));
+            else
+                show_ie_page(resp, len, op);
+        } else
+            pr2serr("Unable to find temperature in either Temperature or "
+                    "IE log page\n");
+    }
+    sg_cmds_close_device(sg_fd);
+    return (res >= 0) ? res : SG_LIB_CAT_OTHER;
+}
+
+static int
+decode_pg_arg(struct opts_t * op)
+{
+    int n, nn;
+    const struct log_elem * lep;
+    char * cp;
+    char b[80];
+
+    if (isalpha(op->pg_arg[0])) {
+        if (strlen(op->pg_arg) >= (sizeof(b) - 1)) {
+            pr2serr("argument to '--page=' is too long\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        strcpy(b, op->pg_arg);
+        cp = (char *)strchr(b, ',');
+        if (cp)
+            *cp = '\0';
+        lep = acron_search(b);
+        if (NULL == lep) {
+            pr2serr("bad argument to '--page=' no acronyn match to "
+                    "'%s'\n", b);
+            pr2serr("  Try using '-e' or'-ee' to see available "
+                    "acronyns\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        op->lep = lep;
+        op->pg_code = lep->pg_code;
+        if (cp) {
+            nn = sg_get_num_nomult(cp + 1);
+            if ((nn < 0) || (nn > 255)) {
+                pr2serr("Bad second value in argument to "
+                        "'--page='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->subpg_code = nn;
+        } else
+            op->subpg_code = lep->subpg_code;
+    } else { /* numeric arg: either 'pg_num' or 'pg_num,subpg_num' */
+        cp = (char *)strchr(op->pg_arg, ',');
+        n = sg_get_num_nomult(op->pg_arg);
+        if ((n < 0) || (n > 63)) {
+            pr2serr("Bad argument to '--page='\n");
+            usage(1);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (cp) {
+            nn = sg_get_num_nomult(cp + 1);
+            if ((nn < 0) || (nn > 255)) {
+                pr2serr("Bad second value in argument to "
+                        "'--page='\n");
+                usage(1);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else
+            nn = 0;
+        op->pg_code = n;
+        op->subpg_code = nn;
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, pg_len, res, resp_len;
+    int in_len = -1;
+    int ret = 0;
+    struct sg_simple_inquiry_resp inq_out;
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    memset(rsp_buff, 0, sizeof(rsp_buff));
+    /* N.B. some disks only give data for current cumulative */
+    op->page_control = 1;
+    op->dev_pdt = -1;
+    res = process_cl(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        usage_for(op->do_help, op);
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+    if ((op->do_enumerate > 0) || (op->do_enum_vendor > 0)) {
+        if (op->device_name && op->verbose)
+            pr2serr("Warning: device: %s is being ignored\n",
+                    op->device_name);
+        if ((op->do_enum_vendor > 0) && (0 == op->do_enumerate))
+            op->do_enumerate = 1;
+        enumerate_pages(op);
+        return 0;
+    }
+
+    if (NULL == op->device_name) {
+        if (op->in_fn) {
+            const struct log_elem * lep;
+            const unsigned char * ucp;
+            int pg_code, subpg_code, pdt, n;
+            uint16_t u;
+
+            if (f2hex_arr(op->in_fn, op->do_raw, 0, rsp_buff, &in_len,
+                          sizeof(rsp_buff)))
+                return SG_LIB_FILE_ERROR;
+            if (op->do_raw)
+                op->do_raw = 0;    /* can interfere on decode */
+            if (in_len < 4) {
+                pr2serr("--in=%s only decoded %d bytes (needs 4 at least)\n",
+                        op->in_fn, in_len);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if (op->pg_arg && (0 == op->do_brief))
+                pr2serr(">>> --page=%s option is being ignored, using values "
+                        "in file: %s\n", op->pg_arg, op->in_fn);
+            for (ucp = rsp_buff, k = 0; k < in_len; ucp += n, k += n) {
+                pg_code = ucp[0] & 0x3f;
+                subpg_code = (ucp[0] & 0x40) ? ucp[1] : 0;
+                u = sg_get_unaligned_be16(ucp + 2);
+                n = u + 4;
+                if (n > (in_len - k)) {
+                    pr2serr("bytes decoded remaining (%d) less than lpage "
+                            "length (%d), try decoding anyway\n", in_len - k,
+                            n);
+                    n = in_len - k;
+                }
+                pdt = (op->filter_given && (op->filter >= 0)) ?
+                      op->filter : -1;
+                op->dev_pdt = pdt;
+                lep = pg_subpg_pdt_search(pg_code, subpg_code, pdt);
+                if (lep) {
+                    if (lep->show_pagep)
+                        (*lep->show_pagep)(ucp, n, op);
+                    else
+                        printf("Unable to decode %s [%s]\n", lep->name,
+                               lep->acron);
+                } else {
+                    printf("Unable to decode page=0x%x", pg_code);
+                    if (subpg_code > 0)
+                        printf(", subpage=0x%x", subpg_code);
+                    if (pdt >= 0)
+                        printf(", pdt=0x%x\n", pdt);
+                    else
+                        printf("\n");
+                }
+            }
+            return 0;
+        }
+        pr2serr("No DEVICE argument given\n");
+        usage_for(1, op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_select) {
+        if (op->do_temperature) {
+            pr2serr("--select cannot be used with --temperature\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->do_transport) {
+            pr2serr("--select cannot be used with --transport\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    } else if (op->do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+    if (op->do_all) {
+        if (op->do_select) {
+            pr2serr("--all conflicts with --select\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->filter) {
+            pr2serr("--all conflicts with --filter\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (op->in_fn) {
+        if (! op->do_select) {
+            pr2serr("--in=FN can only be used with --select when DEVICE "
+                    "given\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (f2hex_arr(op->in_fn, op->do_raw, 0, rsp_buff, &in_len,
+                      sizeof(rsp_buff)))
+            return SG_LIB_FILE_ERROR;
+    }
+    if (op->pg_arg) {
+        if (op->do_all) {
+            if (0 == op->do_brief)
+                pr2serr(">>> warning: --page=%s ignored when --all given\n",
+                        op->pg_arg);
+        } else {
+            res = decode_pg_arg(op);
+            if (res)
+                return res;
+        }
+    }
+
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+    win32_spt_init_state = scsi_pt_win32_spt_state();
+    if (op->verbose > 4)
+        pr2serr("Initial win32 SPT interface state: %s\n",
+                win32_spt_init_state ? "direct" : "indirect");
+#endif
+#endif
+    sg_fd = sg_cmds_open_device(op->device_name, op->o_readonly,
+                                op->verbose);
+    if ((sg_fd < 0) && (0 == op->o_readonly))
+        sg_fd = sg_cmds_open_device(op->device_name, 1 /* ro */,
+                                    op->verbose);
+    if (sg_fd < 0) {
+        pr2serr("error opening file: %s: %s \n", op->device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (op->do_list || op->do_all) {
+        op->pg_code = SUPP_PAGES_LPAGE;
+        if ((op->do_list > 1) || (op->do_all > 1))
+            op->subpg_code = SUPP_SPGS_SUBPG;
+    }
+    if (op->do_transport) {
+        if ((op->pg_code > 0) || (op->subpg_code > 0) ||
+            op->do_temperature) {
+            pr2serr("'-T' should not be mixed with options implying other "
+                    "pages\n");
+            return SG_LIB_FILE_ERROR;
+        }
+        op->pg_code = PROTO_SPECIFIC_LPAGE;
+    }
+    pg_len = 0;
+
+    if (op->no_inq < 2)  {
+        if (sg_simple_inquiry(sg_fd, &inq_out, 1, op->verbose)) {
+            pr2serr("%s doesn't respond to a SCSI INQUIRY\n",
+                    op->device_name);
+            sg_cmds_close_device(sg_fd);
+            return SG_LIB_CAT_OTHER;
+        }
+        op->dev_pdt = inq_out.peripheral_type;
+        if ((0 == op->do_raw) && (0 == op->do_hex) && (0 == op->do_name) &&
+            (0 == op->no_inq) && (0 == op->do_brief))
+            printf("    %.8s  %.16s  %.4s\n", inq_out.vendor,
+                   inq_out.product, inq_out.revision);
+    } else
+        memset(&inq_out, 0, sizeof(inq_out));
+
+    if (1 == op->do_temperature)
+        return fetchTemperature(sg_fd, rsp_buff, SHORT_RESP_LEN, op);
+
+    if (op->do_select) {
+        k = sg_ll_log_select(sg_fd, !!(op->do_pcreset), op->do_sp,
+                             op->page_control, op->pg_code, op->subpg_code,
+                             rsp_buff, ((in_len > 0) ? in_len : 0),
+                             1, op->verbose);
+        if (k) {
+            if (SG_LIB_CAT_NOT_READY == k)
+                pr2serr("log_select: device not ready\n");
+            else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+                pr2serr("log_select: field in cdb illegal\n");
+            else if (SG_LIB_CAT_INVALID_OP == k)
+                pr2serr("log_select: not supported\n");
+            else if (SG_LIB_CAT_UNIT_ATTENTION == k)
+                pr2serr("log_select: unit attention\n");
+            else if (SG_LIB_CAT_ABORTED_COMMAND == k)
+                pr2serr("log_select: aborted command\n");
+            else
+                pr2serr("log_select: failed (%d), try '-v' for more "
+                        "information\n", k);
+        }
+        return (k >= 0) ?  k : SG_LIB_CAT_OTHER;
+    }
+    resp_len = (op->maxlen > 0) ? op->maxlen : MX_ALLOC_LEN;
+    res = do_logs(sg_fd, rsp_buff, resp_len, op);
+    if (0 == res) {
+        pg_len = sg_get_unaligned_be16(rsp_buff + 2);
+        if ((pg_len + 4) > resp_len) {
+            pr2serr("Only fetched %d bytes of response (available: %d "
+                    "bytes)\n    truncate output\n",
+                   resp_len, pg_len + 4);
+            pg_len = resp_len - 4;
+        }
+    } else if (SG_LIB_CAT_INVALID_OP == res)
+        pr2serr("log_sense: not supported\n");
+    else if (SG_LIB_CAT_NOT_READY == res)
+        pr2serr("log_sense: device not ready\n");
+    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+        pr2serr("log_sense: field in cdb illegal\n");
+    else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+        pr2serr("log_sense: unit attention\n");
+    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
+        pr2serr("log_sense: aborted command\n");
+    if (0 == op->do_all) {
+        if (op->filter_given) {
+            if (op->do_hex > 2)
+                dStrHex((const char *)rsp_buff, pg_len + 4,
+                        (op->do_hex < 4));
+            else
+                show_ascii_page(rsp_buff, pg_len + 4, op);
+        } else if (op->do_raw)
+            dStrRaw((const char *)rsp_buff, pg_len + 4);
+        else if (op->do_hex > 1)
+            dStrHex((const char *)rsp_buff, pg_len + 4,
+                    (2 == op->do_hex) ? 0 : -1);
+        else if (pg_len > 1) {
+            if (op->do_hex) {
+                if (rsp_buff[0] & 0x40)
+                    printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, "
+                           "page_len=0x%x\n", rsp_buff[0] & 0x3f, rsp_buff[1],
+                           !!(rsp_buff[0] & 0x80), pg_len);
+                else
+                    printf("Log page code=0x%x, DS=%d, SPF=0, page_len=0x%x\n",
+                           rsp_buff[0] & 0x3f, !!(rsp_buff[0] & 0x80), pg_len);
+                dStrHex((const char *)rsp_buff, pg_len + 4, 1);
+            }
+            else
+                show_ascii_page(rsp_buff, pg_len + 4, op);
+        }
+    }
+    ret = res;
+
+    if (op->do_all && (pg_len > 1)) {
+        int my_len = pg_len;
+        int spf;
+        uint8_t parr[1024];
+
+        spf = !!(rsp_buff[0] & 0x40);
+        if (my_len > (int)sizeof(parr)) {
+            pr2serr("Unexpectedly large page_len=%d, trim to %d\n", my_len,
+                    (int)sizeof(parr));
+            my_len = sizeof(parr);
+        }
+        memcpy(parr, rsp_buff + 4, my_len);
+        for (k = 0; k < my_len; ++k) {
+            if (0 == op->do_raw)
+                printf("\n");
+            op->pg_code = parr[k] & 0x3f;
+            if (spf)
+                op->subpg_code = parr[++k];
+            else
+                op->subpg_code = NOT_SPG_SUBPG;
+
+            res = do_logs(sg_fd, rsp_buff, resp_len, op);
+            if (0 == res) {
+                pg_len = sg_get_unaligned_be16(rsp_buff + 2);
+                if ((pg_len + 4) > resp_len) {
+                    pr2serr("Only fetched %d bytes of response, truncate "
+                            "output\n", resp_len);
+                    pg_len = resp_len - 4;
+                }
+                if (op->do_raw)
+                    dStrRaw((const char *)rsp_buff, pg_len + 4);
+                else if (op->do_hex > 1)
+                    dStrHex((const char *)rsp_buff, pg_len + 4,
+                            (2 == op->do_hex) ? 0 : -1);
+                else if (op->do_hex) {
+                    if (rsp_buff[0] & 0x40)
+                        printf("Log page code=0x%x,0x%x, DS=%d, SPF=1, page_"
+                               "len=0x%x\n", rsp_buff[0] & 0x3f, rsp_buff[1],
+                               !!(rsp_buff[0] & 0x80), pg_len);
+                    else
+                        printf("Log page code=0x%x, DS=%d, SPF=0, page_len="
+                               "0x%x\n", rsp_buff[0] & 0x3f,
+                               !!(rsp_buff[0] & 0x80), pg_len);
+                    dStrHex((const char *)rsp_buff, pg_len + 4, 1);
+                }
+                else
+                    show_ascii_page(rsp_buff, pg_len + 4, op);
+            } else if (SG_LIB_CAT_INVALID_OP == res)
+                pr2serr("log_sense: page=0x%x,0x%x not supported\n",
+                        op->pg_code, op->subpg_code);
+            else if (SG_LIB_CAT_NOT_READY == res)
+                pr2serr("log_sense: device not ready\n");
+            else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+                pr2serr("log_sense: field in cdb illegal "
+                        "[page=0x%x,0x%x]\n", op->pg_code, op->subpg_code);
+            else if (SG_LIB_CAT_UNIT_ATTENTION == res)
+                pr2serr("log_sense: unit attention\n");
+            else if (SG_LIB_CAT_ABORTED_COMMAND == res)
+                pr2serr("log_sense: aborted command\n");
+            else
+                pr2serr("log_sense: failed, try '-v' for more information\n");
+        }
+    }
+    sg_cmds_close_device(sg_fd);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_luns.c b/sg3_utils/src/sg_luns.c
new file mode 100644
index 0000000..b7c0d5b
--- /dev/null
+++ b/sg3_utils/src/sg_luns.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (c) 2004-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI REPORT LUNS command to the given SCSI device
+ * and decodes the response.
+ */
+
+static const char * version_str = "1.30 20151219";
+
+#define MAX_RLUNS_BUFF_LEN (1024 * 1024)
+#define DEF_RLUNS_BUFF_LEN (1024 * 8)
+
+
+static struct option long_options[] = {
+        {"decode", no_argument, 0, 'd'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+#ifdef SG_LIB_LINUX
+        {"linux", no_argument, 0, 'l'},
+#endif
+        {"lu_cong", no_argument, 0, 'L'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"quiet", no_argument, 0, 'q'},
+        {"raw", no_argument, 0, 'r'},
+        {"readonly", no_argument, 0, 'R'},
+        {"select", required_argument, 0, 's'},
+        {"test", required_argument, 0, 't'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+#ifdef SG_LIB_LINUX
+    pr2serr("Usage: sg_luns    [--decode] [--help] [--hex] [--linux] "
+            "[--lu_cong]\n"
+            "                  [--maxlen=LEN] [--quiet] [--raw] "
+            "[--readonly]\n"
+            "                  [--select=SR] [--verbose] [--version] "
+            "DEVICE\n");
+#else
+    pr2serr("Usage: sg_luns    [--decode] [--help] [--hex] [--lu_cong] "
+            "[--maxlen=LEN]\n"
+            "                  [--quiet] [--raw] [--readonly] "
+            "[--select=SR]\n"
+            "                  [--verbose] [--version] DEVICE\n");
+#endif
+    pr2serr("     or\n"
+            "       sg_luns    --test=ALUN [--hex] [--lu_cong] [--verbose]\n"
+            "  where:\n"
+            "    --decode|-d        decode all luns into component parts\n"
+            "    --help|-h          print out usage message\n"
+            "    --hex|-H           output response in hexadecimal; used "
+            "twice\n"
+            "                       shows decoded values in hex\n");
+#ifdef SG_LIB_LINUX
+    pr2serr("    --linux|-l         show Linux integer lun after T10 "
+            "representation\n");
+#endif
+    pr2serr("    --lu_cong|-L       decode as if LU_CONG is set; used "
+            "twice:\n"
+            "                       decode as if LU_CONG is clear\n"
+            "    --maxlen=LEN|-m LEN    max response length (allocation "
+            "length in cdb)\n"
+            "                           (def: 0 -> %d bytes)\n"
+            "    --quiet|-q         output only ASCII hex lun values\n"
+            "    --raw|-r           output response in binary\n"
+            "    --readonly|-R      open DEVICE read-only (def: read-write)\n"
+            "    --select=SR|-s SR    select report SR (def: 0)\n"
+            "                          0 -> luns apart from 'well "
+            "known' lus\n"
+            "                          1 -> only 'well known' "
+            "logical unit numbers\n"
+            "                          2 -> all luns\n"
+            "                          0x10 -> administrative luns\n"
+            "                          0x11 -> admin luns + "
+            "non-conglomerate luns\n"
+            "                          0x12 -> admin lun + its "
+            "subsidiary luns\n"
+            "    --test=ALUN|-t ALUN    decode ALUN and ignore most other "
+            "options\n"
+            "                           and DEVICE (apart from '-H')\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "Performs a SCSI REPORT LUNS command or decodes the given ALUN. "
+            "When SR is\n0x10 or 0x11 DEVICE must be LUN 0 or REPORT LUNS "
+            "well known logical unit;\nwhen SR is 0x12 DEVICE must be an "
+            "administrative logical unit. When the\n--test=ALUN option is "
+            "given, decodes ALUN rather than sending a REPORT\nLUNS "
+            "command.\n", DEF_RLUNS_BUFF_LEN );
+}
+
+/* Decoded according to SAM-5 rev 10. Note that one draft: BCC rev 0,
+ * defines its own "bridge addressing method" in place of the SAM-3
+ * "logical addressing method".  */
+static void
+decode_lun(const char * leadin, const unsigned char * lunp, int lu_cong,
+           int do_hex, int verbose)
+{
+    int k, j, x, a_method, bus_id, target, lun, len_fld, e_a_method;
+    int next_level, lu_cong_admin;
+    unsigned char not_spec[8] = {0xff, 0xff, 0xff, 0xff,
+                                 0xff, 0xff, 0xff, 0xff};
+    char l_leadin[128];
+    char b[256];
+    uint64_t ull;
+
+    if (0 == memcmp(lunp, not_spec, sizeof(not_spec))) {
+        printf("%sLogical unit not specified\n", leadin);
+        return;
+    }
+    lu_cong_admin = lu_cong;
+    memset(l_leadin, 0, sizeof(l_leadin));
+    for (k = 0; k < 4; ++k, lunp += 2) {
+        next_level = 0;
+        strncpy(l_leadin, leadin, sizeof(l_leadin) - 3);
+        if (k > 0) {
+            if (lu_cong) {
+                lu_cong_admin = 0;
+                if ((0 == lunp[0]) && (0 == lunp[1])) {
+                    printf("%s>>>> Administrative LU\n", l_leadin);
+                    if (do_hex || verbose)
+                         printf("        since Subsidiary element is "
+                                "0x0000\n");
+                    break;
+                } else
+                    printf("%s>>Subsidiary element:\n", l_leadin);
+            } else
+                printf("%s>>%s level addressing:\n", l_leadin, ((1 == k) ?
+                         "Second" : ((2 == k) ? "Third" : "Fourth")));
+            strcat(l_leadin, "  ");
+        } else if (lu_cong) {
+            printf("%s>>Administrative element:\n", l_leadin);
+            strcat(l_leadin, "  ");
+        }
+        a_method = (lunp[0] >> 6) & 0x3;
+        switch (a_method) {
+        case 0:         /* peripheral device addressing method */
+            if (lu_cong) {
+                snprintf(b, sizeof(b), "%sSimple lu addressing: ",
+                         l_leadin);
+                x = 0x3fff & sg_get_unaligned_be16(lunp + 0);
+                if (do_hex)
+                    printf("%s0x%04x\n", b, x);
+                else
+                    printf("%s%d\n", b, x);
+                if (lu_cong_admin)
+                    next_level = 1;
+            } else {
+                bus_id = lunp[0] & 0x3f;
+                snprintf(b, sizeof(b), "%sPeripheral device addressing: ",
+                         l_leadin);
+                if ((0 == bus_id) && (0 == verbose)) {
+                    if (do_hex)
+                        printf("%slun=0x%02x\n", b, lunp[1]);
+                    else
+                        printf("%slun=%d\n", b, lunp[1]);
+                } else {
+                    if (do_hex)
+                        printf("%sbus_id=0x%02x, %s=0x%02x\n", b, bus_id,
+                               (bus_id ? "target" : "lun"), lunp[1]);
+                    else
+                        printf("%sbus_id=%d, %s=%d\n", b, bus_id,
+                               (bus_id ? "target" : "lun"), lunp[1]);
+                }
+                if (bus_id)
+                    next_level = 1;
+            }
+            break;
+        case 1:         /* flat space addressing method */
+            lun = 0x3fff & sg_get_unaligned_be16(lunp + 0);
+            if (lu_cong) {
+                printf("%sSince LU_CONG=1, unexpected Flat space "
+                       "addressing: lun=0x%04x\n", l_leadin, lun);
+                break;
+            }
+            if (do_hex)
+                printf("%sFlat space addressing: lun=0x%04x\n", l_leadin,
+                       lun);
+            else
+                printf("%sFlat space addressing: lun=%d\n", l_leadin, lun);
+            break;
+        case 2:         /* logical unit addressing method */
+            target = (lunp[0] & 0x3f);
+            bus_id = (lunp[1] >> 5) & 0x7;
+            lun = lunp[1] & 0x1f;
+            if (lu_cong) {
+                printf("%sSince LU_CONG=1, unexpected lu addressing: "
+                       "bus_id=0x%x, target=0x%02x, lun=0x%02x\n", l_leadin,
+                       bus_id, target, lun);
+                break;
+            }
+            if (do_hex)
+                printf("%sLogical unit addressing: bus_id=0x%x, "
+                       "target=0x%02x, lun=0x%02x\n", l_leadin, bus_id,
+                       target, lun);
+            else
+                printf("%sLogical unit addressing: bus_id=%d, target=%d, "
+                       "lun=%d\n", l_leadin, bus_id, target, lun);
+            break;
+        case 3:         /* extended logical unit + flat space addressing */
+            len_fld = (lunp[0] & 0x30) >> 4;
+            e_a_method = lunp[0] & 0xf;
+            x = lunp[1];
+            if ((0 == len_fld) && (1 == e_a_method)) {
+                snprintf(b, sizeof(b), "well known logical unit");
+                switch (x) {
+                case 1:
+                    printf("%sREPORT LUNS %s\n", l_leadin, b);
+                    break;
+                case 2:         /* obsolete in spc5r01 */
+                    printf("%sACCESS CONTROLS %s\n", l_leadin, b);
+                    break;
+                case 3:
+                    printf("%sTARGET LOG PAGES %s\n", l_leadin, b);
+                    break;
+                case 4:
+                    printf("%sSECURITY PROTOCOL %s\n", l_leadin, b);
+                    break;
+                case 5:
+                    printf("%sMANAGEMENT PROTOCOL %s\n", l_leadin, b);
+                    break;
+                default:
+                    if (do_hex)
+                        printf("%s%s 0x%02x\n", l_leadin, b, x);
+                    else
+                        printf("%s%s %d\n", l_leadin, b, x);
+                    break;
+                }
+            } else if ((1 == len_fld) && (2 == e_a_method)) {
+                x = sg_get_unaligned_be24(lunp + 1);
+                if (do_hex)
+                    printf("%sExtended flat space addressing: lun=0x%06x\n",
+                           l_leadin, x);
+                else
+                    printf("%sExtended flat space addressing: lun=%d\n",
+                           l_leadin, x);
+            } else if ((2 == len_fld) && (2 == e_a_method)) {
+                ull = 0;
+                for (j = 0; j < 5; ++j) {
+                    if (j > 0)
+                        ull <<= 8;
+                    ull |= lunp[1 + j];
+                }
+                if (do_hex)
+                    printf("%sLong extended flat space addressing: "
+                           "lun=0x%010" PRIx64 "\n", l_leadin, ull);
+                else
+                    printf("%sLong extended flat space addressing: "
+                           "lun=%" PRIu64 "\n", l_leadin, ull);
+            } else if ((3 == len_fld) && (0xf == e_a_method))
+                printf("%sLogical unit _not_ specified addressing\n",
+                       l_leadin);
+            else {
+                if (len_fld < 2) {
+                    if (1 == len_fld)
+                        x = sg_get_unaligned_be24(lunp + 1);
+                    if (do_hex)
+                        printf("%sExtended logical unit addressing: "
+                               "length=%d, e.a. method=%d, value=0x%06x\n",
+                               l_leadin, len_fld, e_a_method, x);
+                    else
+                        printf("%sExtended logical unit addressing: "
+                               "length=%d, e.a. method=%d, value=%d\n",
+                               l_leadin, len_fld, e_a_method, x);
+                } else {
+                    ull = 0;
+                    x = (2 == len_fld) ? 5 : 7;
+                    for (j = 0; j < x; ++j) {
+                        if (j > 0)
+                            ull <<= 8;
+                        ull |= lunp[1 + j];
+                    }
+                    if (do_hex) {
+                        printf("%sExtended logical unit addressing: "
+                               "length=%d, e. a. method=%d, ", l_leadin,
+                               len_fld, e_a_method);
+                        if (5 == len_fld)
+                                printf("value=0x%010" PRIx64 "\n", ull);
+                        else
+                                printf("value=0x%014" PRIx64 "\n", ull);
+                    } else
+                        printf("%sExtended logical unit addressing: "
+                               "length=%d, e. a. method=%d, value=%" PRIu64
+                               "\n", l_leadin, len_fld, e_a_method, ull);
+                }
+            }
+            break;
+        default:
+            printf("%s<<decode_lun: faulty logic>>\n", l_leadin);
+            break;
+        }
+        if (next_level)
+            continue;
+        if ((2 == a_method) && (k < 3) && (lunp[2] || lunp[3]))
+            printf("%s<<unexpected data at next level, continue>>\n",
+                   l_leadin);
+        break;
+    }
+}
+
+#ifdef SG_LIB_LINUX
+static void
+linux2t10_lun(uint64_t linux_lun, unsigned char t10_lun[])
+{
+    int k;
+
+    for (k = 0; k < 8; k += 2, linux_lun >>= 16)
+        sg_put_unaligned_be16((uint16_t)linux_lun, t10_lun + k);
+}
+
+static uint64_t
+t10_2linux_lun(const unsigned char t10_lun[])
+{
+    int k;
+    const unsigned char * cp;
+    uint64_t res;
+
+    res = sg_get_unaligned_be16(t10_lun + 6);
+    for (cp = t10_lun + 4, k = 0; k < 3; ++k, cp -= 2)
+        res = (res << 16) + sg_get_unaligned_be16(cp);
+    return res;
+}
+
+/* Copy of t10_lun --> Linux unsigned int (i.e. 32 bit ) present in Linux
+ * kernel, up to least lk 3.8.0, extended to 64 bits.
+ * BEWARE: for sizeof(int==4) this function is BROKEN and is left here as
+ * as example and may soon be removed. */
+static uint64_t
+t10_2linux_lun64bitBR(const unsigned char t10_lun[])
+{
+    int i;
+    uint64_t lun;
+
+    lun = 0;
+    for (i = 0; i < (int)sizeof(lun); i += 2)
+        lun = lun | (((t10_lun[i] << 8) | t10_lun[i + 1]) << (i * 8));
+    return lun;
+}
+#endif  /* SG_LIB_LINUX */
+
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, m, off, res, c, list_len, len_cap, luns, trunc;
+    int decode = 0;
+    int do_hex = 0;
+#ifdef SG_LIB_LINUX
+    int do_linux = 0;
+#endif
+    int lu_cong = 0;
+    int lu_cong_given = 0;
+    int maxlen = 0;
+    int do_quiet = 0;
+    int do_raw = 0;
+    int o_readonly = 0;
+    int select_rep = 0;
+    int verbose = 0;
+#ifdef SG_LIB_LINUX
+    int test_linux_in = 0;
+    int test_linux_out = 0;
+    int test_linux_out2 = 0;
+#endif
+    unsigned int h;
+    const char * test_arg = NULL;
+    const char * device_name = NULL;
+    const char * cp;
+    unsigned char lun_arr[8];
+    struct sg_simple_inquiry_resp sir;
+    unsigned char * reportLunsBuff = NULL;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+#ifdef SG_LIB_LINUX
+        c = getopt_long(argc, argv, "dhHlLm:qrRs:t:vV", long_options,
+                        &option_index);
+#else
+        c = getopt_long(argc, argv, "dhHLm:qrRs:t:vV", long_options,
+                        &option_index);
+#endif
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'd':
+            decode = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+#ifdef SG_LIB_LINUX
+        case 'l':
+            ++do_linux;
+            break;
+#endif
+        case 'L':
+            ++lu_cong;
+            ++lu_cong_given;
+            break;
+        case 'm':
+            maxlen = sg_get_num(optarg);
+            if ((maxlen < 0) || (maxlen > MAX_RLUNS_BUFF_LEN)) {
+                pr2serr("argument to '--maxlen' should be %d or less\n",
+                        MAX_RLUNS_BUFF_LEN);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'q':
+            ++do_quiet;
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 'R':
+            ++o_readonly;
+            break;
+        case 's':
+           select_rep = sg_get_num(optarg);
+           if ((select_rep < 0) || (select_rep > 255)) {
+                pr2serr("bad argument to '--select', expect 0 to 255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 't':
+            test_arg = optarg;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (test_arg) {
+        memset(lun_arr, 0, sizeof(lun_arr));
+        cp = test_arg;
+        /* check for leading 'L' */
+#ifdef SG_LIB_LINUX
+        if ('L' == toupper(cp[0])) {
+            uint64_t ull;
+
+            if (1 != sscanf(cp + 1, " %" SCNu64, &ull)) {
+                pr2serr("Unable to read Linux style LUN integer given to "
+                        "--test=\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            linux2t10_lun(ull, lun_arr);
+            test_linux_in = 1;
+        } else
+#endif
+        {
+            /* Check if trailing 'L' or 'W' */
+            m = strlen(cp);    /* must be at least 1 char in test_arg */
+#ifdef SG_LIB_LINUX
+            if ('L' == toupper(cp[m - 1]))
+                test_linux_out = 1;
+            else if ('W' == toupper(cp[m - 1]))
+                test_linux_out2 = 1;
+#endif
+            if (('0' == cp[0]) && ('X' == toupper(cp[1])))
+                cp += 2;
+            if (strchr(cp, ' ') || strchr(cp, '\t')) {
+                for (k = 0; k < 8; ++k, cp += m) {
+                    if (1 != sscanf(cp, " %2x%n", &h, &m))
+                        break;
+                    lun_arr[k] = h & 0xff;
+                }
+            } else {
+                for (k = 0; k < 8; ++k, cp += 2) {
+                if (1 != sscanf(cp, "%2x", &h))
+                        break;
+                    lun_arr[k] = h & 0xff;
+                }
+            }
+            if (0 == k) {
+                pr2serr("expected a hex number, optionally prefixed by "
+                        "'0x'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        }
+#ifdef SG_LIB_LINUX
+        if (verbose || test_linux_in || test_linux_out2)
+#else
+        if (verbose)
+#endif
+        {
+            printf("64 bit LUN in T10 preferred (hex) format: ");
+            for (k = 0; k < 8; ++k)
+                printf(" %02x", lun_arr[k]);
+            printf("\n");
+        }
+#ifdef SG_LIB_LINUX
+        if (test_linux_out) {
+            if (do_hex > 1)
+                printf("Linux 'word flipped' integer LUN representation: "
+                       "0x%016" PRIx64 "\n", t10_2linux_lun(lun_arr));
+            else if (do_hex)
+                printf("Linux 'word flipped' integer LUN representation: 0x%"
+                       PRIx64 "\n", t10_2linux_lun(lun_arr));
+            else
+                printf("Linux 'word flipped' integer LUN representation: %"
+                       PRIu64 "\n", t10_2linux_lun(lun_arr));
+        } else if (test_linux_out2) {
+            if (do_hex > 1)
+                printf("Linux internal 64 bit LUN representation: 0x%016"
+                       PRIx64 "\n", t10_2linux_lun64bitBR(lun_arr));
+            else if (do_hex)
+                printf("Linux internal 64 bit LUN representation: 0x%"
+                       PRIx64 "\n", t10_2linux_lun64bitBR(lun_arr));
+            else
+                printf("Linux internal 64 bit LUN representation: %"
+                       PRIu64 "\n", t10_2linux_lun64bitBR(lun_arr));
+        }
+#endif
+        printf("Decoded LUN:\n");
+        decode_lun("  ", lun_arr, (lu_cong % 2), do_hex, verbose);
+        return 0;
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (decode && (! lu_cong_given)) {
+        /* check if LU_CONG set in standard INQUIRY response */
+        res = sg_simple_inquiry(sg_fd, &sir, 0, verbose);
+        ret = res;
+        if (res) {
+            pr2serr("fetching standard INQUIRY response failed\n");
+            goto the_end;
+        }
+        lu_cong = !!(0x40 & sir.byte_1);
+        if (verbose && lu_cong)
+            pr2serr("LU_CONG bit set in standard INQUIRY response\n");
+    }
+
+    if (0 == maxlen)
+        maxlen = DEF_RLUNS_BUFF_LEN;
+    reportLunsBuff = (unsigned char *)calloc(1, maxlen);
+    if (NULL == reportLunsBuff) {
+        pr2serr("unable to malloc %d bytes\n", maxlen);
+        return SG_LIB_CAT_OTHER;
+    }
+    trunc = 0;
+
+    res = sg_ll_report_luns(sg_fd, select_rep, reportLunsBuff, maxlen, 1,
+                            verbose);
+    ret = res;
+    if (0 == res) {
+        list_len = sg_get_unaligned_be32(reportLunsBuff + 0);
+        len_cap = list_len + 8;
+        if (len_cap > maxlen)
+            len_cap = maxlen;
+        if (do_raw) {
+            dStrRaw((const char *)reportLunsBuff, len_cap);
+            goto the_end;
+        }
+        if (1 == do_hex) {
+            dStrHex((const char *)reportLunsBuff, len_cap, 1);
+            goto the_end;
+        }
+        luns = (list_len / 8);
+        if (0 == do_quiet)
+            printf("Lun list length = %d which imples %d lun entr%s\n",
+                   list_len, luns, ((1 == luns) ? "y" : "ies"));
+        if ((list_len + 8) > maxlen) {
+            luns = ((maxlen - 8) / 8);
+            trunc = 1;
+            pr2serr("  <<too many luns for internal buffer, will show %d "
+                    "lun%s>>\n", luns, ((1 == luns) ? "" : "s"));
+        }
+        if (verbose > 1) {
+            pr2serr("\nOutput response in hex\n");
+            dStrHexErr((const char *)reportLunsBuff,
+                       (trunc ? maxlen : list_len + 8), 1);
+        }
+        for (k = 0, off = 8; k < luns; ++k, off += 8) {
+            if (0 == do_quiet) {
+                if (0 == k)
+                    printf("Report luns [select_report=0x%x]:\n", select_rep);
+                printf("    ");
+            }
+            for (m = 0; m < 8; ++m)
+                printf("%02x", reportLunsBuff[off + m]);
+#ifdef SG_LIB_LINUX
+            if (do_linux) {
+                uint64_t lin_lun;
+
+                lin_lun = t10_2linux_lun(reportLunsBuff + off);
+                if (do_hex > 1)
+                    printf("    [0x%" PRIx64 "]", lin_lun);
+                else
+                    printf("    [%" PRIu64 "]", lin_lun);
+            }
+#endif
+            printf("\n");
+            if (decode)
+                decode_lun("      ", reportLunsBuff + off, (lu_cong % 2),
+                           do_hex, verbose);
+        }
+    } else if (SG_LIB_CAT_INVALID_OP == res)
+        pr2serr("Report Luns command not supported (support mandatory in "
+                "SPC-3)\n");
+    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
+        pr2serr("Report Luns, aborted command\n");
+    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+        pr2serr("Report Luns command has bad field in cdb\n");
+    else {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Report Luns command: %s\n", b);
+    }
+
+the_end:
+    if (reportLunsBuff)
+        free(reportLunsBuff);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_map.c b/sg3_utils/src/sg_map.c
new file mode 100644
index 0000000..6a15675
--- /dev/null
+++ b/sg3_utils/src/sg_map.c
@@ -0,0 +1,503 @@
+/* Utility program for the Linux OS SCSI generic ("sg") device driver.
+*  Copyright (C) 2000-2007 D. Gilbert
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This shows the mapping from "sg" devices to other scsi devices
+   (i.e. sd, scd or st) if any.
+
+   Note: This program requires sg version 2 or better.
+
+   Version 0.19 20041203
+
+   Version 1.02 20050511
+        - allow for sparse disk name with up to 3 letter SCSI
+          disk device node names (e.g. /dev/sdaaa)
+          [Nate Dailey < Nate dot Dailey at stratus dot com >]
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_io_linux.h"
+
+
+static const char * version_str = "1.09 20130507";
+
+static const char * devfs_id = "/dev/.devfsd";
+
+#define NUMERIC_SCAN_DEF 1   /* change to 0 to make alpha scan default */
+
+#define INQUIRY_RESP_INITIAL_LEN 36
+#define MAX_SG_DEVS 4096
+#define PRESENT_ARRAY_SIZE MAX_SG_DEVS
+
+static const char * sysfs_sg_dir = "/sys/class/scsi_generic";
+static char gen_index_arr[PRESENT_ARRAY_SIZE];
+static int has_sysfs_sg = 0;
+
+
+typedef struct my_map_info
+{
+    int active;
+    int lin_dev_type;
+    int oth_dev_num;
+    struct sg_scsi_id sg_dat;
+    char vendor[8];
+    char product[16];
+    char revision[4];
+} my_map_info_t;
+
+
+#define MAX_SD_DEVS (26 + 26*26 + 26*26*26) /* sdX, sdXX, sdXXX */
+                 /* (26 + 676 + 17576) = 18278 */
+#define MAX_SR_DEVS 128
+#define MAX_ST_DEVS 128
+#define MAX_OSST_DEVS 128
+#define MAX_ERRORS 5
+
+static my_map_info_t map_arr[MAX_SG_DEVS];
+
+#define LIN_DEV_TYPE_UNKNOWN 0
+#define LIN_DEV_TYPE_SD 1
+#define LIN_DEV_TYPE_SR 2
+#define LIN_DEV_TYPE_ST 3
+#define LIN_DEV_TYPE_SCD 4
+#define LIN_DEV_TYPE_OSST 5
+
+
+typedef struct my_scsi_idlun {
+/* why can't userland see this structure ??? */
+    int dev_id;
+    int host_unique_id;
+} My_scsi_idlun;
+
+
+#define EBUFF_SZ 256
+static char ebuff[EBUFF_SZ];
+
+static void scan_dev_type(const char * leadin, int max_dev, int do_numeric,
+                          int lin_dev_type, int last_sg_ind);
+
+static void usage()
+{
+    printf("Usage: sg_map [-a] [-h] [-i] [-n] [-sd] [-scd or -sr] [-st] "
+           "[-V] [-x]\n");
+    printf("  where:\n");
+    printf("    -a      do alphabetic scan (ie sga, sgb, sgc)\n");
+    printf("    -h or -?    show this usage message then exit\n");
+    printf("    -i      also show device INQUIRY strings\n");
+    printf("    -n      do numeric scan (i.e. sg0, sg1, sg2) "
+           "(default)\n");
+    printf("    -sd     show mapping to disks\n");
+    printf("    -scd    show mapping to cdroms (look for /dev/scd<n>\n");
+    printf("    -sr     show mapping to cdroms (look for /dev/sr<n>\n");
+    printf("    -st     show mapping to tapes (st and osst devices)\n");
+    printf("    -V      print version string then exit\n");
+    printf("    -x      also show bus,chan,id,lun and type\n\n");
+    printf("If no '-s*' arguments given then show all mappings. This "
+           "utility\nis DEPRECATED, do not use in Linux 2.6 series or "
+           "later.\n");
+}
+
+static int scandir_select(const struct dirent * s)
+{
+    int k;
+
+    if (1 == sscanf(s->d_name, "sg%d", &k)) {
+        if ((k >= 0) && (k < PRESENT_ARRAY_SIZE)) {
+            gen_index_arr[k] = 1;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int sysfs_sg_scan(const char * dir_name)
+{
+    struct dirent ** namelist;
+    int num, k;
+
+    num = scandir(dir_name, &namelist, scandir_select, NULL);
+    if (num < 0)
+        return -errno;
+    for (k = 0; k < num; ++k)
+        free(namelist[k]);
+    free(namelist);
+    return num;
+}
+
+static void make_dev_name(char * fname, const char * leadin, int k,
+                          int do_numeric)
+{
+    char buff[64];
+    int  ones,tens,hundreds; /* for lack of a better name */
+    int  buff_idx;
+
+    strcpy(fname, leadin ? leadin : "/dev/sg");
+    if (do_numeric) {
+        sprintf(buff, "%d", k);
+        strcat(fname, buff);
+    }
+    else if (k >= (26 + 26*26 + 26*26*26)) {
+        strcat(fname, "xxxx");
+    }
+    else {
+        ones = k % 26;
+
+        if ((k - 26) >= 0)
+            tens = ((k-26)/26) % 26;
+        else tens = -1;
+
+        if ((k - (26 + 26*26)) >= 0)
+             hundreds = ((k - (26 + 26*26))/(26*26)) % 26;
+        else hundreds = -1;
+
+        buff_idx = 0;
+        if (hundreds >= 0) buff[buff_idx++] = 'a' + (char)hundreds;
+        if (tens >= 0) buff[buff_idx++] = 'a' + (char)tens;
+        buff[buff_idx++] = 'a' + (char)ones;
+        buff[buff_idx] = '\0';
+        strcat(fname, buff);
+    }
+}
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, res, k;
+    int do_numeric = NUMERIC_SCAN_DEF;
+    int do_all_s = 1;
+    int do_sd = 0;
+    int do_st = 0;
+    int do_osst = 0;
+    int do_sr = 0;
+    int do_scd = 0;
+    int do_extra = 0;
+    int do_inquiry = 0;
+    char fname[64];
+    int num_errors = 0;
+    int num_silent = 0;
+    int eacces_err = 0;
+    int last_sg_ind = -1;
+    struct stat a_stat;
+
+    for (k = 1; k < argc; ++k) {
+        if (0 == strcmp("-n", argv[k]))
+            do_numeric = 1;
+        else if (0 == strcmp("-a", argv[k]))
+            do_numeric = 0;
+        else if (0 == strcmp("-x", argv[k]))
+            do_extra = 1;
+        else if (0 == strcmp("-i", argv[k]))
+            do_inquiry = 1;
+        else if (0 == strcmp("-sd", argv[k])) {
+            do_sd = 1;
+            do_all_s = 0;
+        } else if (0 == strcmp("-st", argv[k])) {
+            do_st = 1;
+            do_osst = 1;
+            do_all_s = 0;
+        } else if (0 == strcmp("-sr", argv[k])) {
+            do_sr = 1;
+            do_all_s = 0;
+        } else if (0 == strcmp("-scd", argv[k])) {
+            do_scd = 1;
+            do_all_s = 0;
+        } else if (0 == strcmp("-V", argv[k])) {
+            fprintf(stderr, "Version string: %s\n", version_str);
+            exit(0);
+        } else if ((0 == strcmp("-?", argv[k])) ||
+                   (0 == strncmp("-h", argv[k], 2))) {
+            printf(
+            "Show mapping from sg devices to other scsi device names\n\n");
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        } else if (*argv[k] == '-') {
+            printf("Unknown switch: %s\n", argv[k]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        } else if (*argv[k] != '-') {
+            printf("Unknown argument\n");
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if ((stat(sysfs_sg_dir, &a_stat) >= 0) && (S_ISDIR(a_stat.st_mode)))
+        has_sysfs_sg = sysfs_sg_scan(sysfs_sg_dir);
+
+    if (stat(devfs_id, &a_stat) == 0)
+        printf("# Note: the devfs pseudo file system is present\n");
+
+    for (k = 0, res = 0; (k < MAX_SG_DEVS) && (num_errors < MAX_ERRORS);
+         ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
+            perror("sg_map: close error");
+            return SG_LIB_FILE_ERROR;
+        }
+        if (has_sysfs_sg) {
+           if (0 == gen_index_arr[k]) {
+                sg_fd = -1;
+                continue;
+            }
+            make_dev_name(fname, "/dev/sg", k, 1);
+        } else
+            make_dev_name(fname, "/dev/sg", k, do_numeric);
+
+        sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
+        if (sg_fd < 0) {
+            if (EBUSY == errno) {
+                map_arr[k].active = -2;
+                continue;
+            }
+            else if ((ENODEV == errno) || (ENOENT == errno) ||
+                     (ENXIO == errno)) {
+                ++num_errors;
+                ++num_silent;
+                map_arr[k].active = -1;
+                continue;
+            }
+            else {
+                if (EACCES == errno)
+                    eacces_err = 1;
+                snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
+                perror(ebuff);
+                ++num_errors;
+                continue;
+            }
+        }
+        res = ioctl(sg_fd, SG_GET_SCSI_ID, &map_arr[k].sg_dat);
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ,
+                     "device %s failed on sg ioctl, skip", fname);
+            perror(ebuff);
+            ++num_errors;
+            continue;
+        }
+        if (do_inquiry) {
+            char buff[INQUIRY_RESP_INITIAL_LEN];
+
+            if (0 == sg_ll_inquiry(sg_fd, 0, 0, 0, buff, sizeof(buff),
+                                   1, 0)) {
+                memcpy(map_arr[k].vendor, &buff[8], 8);
+                memcpy(map_arr[k].product, &buff[16], 16);
+                memcpy(map_arr[k].revision, &buff[32], 4);
+            }
+        }
+        map_arr[k].active = 1;
+        map_arr[k].oth_dev_num = -1;
+        last_sg_ind = k;
+    }
+    if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) {
+        printf("Stopping because there are too many error\n");
+        if (eacces_err)
+            printf("    root access may be required\n");
+        return SG_LIB_FILE_ERROR;
+    }
+    if (last_sg_ind < 0) {
+        printf("Stopping because no sg devices found\n");
+    }
+
+    if (do_all_s || do_sd)
+        scan_dev_type("/dev/sd", MAX_SD_DEVS, 0, LIN_DEV_TYPE_SD, last_sg_ind);
+    if (do_all_s || do_sr)
+        scan_dev_type("/dev/sr", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SR, last_sg_ind);
+    if (do_all_s || do_scd)
+        scan_dev_type("/dev/scd", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SCD,
+                      last_sg_ind);
+    if (do_all_s || do_st)
+        scan_dev_type("/dev/nst", MAX_ST_DEVS, 1, LIN_DEV_TYPE_ST,
+                      last_sg_ind);
+    if (do_all_s || do_osst)
+        scan_dev_type("/dev/osst", MAX_OSST_DEVS, 1, LIN_DEV_TYPE_OSST,
+                      last_sg_ind);
+
+    for (k = 0; k <= last_sg_ind; ++k) {
+        if (has_sysfs_sg) {
+           if (0 == gen_index_arr[k]) {
+                continue;
+            }
+            make_dev_name(fname, "/dev/sg", k, 1);
+        } else
+            make_dev_name(fname, "/dev/sg", k, do_numeric);
+        printf("%s", fname);
+        switch (map_arr[k].active)
+        {
+        case -2:
+            printf(do_extra ? "  -2 -2 -2 -2  -2" : "  busy");
+            break;
+        case -1:
+            printf(do_extra ? "  -1 -1 -1 -1  -1" : "  not present");
+            break;
+        case 0:
+            printf(do_extra ? "  -3 -3 -3 -3  -3" : "  some error");
+            break;
+        case 1:
+            if (do_extra)
+                printf("  %d %d %d %d  %d", map_arr[k].sg_dat.host_no,
+                       map_arr[k].sg_dat.channel, map_arr[k].sg_dat.scsi_id,
+                       map_arr[k].sg_dat.lun, map_arr[k].sg_dat.scsi_type);
+            switch (map_arr[k].lin_dev_type)
+            {
+            case LIN_DEV_TYPE_SD:
+                make_dev_name(fname, "/dev/sd" , map_arr[k].oth_dev_num, 0);
+                printf("  %s", fname);
+                break;
+            case LIN_DEV_TYPE_ST:
+                make_dev_name(fname, "/dev/nst" , map_arr[k].oth_dev_num, 1);
+                printf("  %s", fname);
+                break;
+            case LIN_DEV_TYPE_OSST:
+                make_dev_name(fname, "/dev/osst" , map_arr[k].oth_dev_num, 1);
+                printf("  %s", fname);
+                break;
+            case LIN_DEV_TYPE_SR:
+                make_dev_name(fname, "/dev/sr" , map_arr[k].oth_dev_num, 1);
+                printf("  %s", fname);
+                break;
+            case LIN_DEV_TYPE_SCD:
+                make_dev_name(fname, "/dev/scd" , map_arr[k].oth_dev_num, 1);
+                printf("  %s", fname);
+                break;
+            default:
+                break;
+            }
+            if (do_inquiry)
+                printf("  %.8s  %.16s  %.4s", map_arr[k].vendor,
+                       map_arr[k].product, map_arr[k].revision);
+            break;
+        default:
+            printf("  bad logic\n");
+            break;
+        }
+        printf("\n");
+    }
+    return 0;
+}
+
+static int find_dev_in_sg_arr(My_scsi_idlun * my_idlun, int host_no,
+                              int last_sg_ind)
+{
+    int k;
+    struct sg_scsi_id * sidp;
+
+    for (k = 0; k <= last_sg_ind; ++k) {
+        sidp = &(map_arr[k].sg_dat);
+        if ((host_no == sidp->host_no) &&
+            ((my_idlun->dev_id & 0xff) == sidp->scsi_id) &&
+            (((my_idlun->dev_id >> 8) & 0xff) == sidp->lun) &&
+            (((my_idlun->dev_id >> 16) & 0xff) == sidp->channel))
+            return k;
+    }
+    return -1;
+}
+
+static void scan_dev_type(const char * leadin, int max_dev, int do_numeric,
+                          int lin_dev_type, int last_sg_ind)
+{
+    int k, res, ind, sg_fd = 0;
+    int num_errors = 0;
+    int num_silent = 0;
+    int host_no = -1;
+    My_scsi_idlun my_idlun;
+    char fname[64];
+
+    for (k = 0, res = 0; (k < max_dev)  && (num_errors < MAX_ERRORS);
+         ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
+
+/* ignore close() errors */
+#if 0
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
+            perror("sg_map: close error");
+#ifndef IGN_CLOSE_ERR
+            return;
+#else
+            ++num_errors;
+            sg_fd = 0;
+#endif
+        }
+#endif
+        make_dev_name(fname, leadin, k, do_numeric);
+#ifdef DEBUG
+        printf ("Trying %s: ", fname);
+#endif
+
+        sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
+        if (sg_fd < 0) {
+#ifdef DEBUG
+            printf ("ERROR %i\n", errno);
+#endif
+            if (EBUSY == errno) {
+                printf("Device %s is busy\n", fname);
+                ++num_errors;
+            } else if ((ENODEV == errno) || (ENXIO == errno)) {
+                ++num_errors;
+                ++num_silent;
+            } else if (ENOENT != errno) { /* ignore ENOENT for sparse names */
+                snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
+                perror(ebuff);
+                ++num_errors;
+            }
+            continue;
+        }
+
+        res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ,
+                     "device %s failed on scsi ioctl(idlun), skip", fname);
+            perror(ebuff);
+            ++num_errors;
+#ifdef DEBUG
+            printf ("Couldn't get IDLUN!\n");
+#endif
+            continue;
+        }
+        res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ,
+                 "device %s failed on scsi ioctl(bus_number), skip", fname);
+            perror(ebuff);
+            ++num_errors;
+#ifdef DEBUG
+            printf ("Couldn't get BUS!\n");
+#endif
+            continue;
+        }
+#ifdef DEBUG
+        printf ("%i(%x) %i %i %i %i\n", host_no, my_idlun.host_unique_id,
+                (my_idlun.dev_id>>24)&0xff, (my_idlun.dev_id>>16)&0xff,
+                (my_idlun.dev_id>>8)&0xff, my_idlun.dev_id&0xff);
+#endif
+        ind = find_dev_in_sg_arr(&my_idlun, host_no, last_sg_ind);
+        if (ind >= 0) {
+            map_arr[ind].oth_dev_num = k;
+            map_arr[ind].lin_dev_type = lin_dev_type;
+        }
+        else
+            printf("Strange, could not find device %s mapped to sg device??\n",
+                   fname);
+    }
+}
diff --git a/sg3_utils/src/sg_map26.c b/sg3_utils/src/sg_map26.c
new file mode 100644
index 0000000..1a1301a
--- /dev/null
+++ b/sg3_utils/src/sg_map26.c
@@ -0,0 +1,1278 @@
+/*
+ * Copyright (c) 2005-2016 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.
+ */
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program maps a primary SCSI device node name to the corresponding
+ * SCSI generic device node name (or vice versa). Targets linux
+ * kernel 2.6 or 3 series. Sysfs device names can also be mapped.
+ */
+
+/* #define _XOPEN_SOURCE 500 */
+/* needed to see DT_REG and friends when compiled with: c99 pedantic */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/major.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+
+static const char * version_str = "1.11 20160121";
+
+#define ME "sg_map26: "
+
+#define NT_NO_MATCH 0
+#define NT_SD 1
+#define NT_SR 2
+#define NT_HD 3
+#define NT_ST 4
+#define NT_OSST 5
+#define NT_SG 6
+#define NT_CH 7
+#define NT_REG 8
+#define NT_DIR 9
+
+#define NAME_LEN_MAX 260
+#define D_NAME_LEN_MAX 516
+
+#ifndef SCSI_CHANGER_MAJOR
+#define SCSI_CHANGER_MAJOR 86
+#endif
+#ifndef OSST_MAJOR
+#define OSST_MAJOR 206
+#endif
+
+/* scandir() and stat() categories */
+#define FT_OTHER 0
+#define FT_REGULAR 1
+#define FT_BLOCK 2
+#define FT_CHAR 3
+#define FT_DIR 4
+
+/* older major.h headers may not have these */
+#ifndef SCSI_DISK8_MAJOR
+#define SCSI_DISK8_MAJOR        128
+#define SCSI_DISK9_MAJOR        129
+#define SCSI_DISK10_MAJOR       130
+#define SCSI_DISK11_MAJOR       131
+#define SCSI_DISK12_MAJOR       132
+#define SCSI_DISK13_MAJOR       133
+#define SCSI_DISK14_MAJOR       134
+#define SCSI_DISK15_MAJOR       135
+#endif
+
+/* st minor decodes from Kai Makisara 20081008 */
+#define ST_NBR_MODE_BITS 2
+#define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS)
+#define TAPE_NR(minor) ( (((minor) & ~255) >> (ST_NBR_MODE_BITS + 1)) | \
+    ((minor) & ~(-1 << ST_MODE_SHIFT)) )
+
+static const char * sys_sg_dir = "/sys/class/scsi_generic/";
+static const char * sys_sd_dir = "/sys/block/";
+static const char * sys_sr_dir = "/sys/block/";
+static const char * sys_hd_dir = "/sys/block/";
+static const char * sys_st_dir = "/sys/class/scsi_tape/";
+static const char * sys_sch_dir = "/sys/class/scsi_changer/";
+static const char * sys_osst_dir = "/sys/class/onstream_tape/";
+static const char * def_dev_dir = "/dev";
+
+
+static struct option long_options[] = {
+        {"dev_dir", 1, 0, 'd'},
+        {"given_is", 1, 0, 'g'},
+        {"help", 0, 0, 'h'},
+        {"result", 1, 0, 'r'},
+        {"symlink", 0, 0, 's'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static const char * nt_names[] = {
+        "No matching",
+        "disk",
+        "cd/dvd",
+        "hd",
+        "tape",
+        "tape (osst)",
+        "generic (sg)",
+        "changer",
+        "regular file",
+        "directory",
+};
+
+#ifdef __GNUC__
+static int pr2serr(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2serr(const char * fmt, ...);
+#endif
+
+
+static int
+pr2serr(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+static void
+usage()
+{
+        pr2serr("Usage: sg_map26 [--dev_dir=DIR] [--given_is=0...1] [--help] "
+                "[--result=0...3]\n"
+                "                [--symlink] [--verbose] [--version] "
+                "DEVICE\n"
+                "  where:\n"
+                "    --dev_dir=DIR | -d DIR    search in DIR for "
+                "resulting special\n"
+                "                            (def: directory of DEVICE "
+                "or '/dev')\n"
+                "    --given_is=0...1 | -g 0...1    variety of given "
+                "DEVICE\n"
+                "                                   0->block or char special "
+                "(or symlink to)\n"
+                "                                   1->sysfs device, 'dev' or "
+                "parent\n"
+                "    --help | -h       print out usage message\n"
+                "    --result=0...3 | -r 0...3    variety of file(s) to "
+                "find\n"
+                "                                 0->mapped block or char "
+                "special(def)\n"
+                "                                 1->mapped sysfs path\n"
+                "                                 2->matching block or "
+                "char special\n"
+                "                                 3->matching sysfs "
+                "path\n"
+                "    --symlink | -s    symlinks to special included in "
+                "result\n"
+                "    --verbose | -v    increase verbosity of output\n"
+                "    --version | -V    print version string and exit\n\n"
+                "Maps SCSI device node to corresponding generic node (and "
+                "vv)\n"
+                );
+}
+
+
+/* ssafe_strerror() contributed by Clayton Weaver <cgweav at email dot com>
+   Allows for situation in which strerror() is given a wild value (or the
+   C library is incomplete) and returns NULL. Still not thread safe.
+ */
+
+static char safe_errbuf[64] = {'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ',
+                               'e', 'r', 'r', 'n', 'o', ':', ' ', 0};
+
+static char *
+ssafe_strerror(int errnum)
+{
+        size_t len;
+        char * errstr;
+
+        errstr = strerror(errnum);
+        if (NULL == errstr) {
+                len = strlen(safe_errbuf);
+                snprintf(safe_errbuf + len, sizeof(safe_errbuf) - len, "%i",
+                         errnum);
+                safe_errbuf[sizeof(safe_errbuf) - 1] = '\0';  /* bombproof */
+                return safe_errbuf;
+        }
+        return errstr;
+}
+
+static int
+nt_typ_from_filename(const char * filename, int * majj, int * minn)
+{
+        struct stat st;
+        int ma, mi;
+
+        if (stat(filename, &st) < 0)
+                return -errno;
+        ma = major(st.st_rdev);
+        mi = minor(st.st_rdev);
+        if (majj)
+                *majj = ma;
+        if (minn)
+                *minn = mi;
+        if (S_ISCHR(st.st_mode)) {
+                switch(ma) {
+                case OSST_MAJOR:
+                        return NT_OSST;
+                case SCSI_GENERIC_MAJOR:
+                        return NT_SG;
+                case SCSI_TAPE_MAJOR:
+                        return NT_ST;
+                case SCSI_CHANGER_MAJOR:
+                        return NT_CH;
+                default:
+                        return NT_NO_MATCH;
+                }
+        } else if (S_ISBLK(st.st_mode)) {
+                switch(ma) {
+                case SCSI_DISK0_MAJOR: case SCSI_DISK1_MAJOR:
+                case SCSI_DISK2_MAJOR: case SCSI_DISK3_MAJOR:
+                case SCSI_DISK4_MAJOR: case SCSI_DISK5_MAJOR:
+                case SCSI_DISK6_MAJOR: case SCSI_DISK7_MAJOR:
+                case SCSI_DISK8_MAJOR: case SCSI_DISK9_MAJOR:
+                case SCSI_DISK10_MAJOR: case SCSI_DISK11_MAJOR:
+                case SCSI_DISK12_MAJOR: case SCSI_DISK13_MAJOR:
+                case SCSI_DISK14_MAJOR: case SCSI_DISK15_MAJOR:
+                        return NT_SD;
+                case SCSI_CDROM_MAJOR:
+                        return NT_SR;
+                case IDE0_MAJOR: case IDE1_MAJOR:
+                case IDE2_MAJOR: case IDE3_MAJOR:
+                case IDE4_MAJOR: case IDE5_MAJOR:
+                case IDE6_MAJOR: case IDE7_MAJOR:
+                case IDE8_MAJOR: case IDE9_MAJOR:
+                        return NT_HD;
+                default:
+                        return NT_NO_MATCH;
+                }
+        } else if (S_ISREG(st.st_mode))
+                return NT_REG;
+        else if (S_ISDIR(st.st_mode))
+                return NT_DIR;
+        return NT_NO_MATCH;
+}
+
+static int
+nt_typ_from_major(int ma)
+{
+        switch(ma) {
+        case SCSI_DISK0_MAJOR: case SCSI_DISK1_MAJOR:
+        case SCSI_DISK2_MAJOR: case SCSI_DISK3_MAJOR:
+        case SCSI_DISK4_MAJOR: case SCSI_DISK5_MAJOR:
+        case SCSI_DISK6_MAJOR: case SCSI_DISK7_MAJOR:
+        case SCSI_DISK8_MAJOR: case SCSI_DISK9_MAJOR:
+        case SCSI_DISK10_MAJOR: case SCSI_DISK11_MAJOR:
+        case SCSI_DISK12_MAJOR: case SCSI_DISK13_MAJOR:
+        case SCSI_DISK14_MAJOR: case SCSI_DISK15_MAJOR:
+                return NT_SD;
+        case SCSI_CDROM_MAJOR:
+                return NT_SR;
+        case IDE0_MAJOR: case IDE1_MAJOR:
+        case IDE2_MAJOR: case IDE3_MAJOR:
+        case IDE4_MAJOR: case IDE5_MAJOR:
+        case IDE6_MAJOR: case IDE7_MAJOR:
+        case IDE8_MAJOR: case IDE9_MAJOR:
+                return NT_HD;
+        case OSST_MAJOR:
+                return NT_OSST;
+        case SCSI_GENERIC_MAJOR:
+                return NT_SG;
+        case SCSI_TAPE_MAJOR:
+                return NT_ST;
+        case SCSI_CHANGER_MAJOR:
+                return NT_CH;
+        default:
+                return NT_NO_MATCH;
+        }
+        return NT_NO_MATCH;
+}
+
+
+struct node_match_item {
+        const char * dir_name;
+        int file_type;
+        int majj;
+        int minn;
+        int follow_symlink;
+};
+
+static struct node_match_item nd_match;
+
+static int
+nd_match_scandir_select(const struct dirent * s)
+{
+        struct stat st;
+        char name[D_NAME_LEN_MAX];
+        int symlnk = 0;
+
+        switch (s->d_type) {
+        case DT_BLK:
+                if (FT_BLOCK != nd_match.file_type)
+                        return 0;
+                break;
+        case DT_CHR:
+                if (FT_CHAR != nd_match.file_type)
+                        return 0;
+                break;
+        case DT_DIR:
+                return (FT_DIR == nd_match.file_type) ? 1 : 0;
+        case DT_REG:
+                return (FT_REGULAR == nd_match.file_type) ? 1 : 0;
+        case DT_LNK:    /* follow symlinks */
+                if (! nd_match.follow_symlink)
+                        return 0;
+                symlnk = 1;
+                break;
+        default:
+                return 0;
+        }
+        if ((! symlnk) && (-1 == nd_match.majj) && (-1 == nd_match.minn))
+                return 1;
+        strncpy(name, nd_match.dir_name, NAME_LEN_MAX);
+        strcat(name, "/");
+        strncat(name, s->d_name, NAME_LEN_MAX);
+        memset(&st, 0, sizeof(st));
+        if (stat(name, &st) < 0)
+                return 0;
+        if (symlnk) {
+                if (S_ISCHR(st.st_mode)) {
+                        if (FT_CHAR != nd_match.file_type)
+                                return 0;
+                } else if (S_ISBLK(st.st_mode)) {
+                        if (FT_BLOCK != nd_match.file_type)
+                                return 0;
+                } else
+                        return 0;
+        }
+        return (((-1 == nd_match.majj) ||
+                 ((unsigned)major(st.st_rdev) == (unsigned)nd_match.majj)) &&
+                ((-1 == nd_match.minn) ||
+                 ((unsigned)minor(st.st_rdev) == (unsigned)nd_match.minn)))
+               ? 1 : 0;
+}
+
+static int
+list_matching_nodes(const char * dir_name, int file_type, int majj, int minn,
+                    int follow_symlink, int verbose)
+{
+        struct dirent ** namelist;
+        int num, k;
+
+        nd_match.dir_name = dir_name;
+        nd_match.file_type = file_type;
+        nd_match.majj = majj;
+        nd_match.minn = minn;
+        nd_match.follow_symlink = follow_symlink;
+        num = scandir(dir_name, &namelist, nd_match_scandir_select, NULL);
+        if (num < 0) {
+                if (verbose)
+                        pr2serr("scandir: %s %s\n", dir_name,
+                                ssafe_strerror(errno));
+                return -errno;
+        }
+        for (k = 0; k < num; ++k) {
+                printf("%s/%s\n", dir_name, namelist[k]->d_name);
+                free(namelist[k]);
+        }
+        free(namelist);
+        return num;
+}
+
+struct sg_item_t {
+        char name[NAME_LEN_MAX];
+        int ft;
+        int nt;
+        int d_type;
+};
+
+static struct sg_item_t for_first;
+
+static int
+first_scandir_select(const struct dirent * s)
+{
+        if (FT_OTHER != for_first.ft)
+                return 0;
+        if ((DT_LNK != s->d_type) &&
+            ((DT_DIR != s->d_type) || ('.' == s->d_name[0])))
+                return 0;
+        strncpy(for_first.name, s->d_name, NAME_LEN_MAX);
+        for_first.ft = FT_CHAR;  /* dummy */
+        for_first.d_type =  s->d_type;
+        return 1;
+}
+
+/* scan for directory entry that is either a symlink or a directory */
+static int
+scan_for_first(const char * dir_name, int verbose)
+{
+        char name[NAME_LEN_MAX];
+        struct dirent ** namelist;
+        int num, k;
+
+        for_first.ft = FT_OTHER;
+        num = scandir(dir_name, &namelist, first_scandir_select, NULL);
+        if (num < 0) {
+                if (verbose > 0) {
+                        snprintf(name, NAME_LEN_MAX, "scandir: %s", dir_name);
+                        perror(name);
+                }
+                return -1;
+        }
+        for (k = 0; k < num; ++k)
+                free(namelist[k]);
+        free(namelist);
+        return num;
+}
+
+static struct sg_item_t from_sg;
+
+static int
+from_sg_scandir_select(const struct dirent * s)
+{
+        int len;
+
+        if (FT_OTHER != from_sg.ft)
+                return 0;
+        if ((DT_LNK != s->d_type) &&
+            ((DT_DIR != s->d_type) || ('.' == s->d_name[0])))
+                return 0;
+        from_sg.d_type = s->d_type;
+        if (0 == strncmp("scsi_changer", s->d_name, 12)) {
+                strncpy(from_sg.name, s->d_name, NAME_LEN_MAX);
+                from_sg.ft = FT_CHAR;
+                from_sg.nt = NT_CH;
+                return 1;
+        } else if (0 == strncmp("block", s->d_name, 5)) {
+                strncpy(from_sg.name, s->d_name, NAME_LEN_MAX);
+                from_sg.ft = FT_BLOCK;
+                return 1;
+        } else if (0 == strcmp("tape", s->d_name)) {
+                strcpy(from_sg.name, s->d_name);
+                from_sg.ft = FT_CHAR;
+                from_sg.nt = NT_ST;
+                return 1;
+        } else if (0 == strncmp("scsi_tape:st", s->d_name, 12)) {
+                len = strlen(s->d_name);
+                if (isdigit(s->d_name[len - 1])) {
+                        /* want 'st<num>' symlink only */
+                        strcpy(from_sg.name, s->d_name);
+                        from_sg.ft = FT_CHAR;
+                        from_sg.nt = NT_ST;
+                        return 1;
+                } else
+                        return 0;
+        } else if (0 == strncmp("onstream_tape:os", s->d_name, 16)) {
+                strcpy(from_sg.name, s->d_name);
+                from_sg.ft = FT_CHAR;
+                from_sg.nt = NT_OSST;
+                return 1;
+        } else
+                return 0;
+}
+
+static int
+from_sg_scan(const char * dir_name, int verbose)
+{
+        struct dirent ** namelist;
+        int num, k;
+
+        from_sg.ft = FT_OTHER;
+        from_sg.nt = NT_NO_MATCH;
+        num = scandir(dir_name, &namelist, from_sg_scandir_select, NULL);
+        if (num < 0) {
+                if (verbose)
+                        pr2serr("scandir: %s %s\n", dir_name,
+                                ssafe_strerror(errno));
+                return -errno;
+        }
+        if (verbose) {
+                for (k = 0; k < num; ++k)
+                        pr2serr("    %s/%s\n", dir_name,
+                                namelist[k]->d_name);
+        }
+        for (k = 0; k < num; ++k)
+                free(namelist[k]);
+        free(namelist);
+        return num;
+}
+
+static struct sg_item_t to_sg;
+
+static int
+to_sg_scandir_select(const struct dirent * s)
+{
+        if (FT_OTHER != to_sg.ft)
+                return 0;
+        if (DT_LNK != s->d_type)
+                return 0;
+        if (0 == strncmp("scsi_generic", s->d_name, 12)) {
+                strncpy(to_sg.name, s->d_name, NAME_LEN_MAX);
+                to_sg.ft = FT_CHAR;
+                to_sg.nt = NT_SG;
+                return 1;
+        } else
+                return 0;
+}
+
+static int
+to_sg_scan(const char * dir_name)
+{
+        struct dirent ** namelist;
+        int num, k;
+
+        to_sg.ft = FT_OTHER;
+        to_sg.nt = NT_NO_MATCH;
+        num = scandir(dir_name, &namelist, to_sg_scandir_select, NULL);
+        if (num < 0)
+                return -errno;
+        for (k = 0; k < num; ++k)
+                free(namelist[k]);
+        free(namelist);
+        return num;
+}
+
+/* Return 1 if directory, else 0 */
+static int
+if_directory_chdir(const char * dir_name, const char * base_name)
+{
+        char buff[D_NAME_LEN_MAX];
+        struct stat a_stat;
+
+        strcpy(buff, dir_name);
+        strcat(buff, "/");
+        strcat(buff, base_name);
+        if (stat(buff, &a_stat) < 0)
+                return 0;
+        if (S_ISDIR(a_stat.st_mode)) {
+                if (chdir(buff) < 0)
+                        return 0;
+                return 1;
+        }
+        return 0;
+}
+
+/* Return 1 if directory, else 0 */
+static int
+if_directory_ch2generic(const char * dir_name)
+{
+        char buff[NAME_LEN_MAX];
+        struct stat a_stat;
+        const char * old_name = "generic";
+
+        strcpy(buff, dir_name);
+        strcat(buff, "/");
+        strcat(buff, old_name);
+        if ((stat(buff, &a_stat) >= 0) && S_ISDIR(a_stat.st_mode)) {
+                if (chdir(buff) < 0)
+                        return 0;
+                return 1;
+        }
+        /* No "generic", so now look for "scsi_generic:sg<n>" */
+        if (1 != to_sg_scan(dir_name))
+                return 0;
+        strcpy(buff, dir_name);
+        strcat(buff, "/");
+        strcat(buff, to_sg.name);
+        if (stat(buff, &a_stat) < 0)
+                return 0;
+        if (S_ISDIR(a_stat.st_mode)) {
+                if (chdir(buff) < 0)
+                        return 0;
+                return 1;
+        }
+        return 0;
+}
+
+/* Return 1 if found, else 0 if problems */
+static int
+get_value(const char * dir_name, const char * base_name, char * value,
+          int max_value_len)
+{
+        char buff[D_NAME_LEN_MAX];
+        FILE * f;
+        int len;
+
+        if ((NULL == dir_name) && (NULL == base_name))
+                return 0;
+        if (dir_name) {
+                strcpy(buff, dir_name);
+                if (base_name && (strlen(base_name) > 0)) {
+                        strcat(buff, "/");
+                        strcat(buff, base_name);
+                }
+        } else
+                strcpy(buff, base_name);
+        if (NULL == (f = fopen(buff, "r"))) {
+                return 0;
+        }
+        if (NULL == fgets(value, max_value_len, f)) {
+                fclose(f);
+                return 0;
+        }
+        len = strlen(value);
+        if ((len > 0) && (value[len - 1] == '\n'))
+                value[len - 1] = '\0';
+        fclose(f);
+        return 1;
+}
+
+static int
+map_hd(const char * device_dir, int ma, int mi, int result,
+       int follow_symlink, int verbose)
+{
+        char c, num;
+
+        if (2 == result) {
+                num = list_matching_nodes(device_dir, FT_BLOCK,
+                                          ma, mi, follow_symlink,
+                                          verbose);
+                return (num > 0) ? 0 : 1;
+        }
+        switch (ma) {
+        case IDE0_MAJOR: c = 'a'; break;
+        case IDE1_MAJOR: c = 'c'; break;
+        case IDE2_MAJOR: c = 'e'; break;
+        case IDE3_MAJOR: c = 'g'; break;
+        case IDE4_MAJOR: c = 'i'; break;
+        case IDE5_MAJOR: c = 'k'; break;
+        case IDE6_MAJOR: c = 'm'; break;
+        case IDE7_MAJOR: c = 'o'; break;
+        case IDE8_MAJOR: c = 'q'; break;
+        case IDE9_MAJOR: c = 's'; break;
+        default: c = '?'; break;
+        }
+        if (mi > 63)
+                ++c;
+        printf("%shd%c\n", sys_hd_dir, c);
+        return 0;
+}
+
+static int
+map_sd(const char * device_name, const char * device_dir, int ma, int mi,
+       int result, int follow_symlink, int verbose)
+{
+        int index, m_mi, m_ma, num;
+        char value[D_NAME_LEN_MAX];
+        char name[D_NAME_LEN_MAX];
+
+        if (2 == result) {
+                num = list_matching_nodes(device_dir, FT_BLOCK, ma, mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        }
+        if (SCSI_DISK0_MAJOR == ma)
+                index = mi / 16;
+        else if (ma >= SCSI_DISK8_MAJOR)
+                index = (mi / 16) + 128 +
+                        ((ma - SCSI_DISK8_MAJOR) * 16);
+        else
+                index = (mi / 16) + 16 +
+                        ((ma - SCSI_DISK1_MAJOR) * 16);
+        if (index < 26)
+                snprintf(name, sizeof(name), "%ssd%c",
+                         sys_sd_dir, 'a' + index % 26);
+        else if (index < (26 + 1) * 26)
+                snprintf(name, sizeof(name), "%ssd%c%c",
+                         sys_sd_dir,
+                         'a' + index / 26 - 1,'a' + index % 26);
+        else {
+                const unsigned int m1 = (index / 26 - 1) / 26 - 1;
+                const unsigned int m2 = (index / 26 - 1) % 26;
+                const unsigned int m3 =  index % 26;
+
+                snprintf(name, sizeof(name), "%ssd%c%c%c",
+                         sys_sd_dir, 'a' + m1, 'a' + m2, 'a' + m3);
+        }
+        if (3 == result) {
+                printf("%s\n", name);
+                return 0;
+        }
+        if (! get_value(name, "dev", value, sizeof(value))) {
+                pr2serr("Couldn't find sysfs match for device: %s\n",
+                        device_name);
+                return 1;
+        }
+        if (verbose)
+                pr2serr("sysfs sd dev: %s\n", value);
+        if (! if_directory_chdir(name, "device")) {
+                pr2serr("sysfs problem with device: %s\n", device_name);
+                return 1;
+        }
+        if (if_directory_ch2generic(".")) {
+                if (1 == result) {
+                        if (NULL == getcwd(value, sizeof(value)))
+                                value[0] = '\0';
+                        printf("%s\n", value);
+                        return 0;
+                }
+                if (! get_value(".", "dev", value, sizeof(value))) {
+                        pr2serr("Couldn't find sysfs generic dev\n");
+                        return 1;
+                }
+                if (verbose)
+                        printf("matching dev: %s\n", value);
+                if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
+                        pr2serr("Couldn't decode mapped dev\n");
+                        return 1;
+                }
+                num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        } else {
+                pr2serr("sd device: %s does not match any SCSI generic "
+                        "device\n", device_name);
+                pr2serr("    perhaps sg module is not loaded\n");
+                return 1;
+        }
+}
+
+static int
+map_sr(const char * device_name, const char * device_dir, int ma, int mi,
+       int result, int follow_symlink, int verbose)
+{
+        int m_mi, m_ma, num;
+        char value[D_NAME_LEN_MAX];
+        char name[D_NAME_LEN_MAX];
+
+        if (2 == result) {
+                num = list_matching_nodes(device_dir, FT_BLOCK, ma, mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        }
+        snprintf(name, sizeof(name), "%ssr%d", sys_sr_dir, mi);
+        if (3 == result) {
+                printf("%s\n", name);
+                return 0;
+        }
+        if (! get_value(name, "dev", value, sizeof(value))) {
+                pr2serr("Couldn't find sysfs match for device: %s\n",
+                        device_name);
+                return 1;
+        }
+        if (verbose)
+                pr2serr("sysfs sr dev: %s\n", value);
+        if (! if_directory_chdir(name, "device")) {
+                pr2serr("sysfs problem with device: %s\n", device_name);
+                return 1;
+        }
+        if (if_directory_ch2generic(".")) {
+                if (1 == result) {
+                        if (NULL == getcwd(value, sizeof(value)))
+                                value[0] = '\0';
+                        printf("%s\n", value);
+                        return 0;
+                }
+                if (! get_value(".", "dev", value, sizeof(value))) {
+                        pr2serr("Couldn't find sysfs generic dev\n");
+                        return 1;
+                }
+                if (verbose)
+                        printf("matching dev: %s\n", value);
+                if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
+                        pr2serr("Couldn't decode mapped dev\n");
+                        return 1;
+                }
+                num = list_matching_nodes(device_dir, FT_BLOCK, m_ma, m_mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        } else {
+                pr2serr("sr device: %s does not match any SCSI generic "
+                        "device\n", device_name);
+                pr2serr("    perhaps sg module is not loaded\n");
+                return 1;
+        }
+}
+
+static int
+map_st(const char * device_name, const char * device_dir, int ma, int mi,
+       int result, int follow_symlink, int verbose)
+{
+        int m_mi, m_ma, num;
+        char value[D_NAME_LEN_MAX];
+        char name[D_NAME_LEN_MAX];
+
+        if (2 == result) {
+                num = list_matching_nodes(device_dir, FT_CHAR, ma, mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        }
+        snprintf(name, sizeof(name), "%sst%d", sys_st_dir,
+                 TAPE_NR(mi));
+        if (3 == result) {
+                printf("%s\n", name);
+                return 0;
+        }
+        if (! get_value(name, "dev", value, sizeof(value))) {
+                pr2serr("Couldn't find sysfs match for device: %s\n",
+                        device_name);
+                return 1;
+        }
+        if (verbose)
+                pr2serr("sysfs st dev: %s\n", value);
+        if (! if_directory_chdir(name, "device")) {
+                pr2serr("sysfs problem with device: %s\n", device_name);
+                return 1;
+        }
+        if (if_directory_ch2generic(".")) {
+                if (1 == result) {
+                        if (NULL == getcwd(value, sizeof(value)))
+                                value[0] = '\0';
+                        printf("%s\n", value);
+                        return 0;
+                }
+                if (! get_value(".", "dev", value, sizeof(value))) {
+                        pr2serr("Couldn't find sysfs generic dev\n");
+                        return 1;
+                }
+                if (verbose)
+                        printf("matching dev: %s\n", value);
+                if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
+                        pr2serr("Couldn't decode mapped dev\n");
+                        return 1;
+                }
+                num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        } else {
+                pr2serr("st device: %s does not match any SCSI generic "
+                        "device\n", device_name);
+                pr2serr("    perhaps sg module is not loaded\n");
+                return 1;
+        }
+}
+
+static int
+map_osst(const char * device_name, const char * device_dir, int ma, int mi,
+         int result, int follow_symlink, int verbose)
+{
+        int m_mi, m_ma, num;
+        char value[D_NAME_LEN_MAX];
+        char name[D_NAME_LEN_MAX];
+
+        if (2 == result) {
+                num = list_matching_nodes(device_dir, FT_CHAR, ma, mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        }
+        snprintf(name, sizeof(name), "%sosst%d", sys_osst_dir,
+                 TAPE_NR(mi));
+        if (3 == result) {
+                printf("%s\n", name);
+                return 0;
+        }
+        if (! get_value(name, "dev", value, sizeof(value))) {
+                pr2serr("Couldn't find sysfs match for device: %s\n",
+                        device_name);
+                return 1;
+        }
+        if (verbose)
+                pr2serr("sysfs osst dev: %s\n", value);
+        if (! if_directory_chdir(name, "device")) {
+                pr2serr("sysfs problem with device: %s\n", device_name);
+                return 1;
+        }
+        if (if_directory_ch2generic(".")) {
+                if (1 == result) {
+                        if (NULL == getcwd(value, sizeof(value)))
+                                value[0] = '\0';
+                        printf("%s\n", value);
+                        return 0;
+                }
+                if (! get_value(".", "dev", value, sizeof(value))) {
+                        pr2serr("Couldn't find sysfs generic dev\n");
+                        return 1;
+                }
+                if (verbose)
+                        printf("matching dev: %s\n", value);
+                if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
+                        pr2serr("Couldn't decode mapped dev\n");
+                        return 1;
+                }
+                num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        } else {
+                pr2serr("osst device: %s does not match any SCSI generic "
+                        "device\n", device_name);
+                pr2serr("    perhaps sg module is not loaded\n");
+                return 1;
+        }
+}
+
+static int
+map_ch(const char * device_name, const char * device_dir, int ma, int mi,
+       int result, int follow_symlink, int verbose)
+{
+        int m_mi, m_ma, num;
+        char value[D_NAME_LEN_MAX];
+        char name[D_NAME_LEN_MAX];
+
+        if (2 == result) {
+                num = list_matching_nodes(device_dir, FT_CHAR, ma, mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        }
+        snprintf(name, sizeof(name), "%ssch%d", sys_sch_dir, mi);
+        if (3 == result) {
+                printf("%s\n", name);
+                return 0;
+        }
+        if (! get_value(name, "dev", value, sizeof(value))) {
+                pr2serr("Couldn't find sysfs match for device: %s\n",
+                        device_name);
+                return 1;
+        }
+        if (verbose)
+                pr2serr("sysfs sch dev: %s\n", value);
+        if (! if_directory_chdir(name, "device")) {
+                pr2serr("sysfs problem with device: %s\n", device_name);
+                return 1;
+        }
+        if (if_directory_ch2generic(".")) {
+                if (1 == result) {
+                        if (NULL == getcwd(value, sizeof(value)))
+                                value[0] = '\0';
+                        printf("%s\n", value);
+                        return 0;
+                }
+                if (! get_value(".", "dev", value, sizeof(value))) {
+                        pr2serr("Couldn't find sysfs generic dev\n");
+                        return 1;
+                }
+                if (verbose)
+                        printf("matching dev: %s\n", value);
+                if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
+                        pr2serr("Couldn't decode mapped dev\n");
+                        return 1;
+                }
+                num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        } else {
+                pr2serr("sch device: %s does not match any SCSI generic "
+                        "device\n", device_name);
+                pr2serr("    perhaps sg module is not loaded\n");
+                return 1;
+        }
+}
+
+static int
+map_sg(const char * device_name, const char * device_dir, int ma, int mi,
+       int result, int follow_symlink, int verbose)
+{
+        int m_mi, m_ma, num;
+        char value[D_NAME_LEN_MAX];
+        char name[D_NAME_LEN_MAX];
+
+        if (2 == result) {
+                num = list_matching_nodes(device_dir, FT_CHAR, ma, mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        }
+        snprintf(name, sizeof(name), "%ssg%d", sys_sg_dir, mi);
+        if (3 == result) {
+                printf("%s\n", name);
+                return 0;
+        }
+        if (! get_value(name, "dev", value, sizeof(value))) {
+                pr2serr("Couldn't find sysfs match for device: %s\n",
+                        device_name);
+                return 1;
+        }
+        if (verbose)
+                pr2serr("sysfs sg dev: %s\n", value);
+        if (! if_directory_chdir(name, "device")) {
+                pr2serr("sysfs problem with device: %s\n", device_name);
+                return 1;
+        }
+        if ((1 == from_sg_scan(".", verbose)) &&
+            (if_directory_chdir(".", from_sg.name))) {
+                if (DT_DIR == from_sg.d_type) {
+                        if ((1 == scan_for_first(".", verbose)) &&
+                            (if_directory_chdir(".", for_first.name))) {
+                                ;
+                        } else {
+                                pr2serr("unexpected scan_for_first error\n");
+                        }
+                }
+                if (1 == result) {
+                        if (NULL == getcwd(value, sizeof(value)))
+                                value[0] = '\0';
+                        printf("%s\n", value);
+                        return 0;
+                }
+                if (! get_value(".", "dev", value, sizeof(value))) {
+                        pr2serr("Couldn't find sysfs block dev\n");
+                        return 1;
+                }
+                if (verbose)
+                        printf("matching dev: %s\n", value);
+                if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) {
+                        pr2serr("Couldn't decode mapped dev\n");
+                        return 1;
+                }
+                num = list_matching_nodes(device_dir, from_sg.ft, m_ma, m_mi,
+                                          follow_symlink, verbose);
+                return (num > 0) ? 0 : 1;
+        } else {
+                pr2serr("sg device: %s does not match any other SCSI "
+                        "device\n", device_name);
+                return 1;
+        }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+        int c, num, tt, cont, res;
+        int do_dev_dir = 0;
+        int given_is = -1;
+        int result = 0;
+        int follow_symlink = 0;
+        int verbose = 0;
+        char device_name[D_NAME_LEN_MAX];
+        char device_dir[D_NAME_LEN_MAX];
+        char value[D_NAME_LEN_MAX];
+        int ret = 1;
+        int ma, mi;
+
+        memset(device_name, 0, sizeof(device_name));
+        memset(device_dir, 0, sizeof(device_dir));
+        while (1) {
+                int option_index = 0;
+
+                c = getopt_long(argc, argv, "d:hg:r:svV", long_options,
+                                &option_index);
+                if (c == -1)
+                        break;
+
+                switch (c) {
+                case 'd':
+                        strncpy(device_dir, optarg, sizeof(device_dir));
+                        do_dev_dir = 1;
+                        break;
+                case 'g':
+                        num = sscanf(optarg, "%d", &res);
+                        if ((1 == num) && ((0 == res) || (1 == res)))
+                                given_is = res;
+                        else {
+                                pr2serr("value for '--given_to=' must be 0 "
+                                        "or 1\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'h':
+                case '?':
+                        usage();
+                        return 0;
+                case 'r':
+                        num = sscanf(optarg, "%d", &res);
+                        if ((1 == num) && (res >= 0) && (res < 4))
+                                result = res;
+                        else {
+                                pr2serr("value for '--result=' must be "
+                                        "0..3\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 's':
+                        follow_symlink = 1;
+                        break;
+                case 'v':
+                        ++verbose;
+                        break;
+                case 'V':
+                        pr2serr(ME "version: %s\n", version_str);
+                        return 0;
+                default:
+                        pr2serr("unrecognised option code 0x%x ??\n", c);
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                }
+        }
+        if (optind < argc) {
+                if ('\0' == device_name[0]) {
+                        strncpy(device_name, argv[optind],
+                                sizeof(device_name) - 1);
+                        device_name[sizeof(device_name) - 1] = '\0';
+                        ++optind;
+                }
+                if (optind < argc) {
+                        for (; optind < argc; ++optind)
+                                pr2serr("Unexpected extra argument: %s\n",
+                                        argv[optind]);
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                }
+        }
+
+        if (0 == device_name[0]) {
+                pr2serr("missing device name!\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+
+        ma = 0;
+        mi = 0;
+        if (do_dev_dir) {
+                if (if_directory_chdir(".", device_dir)) {
+                        if (getcwd(device_dir, sizeof(device_dir)))
+                                device_dir[sizeof(device_dir) - 1] = '\0';
+                        else
+                                device_dir[0] = '\0';
+                        if (verbose > 1)
+                                pr2serr("Absolute path to dev_dir: %s\n",
+                                        device_dir);
+                } else {
+                        pr2serr("dev_dir: %s invalid\n", device_dir);
+                        return SG_LIB_FILE_ERROR;
+                }
+        } else {
+                strcpy(device_dir, device_name);
+                dirname(device_dir);
+                if (0 == strcmp(device_dir, device_name)) {
+                        if (NULL == getcwd(device_dir, sizeof(device_dir)))
+                                device_dir[0] = '\0';
+                }
+        }
+        ret = nt_typ_from_filename(device_name, &ma, &mi);
+        if (ret < 0) {
+                pr2serr("stat failed on %s: %s\n", device_name,
+                        ssafe_strerror(-ret));
+                return SG_LIB_FILE_ERROR;
+        }
+        if (verbose)
+                pr2serr(" %s: %s device [maj=%d, min=%d]\n", device_name,
+                        nt_names[ret], ma, mi);
+        res = 0;
+        switch (ret) {
+        case NT_SD:
+        case NT_SR:
+        case NT_HD:
+                if (given_is > 0) {
+                        pr2serr("block special but '--given_is=' suggested "
+                                "sysfs device\n");
+                        return SG_LIB_FILE_ERROR;
+                }
+                break;
+        case NT_ST:
+        case NT_OSST:
+        case NT_CH:
+        case NT_SG:
+                if (given_is > 0) {
+                        pr2serr("character special but '--given_is=' "
+                                "suggested sysfs device\n");
+                        return SG_LIB_FILE_ERROR;
+                }
+                break;
+        case NT_REG:
+                if (0 == given_is) {
+                        pr2serr("regular file but '--given_is=' suggested "
+                                "block or char special\n");
+                        return SG_LIB_FILE_ERROR;
+                }
+                strcpy(device_dir, def_dev_dir);
+                break;
+        case NT_DIR:
+                if (0 == given_is) {
+                        pr2serr("directory but '--given_is=' suggested "
+                                "block or char special\n");
+                        return SG_LIB_FILE_ERROR;
+                }
+                strcpy(device_dir, def_dev_dir);
+                break;
+        default:
+                break;
+        }
+
+        tt = NT_NO_MATCH;
+        do {
+                cont = 0;
+                switch (ret) {
+                case NT_NO_MATCH:
+                        res = 1;
+                        break;
+                case NT_SD:
+                        res = map_sd(device_name, device_dir, ma, mi, result,
+                                     follow_symlink, verbose);
+                        break;
+                case NT_SR:
+                        res = map_sr(device_name, device_dir, ma, mi, result,
+                                     follow_symlink, verbose);
+                        break;
+                case NT_HD:
+                        if (result < 2) {
+                                pr2serr("a hd device does not map to a sg "
+                                        "device\n");
+                                return SG_LIB_FILE_ERROR;
+                        }
+                        res = map_hd(device_dir, ma, mi, result,
+                                     follow_symlink, verbose);
+                        break;
+                case NT_ST:
+                        res = map_st(device_name, device_dir, ma, mi, result,
+                                     follow_symlink, verbose);
+                        break;
+                case NT_OSST:
+                        res = map_osst(device_name, device_dir, ma, mi,
+                                       result, follow_symlink, verbose);
+                        break;
+                case NT_CH:
+                        res = map_ch(device_name, device_dir, ma, mi, result,
+                                     follow_symlink, verbose);
+                        break;
+                case NT_SG:
+                        res = map_sg(device_name, device_dir, ma, mi, result,
+                                     follow_symlink, verbose);
+                        break;
+                case NT_REG:
+                        if (! get_value(NULL, device_name, value,
+                                        sizeof(value))) {
+                                pr2serr("Couldn't fetch value from: %s\n",
+                                        device_name);
+                                return SG_LIB_FILE_ERROR;
+                        }
+                        if (verbose)
+                                pr2serr("value: %s\n", value);
+                        if (2 != sscanf(value, "%d:%d", &ma, &mi)) {
+                                pr2serr("Couldn't decode value\n");
+                                return SG_LIB_FILE_ERROR;
+                        }
+                        tt = nt_typ_from_major(ma);
+                        cont = 1;
+                        break;
+                case NT_DIR:
+                        if (! get_value(device_name, "dev", value,
+                                        sizeof(value))) {
+                                pr2serr("Couldn't fetch value from: %s/dev\n",
+                                        device_name);
+                                return SG_LIB_FILE_ERROR;
+                        }
+                        if (verbose)
+                                pr2serr("value: %s\n", value);
+                        if (2 != sscanf(value, "%d:%d", &ma, &mi)) {
+                                pr2serr("Couldn't decode value\n");
+                                return SG_LIB_FILE_ERROR;
+                        }
+                        tt = nt_typ_from_major(ma);
+                        cont = 1;
+                        break;
+                default:
+                        break;
+                }
+                ret = tt;
+        } while (cont);
+        return res;
+}
diff --git a/sg3_utils/src/sg_modes.c b/sg3_utils/src/sg_modes.c
new file mode 100644
index 0000000..01f2450
--- /dev/null
+++ b/sg3_utils/src/sg_modes.c
@@ -0,0 +1,1302 @@
+/*
+ *  Copyright (C) 2000-2015 D. Gilbert
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program outputs information provided by a SCSI MODE SENSE command.
+ *  Does 10 byte MODE SENSE commands by default, Trent Piepho added a "-6"
+ *  switch for force 6 byte mode sense commands.
+ *  This utility cannot modify mode pages. See the sdparm utility for that.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.48 20151219";
+
+#define DEF_ALLOC_LEN (1024 * 4)
+#define DEF_6_ALLOC_LEN 252
+#define PG_CODE_ALL 0x3f
+#define PG_CODE_MASK 0x3f
+#define PG_CODE_MAX 0x3f
+#define SPG_CODE_ALL 0xff
+#define PROTO_SPECIFIC_1 0x18
+#define PROTO_SPECIFIC_2 0x19
+
+#define EBUFF_SZ 256
+
+
+static struct option long_options[] = {
+        {"all", no_argument, 0, 'a'},
+        {"control", required_argument, 0, 'c'},
+        {"dbd", no_argument, 0, 'd'},
+        {"dbout", no_argument, 0, 'D'},
+        {"examine", no_argument, 0, 'e'},
+        {"flexible", no_argument, 0, 'f'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"list", no_argument, 0, 'l'},
+        {"llbaa", no_argument, 0, 'L'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"new", no_argument, 0, 'N'},
+        {"old", no_argument, 0, 'O'},
+        {"page", required_argument, 0, 'p'},
+        {"raw", no_argument, 0, 'r'},
+        {"six", no_argument, 0, '6'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_all;
+    int do_dbd;
+    int do_dbout;
+    int do_examine;
+    int do_flexible;
+    int do_help;
+    int do_hex;
+    int do_list;
+    int do_llbaa;
+    int maxlen;
+    int do_raw;
+    int do_six;
+    int do_verbose;
+    int do_version;
+    int page_control;
+    int pg_code;
+    int subpg_code;
+    int subpg_code_set;
+    const char * device_name;
+    int opt_new;
+};
+
+
+static void
+usage()
+{
+    printf("Usage: sg_modes [--all] [--control=PC] [--dbd] [--dbout] "
+           "[--examine]\n"
+           "                [--flexible] [--help] [--hex] [--list] "
+           "[--llbaa]\n"
+           "                [--maxlen=LEN] [--page=PG[,SPG]] [--raw] [-R] "
+           "[--six]\n"
+           "                [--verbose] [--version] [DEVICE]\n"
+           "  where:\n"
+           "    --all|-a        get all mode pages supported by device\n"
+           "                    use twice to get all mode pages and subpages\n"
+           "    --control=PC|-c PC    page control (default: 0)\n"
+           "                       0: current, 1: changeable,\n"
+           "                       2: (manufacturer's) defaults, 3: saved\n"
+           "    --dbd|-d        disable block descriptors (DBD field in cdb)\n"
+           "    --dbout|-D      disable block descriptor output\n"
+           "    --examine|-e    examine pages # 0 through to 0x3e, note if "
+           "found\n"
+           "    --flexible|-f    be flexible, cope with MODE SENSE 6/10 "
+           "response mixup\n");
+    printf("    --help|-h       print usage message then exit\n"
+           "    --hex|-H        output full response in hex\n"
+           "                    use twice to output page number and header "
+           "in hex\n"
+           "    --list|-l       list common page codes for device peripheral "
+           "type,\n"
+           "                    if no device given then assume disk type\n"
+           "    --llbaa|-L      set Long LBA Accepted (LLBAA field in mode "
+           "sense (10) cdb)\n"
+           "    --maxlen=LEN|-m LEN    max response length (allocation "
+           "length in cdb)\n"
+           "                           (def: 0 -> 4096 or 252 (for MODE "
+           "SENSE 6) bytes)\n"
+           "    --page=PG|-p PG    page code to fetch (def: 63)\n"
+           "    --page=PG,SPG|-p PG,SPG\n"
+           "                       page code and subpage code to fetch "
+           "(defs: 63,0)\n"
+           "    --raw|-r        output response in binary to stdout\n"
+           "    -R              mode page response to stdout, a byte per "
+           "line in ASCII\n"
+           "                    hex (same result as '--raw --raw')\n"
+           "    --six|-6        use MODE SENSE(6), by default uses MODE "
+           "SENSE(10)\n"
+           "    --verbose|-v    increase verbosity\n"
+           "    --version|-V    output version string then exit\n\n"
+           "Performs a SCSI MODE SENSE (10 or 6) command. To access and "
+           "possibly change\nmode page fields see the sdparm utility.\n");
+}
+
+static void
+usage_old()
+{
+    printf("Usage:  sg_modes [-a] [-A] [-c=PC] [-d] [-D] [-e] [-f] [-h] "
+           "[-H] [-l] [-L]\n"
+           "                 [-m=LEN] [-p=PG[,SPG]] [-r] [-subp=SPG] [-v] "
+           "[-V] [-6]\n"
+           "                 [DEVICE]\n"
+           " where:\n"
+           "   -a    get all mode pages supported by device\n"
+           "   -A    get all mode pages and subpages supported by device\n"
+           "   -c=PC    page control (def: 0 [current],"
+           " 1 [changeable],\n"
+           "                               2 [default], 3 [saved])\n"
+           "   -d    disable block descriptors (DBD field in cdb)\n"
+           "   -D    disable block descriptor output\n"
+           "   -e    examine pages # 0 through to 0x3e, note if found\n"
+           "   -f    be flexible, cope with MODE SENSE 6/10 response "
+           "mixup\n");
+    printf("   -h    output page number and header in hex\n"
+           "   -H    output page number and header in hex (same as '-h')\n"
+           "   -l    list common page codes for device peripheral type,\n"
+           "         if no device given then assume disk type\n"
+           "   -L    set Long LBA Accepted (LLBAA field in mode sense "
+           "10 cdb)\n"
+           "   -m=LEN    max response length (allocation length in cdb)\n"
+           "             (def: 0 -> 4096 or 252 (for MODE SENSE 6) bytes)\n"
+           "   -p=PG     page code in hex (def: 3f)\n"
+           "   -p=PG,SPG    both in hex, (defs: 3f,0)\n"
+           "   -r    mode page output to stdout, a byte per line in "
+           "ASCII hex\n"
+           "   -subp=SPG    sub page code in hex (def: 0)\n"
+           "   -v    verbose\n"
+           "   -V    output version string\n"
+           "   -6    Use MODE SENSE(6), by default uses MODE SENSE(10)\n"
+           "   -?    output this usage message\n\n"
+           "Performs a SCSI MODE SENSE (10 or 6) command\n");
+}
+
+static void
+usage_for(const struct opts_t * op)
+{
+    if (op->opt_new)
+        usage();
+    else
+        usage_old();
+}
+
+/* Processes command line options according to new option format. Returns
+ * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
+static int
+process_cl_new(struct opts_t * op, int argc, char * argv[])
+{
+    int c, n, nn;
+    char * cp;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "6aAc:dDefhHlLm:NOp:rRsvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case '6':
+            ++op->do_six;
+            break;
+        case 'a':
+            ++op->do_all;
+            break;
+        case 'A':
+            op->do_all += 2;
+            break;
+        case 'c':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 3)) {
+                pr2serr("bad argument to '--control='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->page_control = n;
+            break;
+        case 'd':
+            ++op->do_dbd;
+            break;
+        case 'D':
+            ++op->do_dbout;
+            break;
+        case 'e':
+            ++op->do_examine;
+            break;
+        case 'f':
+            ++op->do_flexible;
+            break;
+        case 'h':
+        case '?':
+            ++op->do_help;
+            break;
+        case 'H':
+            ++op->do_hex;
+            break;
+        case 'l':
+            ++op->do_list;
+            break;
+        case 'L':
+            ++op->do_llbaa;
+            break;
+        case 'm':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 65535)) {
+                pr2serr("bad argument to '--maxlen='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->maxlen = n;
+            break;
+        case 'N':
+            break;      /* ignore */
+        case 'O':
+            op->opt_new = 0;
+            return 0;
+        case 'p':
+            cp = strchr(optarg, ',');
+            n = sg_get_num_nomult(optarg);
+            if ((n < 0) || (n > 63)) {
+                pr2serr("Bad argument to '--page='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if (cp) {
+                nn = sg_get_num_nomult(cp + 1);
+                if ((nn < 0) || (nn > 255)) {
+                    pr2serr("Bad second value in argument to '--page='\n");
+                    usage();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->subpg_code = nn;
+                op->subpg_code_set = 1;
+            } else
+                nn = 0;
+            op->pg_code = n;
+            break;
+        case 'r':
+            ++op->do_raw;
+            break;
+        case 'R':
+            op->do_raw += 2;
+            break;
+        case 's':
+            ++op->do_six;
+            break;
+        case 'v':
+            ++op->do_verbose;
+            break;
+        case 'V':
+            ++op->do_version;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (op->do_help)
+                break;
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+/* Processes command line options according to old option format. Returns
+ * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
+static int
+process_cl_old(struct opts_t * op, int argc, char * argv[])
+{
+    int k, jmp_out, plen, num, n;
+    unsigned int u, uu;
+    const char * cp;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case '6':
+                    ++op->do_six;
+                    break;
+                case 'a':
+                    ++op->do_all;
+                    break;
+                case 'A':
+                    op->do_all += 2;
+                    break;
+                case 'd':
+                    ++op->do_dbd;
+                    break;
+                case 'D':
+                    ++op->do_dbout;
+                    break;
+                case 'e':
+                    ++op->do_examine;
+                    break;
+                case 'f':
+                    ++op->do_flexible;
+                    break;
+                case 'h':
+                case 'H':
+                    op->do_hex += 2;
+                    break;
+                case 'l':
+                    ++op->do_list;
+                    break;
+                case 'L':
+                    ++op->do_llbaa;
+                    break;
+                case 'N':
+                    op->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case 'r':
+                    op->do_raw += 2;
+                    break;
+                case 'v':
+                    ++op->do_verbose;
+                    break;
+                case 'V':
+                    ++op->do_version;
+                    break;
+                case '?':
+                    ++op->do_help;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            if (0 == strncmp("c=", cp, 2)) {
+                num = sscanf(cp + 2, "%x", &u);
+                if ((1 != num) || (u > 3)) {
+                    pr2serr("Bad page control after 'c=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->page_control = u;
+            } else if (0 == strncmp("m=", cp, 2)) {
+                num = sscanf(cp + 2, "%d", &n);
+                if ((1 != num) || (n < 0) || (n > 65535)) {
+                    pr2serr("Bad argument after 'm=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->maxlen = n;
+            } else if (0 == strncmp("p=", cp, 2)) {
+                if (NULL == strchr(cp + 2, ',')) {
+                    num = sscanf(cp + 2, "%x", &u);
+                    if ((1 != num) || (u > 63)) {
+                        pr2serr("Bad page code value after 'p=' option\n");
+                        usage_old();
+                        return SG_LIB_SYNTAX_ERROR;
+                    }
+                    op->pg_code = u;
+                } else if (2 == sscanf(cp + 2, "%x,%x", &u, &uu)) {
+                    if (uu > 255) {
+                        pr2serr("Bad subpage code value after 'p=' option\n");
+                        usage_old();
+                        return SG_LIB_SYNTAX_ERROR;
+                    }
+                    op->pg_code = u;
+                    op->subpg_code = uu;
+                    op->subpg_code_set = 1;
+                } else {
+                    pr2serr("Bad page code, subpage code sequence after 'p=' "
+                            "option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else if (0 == strncmp("subp=", cp, 5)) {
+                num = sscanf(cp + 5, "%x", &u);
+                if ((1 != num) || (u > 255)) {
+                    pr2serr("Bad sub page code after 'subp=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->subpg_code = u;
+                op->subpg_code_set = 1;
+                if (-1 == op->pg_code)
+                    op->pg_code = 0;
+            } else if (0 == strncmp("-old", cp, 4))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage_old();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == op->device_name)
+            op->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not expecting: %s\n",
+                    op->device_name, cp);
+            usage_old();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+/* Process command line options. First check using new option format unless
+ * the SG3_UTILS_OLD_OPTS environment variable is defined which causes the
+ * old option format to be checked first. Both new and old format can be
+ * countermanded by a '-O' and '-N' options respectively. As soon as either
+ * of these options is detected (when processing the other format), processing
+ * stops and is restarted using the other format. Clear? */
+static int
+process_cl(struct opts_t * op, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        op->opt_new = 0;
+        res = process_cl_old(op, argc, argv);
+        if ((0 == res) && op->opt_new)
+            res = process_cl_new(op, argc, argv);
+    } else {
+        op->opt_new = 1;
+        res = process_cl_new(op, argc, argv);
+        if ((0 == res) && (0 == op->opt_new))
+            res = process_cl_old(op, argc, argv);
+    }
+    return res;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+
+struct page_code_desc {
+    int page_code;
+    int subpage_code;
+    const char * desc;
+};
+
+static struct page_code_desc pc_desc_common[] = {
+    {0x0, 0x0, "Unit Attention condition [vendor specific format]"},
+    {0x2, 0x0, "Disconnect-Reconnect"},
+    {0x9, 0x0, "Peripheral device (obsolete)"},
+    {0xa, 0x0, "Control"},
+    {0xa, 0x1, "Control extension"},
+    {0xa, 0x3, "Command duration limit A"},
+    {0xa, 0x4, "Command duration limit B"},
+    {0x15, 0x0, "Extended"},
+    {0x16, 0x0, "Extended device-type specific"},
+    {0x18, 0x0, "Protocol specific lu"},
+    {0x19, 0x0, "Protocol specific port"},
+    {0x1a, 0x0, "Power condition"},
+    {0x1a, 0x1, "Power consumption"},
+    {0x1c, 0x0, "Informational exceptions control"},
+    {PG_CODE_ALL, 0x0, "[yields all supported pages]"},
+    {PG_CODE_ALL, SPG_CODE_ALL, "[yields all supported pages and subpages]"},
+};
+
+static struct page_code_desc pc_desc_disk[] = {
+    {0x1, 0x0, "Read-Write error recovery"},
+    {0x3, 0x0, "Format (obsolete)"},
+    {0x4, 0x0, "Rigid disk geometry (obsolete)"},
+    {0x5, 0x0, "Flexible geometry (obsolete)"},
+    {0x7, 0x0, "Verify error recovery"},
+    {0x8, 0x0, "Caching"},
+    {0xa, 0x2, "Application tag"},
+    {0xa, 0x5, "IO advice hints grouping"}, /* added sbc4r06 */
+    {0xa, 0x6, "Background operation control"}, /* added sbc4r07 */
+    {0xa, 0xf1, "Parallel ATA control (SAT)"},
+    {0xa, 0xf2, "Reserved (SATA control) (SAT)"},
+    {0xb, 0x0, "Medium types supported (obsolete)"},
+    {0xc, 0x0, "Notch and partition (obsolete)"},
+    {0xd, 0x0, "Power condition (obsolete, moved to 0x1a)"},
+    {0x10, 0x0, "XOR control"}, /* obsolete in sbc3r32 */
+    {0x1a, 0xf1, "ATA Power condition"},
+    {0x1c, 0x1, "Background control"},
+    {0x1c, 0x2, "Logical block provisioning"},
+};
+
+static struct page_code_desc pc_desc_tape[] = {
+    {0x1, 0x0, "Read-Write error recovery"},
+    {0xa, 0xf0, "Control data protection"},
+    {0xf, 0x0, "Data Compression"},
+    {0x10, 0x0, "Device configuration"},
+    {0x10, 0x1, "Device configuration extension"},
+    {0x11, 0x0, "Medium Partition [1]"},
+    {0x12, 0x0, "Medium Partition [2]"},
+    {0x13, 0x0, "Medium Partition [3]"},
+    {0x14, 0x0, "Medium Partition [4]"},
+    {0x1c, 0x0, "Informational exceptions control (tape version)"},
+    {0x1d, 0x0, "Medium configuration"},
+};
+
+static struct page_code_desc pc_desc_cddvd[] = {
+    {0x1, 0x0, "Read-Write error recovery"},
+    {0x3, 0x0, "MRW"},
+    {0x5, 0x0, "Write parameters"},
+    {0x7, 0x0, "Verify error recovery"},
+    {0x8, 0x0, "Caching"},
+    {0xd, 0x0, "CD device parameters (obsolete)"},
+    {0xe, 0x0, "CD audio"},
+    {0x1a, 0x0, "Power condition (mmc)"},
+    {0x1c, 0x0, "Fault/failure reporting control (mmc)"},
+    {0x1d, 0x0, "Timeout and protect"},
+    {0x2a, 0x0, "MM capabilities and mechanical status (obsolete)"},
+};
+
+static struct page_code_desc pc_desc_smc[] = {
+    {0x1d, 0x0, "Element address assignment"},
+    {0x1e, 0x0, "Transport geometry parameters"},
+    {0x1f, 0x0, "Device capabilities"},
+    {0x1f, 0x41, "Extended device capabilities"},
+};
+
+static struct page_code_desc pc_desc_scc[] = {
+    {0x1b, 0x0, "LUN mapping"},
+};
+
+static struct page_code_desc pc_desc_ses[] = {
+    {0x14, 0x0, "Enclosure services management"},
+};
+
+static struct page_code_desc pc_desc_rbc[] = {
+    {0x6, 0x0, "RBC device parameters"},
+};
+
+static struct page_code_desc pc_desc_adc[] = {
+    /* {0xe, 0x0, "ADC device configuration"}, */
+    {0xe, 0x1, "Target device"},
+    {0xe, 0x2, "DT device primary port"},
+    {0xe, 0x3, "Logical unit"},
+    {0xe, 0x4, "Target device serial number"},
+};
+
+static struct page_code_desc *
+mode_page_cs_table(int scsi_ptype, int * size)
+{
+    switch (scsi_ptype)
+    {
+        case -1:        /* common list */
+            *size = sizeof(pc_desc_common) / sizeof(pc_desc_common[0]);
+            return &pc_desc_common[0];
+        case PDT_DISK:         /* disk (direct access) type devices */
+        case PDT_WO:
+        case PDT_OPTICAL:
+            *size = sizeof(pc_desc_disk) / sizeof(pc_desc_disk[0]);
+            return &pc_desc_disk[0];
+        case PDT_TAPE:         /* tape devices */
+        case PDT_PRINTER:
+            *size = sizeof(pc_desc_tape) / sizeof(pc_desc_tape[0]);
+            return &pc_desc_tape[0];
+        case PDT_MMC:         /* cd/dvd/bd devices */
+            *size = sizeof(pc_desc_cddvd) / sizeof(pc_desc_cddvd[0]);
+            return &pc_desc_cddvd[0];
+        case PDT_MCHANGER:         /* medium changer devices */
+            *size = sizeof(pc_desc_smc) / sizeof(pc_desc_smc[0]);
+            return &pc_desc_smc[0];
+        case PDT_SAC:       /* storage array devices */
+            *size = sizeof(pc_desc_scc) / sizeof(pc_desc_scc[0]);
+            return &pc_desc_scc[0];
+        case PDT_SES:       /* enclosure services devices */
+            *size = sizeof(pc_desc_ses) / sizeof(pc_desc_ses[0]);
+            return &pc_desc_ses[0];
+        case PDT_RBC:       /* simplified direct access device */
+            *size = sizeof(pc_desc_rbc) / sizeof(pc_desc_rbc[0]);
+            return &pc_desc_rbc[0];
+        case PDT_ADC:       /* automation device/interface */
+            *size = sizeof(pc_desc_adc) / sizeof(pc_desc_adc[0]);
+            return &pc_desc_adc[0];
+    }
+    *size = 0;
+    return NULL;
+}
+
+static struct page_code_desc pc_desc_t_fcp[] = {
+    {0x18, 0x0, "LU control"},
+    {0x19, 0x0, "Port control"},
+};
+
+static struct page_code_desc pc_desc_t_spi4[] = {
+    {0x18, 0x0, "LU control"},
+    {0x19, 0x0, "Port control short format"},
+    {0x19, 0x1, "Margin control"},
+    {0x19, 0x2, "Saved training configuration value"},
+    {0x19, 0x3, "Negotiated settings"},
+    {0x19, 0x4, "Report transfer capabilities"},
+};
+
+static struct page_code_desc pc_desc_t_sas[] = {
+    {0x18, 0x0, "Protocol specific logical unit (SAS)"},
+    {0x19, 0x0, "Protocol specific port (SAS)"},
+    {0x19, 0x1, "Phy control and discover (SAS)"},
+    {0x19, 0x2, "Shared port control (SAS)"},
+    {0x19, 0x3, "Enhanced phy control (SAS)"},
+};
+
+static struct page_code_desc pc_desc_t_adc[] = {
+    {0xe, 0x1, "Target device"},
+    {0xe, 0x2, "DT device primary port"},
+    {0xe, 0x3, "Logical unit"},
+    {0x18, 0x0, "Protocol specific lu"},
+    {0x19, 0x0, "Protocol specific port"},
+};
+
+static struct page_code_desc *
+mode_page_transp_table(int t_proto, int * size)
+{
+    switch (t_proto)
+    {
+        case TPROTO_FCP:
+            *size = sizeof(pc_desc_t_fcp) / sizeof(pc_desc_t_fcp[0]);
+            return &pc_desc_t_fcp[0];
+        case TPROTO_SPI:
+            *size = sizeof(pc_desc_t_spi4) / sizeof(pc_desc_t_spi4[0]);
+            return &pc_desc_t_spi4[0];
+        case TPROTO_SAS:
+            *size = sizeof(pc_desc_t_sas) / sizeof(pc_desc_t_sas[0]);
+            return &pc_desc_t_sas[0];
+        case TPROTO_ADT:
+            *size = sizeof(pc_desc_t_adc) / sizeof(pc_desc_t_adc[0]);
+            return &pc_desc_t_adc[0];
+    }
+    *size = 0;
+    return NULL;
+}
+
+static const char *
+find_page_code_desc(int page_num, int subpage_num, int scsi_ptype,
+                    int inq_byte6, int t_proto)
+{
+    int k;
+    int num;
+    const struct page_code_desc * pcdp;
+
+    if (t_proto >= 0) {
+        pcdp = mode_page_transp_table(t_proto, &num);
+        if (pcdp) {
+            for (k = 0; k < num; ++k, ++pcdp) {
+                if ((page_num == pcdp->page_code) &&
+                    (subpage_num == pcdp->subpage_code))
+                    return pcdp->desc;
+                else if (page_num < pcdp->page_code)
+                    break;
+            }
+        }
+    }
+    pcdp = mode_page_cs_table(scsi_ptype, &num);
+    if (pcdp) {
+        for (k = 0; k < num; ++k, ++pcdp) {
+            if ((page_num == pcdp->page_code) &&
+                (subpage_num == pcdp->subpage_code))
+                return pcdp->desc;
+            else if (page_num < pcdp->page_code)
+                break;
+        }
+    }
+    if ((0xd != scsi_ptype) && (inq_byte6 & 0x40)) {
+        /* check for attached enclosure services processor */
+        pcdp = mode_page_cs_table(0xd, &num);
+        if (pcdp) {
+            for (k = 0; k < num; ++k, ++pcdp) {
+                if ((page_num == pcdp->page_code) &&
+                    (subpage_num == pcdp->subpage_code))
+                    return pcdp->desc;
+                else if (page_num < pcdp->page_code)
+                    break;
+            }
+        }
+    }
+    if ((0x8 != scsi_ptype) && (inq_byte6 & 0x8)) {
+        /* check for attached medium changer device */
+        pcdp = mode_page_cs_table(0x8, &num);
+        if (pcdp) {
+            for (k = 0; k < num; ++k, ++pcdp) {
+                if ((page_num == pcdp->page_code) &&
+                    (subpage_num == pcdp->subpage_code))
+                    return pcdp->desc;
+                else if (page_num < pcdp->page_code)
+                    break;
+            }
+        }
+    }
+    pcdp = mode_page_cs_table(-1, &num);
+    for (k = 0; k < num; ++k, ++pcdp) {
+        if ((page_num == pcdp->page_code) &&
+            (subpage_num == pcdp->subpage_code))
+            return pcdp->desc;
+        else if (page_num < pcdp->page_code)
+            break;
+    }
+    return NULL;
+}
+
+static void
+list_page_codes(int scsi_ptype, int inq_byte6, int t_proto)
+{
+    int num, num_ptype, pg, spg, c, d, valid_transport;
+    const struct page_code_desc * dp;
+    const struct page_code_desc * pe_dp;
+    char b[64];
+
+    valid_transport = ((t_proto >= 0) && (t_proto <= 0xf)) ? 1 : 0;
+    printf("Page[,subpage]   Name\n");
+    printf("=====================\n");
+    dp = mode_page_cs_table(-1, &num);
+    pe_dp = mode_page_cs_table(scsi_ptype, &num_ptype);
+    while (1) {
+        pg = dp ? dp->page_code : PG_CODE_ALL + 1;
+        spg = dp ? dp->subpage_code : SPG_CODE_ALL;
+        c = (pg << 8) + spg;
+        pg = pe_dp ? pe_dp->page_code : PG_CODE_ALL + 1;
+        spg = pe_dp ? pe_dp->subpage_code : SPG_CODE_ALL;
+        d = (pg << 8) + spg;
+        if (valid_transport &&
+            ((PROTO_SPECIFIC_1 == c) || (PROTO_SPECIFIC_2 == c)))
+            dp = (--num <= 0) ? NULL : (dp + 1); /* skip protocol specific */
+        else if (c == d) {
+            if (pe_dp->subpage_code)
+                printf(" 0x%02x,0x%02x    *  %s\n", pe_dp->page_code,
+                       pe_dp->subpage_code, pe_dp->desc);
+            else
+                printf(" 0x%02x         *  %s\n", pe_dp->page_code,
+                       pe_dp->desc);
+            dp = (--num <= 0) ? NULL : (dp + 1);
+            pe_dp = (--num_ptype <= 0) ? NULL : (pe_dp + 1);
+        } else if (c < d) {
+            if (dp->subpage_code)
+                printf(" 0x%02x,0x%02x       %s\n", dp->page_code,
+                       dp->subpage_code, dp->desc);
+            else
+                printf(" 0x%02x            %s\n", dp->page_code,
+                       dp->desc);
+            dp = (--num <= 0) ? NULL : (dp + 1);
+        } else {
+            if (pe_dp->subpage_code)
+                printf(" 0x%02x,0x%02x       %s\n", pe_dp->page_code,
+                       pe_dp->subpage_code, pe_dp->desc);
+            else
+                printf(" 0x%02x            %s\n", pe_dp->page_code,
+                       pe_dp->desc);
+            pe_dp = (--num_ptype <= 0) ? NULL : (pe_dp + 1);
+        }
+        if ((NULL == dp) && (NULL == pe_dp))
+            break;
+    }
+    if ((0xd != scsi_ptype) && (inq_byte6 & 0x40)) {
+        /* check for attached enclosure services processor */
+        printf("\n    Attached enclosure services processor\n");
+        dp = mode_page_cs_table(0xd, &num);
+        while (dp) {
+            if (dp->subpage_code)
+                printf(" 0x%02x,0x%02x       %s\n", dp->page_code,
+                       dp->subpage_code, dp->desc);
+            else
+                printf(" 0x%02x            %s\n", dp->page_code,
+                       dp->desc);
+            dp = (--num <= 0) ? NULL : (dp + 1);
+        }
+    }
+    if ((0x8 != scsi_ptype) && (inq_byte6 & 0x8)) {
+        /* check for attached medium changer device */
+        printf("\n    Attached medium changer device\n");
+        dp = mode_page_cs_table(0x8, &num);
+        while (dp) {
+            if (dp->subpage_code)
+                printf(" 0x%02x,0x%02x       %s\n", dp->page_code,
+                       dp->subpage_code, dp->desc);
+            else
+                printf(" 0x%02x            %s\n", dp->page_code,
+                       dp->desc);
+            dp = (--num <= 0) ? NULL : (dp + 1);
+        }
+    }
+    if (valid_transport) {
+        printf("\n    Transport protocol: %s\n",
+               sg_get_trans_proto_str(t_proto, sizeof(b), b));
+        dp = mode_page_transp_table(t_proto, &num);
+        while (dp) {
+            if (dp->subpage_code)
+                printf(" 0x%02x,0x%02x       %s\n", dp->page_code,
+                       dp->subpage_code, dp->desc);
+            else
+                printf(" 0x%02x            %s\n", dp->page_code,
+                       dp->desc);
+            dp = (--num <= 0) ? NULL : (dp + 1);
+        }
+    }
+}
+
+static int
+examine_pages(int sg_fd, int inq_pdt, int inq_byte6,
+              const struct opts_t * op)
+{
+    int k, res, header, mresp_len, len;
+    unsigned char rbuf[256];
+    const char * cp;
+
+    mresp_len = (op->do_raw || op->do_hex) ? sizeof(rbuf) : 4;
+    for (header = 0, k = 0; k < PG_CODE_MAX; ++k) {
+        if (op->do_six) {
+            res = sg_ll_mode_sense6(sg_fd, 0, 0, k, 0, rbuf, mresp_len,
+                                    1, op->do_verbose);
+            if (SG_LIB_CAT_INVALID_OP == res) {
+                pr2serr(">>>>>> try again without the '-6' switch for a 10 "
+                        "byte MODE SENSE command\n");
+                return res;
+            } else if (SG_LIB_CAT_NOT_READY == res) {
+                pr2serr("MODE SENSE (6) failed, device not ready\n");
+                return res;
+            }
+        } else {
+            res = sg_ll_mode_sense10(sg_fd, 0, 0, 0, k, 0, rbuf, mresp_len,
+                                     1, op->do_verbose);
+            if (SG_LIB_CAT_INVALID_OP == res) {
+                pr2serr(">>>>>> try again with a '-6' switch for a 6 byte "
+                        "MODE SENSE command\n");
+                return res;
+            } else if (SG_LIB_CAT_NOT_READY == res) {
+                pr2serr("MODE SENSE (10) failed, device not ready\n");
+                return res;
+            }
+        }
+        if (0 == res) {
+            len = op->do_six ? (rbuf[0] + 1) :
+                               (sg_get_unaligned_be16(rbuf + 0) + 2);
+            if (len > mresp_len)
+                len = mresp_len;
+            if (op->do_raw) {
+                dStrRaw((const char *)rbuf, len);
+                continue;
+            }
+            if (op->do_hex > 2) {
+                dStrHex((const char *)rbuf, len, -1);
+                continue;
+            }
+            if (0 == header) {
+                printf("Discovered mode pages:\n");
+                header = 1;
+            }
+            cp = find_page_code_desc(k, 0, inq_pdt, inq_byte6, -1);
+            if (cp)
+                printf("    %s\n", cp);
+            else
+                printf("    [0x%x]\n", k);
+            if (op->do_hex)
+                dStrHex((const char *)rbuf, len, 1);
+        } else if (op->do_verbose) {
+            char b[80];
+
+            sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose - 1);
+            pr2serr("MODE SENSE (%s) failed: %s\n", (op->do_six ? "6" : "10"),
+                    b);
+        }
+    }
+    return res;
+}
+
+static const char * pg_control_str_arr[] = {
+    "current",
+    "changeable",
+    "default",
+    "saved",
+};
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, num, len, res, md_len, bd_len, longlba, page_num, spf;
+    char ebuff[EBUFF_SZ];
+    const char * descp;
+    unsigned char * rsp_buff = NULL;
+    unsigned char def_rsp_buff[DEF_ALLOC_LEN];
+    unsigned char * malloc_rsp_buff = NULL;
+    int rsp_buff_size = DEF_ALLOC_LEN;
+    int ret = 0;
+    int density_code_off, t_proto, inq_pdt, inq_byte6, resp_mode6;
+    int num_ua_pages;
+    unsigned char * ucp;
+    unsigned char uc;
+    struct sg_simple_inquiry_resp inq_out;
+    char pdt_name[64];
+    char b[80];
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->pg_code = -1;
+    res = process_cl(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        usage_for(op);
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+
+    if (NULL == op->device_name) {
+        if (op->do_list) {
+            if ((op->pg_code < 0) || (op->pg_code > PG_CODE_MAX)) {
+                printf("    Assume peripheral device type: disk\n");
+                list_page_codes(0, 0, -1);
+            } else {
+                printf("    peripheral device type: %s\n",
+                       sg_get_pdt_str(op->pg_code, sizeof(pdt_name),
+                                      pdt_name));
+                if (op->subpg_code_set)
+                    list_page_codes(op->pg_code, 0, op->subpg_code);
+                else
+                    list_page_codes(op->pg_code, 0, -1);
+            }
+            return 0;
+        }
+        pr2serr("No DEVICE argument given\n");
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (op->do_examine && (op->pg_code >= 0)) {
+        pr2serr("can't give '-e' and a page number\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if ((op->do_six) && (op->do_llbaa)) {
+        pr2serr("LLBAA not defined for MODE SENSE 6, try without '-L'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->maxlen > 0) {
+        if (op->do_six && (op->maxlen > 255)) {
+            pr2serr("For Mode Sense (6) maxlen cannot exceed 255\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->maxlen > DEF_ALLOC_LEN) {
+            malloc_rsp_buff = (unsigned char *)malloc(op->maxlen);
+            if (NULL == malloc_rsp_buff) {
+                pr2serr("Unable to malloc maxlen=%d bytes\n", op->maxlen);
+                return SG_LIB_SYNTAX_ERROR;
+        }
+            rsp_buff = malloc_rsp_buff;
+        } else
+            rsp_buff = def_rsp_buff;
+        rsp_buff_size = op->maxlen;
+    } else {    /* maxlen == 0 */
+        rsp_buff_size = op->do_six ? DEF_6_ALLOC_LEN : DEF_ALLOC_LEN;
+        rsp_buff = def_rsp_buff;
+    }
+    /* If no pages or list selected than treat as 'a' */
+    if (! ((op->pg_code >= 0) || op->do_all || op->do_list || op->do_examine))
+        op->do_all = 1;
+
+    if (op->do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    if ((sg_fd = sg_cmds_open_device(op->device_name, 1 /* ro */,
+                                     op->do_verbose)) < 0) {
+        pr2serr("error opening file: %s: %s\n", op->device_name,
+                safe_strerror(-sg_fd));
+        if (malloc_rsp_buff)
+            free(malloc_rsp_buff);
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (sg_simple_inquiry(sg_fd, &inq_out, 1, op->do_verbose)) {
+        pr2serr("%s doesn't respond to a SCSI INQUIRY\n", op->device_name);
+        ret = SG_LIB_CAT_OTHER;
+        goto finish;
+    }
+    inq_pdt = inq_out.peripheral_type;
+    inq_byte6 = inq_out.byte_6;
+    if ((0 == op->do_raw) && (op->do_hex < 3))
+        printf("    %.8s  %.16s  %.4s   peripheral_type: %s [0x%x]\n",
+               inq_out.vendor, inq_out.product, inq_out.revision,
+               sg_get_pdt_str(inq_pdt, sizeof(pdt_name), pdt_name), inq_pdt);
+    if (op->do_list) {
+        if (op->subpg_code_set)
+            list_page_codes(inq_pdt, inq_byte6, op->subpg_code);
+        else
+            list_page_codes(inq_pdt, inq_byte6, -1);
+        goto finish;
+    }
+    if (op->do_examine) {
+        ret = examine_pages(sg_fd, inq_pdt, inq_byte6, op);
+        goto finish;
+    }
+    if (PG_CODE_ALL == op->pg_code) {
+        if (0 == op->do_all)
+            ++op->do_all;
+    } else if (op->do_all)
+        op->pg_code = PG_CODE_ALL;
+    if (op->do_all > 1)
+        op->subpg_code = SPG_CODE_ALL;
+
+    if (op->do_raw > 1) {
+        if (op->do_all) {
+            if (op->opt_new)
+                pr2serr("'-R' requires a specific (sub)page, not all\n");
+            else
+                pr2serr("'-r' requires a specific (sub)page, not all\n");
+            usage_for(op);
+            ret = SG_LIB_SYNTAX_ERROR;
+            goto finish;
+        }
+    }
+
+    memset(rsp_buff, 0, rsp_buff_size);
+    if (op->do_six) {
+        res = sg_ll_mode_sense6(sg_fd, op->do_dbd, op->page_control,
+                                op->pg_code, op->subpg_code, rsp_buff,
+                                rsp_buff_size, 1, op->do_verbose);
+        if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr(">>>>>> try again without the '-6' switch for a 10 byte "
+                    "MODE SENSE command\n");
+    } else {
+        res = sg_ll_mode_sense10(sg_fd, op->do_llbaa, op->do_dbd,
+                                 op->page_control, op->pg_code,
+                                 op->subpg_code, rsp_buff, rsp_buff_size,
+                                 1, op->do_verbose);
+        if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr(">>>>>> try again with a '-6' switch for a 6 byte MODE "
+                    "SENSE command\n");
+    }
+    if (SG_LIB_CAT_ILLEGAL_REQ == res) {
+        if (op->subpg_code > 0)
+            pr2serr("invalid field in cdb (perhaps subpages not "
+                    "supported)\n");
+        else if (op->page_control > 0)
+            pr2serr("invalid field in cdb (perhaps page control (PC) not "
+                    "supported)\n");
+        else
+            pr2serr("invalid field in cdb (perhaps page 0x%x not "
+                    "supported)\n", op->pg_code);
+    } else if (res) {
+        sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose);
+        pr2serr("%s\n", b);
+    }
+    ret = res;
+    if (0 == res) {
+        int medium_type, specific, headerlen;
+
+        ret = 0;
+        resp_mode6 = op->do_six;
+        if (op->do_flexible) {
+            num = rsp_buff[0];
+            if (op->do_six && (num < 3))
+                resp_mode6 = 0;
+            if ((0 == op->do_six) && (num > 5)) {
+                if ((num > 11) && (0 == (num % 2)) && (0 == rsp_buff[4]) &&
+                    (0 == rsp_buff[5]) && (0 == rsp_buff[6])) {
+                    rsp_buff[1] = num;
+                    rsp_buff[0] = 0;
+                    pr2serr(">>> msense(10) but resp[0]=%d and not msense(6) "
+                            "response so fix length\n", num);
+                } else
+                    resp_mode6 = 1;
+            }
+        }
+        if (op->do_raw || (1 == op->do_hex) || (op->do_hex > 2))
+            ;
+        else {
+            if (resp_mode6 == op->do_six)
+                printf("Mode parameter header from MODE SENSE(%s):\n",
+                       (op->do_six ? "6" : "10"));
+            else
+                printf(" >>> Mode parameter header from MODE SENSE(%s),\n"
+                       "     decoded as %s byte response:\n",
+                       (op->do_six ? "6" : "10"), (resp_mode6 ? "6" : "10"));
+        }
+        if (resp_mode6) {
+            headerlen = 4;
+            md_len = rsp_buff[0] + 1;
+            bd_len = rsp_buff[3];
+            medium_type = rsp_buff[1];
+            specific = rsp_buff[2];
+            longlba = 0;
+        } else {
+            headerlen = 8;
+            md_len = sg_get_unaligned_be16(rsp_buff + 0) + 2;
+            bd_len = sg_get_unaligned_be16(rsp_buff + 6);
+            medium_type = rsp_buff[2];
+            specific = rsp_buff[3];
+            longlba = rsp_buff[4] & 1;
+        }
+        if ((bd_len + headerlen) > md_len) {
+            pr2serr("Invalid block descriptor length=%d, ignore\n", bd_len);
+            bd_len = 0;
+        }
+        if (op->do_raw || (op->do_hex > 2)) {
+            if (1 == op->do_raw)
+                dStrRaw((const char *)rsp_buff, md_len);
+            else if (op->do_raw > 1) {
+                ucp = rsp_buff + bd_len + headerlen;
+                md_len -= bd_len + headerlen;
+                spf = ((ucp[0] & 0x40) ? 1 : 0);
+                len = (spf ? (sg_get_unaligned_be16(ucp + 2) + 4) :
+                             (ucp[1] + 2));
+                len = (len < md_len) ? len : md_len;
+                for (k = 0; k < len; ++k)
+                    printf("%02x\n", ucp[k]);
+            } else
+                dStrHex((const char *)rsp_buff, md_len, -1);
+            goto finish;
+        }
+        if (1 == op->do_hex) {
+            dStrHex((const char *)rsp_buff, md_len, 1);
+            goto finish;
+        } else if (op->do_hex > 1)
+            dStrHex((const char *)rsp_buff, headerlen, 1);
+        if (0 == inq_pdt)
+            printf("  Mode data length=%d, medium type=0x%.2x, WP=%d,"
+                   " DpoFua=%d, longlba=%d\n", md_len, medium_type,
+                   !!(specific & 0x80), !!(specific & 0x10), longlba);
+        else
+            printf("  Mode data length=%d, medium type=0x%.2x, specific"
+                   " param=0x%.2x, longlba=%d\n", md_len, medium_type,
+                   specific, longlba);
+        if (md_len > rsp_buff_size) {
+            printf("Only fetched %d bytes of response, truncate output\n",
+                   rsp_buff_size);
+            md_len = rsp_buff_size;
+            if (bd_len + headerlen > rsp_buff_size)
+                bd_len = rsp_buff_size - headerlen;
+        }
+        if (! op->do_dbout) {
+            printf("  Block descriptor length=%d\n", bd_len);
+            if (bd_len > 0) {
+                len = 8;
+                density_code_off = 0;
+                num = bd_len;
+                if (longlba) {
+                    printf("> longlba direct access device block "
+                           "descriptors:\n");
+                    len = 16;
+                    density_code_off = 8;
+                }
+                else if (0 == inq_pdt) {
+                    printf("> Direct access device block descriptors:\n");
+                    density_code_off = 4;
+                }
+                else
+                    printf("> General mode parameter block descriptors:\n");
+
+                ucp = rsp_buff + headerlen;
+                while (num > 0) {
+                    printf("   Density code=0x%x\n",
+                           *(ucp + density_code_off));
+                    dStrHex((const char *)ucp, len, 1);
+                    ucp += len;
+                    num -= len;
+                }
+                printf("\n");
+            }
+        }
+        ucp = rsp_buff + bd_len + headerlen;    /* start of mode page(s) */
+        md_len -= bd_len + headerlen;           /* length of mode page(s) */
+        num_ua_pages = 0;
+        for (k = 0; md_len > 0; ++k) { /* got mode page(s) */
+            if ((k > 0) && (! op->do_all) &&
+                (SPG_CODE_ALL != op->subpg_code)) {
+                pr2serr("Unexpectedly received extra mode page responses, "
+                        "ignore\n");
+                break;
+            }
+            uc = *ucp;
+            spf = ((uc & 0x40) ? 1 : 0);
+            len = (spf ? (sg_get_unaligned_be16(ucp + 2) + 4) : (ucp[1] + 2));
+            page_num = ucp[0] & PG_CODE_MASK;
+            if (0x0 == page_num) {
+                ++num_ua_pages;
+                if((num_ua_pages > 3) && (md_len > 0xa00)) {
+                    pr2serr(">>> Seen 3 unit attention pages (only one "
+                            "should be at end)\n     and mpage length=%d, "
+                            "looks malformed, try '-f' option\n", md_len);
+                    break;
+                }
+            }
+            if (op->do_hex) {
+                if (spf)
+                    printf(">> page_code=0x%x, subpage_code=0x%x, page_cont"
+                           "rol=%d\n", page_num, ucp[1], op->page_control);
+                else
+                    printf(">> page_code=0x%x, page_control=%d\n", page_num,
+                           op->page_control);
+            } else {
+                descp = NULL;
+                if ((0x18 == page_num) || (0x19 == page_num)) {
+                    t_proto = (spf ? ucp[5] : ucp[2]) & 0xf;
+                    descp = find_page_code_desc(page_num, (spf ? ucp[1] : 0),
+                                                inq_pdt, inq_byte6, t_proto);
+                } else
+                    descp = find_page_code_desc(page_num, (spf ? ucp[1] : 0),
+                                                inq_pdt, inq_byte6, -1);
+                if (NULL == descp) {
+                    if (spf)
+                        snprintf(ebuff, EBUFF_SZ, "0x%x, subpage_code: 0x%x",
+                                 page_num, ucp[1]);
+                    else
+                        snprintf(ebuff, EBUFF_SZ, "0x%x", page_num);
+                }
+                if (descp)
+                    printf(">> %s, page_control: %s\n", descp,
+                           pg_control_str_arr[op->page_control]);
+                else
+                    printf(">> page_code: %s, page_control: %s\n", ebuff,
+                           pg_control_str_arr[op->page_control]);
+            }
+            num = (len > md_len) ? md_len : len;
+            if ((k > 0) && (num > 256)) {
+                num = 256;
+                pr2serr(">>> page length (%d) > 256 bytes, unlikely trim\n"
+                        "    Try '-f' option\n", len);
+            }
+            dStrHex((const char *)ucp, num , 1);
+            ucp += len;
+            md_len -= len;
+        }
+    }
+
+finish:
+    sg_cmds_close_device(sg_fd);
+    if (malloc_rsp_buff)
+        free(malloc_rsp_buff);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_opcodes.c b/sg3_utils/src/sg_opcodes.c
new file mode 100644
index 0000000..1b42f17
--- /dev/null
+++ b/sg3_utils/src/sg_opcodes.c
@@ -0,0 +1,1020 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *  Copyright (C) 2004-2015 D. Gilbert
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+
+    This program outputs information provided by a SCSI REPORT SUPPORTED
+    OPERATION CODES [0xa3/0xc] and REPORT SUPPORTED TASK MANAGEMENT
+    FUNCTIONS [0xa3/0xd] commands.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+#include "sg_pt.h"
+
+static const char * version_str = "0.45 20151219";    /* spc5r07 */
+
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_TIMEOUT_SECS 60
+
+#define SG_MAINTENANCE_IN 0xa3
+#define RSOC_SA     0xc
+#define RSTMF_SA    0xd
+#define RSOC_CMD_LEN 12
+#define RSTMF_CMD_LEN 12
+#define MX_ALLOC_LEN 8192
+
+#define NAME_BUFF_SZ 128
+
+
+static int peri_type = 0; /* ugly but not easy to pass to alpha compare */
+
+static int do_rsoc(int sg_fd, int rctd, int rep_opts, int rq_opcode,
+                   int rq_servact, void * resp, int mx_resp_len, int noisy,
+                   int verbose);
+static int do_rstmf(int sg_fd, int repd, void * resp, int mx_resp_len,
+                    int noisy, int verbose);
+
+
+static struct option long_options[] = {
+        {"alpha", 0, 0, 'a'},
+        {"compact", 0, 0, 'c'},
+        {"help", 0, 0, 'h'},
+        {"hex", 0, 0, 'H'},
+        {"mask", 0, 0, 'm'},
+        {"no-inquiry", 0, 0, 'n'},
+        {"new", 0, 0, 'N'},
+        {"opcode", 1, 0, 'o'},
+        {"old", 0, 0, 'O'},
+        {"raw", 0, 0, 'r'},
+        {"rctd", 0, 0, 'R'},
+        {"repd", 0, 0, 'q'},
+        {"sa", 1, 0, 's'},
+        {"tmf", 0, 0, 't'},
+        {"unsorted", 0, 0, 'u'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_alpha;
+    int do_compact;
+    int do_help;
+    int do_hex;
+    int no_inquiry;
+    int do_mask;
+    int do_opcode;
+    int do_raw;
+    int do_rctd;
+    int do_repd;
+    int do_servact;
+    int do_verbose;
+    int do_version;
+    int do_unsorted;
+    int do_taskman;
+    const char * device_name;
+    int opt_new;
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage:  sg_opcodes [--alpha] [--compact] [--help] [--hex] "
+            "[--mask]\n"
+            "                   [--no-inquiry] [--opcode=OP[,SA]] [--raw] "
+            "[--rctd]\n"
+            "                   [--repd] [--sa=SA] [--tmf] [--unsorted] "
+            "[--verbose]\n"
+            "                   [--version] DEVICE\n"
+            "  where:\n"
+            "    --alpha|-a      output list of operation codes sorted "
+            "alphabetically\n"
+            "    --compact|-c    more compact output\n"
+            "    --help|-h       print usage message then exit\n"
+            "    --hex|-H        output response in hex\n"
+            "    --mask|-m       and show cdb usage data (a mask) when "
+            "all listed\n"
+            "    --no-inquiry|-n    don't output INQUIRY information\n"
+            "    --opcode=OP|-o OP    first byte of command to query\n"
+            "                         (decimal, prefix with '0x' for hex)\n"
+            "    --opcode=OP,SA|-o OP,SA    opcode (OP) and service action "
+            "(SA)\n"
+            "                         (decimal, each prefix with '0x' for "
+            "hex)\n"
+            "    --raw|-r        output response in binary to stdout\n"
+            "    --rctd|-R       set RCTD (return command timeout "
+            "descriptor) bit\n"
+            "    --repd|-q       set Report Extended Parameter Data bit, "
+            "with --tmf\n"
+            "    --sa=SA|-s SA    service action in addition to opcode\n"
+            "                     (decimal, prefix with '0x' for hex)\n"
+            "    --tmf|-t        output list of supported task management "
+            "functions\n"
+            "    --unsorted|-u    output list of operation codes as is\n"
+            "                     (def: sort by opcode (then service "
+            "action))\n"
+            "    --verbose|-v    increase verbosity\n"
+            "    --version|-V    print version string then exit\n\n"
+            "Performs a SCSI REPORT SUPPORTED OPERATION CODES or a REPORT "
+            "SUPPORTED\nTASK MANAGEMENT FUNCTIONS command.\n");
+}
+
+static void
+usage_old()
+{
+    pr2serr("Usage:  sg_opcodes [-a] [-c] [-H] [-m] [-n] [-o=OP] [-q] [-r] "
+            "[-R] [-s=SA]\n"
+            "                   [-t] [-u] [-v] [-V] DEVICE\n"
+            "  where:\n"
+            "    -a    output list of operation codes sorted "
+            "alphabetically\n"
+            "    -c    more compact output\n"
+            "    -H    print response in hex\n"
+            "    -m    and show cdb usage data (a mask) when all listed\n"
+            "    -n    don't output INQUIRY information\n"
+            "    -o=OP    first byte of command to query (in hex)\n"
+            "    -q    set REPD bit for tmf_s\n"
+            "    -r    output response in binary to stdout\n"
+            "    -R    set RCTD (return command timeout "
+            "descriptor) bit\n"
+            "    -s=SA    in addition to opcode (in hex)\n"
+            "    -t    output list of supported task management functions\n"
+            "    -u    output list of operation codes as is (unsorted)\n"
+            "    -v    verbose\n"
+            "    -V    output version string\n"
+            "    -?    output this usage message\n\n"
+            "Performs a SCSI REPORT SUPPORTED OPERATION CODES (or a REPORT "
+            "TASK MANAGEMENT\nFUNCTIONS) command\n");
+}
+
+static int
+process_cl_new(struct opts_t * optsp, int argc, char * argv[])
+{
+    int c, n;
+    char * cp;
+    char b[32];
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "achHmnNo:OqrRs:tuvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            ++optsp->do_alpha;
+            break;
+        case 'c':
+            ++optsp->do_compact;
+            break;
+        case 'h':
+        case '?':
+            ++optsp->do_help;
+            break;
+        case 'H':
+            ++optsp->do_hex;
+            break;
+        case 'm':
+            ++optsp->do_mask;
+            break;
+        case 'n':
+            ++optsp->no_inquiry;
+            break;
+        case 'N':
+            break;      /* ignore */
+        case 'o':
+            if (strlen(optarg) >= (sizeof(b) - 1)) {
+                pr2serr("argument to '--opcode' too long\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            cp = strchr(optarg, ',');
+            if (cp) {
+                memset(b, 0, sizeof(b));
+                strncpy(b, optarg, cp - optarg);
+                n = sg_get_num(b);
+                if ((n < 0) || (n > 255)) {
+                    pr2serr("bad OP argument to '--opcode'\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                optsp->do_opcode = n;
+                n = sg_get_num(cp + 1);
+                if ((n < 0) || (n > 0xffff)) {
+                    pr2serr("bad SA argument to '--opcode'\n");
+                    usage();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                optsp->do_servact = n;
+            } else {
+                n = sg_get_num(optarg);
+                if ((n < 0) || (n > 255)) {
+                    pr2serr("bad argument to '--opcode'\n");
+                    usage();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                optsp->do_opcode = n;
+            }
+            break;
+        case 'O':
+            optsp->opt_new = 0;
+            return 0;
+        case 'q':
+            ++optsp->do_repd;
+            break;
+        case 'r':
+            ++optsp->do_raw;
+            break;
+        case 'R':
+            ++optsp->do_rctd;
+            break;
+        case 's':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 0xffff)) {
+                pr2serr("bad argument to '--sa'\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            optsp->do_servact = n;
+            break;
+        case 't':
+            ++optsp->do_taskman;
+            break;
+        case 'u':
+            ++optsp->do_unsorted;
+            break;
+        case 'v':
+            ++optsp->do_verbose;
+            break;
+        case 'V':
+            ++optsp->do_version;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (optsp->do_help)
+                break;
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == optsp->device_name) {
+            optsp->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int
+process_cl_old(struct opts_t * optsp, int argc, char * argv[])
+{
+    int k, jmp_out, plen, n, num;
+    const char * cp;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case 'a':
+                    ++optsp->do_alpha;
+                    break;
+                case 'c':
+                    ++optsp->do_compact;
+                    break;
+                case 'H':
+                    ++optsp->do_hex;
+                    break;
+                case 'm':
+                    ++optsp->do_mask;
+                    break;
+                case 'n':
+                    ++optsp->no_inquiry;
+                    break;
+                case 'N':
+                    optsp->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case 'q':
+                    ++optsp->do_repd;
+                    break;
+                case 'R':
+                    ++optsp->do_rctd;
+                    break;
+                case 't':
+                    ++optsp->do_taskman;
+                    break;
+                case 'u':
+                    ++optsp->do_unsorted;
+                    break;
+                case 'v':
+                    ++optsp->do_verbose;
+                    break;
+                case 'V':
+                    ++optsp->do_version;
+                    break;
+                case 'h':
+                case '?':
+                    ++optsp->do_help;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            if (0 == strncmp("o=", cp, 2)) {
+                num = sscanf(cp + 2, "%x", (unsigned int *)&n);
+                if ((1 != num) || (n > 255)) {
+                    pr2serr("Bad number after 'o=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                optsp->do_opcode = n;
+            } else if (0 == strncmp("s=", cp, 2)) {
+                num = sscanf(cp + 2, "%x", (unsigned int *)&n);
+                if (1 != num) {
+                    pr2serr("Bad number after 's=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                optsp->do_servact = n;
+            } else if (0 == strncmp("-old", cp, 4))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage_old();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (NULL == optsp->device_name)
+            optsp->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not expecting: %s\n",
+                    optsp->device_name, cp);
+            usage_old();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int
+process_cl(struct opts_t * optsp, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        optsp->opt_new = 0;
+        res = process_cl_old(optsp, argc, argv);
+        if ((0 == res) && optsp->opt_new)
+            res = process_cl_new(optsp, argc, argv);
+    } else {
+        optsp->opt_new = 1;
+        res = process_cl_new(optsp, argc, argv);
+        if ((0 == res) && (0 == optsp->opt_new))
+            res = process_cl_old(optsp, argc, argv);
+    }
+    return res;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* returns -1 when left < right, 0 when left == right, else returns 1 */
+static int
+opcode_num_compare(const void * left, const void * right)
+{
+    const unsigned char * ll = *(unsigned char **)left;
+    const unsigned char * rr = *(unsigned char **)right;
+    int l_serv_act = 0;
+    int r_serv_act = 0;
+    int l_opc, r_opc;
+
+    if (NULL == ll)
+        return -1;
+    if (NULL == rr)
+        return -1;
+    l_opc = ll[0];
+    if (ll[5] & 1)
+        l_serv_act = sg_get_unaligned_be16(ll + 2);
+    r_opc = rr[0];
+    if (rr[5] & 1)
+        r_serv_act = sg_get_unaligned_be16(rr + 2);
+    if (l_opc < r_opc)
+        return -1;
+    if (l_opc > r_opc)
+        return 1;
+    if (l_serv_act < r_serv_act)
+        return -1;
+    if (l_serv_act > r_serv_act)
+        return 1;
+    return 0;
+}
+
+/* returns -1 when left < right, 0 when left == right, else returns 1 */
+static int
+opcode_alpha_compare(const void * left, const void * right)
+{
+    const unsigned char * ll = *(unsigned char **)left;
+    const unsigned char * rr = *(unsigned char **)right;
+    int l_serv_act = 0;
+    int r_serv_act = 0;
+    char l_name_buff[NAME_BUFF_SZ];
+    char r_name_buff[NAME_BUFF_SZ];
+    int l_opc, r_opc;
+
+    if (NULL == ll)
+        return -1;
+    if (NULL == rr)
+        return -1;
+    l_opc = ll[0];
+    if (ll[5] & 1)
+        l_serv_act = sg_get_unaligned_be16(ll + 2);
+    l_name_buff[0] = '\0';
+    sg_get_opcode_sa_name(l_opc, l_serv_act, peri_type,
+                          NAME_BUFF_SZ, l_name_buff);
+    r_opc = rr[0];
+    if (rr[5] & 1)
+        r_serv_act = sg_get_unaligned_be16(rr + 2);
+    r_name_buff[0] = '\0';
+    sg_get_opcode_sa_name(r_opc, r_serv_act, peri_type,
+                          NAME_BUFF_SZ, r_name_buff);
+    return strncmp(l_name_buff, r_name_buff, NAME_BUFF_SZ);
+}
+
+static void
+list_all_codes(unsigned char * rsoc_buff, int rsoc_len, struct opts_t * op,
+               int sg_fd)
+{
+    int k, j, m, cd_len, serv_act, len, sa_v, opcode, res;
+    unsigned int to;
+    unsigned char * ucp;
+    char name_buff[NAME_BUFF_SZ];
+    char sa_buff[8];
+    unsigned char ** sort_arr = NULL;
+
+    cd_len = sg_get_unaligned_be32(rsoc_buff + 0);
+    if (cd_len > (rsoc_len - 4)) {
+        printf("sg_opcodes: command data length=%d, allocation=%d; "
+               "truncate\n", cd_len, rsoc_len - 4);
+        cd_len = ((rsoc_len - 4) / 8) * 8;
+    }
+    if (0 == cd_len) {
+        printf("sg_opcodes: no commands to display\n");
+        return;
+    }
+    if (op->do_rctd) {
+        if (op->do_compact) {
+            printf("\nOpcode,sa  Nominal  Recommended  Name\n");
+            printf(  "  (hex)    timeout  timeout(sec)     \n");
+            printf("-----------------------------------------------"
+                   "---------\n");
+        } else {
+            printf("\nOpcode  Service    CDB   Nominal  Recommended  Name\n");
+            printf(  "(hex)   action(h)  size  timeout  timeout(sec)     \n");
+            printf("-------------------------------------------------------"
+                   "---------\n");
+        }
+    } else {
+        if (op->do_compact) {
+            printf("\nOpcode,sa  Name\n");
+            printf(  "  (hex)        \n");
+            printf("---------------------------------------\n");
+        } else {
+            printf("\nOpcode  Service    CDB    Name\n");
+            printf(  "(hex)   action(h)  size       \n");
+            printf("-----------------------------------------------\n");
+        }
+    }
+    /* SPC-4 does _not_ require any ordering of opcodes in the response */
+    if (! op->do_unsorted) {
+        sort_arr = (unsigned char **)malloc(cd_len * sizeof(unsigned char *));
+        if (NULL == sort_arr) {
+            printf("sg_opcodes: no memory to sort operation codes, "
+                   "try '-u'\n");
+            return;
+        }
+        memset(sort_arr, 0, cd_len * sizeof(unsigned char *));
+        ucp = rsoc_buff + 4;
+        for (k = 0, j = 0; k < cd_len; ++j, k += len, ucp += len) {
+            sort_arr[j] = ucp;
+            len = (ucp[5] & 0x2) ? 20 : 8;
+        }
+        qsort(sort_arr, j, sizeof(unsigned char *),
+              (op->do_alpha ? opcode_alpha_compare : opcode_num_compare));
+    }
+    for (k = 0, j = 0; k < cd_len; ++j, k += len) {
+        ucp = op->do_unsorted ? (rsoc_buff + 4 + k) : sort_arr[j];
+        len = (ucp[5] & 0x2) ? 20 : 8;
+        opcode = ucp[0];
+        sa_v = ucp[5] & 1;
+        serv_act = 0;
+        if (sa_v) {
+            serv_act = sg_get_unaligned_be16(ucp + 2);
+            sg_get_opcode_sa_name(opcode, serv_act, peri_type, NAME_BUFF_SZ,
+                                  name_buff);
+            if (op->do_compact)
+                snprintf(sa_buff, sizeof(sa_buff), "%-4x", serv_act);
+            else
+                snprintf(sa_buff, sizeof(sa_buff), "%4x", serv_act);
+        } else {
+            sg_get_opcode_name(opcode, peri_type, NAME_BUFF_SZ, name_buff);
+            memset(sa_buff, ' ', sizeof(sa_buff));
+        }
+        if (op->do_rctd) {
+            if (ucp[5] & 0x2) {
+                if (op->do_compact)
+                    printf(" %.2x%c%.4s", opcode, (sa_v ? ',' : ' '),
+                           sa_buff);
+                else
+                    printf(" %.2x     %.4s       %3d", opcode, sa_buff,
+                           sg_get_unaligned_be16(ucp + 6));
+                to = sg_get_unaligned_be32(ucp + 12);
+                if (0 == to)
+                    printf("         -");
+                else
+                    printf("  %8u", to);
+                to = sg_get_unaligned_be32(ucp + 16);
+                if (0 == to)
+                    printf("          -");
+                else
+                    printf("   %8u", to);
+                printf("    %s\n", name_buff);
+            } else
+                if (op->do_compact)
+                    printf(" %.2x%c%.4s                        %s\n", opcode,
+                           (sa_v ? ',' : ' '), sa_buff, name_buff);
+                else
+                    printf(" %.2x     %.4s       %3d                         "
+                           "%s\n", opcode, sa_buff,
+                           sg_get_unaligned_be16(ucp + 6), name_buff);
+        } else
+            if (op->do_compact)
+                printf(" %.2x%c%.4s   %s\n", ucp[0], (sa_v ? ',' : ' '),
+                       sa_buff, name_buff);
+            else
+                printf(" %.2x     %.4s       %3d    %s\n", ucp[0], sa_buff,
+                       sg_get_unaligned_be16(ucp + 6), name_buff);
+        if (op->do_mask) {
+            int cdb_sz;
+            unsigned char b[64];
+
+            memset(b, 0, sizeof(b));
+            res = do_rsoc(sg_fd, 0, (sa_v ? 2 : 1), opcode, serv_act,
+                          b, sizeof(b), 1, op->do_verbose);
+            if (0 == res) {
+                cdb_sz = sg_get_unaligned_be16(b + 2);
+                if ((cdb_sz > 0) && (cdb_sz <= 80)) {
+                    if (op->do_compact)
+                        printf("             usage: ");
+                    else
+                        printf("        cdb usage: ");
+                    for (m = 0; m < cdb_sz; ++m)
+                        printf("%.2x ", b[4 + m]);
+                    printf("\n");
+                }
+            }
+        }
+    }
+    if (sort_arr)
+        free(sort_arr);
+}
+
+static void
+decode_cmd_to_descriptor(unsigned char * dp, int max_b_len, char * b)
+{
+    int len;
+    unsigned int to;
+
+    if ((max_b_len < 2) || (NULL == dp))
+        return;
+    b[max_b_len - 1] = '\0';
+    --max_b_len;
+    len = sg_get_unaligned_be16(dp + 0);
+    if (10 != len) {
+        snprintf(b, max_b_len, "command timeout descriptor length %d "
+                 "(expect 10)", len);
+        return;
+    }
+    to = sg_get_unaligned_be32(dp + 4);
+    if (0 == to)
+        snprintf(b, max_b_len, "no nominal timeout, ");
+    else
+        snprintf(b, max_b_len, "nominal timeout: %u secs, ", to);
+    len = strlen(b);
+    max_b_len -= len;
+    b += len;
+    to = sg_get_unaligned_be32(dp + 8);
+    if (0 == to)
+        snprintf(b, max_b_len, "no recommended timeout");
+    else
+        snprintf(b, max_b_len, "recommended timeout: %u secs", to);
+    return;
+}
+
+static void
+list_one(unsigned char * rsoc_buff, int cd_len, int rep_opts,
+         struct opts_t * op)
+{
+    int k;
+    char name_buff[NAME_BUFF_SZ];
+    unsigned char * ucp;
+    const char * cp;
+    int v = 0;
+
+
+    printf("\n  Opcode=0x%.2x", op->do_opcode);
+    if (rep_opts > 1)
+        printf("  Service_action=0x%.4x", op->do_servact);
+    printf("\n");
+    sg_get_opcode_sa_name(((op->do_opcode > 0) ? op->do_opcode : 0),
+                          ((op->do_servact > 0) ? op->do_servact : 0),
+                          peri_type, NAME_BUFF_SZ, name_buff);
+    printf("  Command_name: %s\n", name_buff);
+    switch((int)(rsoc_buff[1] & 7)) {
+    case 0:
+        cp = "not currently available";
+        break;
+    case 1:
+        cp = "NOT supported";
+        break;
+    case 3:
+        cp = "supported [conforming to SCSI standard]";
+        v = 1;
+        break;
+    case 5:
+        cp = "supported [in a vendor specific manner]";
+        v = 1;
+        break;
+    default:
+        snprintf(name_buff, NAME_BUFF_SZ, "support reserved [0x%x]",
+                 rsoc_buff[1] & 7);
+        cp = name_buff;
+        break;
+    }
+    printf("  Command %s\n", cp);
+    if (v) {
+        printf("  Usage data: ");
+        ucp = rsoc_buff + 4;
+        for (k = 0; k < cd_len; ++k)
+            printf("%.2x ", ucp[k]);
+        printf("\n");
+    }
+    if (0x80 & rsoc_buff[1]) {      /* CTDP */
+        ucp = rsoc_buff + 4 + cd_len;
+        decode_cmd_to_descriptor(ucp, NAME_BUFF_SZ, name_buff);
+        printf("  %s\n", name_buff);
+    }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, cd_len, res, len;
+    unsigned char rsoc_buff[MX_ALLOC_LEN];
+    int rep_opts = 0;
+    const char * cp;
+    char buff[48];
+    char b[80];
+    struct sg_simple_inquiry_resp inq_resp;
+    const char * op_name;
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->do_opcode = -1;
+    op->do_servact = -1;
+    res = process_cl(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        if (op->opt_new)
+            usage();
+        else
+            usage_old();
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+
+    if (NULL == op->device_name) {
+        pr2serr("No DEVICE argument given\n");
+        if (op->opt_new)
+            usage();
+        else
+            usage_old();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((-1 != op->do_servact) && (-1 == op->do_opcode)) {
+        pr2serr("When '-s' is chosen, so must '-o' be chosen\n");
+        if (op->opt_new)
+            usage();
+        else
+            usage_old();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_unsorted && op->do_alpha)
+        pr2serr("warning: unsorted ('-u') and alpha ('-a') options chosen, "
+                "ignoring alpha\n");
+    if (op->do_taskman && ((-1 != op->do_opcode) || op->do_alpha ||
+        op->do_unsorted)) {
+        pr2serr("warning: task management functions ('-t') chosen so alpha "
+                "('-a'),\n          unsorted ('-u') and opcode ('-o') "
+                "options ignored\n");
+    }
+    op_name = op->do_taskman ? "Report supported task management functions" :
+              "Report supported operation codes";
+
+    if (op->do_opcode < 0) {
+        if ((sg_fd = scsi_pt_open_device(op->device_name, 1 /* RO */,
+                                         op->do_verbose)) < 0) {
+            pr2serr("sg_opcodes: error opening file (ro): %s: %s\n",
+                    op->device_name, safe_strerror(-sg_fd));
+            return SG_LIB_FILE_ERROR;
+        }
+        if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, op->do_verbose)) {
+            peri_type = inq_resp.peripheral_type;
+            if (! (op->do_raw || op->no_inquiry)) {
+                printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor,
+                       inq_resp.product, inq_resp.revision);
+                cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
+                if (strlen(cp) > 0)
+                    printf("  Peripheral device type: %s\n", cp);
+                else
+                    printf("  Peripheral device type: 0x%x\n", peri_type);
+            }
+        } else {
+            pr2serr("sg_opcodes: %s doesn't respond to a SCSI INQUIRY\n",
+                    op->device_name);
+            return SG_LIB_CAT_OTHER;
+        }
+        res = scsi_pt_close_device(sg_fd);
+        if (res < 0) {
+            pr2serr("close error: %s\n", safe_strerror(-res));
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    if ((sg_fd = scsi_pt_open_device(op->device_name, 0 /* RW */,
+                                     op->do_verbose)) < 0) {
+        pr2serr("sg_opcodes: error opening file (rw): %s: %s\n",
+                op->device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (op->do_opcode >= 0)
+        rep_opts = ((op->do_servact >= 0) ? 2 : 1);
+    memset(rsoc_buff, 0, sizeof(rsoc_buff));
+    if (op->do_taskman)
+        res = do_rstmf(sg_fd, op->do_repd, rsoc_buff,
+                       (op->do_repd ? 16 : 4), 1, op->do_verbose);
+    else
+        res = do_rsoc(sg_fd, op->do_rctd, rep_opts, op->do_opcode,
+                      op->do_servact, rsoc_buff, sizeof(rsoc_buff), 1,
+                      op->do_verbose);
+    if (res) {
+        sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose);
+        pr2serr("%s: %s\n", op_name, b);
+        goto err_out;
+    }
+    if (op->do_taskman) {
+        if (op->do_raw) {
+            dStrRaw((const char *)rsoc_buff, (op->do_repd ? 16 : 4));
+            goto err_out;
+        }
+        printf("\nTask Management Functions supported by device:\n");
+        if (op->do_hex) {
+            dStrHex((const char *)rsoc_buff, (op->do_repd ? 16 : 4), 1);
+            goto err_out;
+        }
+        if (rsoc_buff[0] & 0x80)
+            printf("    Abort task\n");
+        if (rsoc_buff[0] & 0x40)
+            printf("    Abort task set\n");
+        if (rsoc_buff[0] & 0x20)
+            printf("    Clear ACA\n");
+        if (rsoc_buff[0] & 0x10)
+            printf("    Clear task set\n");
+        if (rsoc_buff[0] & 0x8)
+            printf("    Logical unit reset\n");
+        if (rsoc_buff[0] & 0x4)
+            printf("    Query task\n");
+        if (rsoc_buff[0] & 0x2)
+            printf("    Target reset\n");
+        if (rsoc_buff[0] & 0x1)
+            printf("    Wakeup\n");
+        if (rsoc_buff[1] & 0x4)
+            printf("    Query asynchronous event\n");
+        if (rsoc_buff[1] & 0x2)
+            printf("    Query task set\n");
+        if (rsoc_buff[1] & 0x1)
+            printf("    I_T nexus reset\n");
+        if (op->do_repd) {
+            if (rsoc_buff[3] < 0xc) {
+                pr2serr("when REPD given, byte 3 of response should be >= "
+                        "12\n");
+                res = SG_LIB_CAT_OTHER;
+                goto err_out;
+            } else
+                printf("  Extended parameter data:\n");
+            printf("    TMFTMOV=%d\n", !!(rsoc_buff[4] & 0x1));
+            printf("    ATTS=%d\n", !!(rsoc_buff[6] & 0x80));
+            printf("    ATSTS=%d\n", !!(rsoc_buff[6] & 0x40));
+            printf("    CACATS=%d\n", !!(rsoc_buff[6] & 0x20));
+            printf("    CTSTS=%d\n", !!(rsoc_buff[6] & 0x10));
+            printf("    LURTS=%d\n", !!(rsoc_buff[6] & 0x8));
+            printf("    QTTS=%d\n", !!(rsoc_buff[6] & 0x4));
+            printf("    QAETS=%d\n", !!(rsoc_buff[7] & 0x4));
+            printf("    QTSTS=%d\n", !!(rsoc_buff[7] & 0x2));
+            printf("    ITNRTS=%d\n", !!(rsoc_buff[7] & 0x1));
+            printf("    tmf long timeout: %d (100 ms units)\n",
+                   sg_get_unaligned_be32(rsoc_buff + 8));
+            printf("    tmf short timeout: %d (100 ms units)\n",
+                   sg_get_unaligned_be32(rsoc_buff + 12));
+        }
+    } else if (0 == rep_opts) {  /* list all supported operation codes */
+        len = sg_get_unaligned_be32(rsoc_buff + 0) + 4;
+        if (len > (int)sizeof(rsoc_buff))
+            len = sizeof(rsoc_buff);
+        if (op->do_raw) {
+            dStrRaw((const char *)rsoc_buff, len);
+            goto err_out;
+        }
+        if (op->do_hex) {
+            dStrHex((const char *)rsoc_buff, len, 1);
+            goto err_out;
+        }
+        list_all_codes(rsoc_buff, sizeof(rsoc_buff), op, sg_fd);
+    } else {    /* asked about specific command */
+        cd_len = sg_get_unaligned_be16(rsoc_buff + 2);
+        len = cd_len + 4;
+        if (len > (int)sizeof(rsoc_buff))
+            len = sizeof(rsoc_buff);
+        if (op->do_raw) {
+            dStrRaw((const char *)rsoc_buff, len);
+            goto err_out;
+        }
+        if (op->do_hex) {
+            dStrHex((const char *)rsoc_buff, len, 1);
+            goto err_out;
+        }
+        list_one(rsoc_buff, cd_len, rep_opts, op);
+    }
+    res = 0;
+
+err_out:
+    scsi_pt_close_device(sg_fd);
+    return res;
+}
+
+static int
+do_rsoc(int sg_fd, int rctd, int rep_opts, int rq_opcode, int rq_servact,
+        void * resp, int mx_resp_len, int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rsocCmdBlk[RSOC_CMD_LEN] = {SG_MAINTENANCE_IN, RSOC_SA, 0,
+                                              0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (rctd)
+        rsocCmdBlk[2] |= 0x80;
+    if (rep_opts)
+        rsocCmdBlk[2] |= (rep_opts & 0x7);
+    if (rq_opcode > 0)
+        rsocCmdBlk[3] = (rq_opcode & 0xff);
+    if (rq_servact > 0)
+        sg_put_unaligned_be16((uint16_t)rq_servact, rsocCmdBlk + 4);
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rsocCmdBlk + 6);
+
+    if (verbose) {
+        pr2serr("    Report Supported Operation Codes cmd: ");
+        for (k = 0; k < RSOC_CMD_LEN; ++k)
+            pr2serr("%02x ", rsocCmdBlk[k]);
+        pr2serr("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("Report Supported Operation Codes: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rsocCmdBlk, sizeof(rsocCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_TIMEOUT_SECS, verbose);
+    ret = sg_cmds_process_resp(ptvp, "Report Supported Operation Codes", res,
+                               mx_resp_len, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+static int
+do_rstmf(int sg_fd, int repd, void * resp, int mx_resp_len, int noisy,
+         int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rstmfCmdBlk[RSTMF_CMD_LEN] = {SG_MAINTENANCE_IN, RSTMF_SA,
+                                       0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (repd)
+        rstmfCmdBlk[2] = 0x80;
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rstmfCmdBlk + 6);
+
+    if (verbose) {
+        pr2serr("    Report Supported Task Management Functions cmd: ");
+        for (k = 0; k < RSTMF_CMD_LEN; ++k)
+            pr2serr("%02x ", rstmfCmdBlk[k]);
+        pr2serr("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("Report Supported Task Management Functions: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rstmfCmdBlk, sizeof(rstmfCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_TIMEOUT_SECS, verbose);
+    ret = sg_cmds_process_resp(ptvp, "Report Supported Task management "
+                               "functions", res, mx_resp_len, sense_b, noisy,
+                                verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
diff --git a/sg3_utils/src/sg_persist.c b/sg3_utils/src/sg_persist.c
new file mode 100644
index 0000000..9d19e79
--- /dev/null
+++ b/sg3_utils/src/sg_persist.c
@@ -0,0 +1,1296 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *  Copyright (C) 2004-2016 D. Gilbert
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program issues the SCSI PERSISTENT IN and OUT commands.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "0.52 20160201";
+
+
+#define PRIN_RKEY_SA     0x0
+#define PRIN_RRES_SA     0x1
+#define PRIN_RCAP_SA     0x2
+#define PRIN_RFSTAT_SA   0x3
+#define PROUT_REG_SA     0x0
+#define PROUT_RES_SA     0x1
+#define PROUT_REL_SA     0x2
+#define PROUT_CLEAR_SA   0x3
+#define PROUT_PREE_SA    0x4
+#define PROUT_PREE_AB_SA 0x5
+#define PROUT_REG_IGN_SA 0x6
+#define PROUT_REG_MOVE_SA 0x7
+#define PROUT_REPL_LOST_SA 0x8
+#define MX_ALLOC_LEN 8192
+#define MX_TIDS 32
+#define MX_TID_LEN 256
+
+#define SG_PERSIST_IN_RDONLY "SG_PERSIST_IN_RDONLY"
+
+struct opts_t {
+    unsigned int prout_type;
+    uint64_t param_rk;
+    uint64_t param_sark;
+    unsigned int param_rtp;
+    int prin;
+    int prin_sa;
+    int prout_sa;
+    int param_alltgpt;
+    int param_aptpl;
+    int param_unreg;
+    int inquiry;
+    int hex;
+    int readonly;
+    unsigned char transportid_arr[MX_TIDS * MX_TID_LEN];
+    int num_transportids;
+    unsigned int alloc_len;
+    int verbose;
+};
+
+
+static struct option long_options[] = {
+    {"alloc-length", required_argument, 0, 'l'},
+    {"clear", no_argument, 0, 'C'},
+    {"device", required_argument, 0, 'd'},
+    {"help", no_argument, 0, 'h'},
+    {"hex", no_argument, 0, 'H'},
+    {"in", no_argument, 0, 'i'},
+    {"no-inquiry", no_argument, 0, 'n'},
+    {"out", no_argument, 0, 'o'},
+    {"param-alltgpt", no_argument, 0, 'Y'},
+    {"param-aptpl", no_argument, 0, 'Z'},
+    {"param-rk", required_argument, 0, 'K'},
+    {"param-sark", required_argument, 0, 'S'},
+    {"param-unreg", no_argument, 0, 'U'},
+    {"preempt", no_argument, 0, 'P'},
+    {"preempt-abort", no_argument, 0, 'A'},
+    {"prout-type", required_argument, 0, 'T'},
+    {"read-full-status", no_argument, 0, 's'},
+    {"read-keys", no_argument, 0, 'k'},
+    {"readonly", no_argument, 0, 'y'},
+    {"read-reservation", no_argument, 0, 'r'},
+    {"read-status", no_argument, 0, 's'},
+    {"register", no_argument, 0, 'G'},
+    {"register-ignore", no_argument, 0, 'I'},
+    {"register-move", no_argument, 0, 'M'},
+    {"release", no_argument, 0, 'L'},
+    {"relative-target-port", required_argument, 0, 'Q'},
+    {"replace-lost", no_argument, 0, 'z'},
+    {"report-capabilities", no_argument, 0, 'c'},
+    {"reserve", no_argument, 0, 'R'},
+    {"transport-id", required_argument, 0, 'X'},
+    {"unreg", no_argument, 0, 'U'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {0, 0, 0, 0}
+};
+
+static const char * prin_sa_strs[] = {
+    "Read keys",
+    "Read reservation",
+    "Report capabilities",
+    "Read full status",
+    "[reserved 0x4]",
+    "[reserved 0x5]",
+    "[reserved 0x6]",
+    "[reserved 0x7]",
+};
+static const int num_prin_sa_strs = sizeof(prin_sa_strs) /
+                                    sizeof(prin_sa_strs[0]);
+
+static const char * prout_sa_strs[] = {
+    "Register",
+    "Reserve",
+    "Release",
+    "Clear",
+    "Preempt",
+    "Preempt and abort",
+    "Register and ignore existing key",
+    "Register and move",
+    "Replace lost reservation",
+    "[reserved 0x9]",
+};
+static const int num_prout_sa_strs = sizeof(prout_sa_strs) /
+                                     sizeof(prout_sa_strs[0]);
+
+static const char * pr_type_strs[] = {
+    "obsolete [0]",
+    "Write Exclusive",
+    "obsolete [2]",
+    "Exclusive Access",
+    "obsolete [4]",
+    "Write Exclusive, registrants only",
+    "Exclusive Access, registrants only",
+    "Write Exclusive, all registrants",
+    "Exclusive Access, all registrants",
+    "obsolete [9]", "obsolete [0xa]", "obsolete [0xb]", "obsolete [0xc]",
+    "obsolete [0xd]", "obsolete [0xe]", "obsolete [0xf]",
+};
+
+
+static void
+usage(int help)
+{
+    if (help < 2) {
+        pr2serr("Usage: sg_persist [OPTIONS] [DEVICE]\n"
+                "  where the main OPTIONS are:\n"
+                "    --clear|-C                 PR Out: Clear\n"
+                "    --help|-h                  print usage message, "
+                "twice for more\n"
+                "    --in|-i                    request PR In command "
+                "(default)\n"
+                "    --out|-o                   request PR Out command\n"
+                "    --param-rk=RK|-K RK        PR Out parameter reservation "
+                "key\n"
+                "                               (RK is in hex)\n"
+                "    --param-sark=SARK|-S SARK    PR Out parameter service "
+                "action\n"
+                "                                 reservation key (SARK is "
+                "in hex)\n"
+                "    --preempt|-P               PR Out: Preempt\n"
+                "    --preempt-abort|-A         PR Out: Preempt and Abort\n"
+                "    --prout-type=TYPE|-T TYPE    PR Out type field (see "
+                "'-hh')\n"
+                "    --read-full-status|-s      PR In: Read Full Status\n"
+                "    --read-keys|-k             PR In: Read Keys "
+                "(default)\n");
+        pr2serr("    --read-reservation|-r      PR In: Read Reservation\n"
+                "    --read-status|-s           PR In: Read Full Status\n"
+                "    --register|-G              PR Out: Register\n"
+                "    --register-ignore|-I       PR Out: Register and Ignore\n"
+                "    --register-move|-M         PR Out: Register and Move\n"
+                "                               for '--register-move'\n"
+                "    --release|-L               PR Out: Release\n"
+                "    --replace-lost|-x          PR Out: Replace Lost "
+                "Reservation\n"
+                "    --report-capabilities|-c   PR In: Report Capabilities\n"
+                "    --reserve|-R               PR Out: Reserve\n"
+                "    --unreg|-U                 optional with PR Out "
+                "Register and Move\n\n"
+                "Performs a SCSI PERSISTENT RESERVE (IN or OUT) command. "
+                "Invoking\n'sg_persist DEVICE' will do a PR In Read Keys "
+                "command. Use '-hh'\nfor more options and TYPE meanings.\n");
+    } else {
+        pr2serr("Usage: sg_persist [OPTIONS] [DEVICE]\n"
+                "  where the other OPTIONS are:\n"
+                "    --alloc-length=LEN|-l LEN    allocation length hex "
+                "value (used with\n"
+                "                                 PR In only) (default: 8192 "
+                "(2000 in hex))\n"
+                "    --device=DEVICE|-d DEVICE    supply DEVICE as an option "
+                "rather than\n"
+                "                                 an argument\n"
+                "    --hex|-H                   output response in hex (for "
+                "PR In commands)\n"
+                "    --no-inquiry|-n            skip INQUIRY (default: do "
+                "INQUIRY)\n"
+                "    --param-alltgpt|-Y         PR Out parameter "
+                "'ALL_TG_PT'\n"
+                "    --param-aptpl|-Z           PR Out parameter 'APTPL'\n"
+                "    --readonly|-y              open DEVICE read-only (def: "
+                "read-write)\n"
+                "    --relative-target-port=RTPI|-Q RTPI    relative target "
+                "port "
+                "identifier\n"
+                "    --transport-id=TIDS|-X TIDS    one or more "
+                "TransportIDs can\n"
+                "                                   be given in several "
+                "forms\n"
+                "    --verbose|-v               output additional debug "
+                "information\n"
+                "    --version|-V               output version string\n\n"
+                "For the main options use '--help' or '-h' once.\n\n\n");
+        pr2serr("PR Out TYPE field value meanings:\n"
+                "  0:    obsolete (was 'read shared' in SPC)\n"
+                "  1:    write exclusive\n"
+                "  2:    obsolete (was 'read exclusive')\n"
+                "  3:    exclusive access\n"
+                "  4:    obsolete (was 'shared access')\n"
+                "  5:    write exclusive, registrants only\n"
+                "  6:    exclusive access, registrants only\n"
+                "  7:    write exclusive, all registrants\n"
+                "  8:    exclusive access, all registrants\n");
+    }
+}
+
+/* If num_tids==0 then only one TransportID is assumed with len bytes in
+ * it. If num_tids>0 then that many TransportIDs is assumed, each in an
+ * element that is MX_TID_LEN bytes long (and the 'len' argument is
+ * ignored). */
+static void
+decode_transport_id(const char * leadin, unsigned char * ucp, int len,
+                    int num_tids)
+{
+    int format_code, proto_id, num, k;
+    int bump;
+
+    if (num_tids > 0)
+        len = num_tids * MX_TID_LEN;
+    for (k = 0, bump = MX_TID_LEN; k < len; k += bump, ucp += bump) {
+        if ((len < 24) || (0 != (len % 4)))
+            printf("%sTransport Id short or not multiple of 4 "
+                   "[length=%d]:\n", leadin, len);
+        else
+            printf("%sTransport Id of initiator:\n", leadin);
+        format_code = ((ucp[0] >> 6) & 0x3);
+        proto_id = (ucp[0] & 0xf);
+        switch (proto_id) {
+        case TPROTO_FCP: /* Fibre channel */
+            printf("%s  FCP-2 World Wide Name:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 8, -1);
+            break;
+        case TPROTO_SPI: /* Parallel SCSI */
+            printf("%s  Parallel SCSI initiator SCSI address: 0x%x\n",
+                   leadin, sg_get_unaligned_be16(ucp + 2));
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            printf("%s  relative port number (of corresponding target): "
+                   "0x%x\n", leadin,  sg_get_unaligned_be16(ucp + 6));
+            break;
+        case TPROTO_SSA:
+            printf("%s  SSA (transport id not defined):\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            break;
+        case TPROTO_1394: /* IEEE 1394 */
+            printf("%s  IEEE 1394 EUI-64 name:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 8, -1);
+            break;
+        case TPROTO_SRP:
+            printf("%s  RDMA initiator port identifier:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 16, -1);
+            break;
+        case TPROTO_ISCSI:
+            printf("%s  iSCSI ", leadin);
+            num =  sg_get_unaligned_be16(ucp + 2);
+            if (0 == format_code)
+                printf("name: %.*s\n", num, &ucp[4]);
+            else if (1 == format_code)
+                printf("name and session id: %.*s\n", num, &ucp[4]);
+            else {
+                printf("  [Unexpected format code: %d]\n", format_code);
+                dStrHex((const char *)ucp, num + 4, -1);
+            }
+            break;
+        case TPROTO_SAS:
+            printf("%s  SAS address: 0x%016" PRIx64 "\n", leadin,
+                   sg_get_unaligned_be64(ucp + 4));
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            break;
+        case TPROTO_ADT:
+            printf("%s  ADT:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            break;
+        case TPROTO_ATA:
+            printf("%s  ATAPI:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            break;
+        case TPROTO_UAS:
+            printf("%s  UAS:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            break;
+        case TPROTO_SOP:
+            printf("%s  SOP ", leadin);
+            num =  sg_get_unaligned_be16(ucp + 2);
+            if (0 == format_code)
+                printf("Routing ID: 0x%x\n", num);
+            else {
+                printf("  [Unexpected format code: %d]\n", format_code);
+                dStrHex((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            }
+            break;
+        case TPROTO_NONE:
+            pr2serr("%s  No specified protocol\n", leadin);
+            /* dStrHexErr((const char *)ucp, ((len > 24) ? 24 : len), -1); */
+            break;
+        default:
+            pr2serr("%s  unknown protocol id=0x%x  format_code=%d\n",
+                    leadin, proto_id, format_code);
+            dStrHexErr((const char *)ucp, ((len > 24) ? 24 : len), -1);
+            break;
+        }
+    }
+}
+
+static int
+prin_work(int sg_fd, const struct opts_t * op)
+{
+    int k, j, num, res, add_len, add_desc_len;
+    unsigned int pr_gen;
+    unsigned char * ucp;
+    unsigned char pr_buff[MX_ALLOC_LEN];
+
+    memset(pr_buff, 0, sizeof(pr_buff));
+    res = sg_ll_persistent_reserve_in(sg_fd, op->prin_sa, pr_buff,
+                                      op->alloc_len, 1, op->verbose);
+    if (res) {
+        char b[64];
+        char bb[80];
+
+        if (op->prin_sa < num_prin_sa_strs)
+            snprintf(b, sizeof(b), "%s", prin_sa_strs[op->prin_sa]);
+        else
+            snprintf(b, sizeof(b), "service action=0x%x", op->prin_sa);
+
+        if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr("PR in (%s): command not supported\n", b);
+        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+            pr2serr("PR in (%s): bad field in cdb or parameter list (perhaps "
+                    "unsupported service action)\n", b);
+        else {
+            sg_get_category_sense_str(res, sizeof(bb), bb, op->verbose);
+            pr2serr("PR in (%s): %s\n", b, bb);
+        }
+        return res;
+    }
+    if (PRIN_RCAP_SA == op->prin_sa) {
+        if (8 != pr_buff[1]) {
+            pr2serr("Unexpected response for PRIN Report Capabilities\n");
+            if (op->hex)
+                dStrHex((const char *)pr_buff, pr_buff[1], 1);
+            return SG_LIB_CAT_MALFORMED;
+        }
+        if (op->hex)
+            dStrHex((const char *)pr_buff, 8, 1);
+        else {
+            printf("Report capabilities response:\n");
+            printf("  Compatible Reservation Handling(CRH): %d\n",
+                   !!(pr_buff[2] & 0x10));
+            printf("  Specify Initiator Ports Capable(SIP_C): %d\n",
+                   !!(pr_buff[2] & 0x8));
+            printf("  All Target Ports Capable(ATP_C): %d\n",
+                   !!(pr_buff[2] & 0x4));
+            printf("  Persist Through Power Loss Capable(PTPL_C): %d\n",
+                   !!(pr_buff[2] & 0x1));
+            printf("  Type Mask Valid(TMV): %d\n", !!(pr_buff[3] & 0x80));
+            printf("  Allow Commands: %d\n", (pr_buff[3] >> 4) & 0x7);
+            printf("  Persist Through Power Loss Active(PTPL_A): %d\n",
+                   !!(pr_buff[3] & 0x1));
+            if (pr_buff[3] & 0x80) {
+                printf("    Support indicated in Type mask:\n");
+                printf("      %s: %d\n", pr_type_strs[7],
+                       !!(pr_buff[4] & 0x80));
+                printf("      %s: %d\n", pr_type_strs[6],
+                       !!(pr_buff[4] & 0x40));
+                printf("      %s: %d\n", pr_type_strs[5],
+                       !!(pr_buff[4] & 0x20));
+                printf("      %s: %d\n", pr_type_strs[3],
+                       !!(pr_buff[4] & 0x8));
+                printf("      %s: %d\n", pr_type_strs[1],
+                       !!(pr_buff[4] & 0x2));
+                printf("      %s: %d\n", pr_type_strs[8],
+                       !!(pr_buff[5] & 0x1));
+            }
+        }
+    } else {
+        pr_gen =  sg_get_unaligned_be32(pr_buff + 0);
+        add_len = sg_get_unaligned_be32(pr_buff + 4);
+        if (op->hex) {
+            if (op->hex > 1)
+                dStrHex((const char *)pr_buff, add_len + 8,
+                        ((2 == op->hex) ? 1 : -1));
+            else {
+                printf("  PR generation=0x%x, ", pr_gen);
+                if (add_len <= 0)
+                    printf("Additional length=%d\n", add_len);
+                if (add_len > ((int)sizeof(pr_buff) - 8)) {
+                    printf("Additional length too large=%d, truncate\n",
+                           add_len);
+                    dStrHex((const char *)(pr_buff + 8), sizeof(pr_buff) - 8,
+                            1);
+                } else {
+                    printf("Additional length=%d\n", add_len);
+                    dStrHex((const char *)(pr_buff + 8), add_len, 1);
+                }
+            }
+        } else if (PRIN_RKEY_SA == op->prin_sa) {
+            printf("  PR generation=0x%x, ", pr_gen);
+            num = add_len / 8;
+            if (num > 0) {
+                if (1 == num)
+                    printf("1 registered reservation key follows:\n");
+                else
+                    printf("%d registered reservation keys follow:\n", num);
+                ucp = pr_buff + 8;
+                for (k = 0; k < num; ++k, ucp += 8)
+                    printf("    0x%" PRIx64 "\n",
+                           sg_get_unaligned_be64(ucp + 0));
+            } else
+                printf("there are NO registered reservation keys\n");
+        } else if (PRIN_RRES_SA == op->prin_sa) {
+            printf("  PR generation=0x%x, ", pr_gen);
+            num = add_len / 16;
+            if (num > 0) {
+                printf("Reservation follows:\n");
+                ucp = pr_buff + 8;
+                printf("    Key=0x%" PRIx64 "\n", sg_get_unaligned_be64(ucp));
+                j = ((ucp[13] >> 4) & 0xf);
+                if (0 == j)
+                    printf("    scope: LU_SCOPE, ");
+                else
+                    printf("    scope: %d ", j);
+                j = (ucp[13] & 0xf);
+                printf(" type: %s\n", pr_type_strs[j]);
+            } else
+                printf("there is NO reservation held\n");
+        } else if (PRIN_RFSTAT_SA == op->prin_sa) {
+            printf("  PR generation=0x%x\n", pr_gen);
+            ucp = pr_buff + 8;
+            if (0 == add_len) {
+                printf("  No full status descriptors\n");
+                if (op->verbose)
+                printf("  So there are no registered IT nexuses\n");
+            }
+            for (k = 0; k < add_len; k += num, ucp += num) {
+                add_desc_len = sg_get_unaligned_be32(ucp + 20);
+                num = 24 + add_desc_len;
+                printf("    Key=0x%" PRIx64 "\n", sg_get_unaligned_be64(ucp));
+                if (ucp[12] & 0x2)
+                    printf("      All target ports bit set\n");
+                else {
+                    printf("      All target ports bit clear\n");
+                    printf("      Relative port address: 0x%x\n",
+                           sg_get_unaligned_be16(ucp + 18));
+                }
+                if (ucp[12] & 0x1) {
+                    printf("      << Reservation holder >>\n");
+                    j = ((ucp[13] >> 4) & 0xf);
+                    if (0 == j)
+                        printf("      scope: LU_SCOPE, ");
+                    else
+                        printf("      scope: %d ", j);
+                    j = (ucp[13] & 0xf);
+                    printf(" type: %s\n", pr_type_strs[j]);
+                } else
+                    printf("      not reservation holder\n");
+                if (add_desc_len > 0)
+                    decode_transport_id("      ", &ucp[24], add_desc_len, 0);
+            }
+        }
+    }
+    return 0;
+}
+
+/* Compact the 2 dimensional transportid_arr into a one dimensional
+ * array in place returning the length. */
+static int
+compact_transportid_array(struct opts_t * op)
+{
+    int k, off, protocol_id, len;
+    int compact_len = 0;
+    unsigned char * ucp = op->transportid_arr;
+
+    for (k = 0, off = 0; ((k < op->num_transportids) && (k < MX_TIDS));
+         ++k, off += MX_TID_LEN) {
+        protocol_id = ucp[off] & 0xf;
+        if (TPROTO_ISCSI == protocol_id) {
+            len = sg_get_unaligned_be16(ucp + off + 2) + 4;
+            if (len < 24)
+                len = 24;
+            if (off > compact_len)
+                memmove(ucp + compact_len, ucp + off, len);
+            compact_len += len;
+
+        } else {
+            if (off > compact_len)
+                memmove(ucp + compact_len, ucp + off, 24);
+            compact_len += 24;
+        }
+    }
+    return compact_len;
+}
+
+static int
+prout_work(int sg_fd, struct opts_t * op)
+{
+    int len, res, t_arr_len;
+    unsigned char pr_buff[MX_ALLOC_LEN];
+    char b[64];
+    char bb[80];
+
+    t_arr_len = compact_transportid_array(op);
+    memset(pr_buff, 0, sizeof(pr_buff));
+    sg_put_unaligned_be64(op->param_rk, pr_buff + 0);
+    sg_put_unaligned_be64(op->param_sark, pr_buff + 8);
+    if (op->param_alltgpt)
+        pr_buff[20] |= 0x4;
+    if (op->param_aptpl)
+        pr_buff[20] |= 0x1;
+    len = 24;
+    if (t_arr_len > 0) {
+        pr_buff[20] |= 0x8;     /* set SPEC_I_PT bit */
+        memcpy(&pr_buff[28], op->transportid_arr, t_arr_len);
+        len += (t_arr_len + 4);
+        sg_put_unaligned_be32((uint32_t)t_arr_len, pr_buff + 24);
+    }
+    res = sg_ll_persistent_reserve_out(sg_fd, op->prout_sa, 0,
+                                       op->prout_type, pr_buff, len, 1,
+                                       op->verbose);
+    if (res || op->verbose) {
+        if (op->prout_sa < num_prout_sa_strs)
+            snprintf(b, sizeof(b), "%s", prout_sa_strs[op->prout_sa]);
+        else
+            snprintf(b, sizeof(b), "service action=0x%x", op->prout_sa);
+        if (res) {
+            if (SG_LIB_CAT_INVALID_OP == res)
+                pr2serr("PR out (%s): command not supported\n", b);
+            else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+                pr2serr("PR out (%s): bad field in cdb or parameter list "
+                        "(perhaps unsupported service action)\n", b);
+            else {
+                sg_get_category_sense_str(res, sizeof(bb), bb, op->verbose);
+                pr2serr("PR out (%s): %s\n", b, bb);
+            }
+            return res;
+        } else if (op->verbose)
+            pr2serr("PR out: command (%s) successful\n", b);
+    }
+    return 0;
+}
+
+static int
+prout_reg_move_work(int sg_fd, struct opts_t * op)
+{
+    int len, res, t_arr_len;
+    unsigned char pr_buff[MX_ALLOC_LEN];
+
+    t_arr_len = compact_transportid_array(op);
+    memset(pr_buff, 0, sizeof(pr_buff));
+    sg_put_unaligned_be64(op->param_rk, pr_buff + 0);
+    sg_put_unaligned_be64(op->param_sark, pr_buff + 8);
+    if (op->param_unreg)
+        pr_buff[17] |= 0x2;
+    if (op->param_aptpl)
+        pr_buff[17] |= 0x1;
+    sg_put_unaligned_be16(op->param_rtp, pr_buff + 18);
+    len = 24;
+    if (t_arr_len > 0) {
+        memcpy(&pr_buff[24], op->transportid_arr, t_arr_len);
+        len += t_arr_len;
+        sg_put_unaligned_be32((uint32_t)t_arr_len, pr_buff + 20);
+    }
+    res = sg_ll_persistent_reserve_out(sg_fd, PROUT_REG_MOVE_SA, 0,
+                                       op->prout_type, pr_buff, len, 1,
+                                       op->verbose);
+    if (res) {
+       if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr("PR out (register and move): command not supported\n");
+        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+            pr2serr("PR out (register and move): bad field in cdb or "
+                    "parameter list (perhaps unsupported service action)\n");
+        else {
+            char bb[80];
+
+            sg_get_category_sense_str(res, sizeof(bb), bb, op->verbose);
+            pr2serr("PR out (register and move): %s\n", bb);
+        }
+        return res;
+    } else if (op->verbose)
+        pr2serr("PR out: 'register and move' command successful\n");
+    return 0;
+}
+
+/* Decode various symbolic forms of TransportIDs into SPC-4 format.
+ * Returns 1 if one found, else returns 0. */
+static int
+decode_sym_transportid(const char * lcp, unsigned char * tidp)
+{
+    int k, j, n, b, c, len, alen;
+    unsigned int ui;
+    const char * ecp;
+    const char * isip;
+
+    memset(tidp, 0, 24);
+    if ((0 == memcmp("sas,", lcp, 4)) || (0 == memcmp("SAS,", lcp, 4))) {
+        lcp += 4;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF");
+        if (16 != k) {
+            pr2serr("badly formed symbolic SAS TransportID: %s\n", lcp);
+            return 0;
+        }
+        tidp[0] = TPROTO_SAS;
+        for (k = 0, j = 0, b = 0; k < 16; ++k) {
+            c = lcp[k];
+            if (isdigit(c))
+                n = c - 0x30;
+            else if (isupper(c))
+                n = c - 0x37;
+            else
+                n = c - 0x57;
+            if (k & 1) {
+                tidp[4 + j] = b | n;
+                ++j;
+            } else
+                b = n << 4;
+        }
+        return 1;
+    } else if ((0 == memcmp("spi,", lcp, 4)) ||
+               (0 == memcmp("SPI,", lcp, 4))) {
+        lcp += 4;
+        if (2 != sscanf(lcp, "%d,%d", &b, &c)) {
+            pr2serr("badly formed symbolic SPI TransportID: %s\n", lcp);
+            return 0;
+        }
+        tidp[0] = TPROTO_SPI;
+        sg_put_unaligned_be16((uint16_t)b, tidp + 2);
+        sg_put_unaligned_be16((uint16_t)c, tidp + 6);
+        return 1;
+    } else if ((0 == memcmp("fcp,", lcp, 4)) ||
+               (0 == memcmp("FCP,", lcp, 4))) {
+        lcp += 4;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF");
+        if (16 != k) {
+            pr2serr("badly formed symbolic FCP TransportID: %s\n", lcp);
+            return 0;
+        }
+        tidp[0] = TPROTO_FCP;
+        for (k = 0, j = 0, b = 0; k < 16; ++k) {
+            c = lcp[k];
+            if (isdigit(c))
+                n = c - 0x30;
+            else if (isupper(c))
+                n = c - 0x37;
+            else
+                n = c - 0x57;
+            if (k & 1) {
+                tidp[8 + j] = b | n;
+                ++j;
+            } else
+                b = n << 4;
+        }
+        return 1;
+    } else if ((0 == memcmp("sbp,", lcp, 4)) ||
+               (0 == memcmp("SBP,", lcp, 4))) {
+        lcp += 4;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF");
+        if (16 != k) {
+            pr2serr("badly formed symbolic SBP TransportID: %s\n", lcp);
+            return 0;
+        }
+        tidp[0] = TPROTO_1394;
+        for (k = 0, j = 0, b = 0; k < 16; ++k) {
+            c = lcp[k];
+            if (isdigit(c))
+                n = c - 0x30;
+            else if (isupper(c))
+                n = c - 0x37;
+            else
+                n = c - 0x57;
+            if (k & 1) {
+                tidp[8 + j] = b | n;
+                ++j;
+            } else
+                b = n << 4;
+        }
+        return 1;
+    } else if ((0 == memcmp("srp,", lcp, 4)) ||
+               (0 == memcmp("SRP,", lcp, 4))) {
+        lcp += 4;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF");
+        if (16 != k) {
+            pr2serr("badly formed symbolic SRP TransportID: %s\n", lcp);
+            return 0;
+        }
+        tidp[0] = TPROTO_SRP;
+        for (k = 0, j = 0, b = 0; k < 32; ++k) {
+            c = lcp[k];
+            if (isdigit(c))
+                n = c - 0x30;
+            else if (isupper(c))
+                n = c - 0x37;
+            else
+                n = c - 0x57;
+            if (k & 1) {
+                tidp[8 + j] = b | n;
+                ++j;
+            } else
+                b = n << 4;
+        }
+        return 1;
+    } else if (0 == memcmp("iqn.", lcp, 4)) {
+        ecp = strpbrk(lcp, " \t");
+        isip = strstr(lcp, ",i,0x");
+        if (ecp && (isip > ecp))
+            isip = NULL;
+        len = ecp ? (ecp - lcp) : (int)strlen(lcp);
+        tidp[0] = TPROTO_ISCSI | (isip ? 0x40 : 0x0);
+        alen = len + 1; /* at least one trailing null */
+        if (alen < 20)
+            alen = 20;
+        else if (0 != (alen % 4))
+            alen = ((alen / 4) + 1) * 4;
+        if (alen > 241) { /* sam5r02.pdf A.2 (Annex) */
+            pr2serr("iSCSI name too long, alen=%d\n", alen);
+            return 0;
+        }
+        tidp[3] = alen & 0xff;
+        memcpy(tidp + 4, lcp, len);
+        return 1;
+    } else if ((0 == memcmp("sop,", lcp, 4)) ||
+               (0 == memcmp("SOP,", lcp, 4))) {
+        lcp += 4;
+        if (2 != sscanf(lcp, "%x", &ui)) {
+            pr2serr("badly formed symbolic SOP TransportID: %s\n", lcp);
+            return 0;
+        }
+        tidp[0] = TPROTO_SOP;
+        sg_put_unaligned_be16((uint16_t)ui, tidp + 2);
+        return 1;
+    }
+    pr2serr("unable to parse symbolic TransportID: %s\n", lcp);
+    return 0;
+}
+
+/* Read one or more TransportIDs from the given file or stdin. Reads from
+ * stdin when 'fnp' is NULL. Returns 0 if successful, 1 otherwise. */
+static int
+decode_file_tids(const char * fnp, struct opts_t * op)
+{
+    FILE * fp = stdin;
+    int in_len, k, j, m, split_line;
+    unsigned int h;
+    const char * lcp;
+    char line[1024];
+    char carry_over[4];
+    int off = 0;
+    int num = 0;
+    unsigned char * tid_arr = op->transportid_arr;
+
+    if (fnp) {
+        fp = fopen(fnp, "r");
+        if (NULL == fp) {
+            pr2serr("%s: unable to open %s\n", __func__, fnp);
+            return 1;
+        }
+    }
+    carry_over[0] = 0;
+    for (j = 0, off = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+                split_line = 0;
+            } else
+                split_line = 1;
+        }
+        if (in_len < 1) {
+            carry_over[0] = 0;
+            continue;
+        }
+        if (carry_over[0]) {
+            if (isxdigit(line[0])) {
+                carry_over[1] = line[0];
+                carry_over[2] = '\0';
+                if (1 == sscanf(carry_over, "%x", &h))
+                    tid_arr[off - 1] = h;       /* back up and overwrite */
+                else {
+                    pr2serr("%s: carry_over error ['%s'] around line %d\n",
+                            __func__, carry_over, j + 1);
+                    goto bad;
+                }
+                lcp = line + 1;
+                --in_len;
+            } else
+                lcp = line;
+            carry_over[0] = 0;
+        } else
+            lcp = line;
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        if (decode_sym_transportid(lcp, tid_arr + off))
+            goto my_cont_a;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+        if ((k < in_len) && ('#' != lcp[k])) {
+            pr2serr("%s: syntax error at line %d, pos %d\n", __func__, j + 1,
+                    m + k + 1);
+            goto bad;
+        }
+        for (k = 0; k < 1024; ++k) {
+            if (1 == sscanf(lcp, "%x", &h)) {
+                if (h > 0xff) {
+                    pr2serr("%s: hex number larger than 0xff in line %d, pos "
+                            "%d\n", __func__, j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+                if (split_line && (1 == strlen(lcp))) {
+                    /* single trailing hex digit might be a split pair */
+                    carry_over[0] = *lcp;
+                }
+                if ((off + k) >= (int)sizeof(op->transportid_arr)) {
+                    pr2serr("%s: array length exceeded\n", __func__);
+                    goto bad;
+                }
+                tid_arr[off + k] = h;
+                lcp = strpbrk(lcp, " ,\t");
+                if (NULL == lcp)
+                    break;
+                lcp += strspn(lcp, " ,\t");
+                if ('\0' == *lcp)
+                    break;
+            } else {
+                if ('#' == *lcp) {
+                    --k;
+                    break;
+                }
+                pr2serr("%s: error in line %d, at pos %d\n", __func__, j + 1,
+                        (int)(lcp - line + 1));
+                goto bad;
+            }
+        }
+my_cont_a:
+        off += MX_TID_LEN;
+        if (off >= (MX_TIDS * MX_TID_LEN)) {
+            pr2serr("%s: array length exceeded\n", __func__);
+            goto bad;
+        }
+        ++num;
+    }
+    op->num_transportids = num;
+   if (fnp)
+        fclose(fp);
+    return 0;
+
+bad:
+   if (fnp)
+        fclose(fp);
+   return 1;
+}
+
+/* Build transportid array which may contain one or more TransportIDs.
+ * A single TransportID can appear on the command line either as a list of
+ * comma (or single space) separated ASCII hex bytes, or in some transport
+ * protocol specific form (e.g. "sas,5000c50005b32001"). One or more
+ * TransportIDs may be given in a file (syntax: "file=<name>") or read from
+ * stdin in (when "-" is given). Fuller description in manpage of
+ * sg_persist(8). Returns 0 if successful, else 1 .
+ */
+static int
+build_transportid(const char * inp, struct opts_t * op)
+{
+    int in_len;
+    int k = 0;
+    unsigned int h;
+    const char * lcp;
+    unsigned char * tid_arr = op->transportid_arr;
+    char * cp;
+    char * c2p;
+
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len) {
+        op->num_transportids = 0;
+    }
+    if (('-' == inp[0]) ||
+        (0 == memcmp("file=", inp, 5)) ||
+        (0 == memcmp("FILE=", inp, 5))) {
+        if ('-' == inp[0])
+            lcp = NULL;         /* read from stdin */
+        else
+            lcp = inp + 5;      /* read from given file */
+        return decode_file_tids(lcp, op);
+    } else {        /* TransportID given directly on command line */
+        if (decode_sym_transportid(lcp, tid_arr))
+            goto my_cont_b;
+        k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
+        if (in_len != k) {
+            pr2serr("build_transportid: error at pos %d\n", k + 1);
+            return 1;
+        }
+        for (k = 0; k < (int)sizeof(op->transportid_arr); ++k) {
+            if (1 == sscanf(lcp, "%x", &h)) {
+                if (h > 0xff) {
+                    pr2serr("build_transportid: hex number larger than 0xff "
+                            "at pos %d\n", (int)(lcp - inp + 1));
+                    return 1;
+                }
+                tid_arr[k] = h;
+                cp = (char *)strchr(lcp, ',');
+                c2p = (char *)strchr(lcp, ' ');
+                if (NULL == cp)
+                    cp = c2p;
+                if (NULL == cp)
+                    break;
+                if (c2p && (c2p < cp))
+                    cp = c2p;
+                lcp = cp + 1;
+            } else {
+                pr2serr("build_transportid: error at pos %d\n",
+                        (int)(lcp - inp + 1));
+                return 1;
+            }
+        }
+my_cont_b:
+        op->num_transportids = 1;
+        if (k >= (int)sizeof(op->transportid_arr)) {
+            pr2serr("build_transportid: array length exceeded\n");
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, c, res;
+    const char * device_name = NULL;
+    char buff[48];
+    int help =0;
+    int num_prin_sa = 0;
+    int num_prout_sa = 0;
+    int num_prout_param = 0;
+    int want_prin = 0;
+    int want_prout = 0;
+    int peri_type = 0;
+    int ret = 0;
+    struct sg_simple_inquiry_resp inq_resp;
+    const char * cp;
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->prin = 1;
+    op->prin_sa = -1;
+    op->prout_sa = -1;
+    op->inquiry = 1;
+    op->alloc_len = MX_ALLOC_LEN;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "AcCd:GHhiIkK:l:LMnoPQ:rRsS:T:UvVX:yYzZ",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'A':
+            op->prout_sa = PROUT_PREE_AB_SA;
+            ++num_prout_sa;
+            break;
+        case 'c':
+            op->prin_sa = PRIN_RCAP_SA;
+            ++num_prin_sa;
+            break;
+        case 'C':
+            op->prout_sa = PROUT_CLEAR_SA;
+            ++num_prout_sa;
+            break;
+        case 'd':
+            device_name = optarg;
+            break;
+        case 'G':
+            op->prout_sa = PROUT_REG_SA;
+            ++num_prout_sa;
+            break;
+        case 'h':
+            ++help;
+            break;
+        case 'H':
+            ++op->hex;
+            break;
+        case 'i':
+            want_prin = 1;
+            break;
+        case 'I':
+            op->prout_sa = PROUT_REG_IGN_SA;
+            ++num_prout_sa;
+            break;
+        case 'k':
+            op->prin_sa = PRIN_RKEY_SA;
+            ++num_prin_sa;
+            break;
+        case 'K':
+            if (1 != sscanf(optarg, "%" SCNx64 "", &op->param_rk)) {
+                pr2serr("bad argument to '--param-rk'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++num_prout_param;
+            break;
+        case 'l':
+            if (1 != sscanf(optarg, "%x", &op->alloc_len)) {
+                pr2serr("bad argument to '--alloc-length'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else if (MX_ALLOC_LEN < op->alloc_len) {
+                pr2serr("'--alloc-length' argument exceeds maximum value "
+                        "(%d)\n", MX_ALLOC_LEN);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'L':
+            op->prout_sa = PROUT_REL_SA;
+            ++num_prout_sa;
+            break;
+        case 'M':
+            op->prout_sa = PROUT_REG_MOVE_SA;
+            ++num_prout_sa;
+            break;
+        case 'n':
+            op->inquiry = 0;
+            break;
+        case 'o':
+            want_prout = 1;
+            break;
+        case 'P':
+            op->prout_sa = PROUT_PREE_SA;
+            ++num_prout_sa;
+            break;
+        case 'Q':
+            if (1 != sscanf(optarg, "%x", &op->param_rtp)) {
+                pr2serr("bad argument to '--relative-target-port'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if (op->param_rtp > 0xffff) {
+                pr2serr("argument to '--relative-target-port' 0 to ffff "
+                        "inclusive\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++num_prout_param;
+            break;
+        case 'r':
+            op->prin_sa = PRIN_RRES_SA;
+            ++num_prin_sa;
+            break;
+        case 'R':
+            op->prout_sa = PROUT_RES_SA;
+            ++num_prout_sa;
+            break;
+        case 's':
+            op->prin_sa = PRIN_RFSTAT_SA;
+            ++num_prin_sa;
+            break;
+        case 'S':
+            if (1 != sscanf(optarg, "%" SCNx64 "", &op->param_sark)) {
+                pr2serr("bad argument to '--param-sark'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++num_prout_param;
+            break;
+        case 'T':
+            if (1 != sscanf(optarg, "%x", &op->prout_type)) {
+                pr2serr("bad argument to '--prout-type'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++num_prout_param;
+            break;
+        case 'U':
+            op->param_unreg = 1;
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        case 'X':
+            if (0 != build_transportid(optarg, op)) {
+                pr2serr("bad argument to '--transport-id'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++num_prout_param;
+            break;
+        case 'y':
+            ++op->readonly;
+            break;
+        case 'Y':
+            op->param_alltgpt = 1;
+            ++num_prout_param;
+            break;
+        case 'z':
+            op->prout_sa = PROUT_REPL_LOST_SA;
+            ++num_prout_sa;
+            break;
+        case 'Z':
+            op->param_aptpl = 1;
+            ++num_prout_param;
+            break;
+        case '?':
+            usage(1);
+            return 0;
+        default:
+            pr2serr("unrecognised switch code 0x%x ??\n", c);
+            usage(1);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage(1);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (help > 0) {
+        usage(help);
+        return 0;
+    }
+
+    if (NULL == device_name) {
+        pr2serr("No device name given\n");
+        usage(1);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((want_prout + want_prin) > 1) {
+        pr2serr("choose '--in' _or_ '--out' (not both)\n");
+        usage(1);
+        return SG_LIB_SYNTAX_ERROR;
+    } else if (want_prout) { /* syntax check on PROUT arguments */
+        op->prin = 0;
+        if ((1 != num_prout_sa) || (0 != num_prin_sa)) {
+            pr2serr(">> For Persistent Reserve Out one and only one "
+                    "appropriate\n>> service action must be chosen (e.g. "
+                    "'--register')\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    } else { /* syntax check on PRIN arguments */
+        if (num_prout_sa > 0) {
+            pr2serr(">> When a service action for Persistent Reserve Out "
+                    "is chosen the\n>> '--out' option must be given (as a "
+                    "safeguard)\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (0 == num_prin_sa) {
+            pr2serr(">> No service action given; assume Persistent Reserve "
+                    "In command\n>> with Read Keys service action\n");
+            op->prin_sa = 0;
+            ++num_prin_sa;
+        } else if (num_prin_sa > 1)  {
+            pr2serr("Too many service actions given; choose one only\n");
+            usage(1);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if ((op->param_unreg || op->param_rtp) &&
+        (PROUT_REG_MOVE_SA != op->prout_sa)) {
+        pr2serr("--unreg or --relative-target-port only useful with "
+                "--register-move\n");
+        usage(1);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((PROUT_REG_MOVE_SA == op->prout_sa) &&
+        (1 != op->num_transportids)) {
+        pr2serr("with --register-move one (and only one) --transport-id "
+                "should be given\n");
+        usage(1);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (((PROUT_RES_SA == op->prout_sa) ||
+         (PROUT_REL_SA == op->prout_sa) ||
+         (PROUT_PREE_SA == op->prout_sa) ||
+         (PROUT_PREE_AB_SA == op->prout_sa)) &&
+        (0 == op->prout_type)) {
+        pr2serr("warning>>> --prout-type probably needs to be given\n");
+    }
+    if ((op->verbose > 2) && op->num_transportids) {
+        pr2serr("number of tranport-ids decoded from command line (or "
+                "stdin): %d\n", op->num_transportids);
+        pr2serr("  Decode given transport-ids:\n");
+        decode_transport_id("      ", op->transportid_arr,
+                            0, op->num_transportids);
+    }
+
+    if (op->inquiry) {
+        if ((sg_fd = sg_cmds_open_device(device_name, 1 /* ro */,
+                                         op->verbose)) < 0) {
+            pr2serr("sg_persist: error opening file (ro): %s: %s\n",
+                    device_name, safe_strerror(-sg_fd));
+            return SG_LIB_FILE_ERROR;
+        }
+        if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, op->verbose)) {
+            printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor, inq_resp.product,
+                   inq_resp.revision);
+            peri_type = inq_resp.peripheral_type;
+            cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
+            if (strlen(cp) > 0)
+                printf("  Peripheral device type: %s\n", cp);
+            else
+                printf("  Peripheral device type: 0x%x\n", peri_type);
+        } else {
+            printf("sg_persist: %s doesn't respond to a SCSI INQUIRY\n",
+                   device_name);
+            return SG_LIB_CAT_OTHER;
+        }
+        sg_cmds_close_device(sg_fd);
+    }
+
+    if (0 == op->readonly) {
+        cp = getenv(SG_PERSIST_IN_RDONLY);
+        if (cp && op->prin)
+            op->readonly = 1;
+    } else if (op->readonly > 1)        /* -yy forces open(RW) */
+        op->readonly = 0;
+    if ((sg_fd = sg_cmds_open_device(device_name, op->readonly,
+                                     op->verbose)) < 0) {
+        pr2serr("sg_persist: error opening file (rw): %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (op->prin)
+        ret = prin_work(sg_fd, op);
+    else if (PROUT_REG_MOVE_SA == op->prout_sa)
+        ret = prout_reg_move_work(sg_fd, op);
+    else /* PROUT commands other than 'register and move' */
+        ret = prout_work(sg_fd, op);
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_prevent.c b/sg3_utils/src/sg_prevent.c
new file mode 100644
index 0000000..1771cad
--- /dev/null
+++ b/sg3_utils/src/sg_prevent.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2004-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ * This program issues the SCSI PREVENT ALLOW MEDIUM REMOVAL command to the
+ * given SCSI device.
+ */
+
+static const char * version_str = "1.08 20151219";
+
+#define ME "sg_prevent: "
+
+
+static struct option long_options[] = {
+        {"allow", 0, 0, 'a'},
+        {"help", 0, 0, 'h'},
+        {"prevent", 1, 0, 'p'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void usage()
+{
+    pr2serr("Usage: "
+            "sg_prevent [--allow] [--help] [--prevent=PC] [--verbose] "
+            "[--version]\n"
+            "                  DEVICE\n"
+            "  where:\n"
+            "    --allow|-a            allow media removal\n"
+            "    --help|-h             print usage message then exit\n"
+            "    --prevent=PC|-p PC    prevent code value (def: 1 -> "
+            "prevent)\n"
+            "                            0 -> allow, 1 -> prevent\n"
+            "                            2 -> persistent allow, 3 -> "
+            "persistent prevent\n"
+            "    --verbose|-v          increase verbosity\n"
+            "    --version|-V          print version string and exit\n\n"
+            "Performs a SCSI PREVENT ALLOW MEDIUM REMOVAL command\n");
+
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, res, c;
+    int allow = 0;
+    int prevent = -1;
+    int verbose = 0;
+    const char * device_name = NULL;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ahp:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            allow = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'p':
+           prevent = sg_get_num(optarg);
+           if ((prevent < 0) || (prevent > 3)) {
+                pr2serr("bad argument to '--prevent'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (allow && (prevent >= 0)) {
+        pr2serr("can't give both '--allow' and '--prevent='\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (allow)
+        prevent = 0;
+    else if (prevent < 0)
+        prevent = 1;    /* default is to prevent, as utility name suggests */
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    res = sg_ll_prevent_allow(sg_fd, prevent, 1, verbose);
+    ret = res;
+    if (res) {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Prevent allow medium removal: %s\n", b);
+    }
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_raw.c b/sg3_utils/src/sg_raw.c
new file mode 100644
index 0000000..8fd6dd7
--- /dev/null
+++ b/sg3_utils/src/sg_raw.c
@@ -0,0 +1,630 @@
+/*
+ * A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ * Copyright (C) 2000-2015 Ingo van Lil <inguin@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program can be used to send raw SCSI commands (with an optional
+ * data phase) through a Generic SCSI interface.
+ */
+
+#define _XOPEN_SOURCE 600       /* clear up posix_memalign() warning */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_pt.h"
+#include "sg_pr2serr.h"
+
+#define SG_RAW_VERSION "0.4.14 (2015-12-19)"
+
+#ifdef SG_LIB_WIN32
+#ifndef HAVE_SYSCONF
+#include <windows.h>
+
+static size_t
+win_pagesize(void)
+{
+    SYSTEM_INFO sys_info;
+
+    GetSystemInfo(&sys_info);
+    return sys_info.dwPageSize;
+}
+#endif
+#endif
+
+#define DEFAULT_TIMEOUT 20
+#define MIN_SCSI_CDBSZ 6
+#define MAX_SCSI_CDBSZ 256
+#define MAX_SCSI_DXLEN (64 * 1024)
+
+static struct option long_options[] = {
+    { "binary",  no_argument,       NULL, 'b' },
+    { "help",    no_argument,       NULL, 'h' },
+    { "infile",  required_argument, NULL, 'i' },
+    { "skip",    required_argument, NULL, 'k' },
+    { "nosense", no_argument,       NULL, 'n' },
+    { "outfile", required_argument, NULL, 'o' },
+    { "request", required_argument, NULL, 'r' },
+    { "readonly", no_argument,      NULL, 'R' },
+    { "send",    required_argument, NULL, 's' },
+    { "timeout", required_argument, NULL, 't' },
+    { "verbose", no_argument,       NULL, 'v' },
+    { "version", no_argument,       NULL, 'V' },
+    { 0, 0, 0, 0 }
+};
+
+struct opts_t {
+    char *device_name;
+    unsigned char cdb[MAX_SCSI_CDBSZ];
+    int cdb_length;
+    int do_datain;
+    int datain_len;
+    const char *datain_file;
+    int datain_binary;
+    int do_dataout;
+    int dataout_len;
+    const char *dataout_file;
+    off_t dataout_offset;
+    int timeout;
+    int no_sense;
+    int readonly;
+    int do_help;
+    int do_verbose;
+    int do_version;
+};
+
+
+static void
+version()
+{
+    pr2serr("sg_raw " SG_RAW_VERSION "\n"
+            "Copyright (C) 2007-2012 Ingo van Lil <inguin@gmx.de>\n"
+            "This is free software.  You may redistribute copies of it "
+            "under the terms of\n"
+            "the GNU General Public License "
+            "<http://www.gnu.org/licenses/gpl.html>.\n"
+            "There is NO WARRANTY, to the extent permitted by law.\n");
+}
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_raw [OPTION]* DEVICE CDB0 CDB1 ...\n"
+            "\n"
+            "Options:\n"
+            "  -b, --binary           Dump data in binary form, even when "
+            "writing to stdout\n"
+            "  -h, --help             Show this message and exit\n"
+            "  -i, --infile=IFILE     Read data to send from IFILE (default: "
+            "stdin)\n"
+            "  -k, --skip=LEN         Skip the first LEN bytes when reading "
+            "data to send\n"
+            "  -n, --nosense          Don't display sense information\n"
+            "  -o, --outfile=OFILE    Write binary data to OFILE (def: "
+            "hexdump to stdout)\n"
+            "  -r, --request=RLEN     Request up to RLEN bytes of data "
+            "(data-in)\n"
+            "  -R, --readonly         Open DEVICE read-only (default: "
+            "read-write)\n"
+            "  -s, --send=SLEN        Send SLEN bytes of data (data-out)\n"
+            "  -t, --timeout=SEC      Timeout in seconds (default: 20)\n"
+            "  -v, --verbose          Increase verbosity\n"
+            "  -V, --version          Show version information and exit\n"
+            "\n"
+            "Between 6 and 256 command bytes (two hex digits each) can be "
+            "specified\nand will be sent to DEVICE. Lengths RLEN and SLEN "
+            "are decimal by\ndefault. Bidirectional commands accepted.\n\n"
+            "Simple example: Perform INQUIRY on /dev/sg0:\n"
+            "  sg_raw -r 1k /dev/sg0 12 00 00 00 60 00\n");
+}
+
+static int
+process_cl(struct opts_t * op, int argc, char *argv[])
+{
+    while (1) {
+        int c, n;
+
+        c = getopt_long(argc, argv, "bhi:k:no:r:Rs:t:vV", long_options, NULL);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            op->datain_binary = 1;
+            break;
+        case 'h':
+        case '?':
+            op->do_help = 1;
+            return 0;
+        case 'i':
+            if (op->dataout_file) {
+                pr2serr("Too many '--infile=' options\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->dataout_file = optarg;
+            break;
+        case 'k':
+            n = sg_get_num(optarg);
+            if (n < 0) {
+                pr2serr("Invalid argument to '--skip'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->dataout_offset = n;
+            break;
+        case 'n':
+            op->no_sense = 1;
+            break;
+        case 'o':
+            if (op->datain_file) {
+                pr2serr("Too many '--outfile=' options\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->datain_file = optarg;
+            break;
+        case 'r':
+            op->do_datain = 1;
+            n = sg_get_num(optarg);
+            if (n < 0 || n > MAX_SCSI_DXLEN) {
+                pr2serr("Invalid argument to '--request'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->datain_len = n;
+            break;
+        case 'R':
+            ++op->readonly;
+            break;
+        case 's':
+            op->do_dataout = 1;
+            n = sg_get_num(optarg);
+            if (n < 0 || n > MAX_SCSI_DXLEN) {
+                pr2serr("Invalid argument to '--send'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->dataout_len = n;
+            break;
+        case 't':
+            n = sg_get_num(optarg);
+            if (n < 0) {
+                pr2serr("Invalid argument to '--timeout'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->timeout = n;
+            break;
+        case 'v':
+            ++op->do_verbose;
+            break;
+        case 'V':
+            op->do_version = 1;
+            return 0;
+        default:
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (optind >= argc) {
+        pr2serr("No device specified\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    op->device_name = argv[optind];
+    ++optind;
+
+    while (optind < argc) {
+        char *opt = argv[optind++];
+        char *endptr;
+        int cmd = strtol(opt, &endptr, 16);
+        if (*opt == '\0' || *endptr != '\0' || cmd < 0x00 || cmd > 0xff) {
+            pr2serr("Invalid command byte '%s'\n", opt);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+
+        if (op->cdb_length > MAX_SCSI_CDBSZ) {
+            pr2serr("CDB too long (max. %d bytes)\n", MAX_SCSI_CDBSZ);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        op->cdb[op->cdb_length] = cmd;
+        ++op->cdb_length;
+    }
+
+    if (op->cdb_length < MIN_SCSI_CDBSZ) {
+        pr2serr("CDB too short (min. %d bytes)\n", MIN_SCSI_CDBSZ);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_verbose > 1) {
+        int sa;
+        char b[80];
+
+        if (op->cdb_length > 16) {
+            sa = (op->cdb[8] << 8) + op->cdb[9];
+            if (0x7f != op->cdb[0])
+                printf(">>> Unlikely to be SCSI CDB since all over 16 "
+                       "bytes long should\n>>> start with 0x7f\n");
+        } else
+            sa = op->cdb[1] & 0x1f;
+        sg_get_opcode_sa_name(op->cdb[0], sa, 0, sizeof(b), b);
+        printf("Attempt to decode cdb name: %s\n", b);
+    }
+    return 0;
+}
+
+/* Allocate aligned memory (heap) starting on page boundary */
+static unsigned char *
+my_memalign(int length, unsigned char ** wrkBuffp, const struct opts_t * op)
+{
+    size_t psz;
+    unsigned char * res;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+    psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
+#elif defined(SG_LIB_WIN32)
+    psz = win_pagesize();
+#else
+    psz = 4096;     /* give up, pick likely figure */
+#endif
+
+#ifdef HAVE_POSIX_MEMALIGN
+    {
+        int err;
+        void * wp = NULL;
+
+        err = posix_memalign(&wp, psz, length);
+        if (err || (NULL == wp)) {
+            pr2serr("posix_memalign: error [%d], out of memory?\n", err);
+            return NULL;
+        }
+        memset(wp, 0, length);
+        if (wrkBuffp)
+            *wrkBuffp = (unsigned char *)wp;
+        res = (unsigned char *)wp;
+        if (op->do_verbose > 3)
+            pr2serr("%s: posix, len=%d, wrkBuffp=%p, psz=%d, rp=%p\n",
+                    __func__, length, *wrkBuffp, (int)psz, res);
+        return res;
+    }
+#else
+    {
+        unsigned char * wrkBuff;
+
+        wrkBuff = (unsigned char*)calloc(length + psz, 1);
+        if (NULL == wrkBuff) {
+            if (wrkBuffp)
+                *wrkBuffp = NULL;
+            return NULL;
+        } else if (wrkBuffp)
+            *wrkBuffp = wrkBuff;
+        res = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) &
+                                (~(psz - 1)));
+        if (op->do_verbose > 3)
+            pr2serr("%s: hack, len=%d, wrkBuffp=%p, psz=%d, rp=%p\n",
+                    __func__, length, *wrkBuffp, (int)psz, res);
+        return res;
+    }
+#endif
+}
+
+static int
+skip(int fd, off_t offset)
+{
+    off_t remain;
+    char buffer[512];
+
+    if (lseek(fd, offset, SEEK_SET) >= 0) {
+        return 0;
+    }
+
+    // lseek failed; fall back to reading and discarding data
+    remain = offset;
+    while (remain > 0) {
+        ssize_t amount, done;
+        amount = (remain < (off_t)sizeof(buffer)) ? remain
+                                         : (off_t)sizeof(buffer);
+        done = read(fd, buffer, amount);
+        if (done < 0) {
+            perror("Error reading input data");
+            return SG_LIB_FILE_ERROR;
+        } else if (done == 0) {
+            pr2serr("EOF on input file/stream\n");
+            return SG_LIB_FILE_ERROR;
+        } else {
+            remain -= done;
+        }
+    }
+
+    return 0;
+}
+
+static unsigned char *
+fetch_dataout(struct opts_t * op)
+{
+    unsigned char *buf = NULL;
+    unsigned char *wrkBuf = NULL;
+    int fd, len;
+    int ok = 0;
+
+    if (op->dataout_file) {
+        fd = open(op->dataout_file, O_RDONLY);
+        if (fd < 0) {
+            perror(op->dataout_file);
+            goto bail;
+        }
+
+    } else {
+        fd = STDIN_FILENO;
+    }
+    if (sg_set_binary_mode(fd) < 0) {
+        perror("sg_set_binary_mode");
+        goto bail;
+    }
+
+    if (op->dataout_offset > 0) {
+        if (skip(fd, op->dataout_offset) != 0) {
+            goto bail;
+        }
+    }
+
+    buf = my_memalign(op->dataout_len, &wrkBuf, op);
+    if (buf == NULL) {
+        perror("malloc");
+        goto bail;
+    }
+
+    len = read(fd, buf, op->dataout_len);
+    if (len < 0) {
+        perror("Failed to read input data");
+        goto bail;
+    } else if (len < op->dataout_len) {
+        pr2serr("EOF on input file/stream\n");
+        goto bail;
+    }
+
+    ok = 1;
+
+bail:
+    if (fd >= 0 && fd != STDIN_FILENO)
+        close(fd);
+    if (!ok) {
+        if (wrkBuf)
+            free(wrkBuf);
+        return NULL;
+    }
+    return buf;
+}
+
+static int
+write_dataout(const char *filename, unsigned char *buf, int len)
+{
+    int ret = SG_LIB_FILE_ERROR;
+    int fd;
+
+    if ((filename == NULL) ||
+        ((1 == strlen(filename)) && ('-' == filename[0])))
+        fd = STDOUT_FILENO;
+    else {
+        fd = creat(filename, 0666);
+        if (fd < 0) {
+            perror(filename);
+            goto bail;
+        }
+    }
+    if (sg_set_binary_mode(fd) < 0) {
+        perror("sg_set_binary_mode");
+        goto bail;
+    }
+
+    if (write(fd, buf, len) != len) {
+        perror(filename ? filename : "stdout");
+        goto bail;
+    }
+
+    ret = 0;
+
+bail:
+    if (fd >= 0 && fd != STDOUT_FILENO)
+        close(fd);
+    return ret;
+}
+
+int
+main(int argc, char *argv[])
+{
+    int ret = 0;
+    int res_cat, status, slen, k, ret2;
+    int sg_fd = -1;
+    struct sg_pt_base *ptvp = NULL;
+    unsigned char sense_buffer[32];
+    unsigned char * dxfer_buffer_in = NULL;
+    unsigned char * dxfer_buffer_out = NULL;
+    unsigned char * wrkBuf = NULL;
+    struct opts_t opts;
+    struct opts_t * op;
+    char b[128];
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->timeout = DEFAULT_TIMEOUT;
+    ret = process_cl(op, argc, argv);
+    if (ret != 0) {
+        usage();
+        goto done;
+    } else if (op->do_help) {
+        usage();
+        goto done;
+    } else if (op->do_version) {
+        version();
+        goto done;
+    }
+
+    sg_fd = scsi_pt_open_device(op->device_name, op->readonly,
+                                op->do_verbose);
+    if (sg_fd < 0) {
+        pr2serr("%s: %s\n", op->device_name, safe_strerror(-sg_fd));
+        ret = SG_LIB_FILE_ERROR;
+        goto done;
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (ptvp == NULL) {
+        pr2serr("out of memory\n");
+        ret = SG_LIB_CAT_OTHER;
+        goto done;
+    }
+    if (op->do_verbose) {
+        pr2serr("    cdb to send: ");
+        for (k = 0; k < op->cdb_length; ++k)
+            pr2serr("%02x ", op->cdb[k]);
+        pr2serr("\n");
+        if (op->do_verbose > 1) {
+            sg_get_command_name(op->cdb, 0, sizeof(b) - 1, b);
+            b[sizeof(b) - 1] = '\0';
+            pr2serr("    Command name: %s\n", b);
+        }
+    }
+    set_scsi_pt_cdb(ptvp, op->cdb, op->cdb_length);
+    if (op->do_verbose > 2)
+        pr2serr("sense_buffer=%p, length=%d\n", sense_buffer,
+                (int)sizeof(sense_buffer));
+    set_scsi_pt_sense(ptvp, sense_buffer, sizeof(sense_buffer));
+
+    if (op->do_dataout) {
+        dxfer_buffer_out = fetch_dataout(op);
+        if (dxfer_buffer_out == NULL) {
+            ret = SG_LIB_CAT_OTHER;
+            goto done;
+        }
+        if (op->do_verbose > 2)
+            pr2serr("dxfer_buffer_out=%p, length=%d\n", dxfer_buffer_out,
+                    op->dataout_len);
+        set_scsi_pt_data_out(ptvp, dxfer_buffer_out, op->dataout_len);
+    }
+    if (op->do_datain) {
+        dxfer_buffer_in = my_memalign(op->datain_len, &wrkBuf, op);
+        if (dxfer_buffer_in == NULL) {
+            perror("malloc");
+            ret = SG_LIB_CAT_OTHER;
+            goto done;
+        }
+        if (op->do_verbose > 2)
+            pr2serr("dxfer_buffer_in=%p, length=%d\n", dxfer_buffer_in,
+                    op->datain_len);
+        set_scsi_pt_data_in(ptvp, dxfer_buffer_in, op->datain_len);
+    }
+
+    ret = do_scsi_pt(ptvp, sg_fd, op->timeout, op->do_verbose);
+    if (ret > 0) {
+        if (SCSI_PT_DO_BAD_PARAMS == ret) {
+            pr2serr("do_scsi_pt: bad pass through setup\n");
+            ret = SG_LIB_CAT_OTHER;
+        } else if (SCSI_PT_DO_TIMEOUT == ret) {
+            pr2serr("do_scsi_pt: timeout\n");
+            ret = SG_LIB_CAT_TIMEOUT;
+        } else
+            ret = SG_LIB_CAT_OTHER;
+        goto done;
+    } else if (ret < 0) {
+        pr2serr("do_scsi_pt: %s\n", safe_strerror(-ret));
+        ret = SG_LIB_CAT_OTHER;
+        goto done;
+    }
+
+    slen = 0;
+    res_cat = get_scsi_pt_result_category(ptvp);
+    switch (res_cat) {
+    case SCSI_PT_RESULT_GOOD:
+        ret = 0;
+        break;
+    case SCSI_PT_RESULT_SENSE:
+        slen = get_scsi_pt_sense_len(ptvp);
+        ret = sg_err_category_sense(sense_buffer, slen);
+        break;
+    case SCSI_PT_RESULT_TRANSPORT_ERR:
+        get_scsi_pt_transport_err_str(ptvp, sizeof(b), b);
+        pr2serr(">>> transport error: %s\n", b);
+        ret = SG_LIB_CAT_OTHER;
+        break;
+    case SCSI_PT_RESULT_OS_ERR:
+        get_scsi_pt_os_err_str(ptvp, sizeof(b), b);
+        pr2serr(">>> os error: %s\n", b);
+        ret = SG_LIB_CAT_OTHER;
+        break;
+    default:
+        pr2serr(">>> unknown pass through result category (%d)\n", res_cat);
+        ret = SG_LIB_CAT_OTHER;
+        break;
+    }
+
+    status = get_scsi_pt_status_response(ptvp);
+    pr2serr("SCSI Status: ");
+    sg_print_scsi_status(status);
+    pr2serr("\n\n");
+    if ((SAM_STAT_CHECK_CONDITION == status) && (! op->no_sense)) {
+        if (SCSI_PT_RESULT_SENSE != res_cat)
+            slen = get_scsi_pt_sense_len(ptvp);
+        if (0 == slen)
+            pr2serr(">>> Strange: status is CHECK CONDITION but no Sense "
+                    "Information\n");
+        else {
+            pr2serr("Sense Information:\n");
+            sg_print_sense(NULL, sense_buffer, slen, (op->do_verbose > 0));
+            pr2serr("\n");
+        }
+    }
+    if (SAM_STAT_RESERVATION_CONFLICT == status)
+        ret = SG_LIB_CAT_RES_CONFLICT;
+
+    if (op->do_datain) {
+        int data_len = op->datain_len - get_scsi_pt_resid(ptvp);
+
+        if (ret && !(SG_LIB_CAT_RECOVERED == ret ||
+                     SG_LIB_CAT_NO_SENSE == ret))
+            pr2serr("Error %d occurred, no data received\n", ret);
+        else if (data_len == 0) {
+            pr2serr("No data received\n");
+        } else {
+            if (op->datain_file == NULL && !op->datain_binary) {
+                pr2serr("Received %d bytes of data:\n", data_len);
+                dStrHexErr((const char *)dxfer_buffer_in, data_len, 0);
+            } else {
+                const char * cp = "stdout";
+
+                if (op->datain_file &&
+                    ! ((1 == strlen(op->datain_file)) &&
+                       ('-' == op->datain_file[0])))
+                    cp = op->datain_file;
+                pr2serr("Writing %d bytes of data to %s\n", data_len, cp);
+                ret2 = write_dataout(op->datain_file, dxfer_buffer_in,
+                                     data_len);
+                if (0 != ret2) {
+                    if (0 == ret)
+                        ret = ret2;
+                    goto done;
+                }
+            }
+        }
+    }
+
+done:
+    if (op->do_verbose) {
+        sg_get_category_sense_str(ret, sizeof(b), b, op->do_verbose - 1);
+        pr2serr("%s\n", b);
+    }
+    if (wrkBuf)
+        free(wrkBuf);
+    if (ptvp)
+        destruct_scsi_pt_obj(ptvp);
+    if (sg_fd >= 0)
+        scsi_pt_close_device(sg_fd);
+    return ret;
+}
diff --git a/sg3_utils/src/sg_rbuf.c b/sg3_utils/src/sg_rbuf.c
new file mode 100644
index 0000000..2d201b6
--- /dev/null
+++ b/sg3_utils/src/sg_rbuf.c
@@ -0,0 +1,653 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *  Copyright (C) 1999-2016 D. Gilbert
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ * This program uses the SCSI command READ BUFFER on the given
+ * device, first to find out how big it is and then to read that
+ * buffer (data mode, buffer id 0).
+ */
+
+
+#define _XOPEN_SOURCE 500
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+#define RB_MODE_DESC 3
+#define RB_MODE_DATA 2
+#define RB_MODE_ECHO_DESC 0xb
+#define RB_MODE_ECHO_DATA 0xa
+#define RB_DESC_LEN 4
+#define RB_DEF_SIZE (200*1024*1024)
+#define RB_OPCODE 0x3C
+#define RB_CMD_LEN 10
+
+/* #define SG_DEBUG */
+
+#ifndef SG_FLAG_MMAP_IO
+#define SG_FLAG_MMAP_IO 4
+#endif
+
+
+static const char * version_str = "4.95 20160121";
+
+static struct option long_options[] = {
+        {"buffer", required_argument, 0, 'b'},
+        {"dio", no_argument, 0, 'd'},
+        {"echo", no_argument, 0, 'e'},
+        {"help", no_argument, 0, 'h'},
+        {"mmap", no_argument, 0, 'm'},
+        {"new", no_argument, 0, 'N'},
+        {"old", no_argument, 0, 'O'},
+        {"quick", no_argument, 0, 'q'},
+        {"size", required_argument, 0, 's'},
+        {"time", no_argument, 0, 't'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_buffer;
+    int do_dio;
+    int do_echo;
+    int do_help;
+    int do_mmap;
+    int do_quick;
+    int64_t do_size;
+    int do_time;
+    int do_verbose;
+    int do_version;
+    const char * device_name;
+    int opt_new;
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_rbuf [--buffer=EACH] [--dio] [--echo] "
+            "[--help] [--mmap]\n"
+            "               [--quick] [--size=OVERALL] [--time] [--verbose] "
+            "[--version]\n"
+            "               DEVICE\n");
+    pr2serr("  where:\n"
+            "    --buffer=EACH|-b EACH    buffer size to use (in bytes)\n"
+            "    --dio|-d        requests dio ('-q' overrides it)\n"
+            "    --echo|-e       use echo buffer (def: use data mode)\n"
+            "    --help|-h       print usage message then exit\n"
+            "    --mmap|-m       requests mmap-ed IO (overrides -q, -d)\n"
+            "    --quick|-q      quick, don't xfer to user space\n");
+    pr2serr("    --size=OVERALL|-s OVERALL    total size to read (in bytes)\n"
+            "                    default: 200 MiB\n"
+            "    --time|-t       time the data transfer\n"
+            "    --verbose|-v    increase verbosity (more debug)\n"
+            "    --version|-V    print version string then exit\n\n"
+            "Use SCSI READ BUFFER command (data or echo buffer mode, buffer "
+            "id 0)\nrepeatedly\n");
+}
+
+static void
+usage_old()
+{
+    printf("Usage: sg_rbuf [-b=EACH_KIB] [-d] [-m] [-q] [-s=OVERALL_MIB] "
+           "[-t] [-v] [-V]\n               DEVICE\n");
+    printf("  where:\n");
+    printf("    -b=EACH_KIB    num is buffer size to use (in KiB)\n");
+    printf("    -d       requests dio ('-q' overrides it)\n");
+    printf("    -e       use echo buffer (def: use data mode)\n");
+    printf("    -m       requests mmap-ed IO (overrides -q, -d)\n");
+    printf("    -q       quick, don't xfer to user space\n");
+    printf("    -s=OVERALL_MIB    num is total size to read (in MiB) "
+           "(default: 200 MiB)\n");
+    printf("             maximum total size is 4000 MiB\n");
+    printf("    -t       time the data transfer\n");
+    printf("    -v       increase verbosity (more debug)\n");
+    printf("    -V       print version string then exit\n\n");
+    printf("Use SCSI READ BUFFER command (data or echo buffer mode, buffer "
+           "id 0)\nrepeatedly\n");
+}
+
+static void
+usage_for(const struct opts_t * optsp)
+{
+    if (optsp->opt_new)
+        usage();
+    else
+        usage_old();
+}
+
+static int
+process_cl_new(struct opts_t * optsp, int argc, char * argv[])
+{
+    int c, n;
+    int64_t nn;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "b:dehmNOqs:tvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            n = sg_get_num(optarg);
+            if (n < 0) {
+                pr2serr("bad argument to '--buffer'\n");
+                usage_for(optsp);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            optsp->do_buffer = n;
+            break;
+        case 'd':
+            ++optsp->do_dio;
+            break;
+        case 'e':
+            ++optsp->do_echo;
+            break;
+        case 'h':
+        case '?':
+            ++optsp->do_help;
+            break;
+        case 'm':
+            ++optsp->do_mmap;
+            break;
+        case 'N':
+            break;      /* ignore */
+        case 'O':
+            optsp->opt_new = 0;
+            return 0;
+        case 'q':
+            ++optsp->do_quick;
+            break;
+        case 's':
+           nn = sg_get_llnum(optarg);
+           if (nn < 0) {
+                pr2serr("bad argument to '--size'\n");
+                usage_for(optsp);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            optsp->do_size = nn;
+            break;
+        case 't':
+            ++optsp->do_time;
+            break;
+        case 'v':
+            ++optsp->do_verbose;
+            break;
+        case 'V':
+            ++optsp->do_version;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (optsp->do_help)
+                break;
+            usage_for(optsp);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == optsp->device_name) {
+            optsp->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage_for(optsp);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int
+process_cl_old(struct opts_t * optsp, int argc, char * argv[])
+{
+    int k, jmp_out, plen, num;
+    int64_t nn;
+    const char * cp;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case 'd':
+                    ++optsp->do_dio;
+                    break;
+                case 'e':
+                    ++optsp->do_echo;
+                    break;
+                case 'h':
+                case '?':
+                    ++optsp->do_help;
+                    break;
+                case 'm':
+                    ++optsp->do_mmap;
+                    break;
+                case 'N':
+                    optsp->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case 'q':
+                    ++optsp->do_quick;
+                    break;
+                case 't':
+                    ++optsp->do_time;
+                    break;
+                case 'v':
+                    ++optsp->do_verbose;
+                    break;
+                case 'V':
+                    ++optsp->do_version;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            if (0 == strncmp("b=", cp, 2)) {
+                num = sscanf(cp + 2, "%d", &optsp->do_buffer);
+                if ((1 != num) || (optsp->do_buffer <= 0)) {
+                    printf("Couldn't decode number after 'b=' option\n");
+                    usage_for(optsp);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                optsp->do_buffer *= 1024;
+            }
+            else if (0 == strncmp("s=", cp, 2)) {
+                nn = sg_get_llnum(optarg);
+                if (nn < 0) {
+                    printf("Couldn't decode number after 's=' option\n");
+                    usage_for(optsp);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                optsp->do_size = nn;
+                optsp->do_size *= 1024 * 1024;
+            } else if (0 == strncmp("-old", cp, 4))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage_for(optsp);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == optsp->device_name)
+            optsp->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not expecting: %s\n",
+                    optsp->device_name, cp);
+            usage_for(optsp);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int
+process_cl(struct opts_t * optsp, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        optsp->opt_new = 0;
+        res = process_cl_old(optsp, argc, argv);
+        if ((0 == res) && optsp->opt_new)
+            res = process_cl_new(optsp, argc, argv);
+    } else {
+        optsp->opt_new = 1;
+        res = process_cl_new(optsp, argc, argv);
+        if ((0 == res) && (0 == optsp->opt_new))
+            res = process_cl_old(optsp, argc, argv);
+    }
+    return res;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, j;
+    unsigned int k, num;
+    unsigned char rbCmdBlk [RB_CMD_LEN];
+    unsigned char * rbBuff = NULL;
+    void * rawp = NULL;
+    unsigned char sense_buffer[32];
+    int buf_capacity = 0;
+    int buf_size = 0;
+    int64_t total_size = RB_DEF_SIZE;
+    size_t psz;
+    int dio_incomplete = 0;
+    struct sg_io_hdr io_hdr;
+    struct timeval start_tm, end_tm;
+#ifdef SG_DEBUG
+    int clear = 1;
+#endif
+    struct opts_t opts;
+    struct opts_t * op;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+    psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
+#else
+    psz = 4096;     /* give up, pick likely figure */
+#endif
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    res = process_cl(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        usage_for(op);
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+
+    if (NULL == op->device_name) {
+        pr2serr("No DEVICE argument given\n");
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (op->do_buffer > 0)
+        buf_size = op->do_buffer;
+    if (op->do_size > 0)
+        total_size = op->do_size;
+
+    sg_fd = open(op->device_name, O_RDONLY | O_NONBLOCK);
+    if (sg_fd < 0) {
+        perror("device open error");
+        return SG_LIB_FILE_ERROR;
+    }
+    if (op->do_mmap) {
+        op->do_dio = 0;
+        op->do_quick = 0;
+    }
+    if (NULL == (rawp = malloc(512))) {
+        printf("out of memory (query)\n");
+        return SG_LIB_CAT_OTHER;
+    }
+    rbBuff = (unsigned char *)rawp;
+
+    memset(rbCmdBlk, 0, RB_CMD_LEN);
+    rbCmdBlk[0] = RB_OPCODE;
+    rbCmdBlk[1] = op->do_echo ? RB_MODE_ECHO_DESC : RB_MODE_DESC;
+    rbCmdBlk[8] = RB_DESC_LEN;
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(rbCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = RB_DESC_LEN;
+    io_hdr.dxferp = rbBuff;
+    io_hdr.cmdp = rbCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 60000;     /* 60000 millisecs == 60 seconds */
+    if (op->do_verbose) {
+        pr2serr("    Read buffer (%sdescriptor) cdb: ",
+                (op->do_echo ? "echo " : ""));
+        for (k = 0; k < RB_CMD_LEN; ++k)
+            pr2serr("%02x ", rbCmdBlk[k]);
+        pr2serr("\n");
+    }
+
+    /* do normal IO to find RB size (not dio or mmap-ed at this stage) */
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        perror("SG_IO READ BUFFER descriptor error");
+        if (rawp) free(rawp);
+        return SG_LIB_CAT_OTHER;
+    }
+
+    if (op->do_verbose > 2)
+        pr2serr("      duration=%u ms\n", io_hdr.duration);
+    /* now for the error processing */
+    res = sg_err_category3(&io_hdr);
+    switch (res) {
+    case SG_LIB_CAT_RECOVERED:
+        sg_chk_n_print3("READ BUFFER descriptor, continuing", &io_hdr,
+                        op->do_verbose > 1);
+        /* fall through */
+    case SG_LIB_CAT_CLEAN:
+        break;
+    default: /* won't bother decoding other categories */
+        sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr,
+                        op->do_verbose > 1);
+        if (rawp) free(rawp);
+        return (res >= 0) ? res : SG_LIB_CAT_OTHER;
+    }
+
+    if (op->do_echo) {
+        buf_capacity = 0x1fff & sg_get_unaligned_be16(rbBuff + 2);
+        printf("READ BUFFER reports: echo buffer capacity=%d\n",
+               buf_capacity);
+    } else {
+        buf_capacity = sg_get_unaligned_be24(rbBuff + 1);
+        printf("READ BUFFER reports: buffer capacity=%d, offset "
+               "boundary=%d\n", buf_capacity, (int)rbBuff[0]);
+    }
+
+    if (0 == buf_size)
+        buf_size = buf_capacity;
+    else if (buf_size > buf_capacity) {
+        printf("Requested buffer size=%d exceeds reported capacity=%d\n",
+               buf_size, buf_capacity);
+        if (rawp) free(rawp);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    if (rawp) {
+        free(rawp);
+        rawp = NULL;
+    }
+
+    if (! op->do_dio) {
+        k = buf_size;
+        if (op->do_mmap && (0 != (k % psz)))
+            k = ((k / psz) + 1) * psz;  /* round up to page size */
+        res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, &k);
+        if (res < 0)
+            perror("SG_SET_RESERVED_SIZE error");
+    }
+
+    if (op->do_mmap) {
+        rbBuff = (unsigned char *)mmap(NULL, buf_size, PROT_READ, MAP_SHARED,
+                                       sg_fd, 0);
+        if (MAP_FAILED == rbBuff) {
+            if (ENOMEM == errno) {
+                pr2serr("mmap() out of memory, try a smaller buffer size "
+                        "than %d bytes\n", buf_size);
+                if (op->opt_new)
+                    pr2serr("    [with '--buffer=EACH' where EACH is in "
+                            "bytes]\n");
+                else
+                    pr2serr("    [with '-b=EACH' where EACH is in KiB]\n");
+            } else
+                perror("error using mmap()");
+            return SG_LIB_CAT_OTHER;
+        }
+    }
+    else { /* non mmap-ed IO */
+        rawp = (unsigned char *)malloc(buf_size + (op->do_dio ? psz : 0));
+        if (NULL == rawp) {
+            printf("out of memory (data)\n");
+            return SG_LIB_CAT_OTHER;
+        }
+        /* perhaps use posix_memalign() instead */
+        if (op->do_dio)    /* align to page boundary */
+            rbBuff= (unsigned char *)(((uintptr_t)rawp + psz - 1) &
+                                      (~(psz - 1)));
+        else
+            rbBuff = (unsigned char *)rawp;
+    }
+
+    num = total_size / buf_size;
+    if (op->do_time) {
+        start_tm.tv_sec = 0;
+        start_tm.tv_usec = 0;
+        gettimeofday(&start_tm, NULL);
+    }
+    /* main data reading loop */
+    for (k = 0; k < num; ++k) {
+        memset(rbCmdBlk, 0, RB_CMD_LEN);
+        rbCmdBlk[0] = RB_OPCODE;
+        rbCmdBlk[1] = op->do_echo ? RB_MODE_ECHO_DATA : RB_MODE_DATA;
+        sg_put_unaligned_be24((uint32_t)buf_size, rbCmdBlk + 6);
+#ifdef SG_DEBUG
+        memset(rbBuff, 0, buf_size);
+#endif
+
+        memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof(rbCmdBlk);
+        io_hdr.mx_sb_len = sizeof(sense_buffer);
+        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        io_hdr.dxfer_len = buf_size;
+        if (! op->do_mmap)
+            io_hdr.dxferp = rbBuff;
+        io_hdr.cmdp = rbCmdBlk;
+        io_hdr.sbp = sense_buffer;
+        io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+        io_hdr.pack_id = k;
+        if (op->do_mmap)
+            io_hdr.flags |= SG_FLAG_MMAP_IO;
+        else if (op->do_dio)
+            io_hdr.flags |= SG_FLAG_DIRECT_IO;
+        else if (op->do_quick)
+            io_hdr.flags |= SG_FLAG_NO_DXFER;
+        if (op->do_verbose > 1) {
+            pr2serr("    Read buffer (%sdata) cdb: ",
+                    (op->do_echo ? "echo " : ""));
+            for (j = 0; j < RB_CMD_LEN; ++j)
+                pr2serr("%02x ", rbCmdBlk[j]);
+            pr2serr("\n");
+        }
+
+        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+            if (ENOMEM == errno) {
+                pr2serr("SG_IO data: out of memory, try a smaller buffer "
+                        "size than %d bytes\n", buf_size);
+                if (op->opt_new)
+                    pr2serr("    [with '--buffer=EACH' where EACH is in "
+                            "bytes]\n");
+                else
+                    pr2serr("    [with '-b=EACH' where EACH is in KiB]\n");
+            } else
+                perror("SG_IO READ BUFFER data error");
+            if (rawp) free(rawp);
+            return SG_LIB_CAT_OTHER;
+        }
+
+        if (op->do_verbose > 2)
+            pr2serr("      duration=%u ms\n", io_hdr.duration);
+        /* now for the error processing */
+        res = sg_err_category3(&io_hdr);
+        switch (res) {
+        case SG_LIB_CAT_CLEAN:
+            break;
+        case SG_LIB_CAT_RECOVERED:
+            sg_chk_n_print3("READ BUFFER data, continuing", &io_hdr,
+                            op->do_verbose > 1);
+            break;
+        default: /* won't bother decoding other categories */
+            sg_chk_n_print3("READ BUFFER data error", &io_hdr,
+                            op->do_verbose > 1);
+            if (rawp) free(rawp);
+            return (res >= 0) ? res : SG_LIB_CAT_OTHER;
+        }
+        if (op->do_dio &&
+            ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+            dio_incomplete = 1;    /* flag that dio not done (completely) */
+
+#ifdef SG_DEBUG
+        if (clear) {
+            for (j = 0; j < buf_size; ++j) {
+                if (rbBuff[j] != 0) {
+                    clear = 0;
+                    break;
+                }
+            }
+        }
+#endif
+    }
+    if ((op->do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
+        struct timeval res_tm;
+        double a, b;
+
+        gettimeofday(&end_tm, NULL);
+        res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+        res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+        if (res_tm.tv_usec < 0) {
+            --res_tm.tv_sec;
+            res_tm.tv_usec += 1000000;
+        }
+        a = res_tm.tv_sec;
+        a += (0.000001 * res_tm.tv_usec);
+        b = (double)buf_size * num;
+        printf("time to read data from buffer was %d.%06d secs",
+               (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+        if (a > 0.00001) {
+            if (b > 511)
+                printf(", %.2f MB/sec", b / (a * 1000000.0));
+            printf(", %.2f IOPS", num / a);
+        }
+        printf("\n");
+    }
+    if (dio_incomplete)
+        printf(">> direct IO requested but not done\n");
+    printf("Read %" PRId64 " MiB (actual: %" PRId64 " bytes), buffer "
+           "size=%d KiB (%d bytes)\n", (total_size / (1024 * 1024)),
+           (int64_t)num * buf_size, buf_size / 1024, buf_size);
+
+    if (rawp) free(rawp);
+    res = close(sg_fd);
+    if (res < 0) {
+        perror("close error");
+        return SG_LIB_FILE_ERROR;
+    }
+#ifdef SG_DEBUG
+    if (clear)
+        printf("read buffer always zero\n");
+    else
+        printf("read buffer non-zero\n");
+#endif
+    return (res >= 0) ? res : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_rdac.c b/sg3_utils/src/sg_rdac.c
new file mode 100644
index 0000000..f53cc3d
--- /dev/null
+++ b/sg3_utils/src/sg_rdac.c
@@ -0,0 +1,483 @@
+/*
+ * sg_rdac
+ *
+ * Retrieve / set RDAC options.
+ *
+ * Copyright (C) 2006-2015 Hannes Reinecke <hare@suse.de>
+ *
+ * Based on sg_modes.c and sg_emc_trespass.c; credits from there apply.
+ *
+ *  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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "1.10 20151219";
+
+unsigned char mode6_hdr[] = {
+    0x75, /* Length */
+    0, /* medium */
+    0, /* params */
+    8, /* Block descriptor length */
+};
+
+unsigned char mode10_hdr[] = {
+    0x01, 0x18, /* Length */
+    0, /* medium */
+    0, /* params */
+    0, 0, /* reserved */
+    0, 0, /* block descriptor length */
+};
+
+unsigned char block_descriptor[] = {
+    0, /* Density code */
+    0, 0, 0, /* Number of blocks */
+    0, /* Reserved */
+    0, 0x02, 0, /* 512 byte blocks */
+};
+
+struct rdac_page_common {
+    unsigned char  current_serial[16];
+    unsigned char  alternate_serial[16];
+    unsigned char  current_mode_msb;
+    unsigned char  current_mode_lsb;
+    unsigned char  alternate_mode_msb;
+    unsigned char  alternate_mode_lsb;
+    unsigned char  quiescence;
+    unsigned char  options;
+};
+
+struct rdac_legacy_page {
+    unsigned char  page_code;
+    unsigned char  page_length;
+    struct rdac_page_common attr;
+    unsigned char  lun_table[32];
+    unsigned char  lun_table_exp[32];
+    unsigned short reserved;
+};
+
+struct rdac_expanded_page {
+    unsigned char  page_code;
+    unsigned char  subpage_code;
+    unsigned char  page_length[2];
+    struct rdac_page_common attr;
+    unsigned char  lun_table[256];
+    unsigned char  reserved[2];
+};
+
+static int do_verbose = 0;
+
+static void dump_mode_page( unsigned char *page, int len )
+{
+        int i, k;
+
+        for (k = 0; k < len; k += 16) {
+
+                printf("%x:",k / 16);
+                for (i = 0; i < 16; i++) {
+                        printf(" %02x", page[k + i]);
+                        if (k + i >= len) {
+                                printf("\n");
+                                break;
+                        }
+                }
+                printf("\n");
+        }
+
+}
+
+#define MX_ALLOC_LEN (1024 * 4)
+#define RDAC_CONTROLLER_PAGE 0x2c
+#define RDAC_CONTROLLER_PAGE_LEN 0x68
+#define LEGACY_PAGE 0x00
+#define EXPANDED_LUN_SPACE_PAGE 0x01
+#define EXPANDED_LUN_SPACE_PAGE_LEN 0x128
+#define RDAC_FAIL_ALL_PATHS 0x1
+#define RDAC_FAIL_SELECTED_PATHS 0x2
+#define RDAC_FORCE_QUIESCENCE 0x2
+#define RDAC_QUIESCENCE_TIME 10
+
+static int fail_all_paths(int fd, int use_6_byte)
+{
+        unsigned char fail_paths_pg[308];
+        struct rdac_legacy_page *rdac_page;
+        struct rdac_expanded_page *rdac_page_exp;
+        struct rdac_page_common *rdac_common = NULL;
+
+        int res;
+        char b[80];
+
+        memset(fail_paths_pg, 0, 308);
+        if (use_6_byte) {
+                memcpy(fail_paths_pg, mode6_hdr, 4);
+                memcpy(fail_paths_pg + 4, block_descriptor, 8);
+                rdac_page = (struct rdac_legacy_page *)(fail_paths_pg + 4 + 8);
+                rdac_page->page_code = RDAC_CONTROLLER_PAGE;
+                rdac_page->page_length = RDAC_CONTROLLER_PAGE_LEN;
+                rdac_common = &rdac_page->attr;
+        } else {
+                memcpy(fail_paths_pg, mode10_hdr, 8);
+                rdac_page_exp = (struct rdac_expanded_page *)
+                                (fail_paths_pg + 8);
+                rdac_page_exp->page_code = RDAC_CONTROLLER_PAGE | 0x40;
+                rdac_page_exp->subpage_code = 0x1;
+                sg_put_unaligned_be16(EXPANDED_LUN_SPACE_PAGE_LEN,
+                                      rdac_page_exp->page_length + 0);
+                rdac_common = &rdac_page_exp->attr;
+        }
+
+        rdac_common->current_mode_lsb =  RDAC_FAIL_ALL_PATHS;
+        rdac_common->quiescence = RDAC_QUIESCENCE_TIME;
+        rdac_common->options = RDAC_FORCE_QUIESCENCE;
+
+        if (use_6_byte) {
+                res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
+                                        fail_paths_pg, 118,
+                                        1, (do_verbose ? 2 : 0));
+        } else {
+                res = sg_ll_mode_select10(fd, 1 /* pf */, 0 /* sp */,
+                                        fail_paths_pg, 308,
+                                        1, (do_verbose ? 2: 0));
+        }
+
+        switch (res) {
+        case 0:
+                if (do_verbose)
+                        pr2serr("fail paths successful\n");
+                break;
+        default:
+                sg_get_category_sense_str(res, sizeof(b), b, do_verbose);
+                pr2serr("fail paths failed: %s\n", b);
+                break;
+        }
+
+        return res;
+}
+
+static int fail_this_path(int fd, int lun, int use_6_byte)
+{
+        unsigned char fail_paths_pg[308];
+        struct rdac_legacy_page *rdac_page;
+        struct rdac_expanded_page *rdac_page_exp;
+        struct rdac_page_common *rdac_common = NULL;
+        int res;
+        char b[80];
+
+        if (use_6_byte && lun > 32) {
+                pr2serr("must use 10 byte cdb to fail luns over 32\n");
+                return -1;
+        }
+
+        memset(fail_paths_pg, 0, 308);
+        if (use_6_byte) {
+                memcpy(fail_paths_pg, mode6_hdr, 4);
+                memcpy(fail_paths_pg + 4, block_descriptor, 8);
+                rdac_page = (struct rdac_legacy_page *)(fail_paths_pg + 4 + 8);
+                rdac_page->page_code = RDAC_CONTROLLER_PAGE;
+                rdac_page->page_length = RDAC_CONTROLLER_PAGE_LEN;
+                rdac_common = &rdac_page->attr;
+                memset(rdac_page->lun_table, 0x0, 32);
+                rdac_page->lun_table[lun] = 0x81;
+        } else {
+                memcpy(fail_paths_pg, mode10_hdr, 8);
+                rdac_page_exp = (struct rdac_expanded_page *)
+                                (fail_paths_pg + 8);
+                rdac_page_exp->page_code = RDAC_CONTROLLER_PAGE | 0x40;
+                rdac_page_exp->subpage_code = 0x1;
+                sg_put_unaligned_be16(EXPANDED_LUN_SPACE_PAGE_LEN,
+                                      rdac_page_exp->page_length + 0);
+                rdac_common = &rdac_page_exp->attr;
+                memset(rdac_page_exp->lun_table, 0x0, 256);
+                rdac_page_exp->lun_table[lun] = 0x81;
+        }
+
+        rdac_common->current_mode_lsb =  RDAC_FAIL_SELECTED_PATHS;
+        rdac_common->quiescence = RDAC_QUIESCENCE_TIME;
+        rdac_common->options = RDAC_FORCE_QUIESCENCE;
+
+        if (use_6_byte) {
+                res = sg_ll_mode_select6(fd, 1 /* pf */, 0 /* sp */,
+                                        fail_paths_pg, 118,
+                                        1, (do_verbose ? 2 : 0));
+        } else {
+                res = sg_ll_mode_select10(fd, 1 /* pf */, 0 /* sp */,
+                                        fail_paths_pg, 308,
+                                        1, (do_verbose ? 2: 0));
+        }
+
+        switch (res) {
+        case 0:
+                if (do_verbose)
+                        pr2serr("fail paths successful\n");
+                break;
+        default:
+                sg_get_category_sense_str(res, sizeof(b), b, do_verbose);
+                pr2serr("fail paths page (lun=%d) failed: %s\n", lun, b);
+                break;
+        }
+
+        return res;
+}
+
+static void print_rdac_mode( unsigned char *ptr, int subpg)
+{
+        struct rdac_legacy_page *legacy;
+        struct rdac_expanded_page *expanded;
+        struct rdac_page_common *rdac_ptr = NULL;
+        unsigned char * lun_table = NULL;
+        int i, k, bd_len, lun_table_len;
+
+        if (subpg == 1) {
+                bd_len = ptr[7];
+                expanded = (struct rdac_expanded_page *)(ptr + 8 + bd_len);
+                rdac_ptr = &expanded->attr;
+                lun_table = expanded->lun_table;
+                lun_table_len = 256;
+        } else {
+                bd_len = ptr[3];
+                legacy = (struct rdac_legacy_page *)(ptr + 4 + bd_len);
+                rdac_ptr = &legacy->attr;
+                lun_table = legacy->lun_table;
+                lun_table_len = 32;
+        }
+
+        printf("RDAC %s page\n", (subpg == 1) ? "Expanded" : "Legacy");
+        printf("  Controller serial: %s\n",
+               rdac_ptr->current_serial);
+        printf("  Alternate controller serial: %s\n",
+               rdac_ptr->alternate_serial);
+        printf("  RDAC mode (redundant processor): ");
+        switch (rdac_ptr->current_mode_msb) {
+        case 0x00:
+                printf("alternate controller not present; ");
+                break;
+        case 0x01:
+                printf("alternate controller present; ");
+                break;
+        default:
+                printf("(Unknown controller status 0x%x); ",
+                       rdac_ptr->current_mode_msb);
+                break;
+        }
+        switch (rdac_ptr->current_mode_lsb) {
+        case 0x0:
+                printf("inactive\n");
+                break;
+        case 0x1:
+                printf("active\n");
+                break;
+        case 0x2:
+                printf("Dual active mode\n");
+                break;
+        default:
+                printf("(Unknown mode 0x%x)\n",
+                       rdac_ptr->current_mode_lsb);
+        }
+
+        printf("  RDAC mode (alternate processor): ");
+        switch (rdac_ptr->alternate_mode_msb) {
+        case 0x00:
+                printf("alternate controller not present; ");
+                break;
+        case 0x01:
+                printf("alternate controller present; ");
+                break;
+        default:
+                printf("(Unknown status 0x%x); ",
+                       rdac_ptr->alternate_mode_msb);
+                break;
+        }
+        switch (rdac_ptr->alternate_mode_lsb) {
+        case 0x0:
+                printf("inactive\n");
+                break;
+        case 0x1:
+                printf("active\n");
+                break;
+        case 0x2:
+                printf("Dual active mode\n");
+                break;
+        case 0x3:
+                printf("Not present\n");
+                break;
+        case 0x4:
+                printf("held in reset\n");
+                break;
+        default:
+                printf("(Unknown mode 0x%x)\n",
+                       rdac_ptr->alternate_mode_lsb);
+        }
+        printf("  Quiescence timeout: %d\n", rdac_ptr->quiescence);
+        printf("  RDAC option 0x%x\n", rdac_ptr->options);
+        printf("    ALUA: %s\n", (rdac_ptr->options & 0x4 ? "Enabled" :
+                                                            "Disabled" ));
+        printf("    Force Quiescence: %s\n", (rdac_ptr->options & 0x2 ?
+                                              "Enabled" : "Disabled" ));
+        printf ("  LUN Table: (p = preferred, a = alternate, u = utm lun)\n");
+        printf("         0 1 2 3 4 5 6 7  8 9 a b c d e f\n");
+        for (k = 0; k < lun_table_len; k += 16) {
+                printf("    0x%x:",k / 16);
+                for (i = 0; i < 16; i++) {
+                        switch (lun_table[k + i]) {
+                        case 0x0:
+                                printf(" x");
+                                break;
+                        case 0x1:
+                                printf(" p");
+                                break;
+                        case 0x2:
+                                printf(" a");
+                                break;
+                        case 0x3:
+                                printf(" u");
+                                break;
+                        default:
+                                printf(" ?");
+                                break;
+                        }
+                        if (i == 7) {
+                                printf(" ");
+                        }
+                }
+                printf("\n");
+        }
+}
+
+static void usage()
+{
+    printf("Usage:  sg_rdac [-6] [-a] [-f=LUN] [-v] [-V] DEVICE\n"
+           "  where:\n"
+           "    -6        use 6 byte cdbs for mode sense/select\n"
+           "    -a        transfer all devices to the controller\n"
+           "              serving DEVICE.\n"
+           "    -f=LUN    transfer the device at LUN to the\n"
+           "              controller serving DEVICE\n"
+           "    -v        verbose\n"
+           "    -V        print version then exit\n\n"
+           " Display/Modify RDAC Redundant Controller Page 0x2c.\n"
+           " If [-a] or [-f] is not specified the current settings"
+           " are displayed.\n");
+}
+
+int main(int argc, char * argv[])
+{
+        unsigned char rsp_buff[MX_ALLOC_LEN];
+        char **argptr;
+        char * file_name = 0;
+        int res, fd, k, lun = -1;
+        int fail_all = 0;
+        int fail_path = 0;
+        int ret = 0;
+        int use_6_byte = 0;
+
+        if (argc < 2) {
+                usage ();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+
+        for (k = 1; k < argc; ++k) {
+                argptr = argv + k;
+                if (!strcmp (*argptr, "-v"))
+                        ++do_verbose;
+                else if (!strncmp(*argptr, "-f=",3)) {
+                        ++fail_path;
+                        lun = strtoul(*argptr + 3, NULL, 0);
+                }
+                else if (!strcmp(*argptr, "-a")) {
+                        ++fail_all;
+                }
+                else if (!strcmp(*argptr, "-6")) {
+                        use_6_byte = 1;
+                }
+                else if (!strcmp(*argptr, "-V")) {
+                        pr2serr("sg_rdac version: %s\n", version_str);
+                        return 0;
+                }
+                else if (*argv[k] == '-') {
+                        pr2serr("Unrecognized switch: %s\n", argv[k]);
+                        file_name = 0;
+                        break;
+                }
+                else if (0 == file_name)
+                        file_name = argv[k];
+                else {
+                        pr2serr("too many arguments\n");
+                        file_name = 0;
+                        break;
+                }
+        }
+        if (0 == file_name) {
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+
+        fd = sg_cmds_open_device(file_name, 0 /* rw */, do_verbose);
+        if (fd < 0) {
+                pr2serr("open error: %s: %s\n", file_name, safe_strerror(-fd));
+                usage();
+                return SG_LIB_FILE_ERROR;
+        }
+
+        if (fail_all) {
+                res = fail_all_paths(fd, use_6_byte);
+        } else if (fail_path) {
+                res = fail_this_path(fd, lun, use_6_byte);
+        } else {
+                if (use_6_byte)
+                        res = sg_ll_mode_sense6(fd, /* DBD */ 0, /* PC */0,
+                                                0x2c, 0, rsp_buff, 252,
+                                                1, do_verbose);
+                else
+                        res = sg_ll_mode_sense10(fd, /* llbaa */ 0,
+                                                 /* DBD */ 0,
+                                                 /* page control */0,
+                                                 0x2c, 0x1, rsp_buff, 308,
+                                                 1, do_verbose);
+
+                if (!res) {
+                        if (do_verbose)
+                                dump_mode_page(rsp_buff, rsp_buff[0]);
+                        print_rdac_mode(rsp_buff, !use_6_byte);
+                } else {
+                        if (SG_LIB_CAT_INVALID_OP == res)
+                                pr2serr(">>>>>> try again without the '-6' "
+                                        "switch for a 10 byte MODE SENSE "
+                                        "command\n");
+                        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+                                pr2serr("mode sense: invalid field in cdb "
+                                        "(perhaps subpages or page control "
+                                        "(PC) not supported)\n");
+                        else {
+                                char b[80];
+
+                                sg_get_category_sense_str(res, sizeof(b), b,
+                                                          do_verbose);
+                                pr2serr("mode sense failed: %s\n", b);
+                        }
+                }
+        }
+        ret = res;
+
+        res = sg_cmds_close_device(fd);
+        if (res < 0) {
+                pr2serr("close error: %s\n", safe_strerror(-res));
+                if (0 == ret)
+                        return SG_LIB_FILE_ERROR;
+        }
+        return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_read.c b/sg3_utils/src/sg_read.c
new file mode 100644
index 0000000..7b0b989
--- /dev/null
+++ b/sg3_utils/src/sg_read.c
@@ -0,0 +1,838 @@
+/* A utility program for the Linux OS SCSI generic ("sg") device driver.
+*  Copyright (C) 2001 - 2016 D. Gilbert
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This program reads data from the given SCSI device (typically a disk
+   or cdrom) and discards that data. Its primary goal is to time
+   multiple reads all starting from the same logical address. Its interface
+   is a subset of another member of this package: sg_dd which is a
+   "dd" variant. The input file can be a scsi generic device, a block device,
+   a raw device or a seekable file. Streams such as stdin are not acceptable.
+   The block size ('bs') is assumed to be 512 if not given.
+
+   This version should compile with Linux sg drivers with version numbers
+   >= 30000 . For mmap-ed IO the sg version number >= 30122 .
+
+*/
+
+#define _XOPEN_SOURCE 500
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <linux/major.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "1.25 20160121";
+
+#define DEF_BLOCK_SIZE 512
+#define DEF_BLOCKS_PER_TRANSFER 128
+#define DEF_SCSI_CDBSZ 10
+#define MAX_SCSI_CDBSZ 16
+
+#define ME "sg_read: "
+
+#ifndef SG_FLAG_MMAP_IO
+#define SG_FLAG_MMAP_IO 4
+#endif
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_TIMEOUT 40000       /* 40,000 millisecs == 40 seconds */
+
+#ifndef RAW_MAJOR
+#define RAW_MAJOR 255   /*unlikey value */
+#endif
+
+#define FT_OTHER 1              /* filetype other than sg or raw device */
+#define FT_SG 2                 /* filetype is sg char device */
+#define FT_RAW 4                /* filetype is raw char device */
+#define FT_BLOCK 8              /* filetype is block device */
+#define FT_ERROR 64             /* couldn't "stat" file */
+
+#define MIN_RESERVED_SIZE 8192
+
+static int sum_of_resids = 0;
+
+static int64_t dd_count = -1;
+static int64_t orig_count = 0;
+static int64_t in_full = 0;
+static int in_partial = 0;
+
+static int pack_id_count = 0;
+static int verbose = 0;
+
+static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
+
+static void install_handler (int sig_num, void (*sig_handler) (int sig))
+{
+    struct sigaction sigact;
+
+    sigaction (sig_num, NULL, &sigact);
+    if (sigact.sa_handler != SIG_IGN) {
+        sigact.sa_handler = sig_handler;
+        sigemptyset (&sigact.sa_mask);
+        sigact.sa_flags = 0;
+        sigaction (sig_num, &sigact, NULL);
+    }
+}
+
+static void print_stats(int iters, const char * str)
+{
+    if (orig_count > 0) {
+        if (0 != dd_count)
+            pr2serr("  remaining block count=%" PRId64 "\n", dd_count);
+        pr2serr("%" PRId64 "+%d records in", in_full - in_partial,
+                in_partial);
+        if (iters > 0)
+            pr2serr(", %s commands issued: %d\n", (str ? str : ""), iters);
+        else
+            pr2serr("\n");
+    } else if (iters > 0)
+        pr2serr("%s commands issued: %d\n", (str ? str : ""), iters);
+}
+
+static void interrupt_handler(int sig)
+{
+    struct sigaction sigact;
+
+    sigact.sa_handler = SIG_DFL;
+    sigemptyset (&sigact.sa_mask);
+    sigact.sa_flags = 0;
+    sigaction (sig, &sigact, NULL);
+    pr2serr("Interrupted by signal,");
+    print_stats(0, NULL);
+    kill (getpid (), sig);
+}
+
+static void siginfo_handler(int sig)
+{
+    if (sig) { ; }      /* unused, dummy to suppress warning */
+    pr2serr("Progress report, continuing ...\n");
+    print_stats(0, NULL);
+}
+
+static int dd_filetype(const char * filename)
+{
+    struct stat st;
+
+    if (stat(filename, &st) < 0)
+        return FT_ERROR;
+    if (S_ISCHR(st.st_mode)) {
+        if (RAW_MAJOR == major(st.st_rdev))
+            return FT_RAW;
+        else if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
+            return FT_SG;
+    } else if (S_ISBLK(st.st_mode))
+        return FT_BLOCK;
+    return FT_OTHER;
+}
+
+static void usage()
+{
+    pr2serr("Usage: sg_read  [blk_sgio=0|1] [bpt=BPT] [bs=BS] "
+            "[cdbsz=6|10|12|16]\n"
+            "                count=COUNT [dio=0|1] [dpo=0|1] [fua=0|1] "
+            "if=IFILE\n"
+            "                [mmap=0|1] [no_dfxer=0|1] [odir=0|1] "
+            "[skip=SKIP]\n"
+            "                [time=TI] [verbose=VERB] [--help] "
+            "[--version]\n"
+            "  where:\n"
+            "    blk_sgio 0->normal IO for block devices, 1->SCSI commands "
+            "via SG_IO\n"
+            "    bpt      is blocks_per_transfer (default is 128, or 64 KiB "
+            "for default BS)\n"
+            "             setting 'bpt=0' will do COUNT zero block SCSI "
+            "READs\n"
+            "    bs       must match sector size if IFILE accessed via SCSI "
+            "commands\n"
+            "             (def=512)\n"
+            "    cdbsz    size of SCSI READ command (default is 10)\n"
+            "    count    total bytes read will be BS*COUNT (if no "
+            "error)\n"
+            "             (if negative, do |COUNT| zero block SCSI READs)\n"
+            "    dio      1-> attempt direct IO on sg device, 0->indirect IO "
+            "(def)\n");
+    pr2serr("    dpo      1-> set disable page out (DPO) in SCSI READs\n"
+            "    fua      1-> set force unit access (FUA) in SCSI READs\n"
+            "    if       an sg, block or raw device, or a seekable file (not "
+            "stdin)\n"
+            "    mmap     1->perform mmaped IO on sg device, 0->indirect IO "
+            "(def)\n"
+            "    no_dxfer 1->DMA to kernel buffers only, not user space, "
+            "0->normal(def)\n"
+            "    odir     1->open block device O_DIRECT, 0->don't (def)\n"
+            "    skip     each transfer starts at this logical address "
+            "(def=0)\n"
+            "    time     0->do nothing(def), 1->time from 1st cmd, 2->time "
+            "from 2nd, ...\n"
+            "    verbose  increase level of verbosity (def: 0)\n"
+            "    --help   print this usage message then exit\n"
+            "    --version  print version number then exit\n\n"
+            "Issue SCSI READ commands, each starting from the same logical "
+            "block address\n");
+}
+
+static int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz,
+                             unsigned int blocks, int64_t start_block,
+                             int write_true, int fua, int dpo)
+{
+    int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+    int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
+    int sz_ind;
+
+    memset(cdbp, 0, cdb_sz);
+    if (dpo)
+        cdbp[1] |= 0x10;
+    if (fua)
+        cdbp[1] |= 0x8;
+    switch (cdb_sz) {
+    case 6:
+        sz_ind = 0;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
+        cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
+        if (blocks > 256) {
+            pr2serr(ME "for 6 byte commands, maximum number of blocks is "
+                    "256\n");
+            return 1;
+        }
+        if ((start_block + blocks - 1) & (~0x1fffff)) {
+            pr2serr(ME "for 6 byte commands, can't address blocks beyond "
+                    "%d\n", 0x1fffff);
+            return 1;
+        }
+        if (dpo || fua) {
+            pr2serr(ME "for 6 byte commands, neither dpo nor fua bits "
+                    "supported\n");
+            return 1;
+        }
+        break;
+    case 10:
+        sz_ind = 1;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
+        sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7);
+        if (blocks & (~0xffff)) {
+            pr2serr(ME "for 10 byte commands, maximum number of blocks is "
+                    "%d\n", 0xffff);
+            return 1;
+        }
+        break;
+    case 12:
+        sz_ind = 2;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
+        sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6);
+        break;
+    case 16:
+        sz_ind = 3;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be64(start_block, cdbp + 2);
+        sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10);
+        break;
+    default:
+        pr2serr(ME "expected cdb size of 6, 10, 12, or 16 but got %d\n",
+                cdb_sz);
+        return 1;
+    }
+    return 0;
+}
+
+/* -3 medium/hardware error, -2 -> not ready, 0 -> successful,
+   1 -> recoverable (ENOMEM), 2 -> try again (e.g. unit attention),
+   3 -> try again (e.g. aborted command), -1 -> other unrecoverable error */
+static int sg_bread(int sg_fd, unsigned char * buff, int blocks,
+                    int64_t from_block, int bs, int cdbsz,
+                    int fua, int dpo, int * diop, int do_mmap,
+                    int no_dxfer)
+{
+    int k;
+    unsigned char rdCmd[MAX_SCSI_CDBSZ];
+    unsigned char senseBuff[SENSE_BUFF_LEN];
+    struct sg_io_hdr io_hdr;
+
+    if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, dpo)) {
+        pr2serr(ME "bad cdb build, from_block=%" PRId64 ", blocks=%d\n",
+                from_block, blocks);
+        return -1;
+    }
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = cdbsz;
+    io_hdr.cmdp = rdCmd;
+    if (blocks > 0) {
+        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        io_hdr.dxfer_len = bs * blocks;
+        /* next: shows dxferp unused during mmap-ed IO */
+        if (! do_mmap)
+            io_hdr.dxferp = buff;
+        if (diop && *diop)
+            io_hdr.flags |= SG_FLAG_DIRECT_IO;
+        else if (do_mmap)
+            io_hdr.flags |= SG_FLAG_MMAP_IO;
+        else if (no_dxfer)
+            io_hdr.flags |= SG_FLAG_NO_DXFER;
+    } else
+        io_hdr.dxfer_direction = SG_DXFER_NONE;
+    io_hdr.mx_sb_len = SENSE_BUFF_LEN;
+    io_hdr.sbp = senseBuff;
+    io_hdr.timeout = DEF_TIMEOUT;
+    io_hdr.pack_id = pack_id_count++;
+    if (verbose > 1) {
+        pr2serr( "    read cdb: ");
+        for (k = 0; k < cdbsz; ++k)
+            pr2serr( "%02x ", rdCmd[k]);
+        pr2serr( "\n");
+    }
+
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        if (ENOMEM == errno)
+            return 1;
+        perror("reading (SG_IO) on sg device, error");
+        return -1;
+    }
+
+    if (verbose > 2)
+        pr2serr( "      duration=%u ms\n", io_hdr.duration);
+    switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        if (verbose > 1)
+                sg_chk_n_print3("reading, continue", &io_hdr, 1);
+        break;
+    case SG_LIB_CAT_UNIT_ATTENTION:
+        if (verbose)
+            sg_chk_n_print3("reading", &io_hdr, verbose - 1);
+        return 2;
+    case SG_LIB_CAT_ABORTED_COMMAND:
+        if (verbose)
+            sg_chk_n_print3("reading", &io_hdr, verbose - 1);
+        return 3;
+    case SG_LIB_CAT_NOT_READY:
+        if (verbose)
+            sg_chk_n_print3("reading", &io_hdr, verbose - 1);
+        return -2;
+    case SG_LIB_CAT_MEDIUM_HARD:
+        if (verbose)
+            sg_chk_n_print3("reading", &io_hdr, verbose - 1);
+        return -3;
+    default:
+        sg_chk_n_print3("reading", &io_hdr, verbose);
+        return -1;
+    }
+    if (blocks > 0) {
+        if (diop && *diop &&
+            ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+            *diop = 0;      /* flag that dio not done (completely) */
+        sum_of_resids += io_hdr.resid;
+    }
+    return 0;
+}
+
+#define STR_SZ 1024
+#define INF_SZ 512
+#define EBUFF_SZ 512
+
+
+int main(int argc, char * argv[])
+{
+    int64_t skip = 0;
+    int bs = 0;
+    int bpt = DEF_BLOCKS_PER_TRANSFER;
+    char str[STR_SZ];
+    char * key;
+    char * buf;
+    char inf[INF_SZ];
+    char outf[INF_SZ];
+    int in_type = FT_OTHER;
+    int do_dio = 0;
+    int do_odir = 0;
+    int do_blk_sgio = 0;
+    int do_mmap = 0;
+    int no_dxfer = 0;
+    int do_time = 0;
+    int fua = 0;
+    int dpo = 0;
+    int scsi_cdbsz = DEF_SCSI_CDBSZ;
+    int dio_incomplete = 0;
+    int count_given = 0;
+    int res, k, t, buf_sz, dio_tmp, iters;
+    int infd, blocks, flags, blocks_per;
+    unsigned char * wrkBuff = NULL;
+    unsigned char * wrkPos = NULL;
+    char ebuff[EBUFF_SZ];
+    struct timeval start_tm, end_tm;
+    const char * read_str;
+    int ret = 0;
+    size_t psz;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+    psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
+#else
+    psz = 4096;     /* give up, pick likely figure */
+#endif
+    inf[0] = '\0';
+
+    for (k = 1; k < argc; k++) {
+        if (argv[k]) {
+            strncpy(str, argv[k], STR_SZ);
+            str[STR_SZ - 1] = '\0';
+        } else
+            continue;
+        for (key = str, buf = key; (*buf && (*buf != '=')); )
+            buf++;
+        if (*buf)
+            *buf++ = '\0';
+        if (0 == strcmp(key,"blk_sgio"))
+            do_blk_sgio = sg_get_num(buf);
+        else if (0 == strcmp(key,"bpt")) {
+            bpt = sg_get_num(buf);
+            if (-1 == bpt) {
+                pr2serr( ME "bad argument to 'bpt'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"bs")) {
+            bs = sg_get_num(buf);
+            if (-1 == bs) {
+                pr2serr( ME "bad argument to 'bs'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"cdbsz"))
+            scsi_cdbsz = sg_get_num(buf);
+        else if (0 == strcmp(key,"count")) {
+            count_given = 1;
+            if ('-' == *buf) {
+                dd_count = sg_get_llnum(buf + 1);
+                if (-1 == dd_count) {
+                    pr2serr( ME "bad argument to 'count'\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                dd_count = - dd_count;
+            } else {
+                dd_count = sg_get_llnum(buf);
+                if (-1 == dd_count) {
+                    pr2serr( ME "bad argument to 'count'\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+        } else if (0 == strcmp(key,"dio"))
+            do_dio = sg_get_num(buf);
+        else if (0 == strcmp(key,"dpo"))
+            dpo = sg_get_num(buf);
+        else if (0 == strcmp(key,"fua"))
+            fua = sg_get_num(buf);
+        else if (strcmp(key,"if") == 0)
+            strncpy(inf, buf, INF_SZ);
+        else if (0 == strcmp(key,"mmap"))
+            do_mmap = sg_get_num(buf);
+        else if (0 == strcmp(key,"no_dxfer"))
+            no_dxfer = sg_get_num(buf);
+        else if (0 == strcmp(key,"odir"))
+            do_odir = sg_get_num(buf);
+        else if (strcmp(key,"of") == 0)
+            strncpy(outf, buf, INF_SZ);
+        else if (0 == strcmp(key,"skip")) {
+            skip = sg_get_llnum(buf);
+            if (-1 == skip) {
+                pr2serr( ME "bad argument to 'skip'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"time"))
+            do_time = sg_get_num(buf);
+        else if (0 == strncmp(key, "verb", 4))
+            verbose = sg_get_num(buf);
+        else if (0 == strncmp(key, "--help", 6)) {
+            usage();
+            return 0;
+        } else if (0 == strncmp(key, "--vers", 6)) {
+            pr2serr( ME ": %s\n", version_str);
+            return 0;
+        } else {
+            pr2serr( "Unrecognized argument '%s'\n", key);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (bs <= 0) {
+        bs = DEF_BLOCK_SIZE;
+        if ((dd_count > 0) && (bpt > 0))
+            pr2serr( "Assume default 'bs' (block size) of %d bytes\n", bs);
+    }
+    if (! count_given) {
+        pr2serr("'count' must be given\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (skip < 0) {
+        pr2serr("skip cannot be negative\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (bpt < 1) {
+        if (0 == bpt) {
+            if (dd_count > 0)
+                dd_count = - dd_count;
+        } else {
+            pr2serr("bpt must be greater than 0\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (do_dio && do_mmap) {
+        pr2serr("cannot select both dio and mmap\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (no_dxfer && (do_dio || do_mmap)) {
+        pr2serr("cannot select no_dxfer with dio or mmap\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    install_handler (SIGINT, interrupt_handler);
+    install_handler (SIGQUIT, interrupt_handler);
+    install_handler (SIGPIPE, interrupt_handler);
+    install_handler (SIGUSR1, siginfo_handler);
+
+    if (! inf[0]) {
+        pr2serr("must provide 'if=<filename>'\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (0 == strcmp("-", inf)) {
+        pr2serr("'-' (stdin) invalid as <filename>\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    in_type = dd_filetype(inf);
+    if (FT_ERROR == in_type) {
+        pr2serr("Unable to access: %s\n", inf);
+        return SG_LIB_FILE_ERROR;
+    } else if ((FT_BLOCK & in_type) && do_blk_sgio)
+        in_type |= FT_SG;
+
+    if (FT_SG & in_type) {
+        if ((dd_count < 0) && (6 == scsi_cdbsz)) {
+            pr2serr(ME "SCSI READ (6) can't do zero block reads\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        flags = O_RDWR;
+        if (do_odir)
+            flags |= O_DIRECT;
+        if ((infd = open(inf, flags)) < 0) {
+            flags = O_RDONLY;
+            if (do_odir)
+                flags |= O_DIRECT;
+            if ((infd = open(inf, flags)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for sg reading", inf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+        }
+        if (verbose)
+            pr2serr("Opened %s for SG_IO with flags=0x%x\n", inf, flags);
+        if ((dd_count > 0) && (! (FT_BLOCK & in_type))) {
+            if (verbose > 2) {
+                if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) >= 0)
+                    pr2serr("  SG_GET_RESERVED_SIZE yields: %d\n", t);
+            }
+            t = bs * bpt;
+            if ((do_mmap) && (0 != (t % psz)))
+                t = ((t / psz) + 1) * psz;    /* round up to next pagesize */
+            res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
+            if (res < 0)
+                perror(ME "SG_SET_RESERVED_SIZE error");
+            res = ioctl(infd, SG_GET_VERSION_NUM, &t);
+            if ((res < 0) || (t < 30000)) {
+                pr2serr(ME "sg driver prior to 3.x.y\n");
+                return SG_LIB_CAT_OTHER;
+            }
+            if (do_mmap && (t < 30122)) {
+                pr2serr(ME "mmap-ed IO needs a sg driver version >= 3.1.22\n");
+                return SG_LIB_CAT_OTHER;
+            }
+        }
+    } else {
+        if (do_mmap) {
+            pr2serr(ME "mmap-ed IO only support on sg devices\n");
+            return SG_LIB_CAT_OTHER;
+        }
+        if (dd_count < 0) {
+            pr2serr(ME "negative 'count' only supported with SCSI READs\n");
+            return SG_LIB_CAT_OTHER;
+        }
+        flags = O_RDONLY;
+        if (do_odir)
+            flags |= O_DIRECT;
+        if ((infd = open(inf, flags)) < 0) {
+            snprintf(ebuff,  EBUFF_SZ,
+                     ME "could not open %s for reading", inf);
+            perror(ebuff);
+            return SG_LIB_FILE_ERROR;
+        }
+        if (verbose)
+            pr2serr("Opened %s for Unix reads with flags=0x%x\n", inf, flags);
+        if (skip > 0) {
+            off64_t offset = skip;
+
+            offset *= bs;       /* could exceed 32 bits here! */
+            if (lseek64(infd, offset, SEEK_SET) < 0) {
+                snprintf(ebuff,  EBUFF_SZ,
+                    ME "couldn't skip to required position on %s", inf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+        }
+    }
+
+    if (0 == dd_count)
+        return 0;
+    orig_count = dd_count;
+
+    if (dd_count > 0) {
+        if (do_dio || do_odir || (FT_RAW & in_type)) {
+            wrkBuff = (unsigned char *)malloc(bs * bpt + psz);
+            if (0 == wrkBuff) {
+                pr2serr("Not enough user memory for aligned storage\n");
+                return SG_LIB_CAT_OTHER;
+            }
+            /* perhaps use posix_memalign() instead */
+            wrkPos = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) &
+                                       (~(psz - 1)));
+        } else if (do_mmap) {
+            wrkPos = (unsigned char *)mmap(NULL, bs * bpt,
+                        PROT_READ | PROT_WRITE, MAP_SHARED, infd, 0);
+            if (MAP_FAILED == wrkPos) {
+                perror(ME "error from mmap()");
+                return SG_LIB_CAT_OTHER;
+            }
+        } else {
+            wrkBuff = (unsigned char *)malloc(bs * bpt);
+            if (0 == wrkBuff) {
+                pr2serr("Not enough user memory\n");
+                return SG_LIB_CAT_OTHER;
+            }
+            wrkPos = wrkBuff;
+        }
+    }
+
+    blocks_per = bpt;
+    start_tm.tv_sec = 0;   /* just in case start set condition not met */
+    start_tm.tv_usec = 0;
+
+    if (verbose && (dd_count < 0))
+        pr2serr("About to issue %" PRId64 " zero block SCSI READs\n",
+                0 - dd_count);
+
+    /* main loop */
+    for (iters = 0; dd_count != 0; ++iters) {
+        if ((do_time > 0) && (iters == (do_time - 1)))
+            gettimeofday(&start_tm, NULL);
+        if (dd_count < 0)
+            blocks = 0;
+        else
+            blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
+        if (FT_SG & in_type) {
+            dio_tmp = do_dio;
+            res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+                           fua, dpo, &dio_tmp, do_mmap, no_dxfer);
+            if (1 == res) {     /* ENOMEM, find what's available+try that */
+                if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
+                    perror("RESERVED_SIZE ioctls failed");
+                    break;
+                }
+                if (buf_sz < MIN_RESERVED_SIZE)
+                    buf_sz = MIN_RESERVED_SIZE;
+                blocks_per = (buf_sz + bs - 1) / bs;
+                blocks = blocks_per;
+                pr2serr("Reducing read to %d blocks per loop\n", blocks_per);
+                res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+                               fua, dpo, &dio_tmp, do_mmap, no_dxfer);
+            } else if (2 == res) {
+                pr2serr("Unit attention, try again (r)\n");
+                res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+                               fua, dpo, &dio_tmp, do_mmap, no_dxfer);
+            }
+            if (0 != res) {
+                switch (res) {
+                case -3:
+                    ret = SG_LIB_CAT_MEDIUM_HARD;
+                    pr2serr(ME "SCSI READ medium/hardware error\n");
+                    break;
+                case -2:
+                    ret = SG_LIB_CAT_NOT_READY;
+                    pr2serr(ME "device not ready\n");
+                    break;
+                case 2:
+                    ret = SG_LIB_CAT_UNIT_ATTENTION;
+                    pr2serr(ME "SCSI READ unit attention\n");
+                    break;
+                case 3:
+                    ret = SG_LIB_CAT_ABORTED_COMMAND;
+                    pr2serr(ME "SCSI READ aborted command\n");
+                    break;
+                default:
+                    ret = SG_LIB_CAT_OTHER;
+                    pr2serr(ME "SCSI READ failed\n");
+                    break;
+                }
+                break;
+            } else {
+                in_full += blocks;
+                if (do_dio && (0 == dio_tmp))
+                    dio_incomplete++;
+            }
+        } else {
+            if (iters > 0) { /* subsequent iteration reset skip position */
+                off64_t offset = skip;
+
+                offset *= bs;       /* could exceed 32 bits here! */
+                if (lseek64(infd, offset, SEEK_SET) < 0) {
+                    perror(ME "could not reset skip position");
+                    break;
+                }
+            }
+            while (((res = read(infd, wrkPos, blocks * bs)) < 0) &&
+                   (EINTR == errno))
+                ;
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%" PRId64 " ",
+                         skip);
+                perror(ebuff);
+                break;
+            } else if (res < blocks * bs) {
+                pr2serr(ME "short read: wanted/got=%d/%d bytes, stop\n",
+                        blocks * bs, res);
+                blocks = res / bs;
+                if ((res % bs) > 0) {
+                    blocks++;
+                    in_partial++;
+                }
+                dd_count -= blocks;
+                in_full += blocks;
+                break;
+            }
+            in_full += blocks;
+        }
+        if (dd_count > 0)
+            dd_count -= blocks;
+        else if (dd_count < 0)
+            ++dd_count;
+    }
+    read_str = (FT_SG & in_type) ? "SCSI READ" : "read";
+    if (do_time > 0) {
+        gettimeofday(&end_tm, NULL);
+        if (start_tm.tv_sec || start_tm.tv_usec) {
+            struct timeval res_tm;
+            double a, b, c;
+
+            res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+            res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+            if (res_tm.tv_usec < 0) {
+                --res_tm.tv_sec;
+                res_tm.tv_usec += 1000000;
+            }
+            a = res_tm.tv_sec;
+            a += (0.000001 * res_tm.tv_usec);
+            if (orig_count > 0) {
+                b = (double)bs * (orig_count - dd_count);
+                if (do_time > 1)
+                    c = b - ((double)bs * ((do_time - 1.0) * bpt));
+                else
+                    c = 0.0;
+            } else {
+                b = 0.0;
+                c = 0.0;
+            }
+
+            if (1 == do_time) {
+                pr2serr("Time for all %s commands was %d.%06d secs", read_str,
+                        (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+                if ((orig_count > 0) && (a > 0.00001) && (b > 511))
+                    pr2serr(", %.2f MB/sec\n", b / (a * 1000000.0));
+                else
+                    pr2serr("\n");
+            } else if (2 == do_time) {
+                pr2serr("Time from second %s command to end was %d.%06d secs",
+                        read_str, (int)res_tm.tv_sec,
+                        (int)res_tm.tv_usec);
+                if ((orig_count > 0) && (a > 0.00001) && (c > 511))
+                    pr2serr(", %.2f MB/sec\n", c / (a * 1000000.0));
+                else
+                    pr2serr("\n");
+            } else {
+                pr2serr("Time from start of %s command "
+                        "#%d to end was %d.%06d secs", read_str, do_time,
+                        (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+                if ((orig_count > 0) && (a > 0.00001) && (c > 511))
+                    pr2serr(", %.2f MB/sec\n", c / (a * 1000000.0));
+                else
+                    pr2serr("\n");
+            }
+            if ((iters > 0) && (a > 0.00001))
+                pr2serr("Average number of %s commands per second was %.2f\n",
+                        read_str, (double)iters / a);
+        }
+    }
+
+    if (wrkBuff)
+        free(wrkBuff);
+
+    close(infd);
+    res = 0;
+    if (0 != dd_count) {
+        pr2serr("Some error occurred,");
+        if (0 == ret)
+            ret = SG_LIB_CAT_OTHER;
+    }
+    print_stats(iters, read_str);
+
+    if (dio_incomplete) {
+        int fd;
+        char c;
+
+        pr2serr(">> Direct IO requested but incomplete %d times\n",
+                dio_incomplete);
+        if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
+            if (1 == read(fd, &c, 1)) {
+                if ('0' == c)
+                    pr2serr(">>> %s set to '0' but should be set to '1' for "
+                            "direct IO\n", proc_allow_dio);
+            }
+            close(fd);
+        }
+    }
+    if (sum_of_resids)
+        pr2serr(">> Non-zero sum of residual counts=%d\n", sum_of_resids);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_read_attr.c b/sg3_utils/src/sg_read_attr.c
new file mode 100644
index 0000000..bd02a49
--- /dev/null
+++ b/sg3_utils/src/sg_read_attr.c
@@ -0,0 +1,1138 @@
+/*
+ * Copyright (c) 2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI READ ATTRIBUTE command to the given SCSI device
+ * and decodes the response. Based on spc5r08.pdf
+ */
+
+static const char * version_str = "1.00 20160207";
+
+#define MAX_RATTR_BUFF_LEN (1024 * 1024)
+#define DEF_RATTR_BUFF_LEN (1024 * 8)
+
+#define SG_READ_ATTRIBUTE_CMD 0x8c
+#define SG_READ_ATTRIBUTE_CMDLEN 16
+
+#define RA_ATTR_VAL_SA 0x0
+#define RA_ATTR_LIST_SA 0x1
+#define RA_LV_LIST_SA 0x2
+#define RA_PART_LIST_SA 0x3
+#define RA_SMC2_SA 0x4
+#define RA_SUP_ATTR_SA 0x5
+#define RA_HIGHEST_SA 0x5
+
+#define RA_FMT_BINARY 0x0
+#define RA_FMT_ASCII 0x1
+#define RA_FMT_TEXT 0x2         /* takes into account locale */
+#define RA_FMT_RES 0x3          /* reserved */
+
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT  60      /* 60 seconds */
+
+struct opts_t {
+    int cache;
+    int ea;
+    int enumerate;
+    int filter;
+    int fai;
+    int do_hex;
+    int lvn;
+    int maxlen;
+    int pn;
+    int quiet;
+    int do_raw;
+    int o_readonly;
+    int sa;
+    int verbose;
+};
+
+struct acron_nv_t {
+    const char * acron;
+    const char * name;
+    int val;
+};
+
+struct attr_name_info_t {
+    int id;
+    const char * name;  /* tab ('\t') suggest line break */
+    int format;         /* RA_FMT_BINARY and friends, -1 --> unknown */
+    int len;            /* -1 --> not fixed (variable) */
+    int process;        /* 0 --> print decimal if binary, 1 --> print hex,
+                         * 2 --> further processing */
+};
+
+static struct option long_options[] = {
+    {"cache", no_argument, 0, 'c'},
+    {"enumerate", no_argument, 0, 'e'},
+    {"element", required_argument, 0, 'E'},   /* SMC-3 element address */
+    {"filter", required_argument, 0, 'f'},
+    {"first", required_argument, 0, 'F'},
+    {"help", no_argument, 0, 'h'},
+    {"hex", no_argument, 0, 'H'},
+    {"in", required_argument, 0, 'i'},
+    {"lvn", required_argument, 0, 'l'},
+    {"maxlen", required_argument, 0, 'm'},
+    {"partition", required_argument, 0, 'p'},
+    {"quiet", required_argument, 0, 'q'},
+    {"raw", no_argument, 0, 'r'},
+    {"readonly", no_argument, 0, 'R'},
+    {"sa", required_argument, 0, 's'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {0, 0, 0, 0},   /* sentinel */
+};
+
+static struct acron_nv_t sa_acron_arr[] = {
+    {"av", "attribute values", 0},
+    {"al", "attribute list", 1},
+    {"lvl", "logical volume list", 2},
+    {"pl", "partition list", 3},
+    {"smc", "SMC-2 should define this", 4},
+    {"sa", "supported attributes", 5},
+    {NULL, NULL, -1},           /* sentinel */
+};
+
+static struct attr_name_info_t attr_name_arr[] = {
+/* Device type attributes */
+    {0x0, "Remaining capacity in partition [MiB]", RA_FMT_BINARY, 8, 0},
+    {0x1, "Maximum capacity in partition [MiB]", RA_FMT_BINARY, 8, 0},
+    {0x2, "TapeAlert flags", RA_FMT_BINARY, 8, 0},   /* SSC-4 */
+    {0x3, "Load count", RA_FMT_BINARY, 8, 0},
+    {0x4, "MAM space remaining [B]", RA_FMT_BINARY, 8, 0},
+    {0x5, "Assigning organization", RA_FMT_ASCII, 8, 0}, /* SSC-4 */
+    {0x6, "Format density code", RA_FMT_BINARY, 1, 1},    /* SSC-4 */
+    {0x7, "Initialization count", RA_FMT_BINARY, 2, 0},
+    {0x8, "Volume identifier", RA_FMT_ASCII, 32, 0},
+    {0x9, "Volume change reference", RA_FMT_BINARY, -1, 1}, /* SSC-4 */
+    {0x20a, "Density vendor/serial number at last load", RA_FMT_ASCII, 40, 0},
+    {0x20b, "Density vendor/serial number at load-1", RA_FMT_ASCII, 40, 0},
+    {0x20c, "Density vendor/serial number at load-2", RA_FMT_ASCII, 40, 0},
+    {0x20d, "Density vendor/serial number at load-3", RA_FMT_ASCII, 40, 0},
+    {0x220, "Total MiB written in medium life", RA_FMT_BINARY, 8, 0},
+    {0x221, "Total MiB read in medium life", RA_FMT_BINARY, 8, 0},
+    {0x222, "Total MiB written in current/last load", RA_FMT_BINARY, 8, 0},
+    {0x223, "Total MiB read in current/last load", RA_FMT_BINARY, 8, 0},
+    {0x224, "Logical position of first encrypted block", RA_FMT_BINARY, 8, 2},
+    {0x225, "Logical position of first unencrypted block\tafter first "
+     "encrypted block", RA_FMT_BINARY, 8, 2},
+    {0x340, "Medium usage history", RA_FMT_BINARY, 90, 2},
+    {0x341, "Partition usage history", RA_FMT_BINARY, 60, 2},
+
+/* Medium type attributes */
+    {0x400, "Medium manufacturer", RA_FMT_ASCII, 8, 0},
+    {0x401, "Medium serial number", RA_FMT_ASCII, 32, 0},
+    {0x402, "Medium length [m]", RA_FMT_BINARY, 4, 0},      /* SSC-4 */
+    {0x403, "Medium width [0.1 mm]", RA_FMT_BINARY, 4, 0},  /* SSC-4 */
+    {0x404, "Assigning organization", RA_FMT_ASCII, 8, 0},  /* SSC-4 */
+    {0x405, "Medium density code", RA_FMT_BINARY, 1, 1},    /* SSC-4 */
+    {0x406, "Medium manufacture date", RA_FMT_ASCII, 8, 0},
+    {0x407, "MAM capacity [B]", RA_FMT_BINARY, 8, 0},
+    {0x408, "Medium type", RA_FMT_BINARY, 1, 1},
+    {0x409, "Medium type information", RA_FMT_BINARY, 2, 1},
+    {0x40a, "Numeric medium serial number", -1, -1, 1},
+
+/* Host type attributes */
+    {0x800, "Application vendor", RA_FMT_ASCII, 8, 0},
+    {0x801, "Application name", RA_FMT_ASCII, 32, 0},
+    {0x802, "Application version", RA_FMT_ASCII, 8, 0},
+    {0x803, "User medium text label", RA_FMT_TEXT, 160, 0},
+    {0x804, "Date and time last written", RA_FMT_ASCII, 12, 0},
+    {0x805, "Text localization identifier", RA_FMT_BINARY, 1, 0},
+    {0x806, "Barcode", RA_FMT_ASCII, 32, 0},
+    {0x807, "Owning host textual name", RA_FMT_TEXT, 80, 0},
+    {0x808, "Media pool", RA_FMT_TEXT, 160, 0},
+    {0x809, "Partition user text label", RA_FMT_ASCII, 16, 0},
+    {0x80a, "Load/unload at partition", RA_FMT_BINARY, 1, 0},
+    {0x80a, "Application format version", RA_FMT_ASCII, 16, 0},
+    {0x80c, "Volume coherency information", RA_FMT_BINARY, -1, 1},
+     /* SSC-5 */
+    {0x820, "Medium globally unique identifier", RA_FMT_BINARY, 36, 1},
+    {0x821, "Media pool globally unique identifier", RA_FMT_BINARY, 36, 1},
+
+    {-1, NULL, -1, -1, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_read_attr [--cache] [--element=EA] [--enumerate] "
+            "[--filter=FL]\n"
+            "                    [--first=FAI] [--help] [--hex] [--in=FN] "
+            "[--lvn-LVN]\n"
+            "                    [--maxlen=LEN] [--partition=PN] [--quiet] "
+            "[--raw]\n"
+            "                    [--readonly] [--sa=SA] [--verbose] "
+            "[--version]\n"
+            "                    DEVICE\n");
+    pr2serr("  where:\n"
+            "    --cache|-c         set CACHE bit in cdn (def: clear)\n"
+            "    --enumerate|-e     enumerate known attributes and service "
+            "actions\n"
+            "    --element=EA|-E EA    EA is placed in 'element address' "
+            "field in\n"
+            "                          cdb [SMC-3] (def: 0)\n"
+            "    --filter=FL|-f FL    FL is parameter code to match (def: "
+            "-1 -> all)\n"
+            "    --first=FAI|-F FAI    FAI is placed in 'first attribute "
+            "identifier'\n"
+            "                          field in cdb (def: 0)\n"
+            "    --help|-h          print out usage message\n"
+            "    --hex|-H           output response in hexadecimal; used "
+            "twice\n"
+            "                       shows decoded values in hex\n"
+            "    --in=FN|-i FN      FN is a filename containing attribute "
+            "values in\n"
+            "                       ASCII hex or binary if --raw also "
+            "given\n"
+            "    --lvn=LVN|-l LVN    logical volume number (LVN) (def:0)\n"
+            "    --maxlen=LEN|-m LEN    max response length (allocation "
+            "length in cdb)\n"
+            "                           (def: 0 -> 8192 bytes)\n"
+            "    --partition=PN|-p PN    partition number (PN) (def:0)\n"
+            "    --quiet|-q         reduce the amount of output, can use "
+            "more than once\n"
+            "    --raw|-r           output response in binary\n"
+            "    --readonly|-R      open DEVICE read-only (def: read-write)\n"
+            "    --sa=SA|-s SA      SA is service action (def: 0)\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "Performs a SCSI READ ATTRIBUTE command. It is typically used "
+            "on tape\nsystems.\n");
+}
+
+/* Invokes a SCSI READ ATTRIBUTE command (SPC+SMC).  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_read_attr(int sg_fd, void * resp, int * residp,
+                const struct opts_t * op)
+{
+    int k, ret, res, sense_cat;
+    int noisy = 1;
+    unsigned char raCmdBlk[SG_READ_ATTRIBUTE_CMDLEN] =
+          {SG_READ_ATTRIBUTE_CMD, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
+           0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    raCmdBlk[1] = 0x1f & op->sa;
+    if (op->ea)
+        sg_put_unaligned_be16(op->ea, raCmdBlk + 2);
+    if (op->lvn)
+        raCmdBlk[5] = 0xff & op->lvn;
+    if (op->pn)
+        raCmdBlk[7] = 0xff & op->pn;
+    if (op->fai)
+        sg_put_unaligned_be16(op->fai, raCmdBlk + 8);
+    sg_put_unaligned_be32((uint32_t)op->maxlen, raCmdBlk + 10);
+    if (op->cache)
+        raCmdBlk[14] |= 0x1;
+    if (op->verbose) {
+        pr2serr("    Read attribute cdb: ");
+        for (k = 0; k < SG_READ_ATTRIBUTE_CMDLEN; ++k)
+            pr2serr("%02x ", raCmdBlk[k]);
+        pr2serr("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("%s: out of memory\n", __func__);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, raCmdBlk, sizeof(raCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, op->maxlen);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, op->verbose);
+    ret = sg_cmds_process_resp(ptvp, "read attribute", res, op->maxlen,
+                               sense_b, noisy, op->verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    if (residp)
+        *residp = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+static int
+find_sa_acron(const char * cp)
+{
+    int k;
+    const struct acron_nv_t * anvp;
+    const char * mp;
+
+    for (anvp = sa_acron_arr; anvp->acron ; ++anvp) {
+        for (mp = cp, k = 0; *mp; ++mp, ++k) {
+            if (0 == anvp->acron[k])
+                return anvp->val;
+            if (tolower(*mp) != anvp->acron[k])
+                break;
+        }
+        if ((0 == *mp) && (0 == anvp->acron[k]))
+            return anvp->val;
+    }
+    return -1;  /* not found */
+}
+
+const char * a_format[] = {
+    "binary",
+    "ascii",
+    "text",
+    "format[0x3]",
+};
+
+static void
+enum_attributes(void)
+{
+    const struct attr_name_info_t * anip;
+    const char * cp;
+    char b[32];
+
+    printf("Attribute ID\tLength\tFormat\tName\n");
+    printf("------------------------------------------\n");
+    for (anip = attr_name_arr; anip->name ; ++anip) {
+        if (anip->format < 0)
+            snprintf(b, sizeof(b), "unknown");
+        else
+            snprintf(b, sizeof(b), "%s", a_format[0x3 & anip->format]);
+        printf("  0x%04x:\t%d\t%s\t", anip->id, anip->len, b);
+        cp = strchr(anip->name, '\t');
+        if (cp ) {
+            printf("%.*s\n", (int)(cp - anip->name), anip->name);
+            printf("\t\t\t\t%s\n", cp + 1);
+        } else
+            printf("%s\n", anip->name);
+    }
+}
+
+static void
+enum_sa_acrons(void)
+{
+    const struct acron_nv_t * anvp;
+
+    printf("SA_value\tAcronym\tDescription\n");
+    printf("------------------------------------------\n");
+    for (anvp = sa_acron_arr; anvp->acron ; ++anvp)
+        printf("  %d:\t\t%s\t%s\n", anvp->val, anvp->acron, anvp->name);
+}
+
+/* Read ASCII hex bytes or binary from fname (a file named '-' taken as
+ * stdin). If reading ASCII hex then there should be either one entry per
+ * line or a comma, space or tab separated list of bytes. If no_space is
+ * set then a string of ACSII hex digits is expected, 2 per byte. Everything
+ * from and including a '#' on a line is ignored. Returns 0 if ok, or 1 if
+ * error. */
+static int
+f2hex_arr(const char * fname, int as_binary, int no_space,
+          uint8_t * mp_arr, int * mp_arr_len, int max_arr_len)
+{
+    int fn_len, in_len, k, j, m, split_line, fd, has_stdin;
+    unsigned int h;
+    const char * lcp;
+    FILE * fp;
+    char line[512];
+    char carry_over[4];
+    int off = 0;
+
+    if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len))
+        return 1;
+    fn_len = strlen(fname);
+    if (0 == fn_len)
+        return 1;
+    has_stdin = ((1 == fn_len) && ('-' == fname[0]));  /* read from stdin */
+    if (as_binary) {
+        if (has_stdin) {
+            fd = STDIN_FILENO;
+                if (sg_set_binary_mode(STDIN_FILENO) < 0)
+                    perror("sg_set_binary_mode");
+        } else {
+            fd = open(fname, O_RDONLY);
+            if (fd < 0) {
+                pr2serr("unable to open binary file %s: %s\n", fname,
+                         safe_strerror(errno));
+                return 1;
+            } else if (sg_set_binary_mode(fd) < 0)
+                perror("sg_set_binary_mode");
+        }
+        k = read(fd, mp_arr, max_arr_len);
+        if (k <= 0) {
+            if (0 == k)
+                pr2serr("read 0 bytes from binary file %s\n", fname);
+            else
+                pr2serr("read from binary file %s: %s\n", fname,
+                        safe_strerror(errno));
+            if (! has_stdin)
+                close(fd);
+            return 1;
+        }
+        *mp_arr_len = k;
+        if (! has_stdin)
+            close(fd);
+        return 0;
+    } else {    /* So read the file as ASCII hex */
+        if (has_stdin)
+            fp = stdin;
+        else {
+            fp = fopen(fname, "r");
+            if (NULL == fp) {
+                pr2serr("Unable to open %s for reading\n", fname);
+                return 1;
+            }
+        }
+    }
+
+    carry_over[0] = 0;
+    for (j = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+                split_line = 0;
+            } else
+                split_line = 1;
+        }
+        if (in_len < 1) {
+            carry_over[0] = 0;
+            continue;
+        }
+        if (carry_over[0]) {
+            if (isxdigit(line[0])) {
+                carry_over[1] = line[0];
+                carry_over[2] = '\0';
+                if (1 == sscanf(carry_over, "%4x", &h))
+                    mp_arr[off - 1] = h;       /* back up and overwrite */
+                else {
+                    pr2serr("%s: carry_over error ['%s'] around line %d\n",
+                            __func__, carry_over, j + 1);
+                    goto bad;
+                }
+                lcp = line + 1;
+                --in_len;
+            } else
+                lcp = line;
+            carry_over[0] = 0;
+        } else
+            lcp = line;
+
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+        if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) {
+            pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
+                    j + 1, m + k + 1);
+            goto bad;
+        }
+        if (no_space) {
+            for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
+                 ++k, lcp += 2) {
+                if (1 != sscanf(lcp, "%2x", &h)) {
+                    pr2serr("%s: bad hex number in line %d, pos %d\n",
+                            __func__, j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+                if ((off + k) >= max_arr_len) {
+                    pr2serr("%s: array length exceeded\n", __func__);
+                    goto bad;
+                }
+                mp_arr[off + k] = h;
+            }
+            if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
+                carry_over[0] = *lcp;
+            off += k;
+        } else {
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%4x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("%s: hex number larger than 0xff in line %d, "
+                                "pos %d\n", __func__, j + 1,
+                                (int)(lcp - line + 1));
+                        goto bad;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("%s: array length exceeded\n", __func__);
+                        goto bad;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if (('#' == *lcp) || ('\r' == *lcp)) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("%s: error in line %d, at pos %d\n", __func__,
+                            j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+            }
+            off += (k + 1);
+        }
+    }
+    *mp_arr_len = off;
+    if (stdin != fp)
+        fclose(fp);
+    return 0;
+bad:
+    if (stdin != fp)
+        fclose(fp);
+    return 1;
+}
+
+/* Returns 1 if 'ucp' all 0xff bytes, returns 2 is all 0xff bytes apart
+ * from last being 0xfe; otherwise returns 0. */
+static int
+all_ffs_or_last_fe(const unsigned char * ucp, int len)
+{
+    for ( ; len > 0; ++ucp, --len) {
+        if (*ucp < 0xfe)
+            return 0;
+        if (0xfe == *ucp)
+            return (1 == len) ? 2 : 0;
+
+    }
+    return 1;
+}
+
+static char *
+attr_id_lookup(unsigned int id, const struct attr_name_info_t ** anipp,
+               int blen, char * b)
+{
+    const struct attr_name_info_t * anip;
+
+    for (anip = attr_name_arr; anip->name; ++anip) {
+        if (id == (unsigned int)anip->id)
+            break;
+    }
+    if (anip->name) {
+        snprintf(b, blen, "%s", anip->name);
+        if (anipp)
+            *anipp = anip;
+        return b;
+    }
+    if (anipp)
+        *anipp = NULL;
+    if (id < 0x400)
+        snprintf(b, blen, "Unknown device attribute 0x%x", id);
+    else if (id < 0x800)
+        snprintf(b, blen, "Unknown medium attribute 0x%x", id);
+    else if (id < 0xc00)
+        snprintf(b, blen, "Unknown host attribute 0x%x", id);
+    else if (id < 0x1000)
+        snprintf(b, blen, "Vendor specific device attribute 0x%x", id);
+    else if (id < 0x1400)
+        snprintf(b, blen, "Vendor specific medium attribute 0x%x", id);
+    else if (id < 0x1800)
+        snprintf(b, blen, "Vendor specific host attribute 0x%x", id);
+    else
+        snprintf(b, blen, "Reserved attribute 0x%x", id);
+    return b;
+}
+
+static void
+decode_attr_list(const unsigned char * alp, int len, bool supported,
+                 const struct opts_t * op)
+{
+    int id;
+    char b[160];
+    char * cp;
+    char * c2p;
+    const char * leadin = supported ? "Supported a" : "A";
+
+    if (op->verbose)
+        printf("%sttribute list: [len=%d]\n", leadin, len);
+    else if (0 == op->quiet)
+        printf("%sttribute list:\n", leadin);
+    if (op->do_hex) {
+        dStrHex((const char *)alp, len, 0);
+        return;
+    }
+    for ( ; len > 0; alp += 2, len -= 2) {
+        id = sg_get_unaligned_be16(alp + 0);
+        if ((op->filter >= 0) && (op->filter != id))
+            continue;
+        if (op->verbose)
+            printf("  0x%.4x:\t", id);
+        cp = attr_id_lookup(id, NULL, sizeof(b), b);
+        c2p = strchr(cp, '\t');
+        if (c2p) {
+            printf("  %.*s -\n", (int)(c2p - cp), cp);
+            if (op->verbose)
+                printf("\t\t      %s\n", c2p + 1);
+            else
+                printf("      %s\n", c2p + 1);
+        } else
+            printf("  %s\n", cp);
+    }
+}
+
+static void
+helper_full_attr(const unsigned char * alp, int len, int id,
+                 const struct attr_name_info_t * anip,
+                 const struct opts_t * op)
+{
+    int k;
+    const unsigned char * ucp;
+
+    if (op->verbose)
+        printf("[r%c] ", (0x80 & alp[2]) ? 'o' : 'w');
+    if (op->verbose > 3)
+        pr2serr("%s: id=0x%x, len=%d, anip->format=%d, anip->len=%d\n",
+                __func__, id, len, anip->format, anip->len);
+    switch (id) {
+    case 0x224:         /* logical position of first encrypted block */
+        k = all_ffs_or_last_fe(alp + 5, len - 5);
+        if (1 == k)
+            printf("<unknown> [ff]\n");
+        else if (2 == k)
+            printf("<unknown [fe]>\n");
+        else {
+            if ((len - 5) <= 8)
+                printf("%" PRIx64, sg_get_unaligned_be(len - 5, alp + 5));
+            else {
+                printf("\n");
+                dStrHex((const char *)(alp + 5), len - 5, 0);
+            }
+        }
+        break;
+    case 0x225:         /* logical position of first unencrypted block
+                         * after first encrypted block */
+        k = all_ffs_or_last_fe(alp + 5, len - 5);
+        if (1 == k)
+            printf("<unknown> [ff]\n");
+        else if (2 == k)
+            printf("<unknown [fe]>\n");
+        else {
+            if ((len - 5) <= 8)
+                printf("%" PRIx64, sg_get_unaligned_be(len - 5, alp + 5));
+            else {
+                printf("\n");
+                dStrHex((const char *)(alp + 5), len - 5, 0);
+            }
+        }
+        break;
+    case 0x340:         /* Medium Usage history */
+        ucp = alp + 5;
+        printf("\n");
+        if ((len - 5) < 90) {
+            pr2serr("%s: expected 90 bytes, got %d\n", __func__, len - 5);
+            break;
+        }
+        printf("    Current amount of data written [MiB]: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 0));
+        printf("    Current write retry count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 6));
+        printf("    Current amount of data read [MiB]: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 12));
+        printf("    Current read retry count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 18));
+        printf("    Previous amount of data written [MiB]: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 24));
+        printf("    Previous write retry count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 30));
+        printf("    Previous amount of data read [MiB]: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 36));
+        printf("    Previous read retry count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 42));
+        printf("    Total amount of data written [MiB]: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 48));
+        printf("    Total write retry count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 54));
+        printf("    Total amount of data read [MiB]: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 60));
+        printf("    Total read retry count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 66));
+        printf("    Load count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 72));
+        printf("    Total change partition count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 78));
+        printf("    Total partition initialization count: %" PRIu64 "\n",
+               sg_get_unaligned_be48(ucp + 84));
+        break;
+    case 0x341:         /* Partition Usage history */
+        ucp = alp + 5;
+        printf("\n");
+        if ((len - 5) < 60) {
+            pr2serr("%s: expected 60 bytes, got %d\n", __func__, len - 5);
+            break;
+        }
+        printf("    Current amount of data written [MiB]: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 0));
+        printf("    Current write retry count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 4));
+        printf("    Current amount of data read [MiB]: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 8));
+        printf("    Current read retry count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 12));
+        printf("    Previous amount of data written [MiB]: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 16));
+        printf("    Previous write retry count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 20));
+        printf("    Previous amount of data read [MiB]: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 24));
+        printf("    Previous read retry count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 28));
+        printf("    Total amount of data written [MiB]: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 32));
+        printf("    Total write retry count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 36));
+        printf("    Total amount of data read [MiB]: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 40));
+        printf("    Total read retry count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 44));
+        printf("    Load count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 48));
+        printf("    change partition count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 52));
+        printf("    partition initialization count: %" PRIu32 "\n",
+               sg_get_unaligned_be32(ucp + 56));
+        break;
+    default:
+        pr2serr("%s: unknown attribute id: 0x%x\n", __func__, id);
+        printf("  In hex:\n");
+        dStrHex((const char *)alp, len, 0);
+        break;
+    }
+}
+
+static void
+decode_attr_vals(const unsigned char * alp, int len, const struct opts_t * op)
+{
+    int bump, id, alen;
+    uint64_t ull;
+    char * cp;
+    char * c2p;
+    const struct attr_name_info_t * anip;
+    char b[160];
+
+    if (op->verbose)
+        printf("Attribute values: [len=%d]\n", len);
+    else if (op->filter < 0) {
+        if (0 == op->quiet)
+            printf("Attribute values:\n");
+        if (op->do_hex) {       /* only expect -HH to get through here */
+            dStrHex((const char *)alp, len, 0);
+            return;
+        }
+    }
+    for ( ; len > 4; alp += bump, len -= bump) {
+        id = sg_get_unaligned_be16(alp + 0);
+        bump = sg_get_unaligned_be16(alp + 3) + 5;
+        alen = bump - 5;
+        if ((op->filter >= 0) && (op->filter != id)) {
+            if (id < op->filter)
+                continue;
+            else
+                break;  /* Assume array is ascending id order */
+        }
+        anip = NULL;
+        cp = attr_id_lookup(id, &anip, sizeof(b), b);
+        if (op->quiet < 2) {
+            c2p = strchr(cp, '\t');
+            if (c2p) {
+                printf("  %.*s -\n", (int)(c2p - cp), cp);
+                printf("      %s: ", c2p + 1);
+            } else
+                printf("  %s: ", cp);
+        }
+        if (op->verbose)
+            printf("[r%c] ", (0x80 & alp[2]) ? 'o' : 'w');
+        if (anip) {
+            if ((RA_FMT_BINARY == anip->format) && (bump <= 13)) {
+                ull = sg_get_unaligned_be(alen, alp + 5);
+                if (0 == anip->process)
+                    printf("%" PRIu64 "\n", ull);
+                else if (1 == anip->process)
+                    printf("0x%" PRIx64 "\n", ull);
+                else
+                    helper_full_attr(alp, bump, id, anip, op);
+                if (op->verbose) {
+                    if ((anip->len > 0) && (alen > 0) && (alen != anip->len))
+                        printf(" <<< T10 length (%d) differs from length in "
+                               "response (%d) >>>\n", anip->len, alen);
+                }
+            } else if (RA_FMT_BINARY == anip->format) {
+                if (2 == anip->process)
+                    helper_full_attr(alp, bump, id, anip, op);
+                else {
+                    printf("\n");
+                    dStrHex((const char *)(alp + 5), alen, 0);
+                }
+           } else {
+                if (2 == anip->process)
+                    helper_full_attr(alp, bump, id, anip, op);
+                else {
+                    printf("%.*s\n", alen, alp + 5);
+                    if (op->verbose) {
+                        if ((anip->len > 0) && (alen > 0) &&
+                            (alen != anip->len))
+                            printf(" <<< T10 length (%d) differs from length "
+                                   "in response (%d) >>>\n", anip->len, alen);
+                    }
+                }
+            }
+        } else {
+            if (op->verbose > 1)
+                printf("Attribute id lookup failed, in hex:\n");
+            else
+                printf("\n");
+            dStrHex((const char *)(alp + 5), alen, 0);
+        }
+    }
+    if (op->verbose && (len > 0) && (len <= 4))
+        pr2serr("warning: iterate of attributes should end a residual of "
+                "%d\n", len);
+}
+
+static void
+decode_all_sa_s(const unsigned char * rabp, int len, const struct opts_t * op)
+{
+    if (op->do_hex && (2 != op->do_hex)) {
+        dStrHex((const char *)rabp, len, ((1 == op->do_hex) ? 1 : -1));
+        return;
+    }
+    switch (op->sa) {
+    case RA_ATTR_VAL_SA:
+        decode_attr_vals(rabp + 4, len - 4, op);
+        break;
+    case RA_ATTR_LIST_SA:
+        decode_attr_list(rabp + 4, len - 4, false, op);
+        break;
+    case RA_LV_LIST_SA:
+        if ((0 == op->quiet) || op->verbose)
+            printf("Logical volume list:\n");
+        if (len < 4) {
+            pr2serr(">>> response length unexpectedly short: %d bytes\n",
+                    len);
+            break;
+        }
+        printf("  First logical volume number: %u\n", rabp[2]);
+        printf("  Number of logical volumes available: %u\n", rabp[3]);
+        break;
+    case RA_PART_LIST_SA:
+        if ((0 == op->quiet) || op->verbose)
+            printf("Partition number list:\n");
+        if (len < 4) {
+            pr2serr(">>> response length unexpectedly short: %d bytes\n",
+                    len);
+            break;
+        }
+        printf("  First partition number: %u\n", rabp[2]);
+        printf("  Number of partitions available: %u\n", rabp[3]);
+        break;
+    case RA_SMC2_SA:
+        printf("Used by SMC-2, not information, output in hex:\n");
+        dStrHex((const char *)rabp, len, 0);
+        break;
+    case RA_SUP_ATTR_SA:
+        decode_attr_list(rabp + 4, len - 4, true, op);
+        break;
+    default:
+        printf("Unrecognized service action [0x%x], response in hex:\n",
+               op->sa);
+        dStrHex((const char *)rabp, len, 0);
+        break;
+    }
+}
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, len, resid, rlen, in_len;
+    unsigned int ra_len;
+    int ret = 0;
+    const char * device_name = NULL;
+    const char * fname = NULL;
+    unsigned char * rabp = NULL;
+    struct opts_t opts;
+    struct opts_t * op;
+    char b[80];
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->filter = -1;
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ceE:f:F:hHi:l:m:p:qrRs:vV",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            ++op->cache;
+            break;
+        case 'e':
+            ++op->enumerate;
+            break;
+        case 'E':
+           op->ea = sg_get_num(optarg);
+           if ((op->ea < 0) || (op->ea > 65535)) {
+                pr2serr("bad argument to '--ea=EA', expect 0 to 65535\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'f':
+           op->filter = sg_get_num(optarg);
+           if ((op->filter < -3) || (op->filter > 65535)) {
+                pr2serr("bad argument to '--filter=FL', expect -3 to "
+                        "65535\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'F':
+           op->fai = sg_get_num(optarg);
+           if ((op->fai < 0) || (op->fai > 65535)) {
+                pr2serr("bad argument to '--first=FAI', expect 0 to 65535\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++op->do_hex;
+            break;
+        case 'i':
+            fname = optarg;
+            break;
+        case 'l':
+           op->lvn = sg_get_num(optarg);
+           if ((op->lvn < 0) || (op->lvn > 255)) {
+                pr2serr("bad argument to '--lvn=LVN', expect 0 to 255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'm':
+            op->maxlen = sg_get_num(optarg);
+            if ((op->maxlen < 0) || (op->maxlen > MAX_RATTR_BUFF_LEN)) {
+                pr2serr("argument to '--maxlen' should be %d or "
+                        "less\n", MAX_RATTR_BUFF_LEN);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'p':
+           op->pn = sg_get_num(optarg);
+           if ((op->pn < 0) || (op->pn > 255)) {
+                pr2serr("bad argument to '--pn=PN', expect 0 to 255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'q':
+            ++op->quiet;
+            break;
+        case 'r':
+            ++op->do_raw;
+            break;
+        case 'R':
+            ++op->o_readonly;
+            break;
+        case 's':
+           if (isdigit(*optarg)) {
+               op->sa = sg_get_num(optarg);
+               if ((op->sa < 0) || (op->sa > 63)) {
+                    pr2serr("bad argument to '--sa=SA', expect 0 to 63\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else {
+                res = find_sa_acron(optarg);
+                if (res < 0) {
+                    enum_sa_acrons();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->sa = res;
+            }
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (op->enumerate) {
+        enum_attributes();
+        printf("\n");
+        enum_sa_acrons();
+        return 0;
+    }
+
+    if (fname && device_name) {
+        pr2serr("since '--in=FN' given, ignoring DEVICE\n");
+        device_name = NULL;
+    }
+
+    if (0 == op->maxlen)
+        op->maxlen = DEF_RATTR_BUFF_LEN;
+    rabp = (unsigned char *)calloc(1, op->maxlen);
+    if (NULL == rabp) {
+        pr2serr("unable to calloc %d bytes\n", op->maxlen);
+        return SG_LIB_CAT_OTHER;
+    }
+
+    if (NULL == device_name) {
+        if (fname) {
+            if (f2hex_arr(fname, op->do_raw, 0, rabp, &in_len, op->maxlen))
+                return SG_LIB_FILE_ERROR;
+            if (op->do_raw)
+                op->do_raw = 0;    /* can interfere on decode */
+            if (in_len < 4) {
+                pr2serr("--in=%s only decoded %d bytes (needs 4 at least)\n",
+                        fname, in_len);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            decode_all_sa_s(rabp, in_len, op);
+            goto clean_up;
+        }
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (op->do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, op->o_readonly, op->verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    res = sg_ll_read_attr(sg_fd, rabp, &resid, op);
+    ret = res;
+    if (0 == res) {
+        rlen = op->maxlen - resid;
+        if (rlen < 4) {
+            pr2serr("Response length (%d) too short\n", rlen);
+            ret = SG_LIB_CAT_MALFORMED;
+            goto close_then_end;
+        }
+        if ((op->sa <= RA_HIGHEST_SA) && (op->sa != RA_SMC2_SA)) {
+            ra_len = ((RA_LV_LIST_SA == op->sa) ||
+                      (RA_PART_LIST_SA == op->sa)) ?
+                        (unsigned int)sg_get_unaligned_be16(rabp + 0) :
+                        sg_get_unaligned_be32(rabp + 0) + 2;
+            ra_len += 2;
+        } else
+            ra_len = rlen;
+        if ((int)ra_len > rlen) {
+            if (op->verbose)
+                pr2serr("ra_len available is %d, response length is %d\n",
+                        ra_len, rlen);
+            len = rlen;
+        } else
+            len = (int)ra_len;
+        if (op->do_raw) {
+            dStrRaw((const char *)rabp, len);
+            goto close_then_end;
+        }
+        decode_all_sa_s(rabp, len, op);
+    } else if (SG_LIB_CAT_INVALID_OP == res)
+        pr2serr("Read attribute command not supported\n");
+    else {
+        sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+        pr2serr("Read attribute command: %s\n", b);
+    }
+
+close_then_end:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            ret = SG_LIB_FILE_ERROR;
+    }
+clean_up:
+    if (rabp)
+        free(rabp);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_read_block_limits.c b/sg3_utils/src/sg_read_block_limits.c
new file mode 100644
index 0000000..320dc5e
--- /dev/null
+++ b/sg3_utils/src/sg_read_block_limits.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2009-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI READ BLOCK LIMITS command (SSC) to the given
+ * SCSI device.
+ */
+
+static const char * version_str = "1.04 20151219";
+
+#define MAX_READ_BLOCK_LIMITS_LEN 6
+
+static unsigned char readBlkLmtBuff[MAX_READ_BLOCK_LIMITS_LEN];
+
+
+static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"raw", no_argument, 0, 'r'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_read_block_limits  [--help] [--hex] [--raw] "
+            "[--verbose] [--version]\n"
+            "                             DEVICE\n"
+            "  where:\n"
+            "    --help|-h          print out usage message\n"
+            "    --hex|-H           output response in hexadecimal\n"
+            "    --raw|-r           output response in binary to stdout\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "Performs a SCSI READ BLOCK LIMITS command and decode the "
+            "response\n"
+            );
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, m, res, c;
+    int do_hex = 0;
+    int do_raw = 0;
+    int verbose = 0;
+    const char * device_name = NULL;
+    int ret = 0;
+    uint32_t max_block_size;
+    uint16_t min_block_size;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hHrvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("invalid option -%c ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    memset(readBlkLmtBuff, 0x0, 6);
+    res = sg_ll_read_block_limits(sg_fd, readBlkLmtBuff, 6, 1,
+                            verbose);
+    ret = res;
+    if (0 == res) {
+      if (do_hex) {
+        dStrHex((const char *)readBlkLmtBuff, sizeof(readBlkLmtBuff), 1);
+        goto the_end;
+      } else if (do_raw) {
+        dStrRaw((const char *)readBlkLmtBuff, sizeof(readBlkLmtBuff));
+        goto the_end;
+      }
+
+      max_block_size = sg_get_unaligned_be32(readBlkLmtBuff + 0);
+      min_block_size = sg_get_unaligned_be16(readBlkLmtBuff + 4);
+      k = min_block_size / 1024;
+      pr2serr("Read Block Limits results:\n");
+      pr2serr("\tMinimum block size: %u byte(s)",
+              (unsigned int)min_block_size);
+      if (k != 0)
+        pr2serr(", %d KB", k);
+      pr2serr("\n");
+      k = max_block_size / 1024;
+      m = max_block_size / 1048576;
+      pr2serr("\tMaximum block size: %u byte(s)",
+              (unsigned int)max_block_size);
+      if (k != 0)
+        pr2serr(", %d KB", k);
+      if (m != 0)
+        pr2serr(", %d MB", m);
+      pr2serr("\n");
+    } else {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Read block limits: %s\n", b);
+        if (0 == verbose)
+            pr2serr("    try '-v' option for more information\n");
+    }
+
+the_end:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_read_buffer.c b/sg3_utils/src/sg_read_buffer.c
new file mode 100644
index 0000000..384bb18
--- /dev/null
+++ b/sg3_utils/src/sg_read_buffer.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 2006-2016 Luben Tuikov and 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/*
+ * This utility issues the SCSI READ BUFFER(10 or 16) command to the given
+ * device.
+ */
+
+static const char * version_str = "1.15 20160131";
+
+
+#ifndef SG_READ_BUFFER_10_CMD
+#define SG_READ_BUFFER_10_CMD 0x3c
+#define SG_READ_BUFFER_10_CMDLEN 10
+#endif
+#ifndef SG_READ_BUFFER_16_CMD
+#define SG_READ_BUFFER_16_CMD 0x9b
+#define SG_READ_BUFFER_16_CMDLEN 16
+#endif
+
+#define SENSE_BUFF_LEN  64       /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT  60       /* 60 seconds */
+
+
+static struct option long_options[] = {
+        {"16", no_argument, 0, 'L'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"id", required_argument, 0, 'i'},
+        {"length", required_argument, 0, 'l'},
+        {"long", no_argument, 0, 'L'},
+        {"mode", required_argument, 0, 'm'},
+        {"offset", required_argument, 0, 'o'},
+        {"raw", no_argument, 0, 'r'},
+        {"readonly", no_argument, 0, 'R'},
+        {"specific", required_argument, 0, 'S'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},   /* sentinel */
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_read_buffer [--16] [--help] [--hex] [--id=ID] "
+            "[--length=LEN]\n"
+            "                      [--long] [--mode=MO] [--offset=OFF] "
+            "[--raw]\n"
+            "                      [--readonly] [--specific=MS] [--verbose] "
+            "[--version]\n"
+            "                      DEVICE\n"
+            "  where:\n"
+            "    --16|-L             issue READ BUFFER(16) (def: 10)\n"
+            "    --help|-h           print out usage message\n"
+            "    --hex|-H            print output in hex\n"
+            "    --id=ID|-i ID       buffer identifier (0 (default) to 255)\n"
+            "    --length=LEN|-l LEN    length in bytes to read (def: 4)\n"
+            "    --long|-L           issue READ BUFFER(16) (def: 10)\n"
+            "    --mode=MO|-m MO     read buffer mode, MO is number or "
+            "acronym (def: 0)\n"
+            "    --offset=OFF|-o OFF    buffer offset (unit: bytes, def: 0)\n"
+            "    --raw|-r            output response to stdout\n"
+            "    --specific=MS|-S MS    mode specific value; 3 bit field (0 "
+            "to 7)\n"
+            "    --readonly|-R       open DEVICE read-only (def: read-write)\n"
+            "    --verbose|-v        increase verbosity\n"
+            "    --version|-V        print version string and exit\n\n"
+            "Performs a SCSI READ BUFFER (10 or 16) command. Use '-m xxx' to "
+            "list\navailable modes. Numbers given in options are decimal "
+            "unless they have\na hex indicator (e.g. a leading '0x').\n"
+           );
+}
+
+
+#define MODE_HEADER_DATA        0
+#define MODE_VENDOR             1
+#define MODE_DATA               2
+#define MODE_DESCRIPTOR         3
+#define MODE_ECHO_BUFFER        0x0A
+#define MODE_ECHO_BDESC         0x0B
+#define MODE_EN_EX_ECHO         0x1A
+#define MODE_ERR_HISTORY        0x1C
+
+static struct mode_s {
+        const char *mode_string;
+        int   mode;
+        const char *comment;
+} modes[] = {
+        { "hd",         MODE_HEADER_DATA, "combined header and data"},
+        { "vendor",     MODE_VENDOR,    "vendor specific"},
+        { "data",       MODE_DATA,      "data"},
+        { "desc",       MODE_DESCRIPTOR, "descriptor"},
+        { "echo",       MODE_ECHO_BUFFER, "read data from echo buffer "
+          "(spc-2)"},
+        { "echo_desc",  MODE_ECHO_BDESC, "echo buffer descriptor (spc-2)"},
+        { "en_ex",      MODE_EN_EX_ECHO,
+          "enable expander communications protocol and echo buffer (spc-3)"},
+        { "err_hist",   MODE_ERR_HISTORY, "error history (spc-4)"},
+        { NULL,   999, NULL},   /* end sentinel */
+};
+
+
+static void
+print_modes(void)
+{
+    const struct mode_s *mp;
+
+    pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
+            "or symbolic:\n");
+    for (mp = modes; mp->mode_string; ++mp) {
+        pr2serr(" %2d (0x%02x)  %-16s%s\n", mp->mode, mp->mode,
+                mp->mode_string, mp->comment);
+    }
+}
+
+/* Invokes a SCSI READ BUFFER(10) command (spc5r02).  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+ll_read_buffer_10(int sg_fd, int rb_mode, int rb_mode_sp, int rb_id,
+                  uint32_t rb_offset, void * resp, int mx_resp_len,
+                  int * residp, int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    uint8_t rb10_cb[SG_READ_BUFFER_10_CMDLEN] =
+          {SG_READ_BUFFER_10_CMD, 0, 0, 0,  0, 0, 0, 0, 0, 0};
+    uint8_t sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    rb10_cb[1] = (uint8_t)(rb_mode & 0x1f);
+    if (rb_mode_sp)
+        rb10_cb[1] |= (uint8_t)((rb_mode_sp & 0x7) << 5);
+    rb10_cb[2] = (uint8_t)rb_id;
+    sg_put_unaligned_be24(rb_offset, rb10_cb + 3);
+    sg_put_unaligned_be24(mx_resp_len, rb10_cb + 6);
+    if (verbose) {
+        pr2serr("    Read buffer(10) cdb: ");
+        for (k = 0; k < SG_READ_BUFFER_10_CMDLEN; ++k)
+            pr2serr("%02x ", rb10_cb[k]);
+        pr2serr("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("Read buffer(10): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rb10_cb, sizeof(rb10_cb));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "Read buffer(10)", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2serr("    Read buffer(10): response%s\n",
+                    (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    if (residp)
+        *residp = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI READ BUFFER(16) command (spc5r02).  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+ll_read_buffer_16(int sg_fd, int rb_mode, int rb_mode_sp, int rb_id,
+                  uint64_t rb_offset, void * resp, int mx_resp_len,
+                  int * residp, int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    uint8_t rb16_cb[SG_READ_BUFFER_16_CMDLEN] =
+          {SG_READ_BUFFER_16_CMD, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0};
+    uint8_t sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    rb16_cb[1] = (uint8_t)(rb_mode & 0x1f);
+    if (rb_mode_sp)
+        rb16_cb[1] |= (uint8_t)((rb_mode_sp & 0x7) << 5);
+    sg_put_unaligned_be64(rb_offset, rb16_cb + 2);
+    sg_put_unaligned_be24(mx_resp_len, rb16_cb + 11);
+    rb16_cb[14] = (uint8_t)rb_id;
+    if (verbose) {
+        pr2serr("    Read buffer(16) cdb: ");
+        for (k = 0; k < SG_READ_BUFFER_16_CMDLEN; ++k)
+            pr2serr("%02x ", rb16_cb[k]);
+        pr2serr("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("Read buffer(16): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rb16_cb, sizeof(rb16_cb));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "Read buffer(16)", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else {
+        if ((verbose > 2) && (ret > 0)) {
+            pr2serr("    Read buffer(16): response%s\n",
+                    (ret > 256 ? ", first 256 bytes" : ""));
+            dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
+        }
+        ret = 0;
+    }
+    if (residp)
+        *residp = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+int
+main(int argc, char * argv[])
+{
+    int res, c, len, k;
+    int sg_fd = -1;
+    int do_help = 0;
+    int do_hex = 0;
+    int do_long = 0;
+    int o_readonly = 0;
+    int rb_id = 0;
+    int rb_len = 4;
+    int rb_mode = 0;
+    int rb_mode_sp = 0;
+    int64_t ll;
+    uint64_t rb_offset = 0;
+    int do_raw = 0;
+    int resid = 0;
+    int verbose = 0;
+    int ret = 0;
+    const char * device_name = NULL;
+    unsigned char * resp;
+    const struct mode_s * mp;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hHi:l:Lm:o:rRS:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            ++do_help;
+            break;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'i':
+            rb_id = sg_get_num(optarg);
+            if ((rb_id < 0) || (rb_id > 255)) {
+                pr2serr("argument to '--id' should be in the range 0 to "
+                        "255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'l':
+            rb_len = sg_get_num(optarg);
+            if (rb_len < 0) {
+                pr2serr("bad argument to '--length'\n");
+                return SG_LIB_SYNTAX_ERROR;
+             }
+             if (rb_len > 0xffffff) {
+                pr2serr("argument to '--length' must be <= 0xffffff\n");
+                return SG_LIB_SYNTAX_ERROR;
+             }
+             break;
+        case 'L':
+            ++do_long;
+            break;
+        case 'm':
+            if (isdigit(*optarg)) {
+                rb_mode = sg_get_num(optarg);
+                if ((rb_mode < 0) || (rb_mode > 31)) {
+                    pr2serr("argument to '--mode' should be in the range 0 "
+                            "to 31\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else {
+                len = strlen(optarg);
+                for (mp = modes; mp->mode_string; ++mp) {
+                    if (0 == strncmp(mp->mode_string, optarg, len)) {
+                        rb_mode = mp->mode;
+                        break;
+                    }
+                }
+                if (NULL == mp) {
+                    print_modes();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+            break;
+        case 'o':
+           ll = sg_get_llnum(optarg);
+           if (ll < 0) {
+                pr2serr("bad argument to '--offset'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            rb_offset = ll;
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 'R':
+            ++o_readonly;
+            break;
+        case 'S':
+           rb_mode_sp = sg_get_num(optarg);
+           if ((rb_mode_sp < 0) || (rb_mode_sp > 7)) {
+                pr2serr("expected argument to '--specific' to be 0 to 7\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (do_help) {
+        if (do_help > 1) {
+            usage();
+            pr2serr("\n");
+            print_modes();
+        } else
+            usage();
+        return 0;
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (rb_len > 0) {
+        resp = (unsigned char *)malloc(rb_len);
+        if (NULL == resp) {
+            pr2serr("unable to allocate %d bytes on the heap\n", rb_len);
+            return SG_LIB_CAT_OTHER;
+        }
+        memset(resp, 0, rb_len);
+    } else
+        resp = NULL;
+
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            ret = SG_LIB_FILE_ERROR;
+            goto fini;
+        }
+    }
+
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+    if (verbose > 4)
+        pr2serr("Initial win32 SPT interface state: %s\n",
+                scsi_pt_win32_spt_state() ? "direct" : "indirect");
+    scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
+#endif
+#endif
+
+    sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        ret = SG_LIB_FILE_ERROR;
+        goto fini;
+    }
+
+    if (do_long)
+        res = ll_read_buffer_16(sg_fd, rb_mode, rb_mode_sp, rb_id, rb_offset,
+                                resp, rb_len, &resid, 1, verbose);
+    else if (rb_offset > 0xffffff) {
+        pr2serr("--offset value is too large for READ BUFFER(10), try "
+                "--16\n");
+        ret = SG_LIB_SYNTAX_ERROR;
+        goto fini;
+    } else
+        res = ll_read_buffer_10(sg_fd, rb_mode, rb_mode_sp, rb_id,
+                                (uint32_t)rb_offset, resp, rb_len, &resid, 1,
+                                verbose);
+    if (0 != res) {
+        char b[80];
+
+        ret = res;
+        if (res > 0) {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("Read buffer(%d) failed: %s\n", (do_long ? 16 : 10), b);
+        }
+        goto fini;
+    }
+    if (resid > 0)
+        rb_len -= resid;        /* got back less than requested */
+    if (rb_len > 0) {
+        if (do_raw)
+            dStrRaw((const char *)resp, rb_len);
+        else if (do_hex || (rb_len < 4))
+            dStrHex((const char *)resp, rb_len, ((do_hex > 1) ? 0 : 1));
+        else {
+            switch (rb_mode) {
+            case MODE_DESCRIPTOR:
+                k = sg_get_unaligned_be24(resp + 1);
+                printf("OFFSET BOUNDARY: %d, Buffer offset alignment: "
+                       "%d-byte\n", resp[0], (1 << resp[0]));
+                printf("BUFFER CAPACITY: %d (0x%x)\n", k, k);
+                break;
+            case MODE_ECHO_BDESC:
+                k = sg_get_unaligned_be16(resp + 2) & 0x1fff;
+                printf("EBOS:%d\n", resp[0] & 1 ? 1 : 0);
+                printf("Echo buffer capacity: %d (0x%x)\n", k, k);
+                break;
+            default:
+                dStrHex((const char *)resp, rb_len, (verbose > 1 ? 0 : 1));
+                break;
+            }
+        }
+    }
+
+fini:
+    if (resp)
+        free(resp);
+    if (sg_fd >= 0) {
+        res = sg_cmds_close_device(sg_fd);
+        if (res < 0) {
+            pr2serr("close error: %s\n", safe_strerror(-res));
+            if (0 == ret)
+                return SG_LIB_FILE_ERROR;
+        }
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_read_long.c b/sg3_utils/src/sg_read_long.c
new file mode 100644
index 0000000..9514787
--- /dev/null
+++ b/sg3_utils/src/sg_read_long.c
@@ -0,0 +1,279 @@
+/* A utility program for the Linux OS SCSI subsystem.
+   *  Copyright (C) 2004-2016 D. Gilbert
+   *  This program is free software; you can redistribute it and/or modify
+   *  it under the terms of the GNU General Public License as published by
+   *  the Free Software Foundation; either version 2, or (at your option)
+   *  any later version.
+
+   This program issues the SCSI command READ LONG to a given SCSI device.
+   It sends the command with the logical block address passed as the lba
+   argument, and the transfer length set to the xfer_len argument. the
+   buffer to be writen to the device filled with 0xff, this buffer includes
+   the sector data and the ECC bytes.
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.21 20160121";
+
+#define MAX_XFER_LEN 10000
+
+#define ME "sg_read_long: "
+
+#define EBUFF_SZ 256
+
+
+static struct option long_options[] = {
+        {"16", 0, 0, 'S'},
+        {"correct", 0, 0, 'c'},
+        {"help", 0, 0, 'h'},
+        {"lba", 1, 0, 'l'},
+        {"out", 1, 0, 'o'},
+        {"pblock", 0, 0, 'p'},
+        {"readonly", 0, 0, 'r'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {"xfer_len", 1, 0, 'x'},
+        {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_read_long [--16] [--correct] [--help] [--lba=LBA] "
+            "[--out=OF]\n"
+            "                    [--pblock] [--readonly] [--verbose] "
+            "[--version]\n"
+            "                    [--xfer_len=BTL] DEVICE\n"
+            "  where:\n"
+            "    --16|-S              do READ LONG(16) (default: "
+            "READ LONG(10))\n"
+            "    --correct|-c         use ECC to correct data "
+            "(default: don't)\n"
+            "    --help|-h            print out usage message\n"
+            "    --lba=LBA|-l LBA     logical block address"
+            " (default: 0)\n"
+            "    --out=OF|-o OF       output in binary to file named OF\n"
+            "    --pblock|-p          fetch physical block containing LBA\n"
+            "    --readonly|-r        open DEVICE read-only (def: open it "
+            "read-write)\n"
+            "    --verbose|-v         increase verbosity\n"
+            "    --version|-V         print version string and"
+            " exit\n"
+            "    --xfer_len=BTL|-x BTL    byte transfer length (< 10000)"
+            " default 520\n\n"
+            "Perform a SCSI READ LONG (10 or 16) command. Reads a single "
+            "block with\nassociated ECC data. The user data could be "
+            "encoded or encrypted.\n");
+}
+
+/* Returns 0 if successful */
+static int
+process_read_long(int sg_fd, int do_16, int pblock, int correct,
+                  uint64_t llba, void * data_out, int xfer_len, int verbose)
+{
+    int offset, res;
+    const char * ten_or;
+    char b[80];
+
+    if (do_16)
+        res = sg_ll_read_long16(sg_fd, pblock, correct, llba, data_out,
+                                xfer_len, &offset, 1, verbose);
+    else
+        res = sg_ll_read_long10(sg_fd, pblock, correct, (unsigned int)llba,
+                                data_out, xfer_len, &offset, 1, verbose);
+    ten_or = do_16 ? "16" : "10";
+    switch (res) {
+    case 0:
+        break;
+    case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:
+        pr2serr("<<< device indicates 'xfer_len' should be %d >>>\n",
+                xfer_len - offset);
+        break;
+    default:
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("  SCSI READ LONG (%s): %s\n", ten_or, b);
+        break;
+    }
+    return res;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, outfd, res, c;
+    unsigned char * readLongBuff = NULL;
+    void * rawp = NULL;
+    int correct = 0;
+    int xfer_len = 520;
+    int do_16 = 0;
+    int pblock = 0;
+    uint64_t llba = 0;
+    int readonly = 0;
+    int verbose = 0;
+    int64_t ll;
+    int got_stdout;
+    const char * device_name = NULL;
+    char out_fname[256];
+    char ebuff[EBUFF_SZ];
+    int ret = 0;
+
+    memset(out_fname, 0, sizeof out_fname);
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "chl:o:prSvVx:", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            correct = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'l':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            llba = (uint64_t)ll;
+            break;
+        case 'o':
+            strncpy(out_fname, optarg, sizeof(out_fname) - 1);
+            break;
+        case 'p':
+            pblock = 1;
+            break;
+        case 'r':
+            ++readonly;
+            break;
+        case 'S':
+            do_16 = 1;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        case 'x':
+            xfer_len = sg_get_num(optarg);
+           if (-1 == xfer_len) {
+                pr2serr("bad argument to '--xfer_len'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (xfer_len >= MAX_XFER_LEN){
+        pr2serr("xfer_len (%d) is out of range ( < %d)\n", xfer_len,
+                MAX_XFER_LEN);
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (NULL == (rawp = malloc(MAX_XFER_LEN))) {
+        pr2serr(ME "out of memory\n");
+        sg_cmds_close_device(sg_fd);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    readLongBuff = (unsigned char *)rawp;
+    memset(rawp, 0x0, MAX_XFER_LEN);
+
+    pr2serr(ME "issue read long (%s) to device %s\n    xfer_len=%d (0x%x), "
+            "lba=%" PRIu64 " (0x%" PRIx64 "), correct=%d\n",
+            (do_16 ? "16" : "10"), device_name, xfer_len, xfer_len, llba,
+            llba, correct);
+
+    if ((ret = process_read_long(sg_fd, do_16, pblock, correct, llba,
+                                 readLongBuff, xfer_len, verbose)))
+        goto err_out;
+
+    if ('\0' == out_fname[0])
+        dStrHex((const char *)rawp, xfer_len, 0);
+    else {
+        got_stdout = (0 == strcmp(out_fname, "-")) ? 1 : 0;
+        if (got_stdout)
+            outfd = STDOUT_FILENO;
+        else {
+            if ((outfd = open(out_fname, O_WRONLY | O_CREAT | O_TRUNC,
+                              0666)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for writing", out_fname);
+                perror(ebuff);
+                goto err_out;
+            }
+        }
+        if (sg_set_binary_mode(outfd) < 0) {
+            perror("sg_set_binary_mode");
+            goto err_out;
+        }
+        res = write(outfd, readLongBuff, xfer_len);
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ, ME "couldn't write to %s", out_fname);
+            perror(ebuff);
+            goto err_out;
+        }
+        if (! got_stdout)
+            close(outfd);
+    }
+
+err_out:
+    if (rawp) free(rawp);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_readcap.c b/sg3_utils/src/sg_readcap.c
new file mode 100644
index 0000000..e05f791
--- /dev/null
+++ b/sg3_utils/src/sg_readcap.c
@@ -0,0 +1,620 @@
+/* This code is does a SCSI READ CAPACITY command on the given device
+   and outputs the result.
+
+*  Copyright (C) 1999 - 2015 D. Gilbert
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This program was originally written with Linux 2.4 kernel series.
+   It now builds for the Linux 2.6 and 3 kernel series and various other
+   operating systems.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "3.95 20151219";
+
+#define ME "sg_readcap: "
+
+#define RCAP_REPLY_LEN 8
+#define RCAP16_REPLY_LEN 32
+
+static struct option long_options[] = {
+    {"brief", 0, 0, 'b'},
+    {"help", 0, 0, 'h'},
+    {"hex", 0, 0, 'H'},
+    {"lba", 1, 0, 'L'},
+    {"long", 0, 0, 'l'},
+    {"16", 0, 0, 'l'},
+    {"new", 0, 0, 'N'},
+    {"old", 0, 0, 'O'},
+    {"pmi", 0, 0, 'p'},
+    {"raw", 0, 0, 'r'},
+    {"readonly", 0, 0, 'R'},
+    {"verbose", 0, 0, 'v'},
+    {"version", 0, 0, 'V'},
+    {"zbc", 0, 0, 'z'},
+    {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_brief;
+    int do_help;
+    int do_hex;
+    int do_lba;
+    int do_long;
+    int do_pmi;
+    int do_raw;
+    int o_readonly;
+    int do_verbose;
+    int do_version;
+    int do_zbc;
+    uint64_t llba;
+    const char * device_name;
+    int opt_new;
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_readcap [--brief] [--help] [--hex] [--lba=LBA] "
+            "[--long] [--16]\n"
+            "                  [--pmi] [--raw] [--readonly] [--verbose] "
+            "[--version]\n"
+            "                  [--zbc] DEVICE\n"
+            "  where:\n"
+            "    --brief|-b      brief, two hex numbers: number of blocks "
+            "and block size\n"
+            "    --help|-h       print this usage message and exit\n"
+            "    --hex|-H        output response in hexadecimal to stdout\n"
+            "    --lba=LBA|-L LBA    yields the last block prior to (head "
+            "movement) delay\n"
+            "                        after LBA [in decimal (def: 0) "
+            "valid with '--pmi']\n"
+            "    --long|-l       use READ CAPACITY (16) cdb (def: use "
+            "10 byte cdb)\n"
+            "    --16            use READ CAPACITY (16) cdb (same as "
+            "--long)\n"
+            "    --pmi|-p        partial medium indicator (without this "
+            "option shows\n"
+            "                    total disk capacity) [made obsolete in "
+            "sbc3r26]\n"
+            "    --raw|-r        output response in binary to stdout\n"
+            "    --readonly|-R    open DEVICE read-only (def: RCAP(16) "
+            "read-write)\n"
+            "    --verbose|-v    increase verbosity\n"
+            "    --version|-V    print version string and exit\n"
+            "    --zbc|-z        show rc_basis ZBC field (implies --16)\n\n"
+            "Perform a SCSI READ CAPACITY (10 or 16) command\n");
+}
+
+static void
+usage_old()
+{
+    pr2serr("Usage:  sg_readcap [-16] [-b] [-h] [-H] [-lba=LBA] "
+            "[-pmi] [-r] [-R]\n"
+            "                   [-v] [-V] [-z] DEVICE\n"
+            "  where:\n"
+            "    -16    use READ CAPACITY (16) cdb (def: use "
+            "10 byte cdb)\n"
+            "    -b     brief, two hex numbers: number of blocks "
+            "and block size\n"
+            "    -h     print this usage message and exit\n"
+            "    -H     output response in hexadecimal to stdout\n"
+            "    -lba=LBA    yields the last block prior to (head "
+            "movement) delay\n"
+            "                after LBA [in hex (def: 0) "
+            "valid with -pmi]\n"
+            "    -pmi   partial medium indicator (without this option "
+            "shows total\n"
+            "           disk capacity)\n"
+            "    -r     output response in binary to stdout\n"
+            "    -R     open DEVICE read-only (def: RCAP(16) read-write)\n"
+            "    -v     increase verbosity\n"
+            "    -V     print version string and exit\n"
+            "    -z     show rc_basis ZBC field (implies -16)\n\n"
+            "Perform a SCSI READ CAPACITY (10 or 16) command\n");
+}
+
+static void
+usage_for(const struct opts_t * op)
+{
+    if (op->opt_new)
+        usage();
+    else
+        usage_old();
+}
+
+static int
+process_cl_new(struct opts_t * op, int argc, char * argv[])
+{
+    int c;
+    int a_one = 0;
+    int64_t nn;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "16bhHlL:NOprRvVz", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case '1':
+            ++a_one;
+            break;
+        case '6':
+            if (a_one)
+                ++op->do_long;
+            break;
+        case 'b':
+            ++op->do_brief;
+            break;
+        case 'h':
+        case '?':
+            ++op->do_help;
+            break;
+        case 'H':
+            ++op->do_hex;
+            break;
+        case 'l':
+            ++op->do_long;
+            break;
+        case 'L':
+            nn = sg_get_llnum(optarg);
+            if (-1 == nn) {
+                pr2serr("bad argument to '--lba='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->llba = nn;
+            /* force READ_CAPACITY16 for large lbas */
+            if (op->llba > 0xfffffffeULL)
+                ++op->do_long;
+            ++op->do_lba;
+            break;
+        case 'N':
+            break;      /* ignore */
+        case 'O':
+            op->opt_new = 0;
+            return 0;
+        case 'p':
+            ++op->do_pmi;
+            break;
+        case 'r':
+            ++op->do_raw;
+            break;
+        case 'R':
+            ++op->o_readonly;
+            break;
+        case 'v':
+            ++op->do_verbose;
+            break;
+        case 'V':
+            ++op->do_version;
+            break;
+        case 'z':
+            ++op->do_zbc;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (op->do_help)
+                break;
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int
+process_cl_old(struct opts_t * op, int argc, char * argv[])
+{
+    int k, jmp_out, plen, num;
+    const char * cp;
+    uint64_t uu;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case '1':
+                    if ('6' == *(cp + 1)) {
+                        ++op->do_long;
+                        ++cp;
+                        --plen;
+                    } else
+                        jmp_out = 1;
+                    break;
+                case 'b':
+                    ++op->do_brief;
+                    break;
+                case 'h':
+                case '?':
+                    ++op->do_help;
+                    break;
+                case 'H':
+                    ++op->do_hex;
+                    break;
+                case 'N':
+                    op->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case 'p':
+                    if (0 == strncmp("pmi", cp, 3)) {
+                        ++op->do_pmi;
+                        cp += 2;
+                        plen -= 2;
+                    } else
+                        jmp_out = 1;
+                    break;
+                case 'r':
+                    ++op->do_raw;
+                    break;
+                case 'R':
+                    ++op->o_readonly;
+                    break;
+                case 'v':
+                    ++op->do_verbose;
+                    break;
+                case 'V':
+                    ++op->do_version;
+                    break;
+                case 'z':
+                    ++op->do_zbc;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            if (0 == strncmp("lba=", cp, 4)) {
+                num = sscanf(cp + 4, "%" SCNx64 "", &uu);
+                if (1 != num) {
+                    printf("Bad value after 'lba=' option\n");
+                    usage();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                /* force READ_CAPACITY16 for large lbas */
+                if (uu > 0xfffffffeULL)
+                    ++op->do_long;
+                op->llba = uu;
+                ++op->do_lba;
+            } else if (0 == strncmp("-old", cp, 4))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == op->device_name)
+            op->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not expecting: %s\n",
+                    op->device_name, cp);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int
+process_cl(struct opts_t * op, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        op->opt_new = 0;
+        res = process_cl_old(op, argc, argv);
+        if ((0 == res) && op->opt_new)
+            res = process_cl_new(op, argc, argv);
+    } else {
+        op->opt_new = 1;
+        res = process_cl_new(op, argc, argv);
+        if ((0 == res) && (0 == op->opt_new))
+            res = process_cl_old(op, argc, argv);
+    }
+    return res;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+static const char *
+rc_basis_str(int rc_basis, char * b, int blen)
+{
+    switch (rc_basis) {
+    case 0:
+        snprintf(b, blen, "last contiguous that's not seq write required");
+        break;
+    case 1:
+        snprintf(b, blen, "last LBA on logical unit");
+        break;
+    default:
+        snprintf(b, blen, "reserved (0x%x)", rc_basis);
+        break;
+    }
+    return b;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, prot_en, p_type, lbppbe, rw_0_flag;
+    uint64_t llast_blk_addr;
+    int ret = 0;
+    uint32_t last_blk_addr, block_size;
+    unsigned char resp_buff[RCAP16_REPLY_LEN];
+    char b[80];
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    res = process_cl(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        usage_for(op);
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+
+    if (NULL == op->device_name) {
+        pr2serr("No DEVICE argument given\n");
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+    if (op->do_zbc) {
+        if (! op->do_long)
+            ++op->do_long;
+    }
+
+    memset(resp_buff, 0, sizeof(resp_buff));
+
+    if ((0 == op->do_pmi) && (op->llba > 0)) {
+        pr2serr(ME "lba can only be non-zero when '--pmi' is set\n");
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_long)
+        rw_0_flag = op->o_readonly;
+    else
+        rw_0_flag = 1;  /* RCAP(10) has opened RO in past, so leave */
+    if ((sg_fd = sg_cmds_open_device(op->device_name, rw_0_flag,
+                                     op->do_verbose)) < 0) {
+        pr2serr(ME "error opening file: %s: %s\n", op->device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (! op->do_long) {
+        res = sg_ll_readcap_10(sg_fd, op->do_pmi, (unsigned int)op->llba,
+                               resp_buff, RCAP_REPLY_LEN, 1, op->do_verbose);
+        ret = res;
+        if (0 == res) {
+            if (op->do_hex || op->do_raw) {
+                if (op->do_raw)
+                    dStrRaw((const char *)resp_buff, RCAP_REPLY_LEN);
+                else if (op->do_hex > 2)
+                    dStrHex((const char *)resp_buff, RCAP_REPLY_LEN, -1);
+                else
+                    dStrHex((const char *)resp_buff, RCAP_REPLY_LEN, 1);
+                goto good;
+            }
+            last_blk_addr = sg_get_unaligned_be32(resp_buff + 0);
+            if (0xffffffff != last_blk_addr) {
+                block_size = sg_get_unaligned_be32(resp_buff + 4);
+                if (op->do_brief) {
+                    printf("0x%" PRIx32 " 0x%" PRIx32 "\n",
+                           last_blk_addr + 1, block_size);
+                    goto good;
+                }
+                printf("Read Capacity results:\n");
+                if (op->do_pmi)
+                    printf("   PMI mode: given lba=0x%" PRIx64 ", last lba "
+                           "before delay=0x%" PRIx32 "\n", op->llba,
+                           last_blk_addr);
+                else
+                    printf("   Last logical block address=%" PRIu32 " (0x%"
+                           PRIx32 "), Number of blocks=%" PRIu32 "\n",
+                           last_blk_addr, last_blk_addr, last_blk_addr + 1);
+                printf("   Logical block length=%u bytes\n", block_size);
+                if (! op->do_pmi) {
+                    uint64_t total_sz = last_blk_addr + 1;
+                    double sz_mb, sz_gb;
+
+                    total_sz *= block_size;
+                    sz_mb = ((double)(last_blk_addr + 1) * block_size) /
+                            (double)(1048576);
+                    sz_gb = ((double)(last_blk_addr + 1) * block_size) /
+                            (double)(1000000000L);
+                    printf("Hence:\n");
+#ifdef SG_LIB_MINGW
+                    printf("   Device size: %" PRIu64 " bytes, %g MiB, %g "
+                           "GB\n", total_sz, sz_mb, sz_gb);
+#else
+                    printf("   Device size: %" PRIu64 " bytes, %.1f MiB, "
+                           "%.2f GB\n", total_sz, sz_mb, sz_gb);
+#endif
+                }
+                goto good;
+            } else {
+                printf("READ CAPACITY (10) indicates device capacity too "
+                       "large\n  now trying 16 byte cdb variant\n");
+                op->do_long = 1;
+            }
+        } else if (SG_LIB_CAT_INVALID_OP == res) {
+            op->do_long = 1;
+            sg_cmds_close_device(sg_fd);
+            if ((sg_fd = sg_cmds_open_device(op->device_name, op->o_readonly,
+                                             op->do_verbose)) < 0) {
+                pr2serr(ME "error re-opening file: %s (rw): %s\n",
+                        op->device_name, safe_strerror(-sg_fd));
+                return SG_LIB_FILE_ERROR;
+            }
+            if (op->do_verbose)
+                pr2serr("READ CAPACITY (10) not supported, trying READ "
+                        "CAPACITY (16)\n");
+        } else if (res) {
+            sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose);
+            pr2serr("READ CAPACITY (10) failed: %s\n", b);
+        }
+    }
+    if (op->do_long) {
+        res = sg_ll_readcap_16(sg_fd, op->do_pmi, op->llba, resp_buff,
+                               RCAP16_REPLY_LEN, 1, op->do_verbose);
+        ret = res;
+        if (0 == res) {
+            if (op->do_hex || op->do_raw) {
+                if (op->do_raw)
+                    dStrRaw((const char *)resp_buff, RCAP16_REPLY_LEN);
+                else if (op->do_hex > 2)
+                    dStrHex((const char *)resp_buff, RCAP16_REPLY_LEN, -1);
+                else
+                    dStrHex((const char *)resp_buff, RCAP16_REPLY_LEN, 1);
+                goto good;
+            }
+            llast_blk_addr = sg_get_unaligned_be64(resp_buff + 0);
+            block_size = sg_get_unaligned_be32(resp_buff + 8);
+            if (op->do_brief) {
+                printf("0x%" PRIx64 " 0x%" PRIx32 "\n", llast_blk_addr + 1,
+                       block_size);
+                goto good;
+            }
+            prot_en = !!(resp_buff[12] & 0x1);
+            p_type = ((resp_buff[12] >> 1) & 0x7);
+            printf("Read Capacity results:\n");
+            printf("   Protection: prot_en=%d, p_type=%d, p_i_exponent=%d",
+                   prot_en, p_type, ((resp_buff[13] >> 4) & 0xf));
+            if (prot_en)
+                printf(" [type %d protection]\n", p_type + 1);
+            else
+                printf("\n");
+            if (op->do_zbc) {
+                int rc_basis = (resp_buff[12] >> 4) & 0x3;
+
+                printf("   ZBC's rc_basis=%d [%s]\n", rc_basis,
+                       rc_basis_str(rc_basis, b, sizeof(b)));
+            }
+            printf("   Logical block provisioning: lbpme=%d, lbprz=%d\n",
+                   !!(resp_buff[14] & 0x80), !!(resp_buff[14] & 0x40));
+            if (op->do_pmi)
+                printf("   PMI mode: given lba=0x%" PRIx64 ", last lba "
+                       "before delay=0x%" PRIx64 "\n", op->llba,
+                       llast_blk_addr);
+            else
+                printf("   Last logical block address=%" PRIu64 " (0x%"
+                       PRIx64 "), Number of logical blocks=%" PRIu64 "\n",
+                       llast_blk_addr, llast_blk_addr, llast_blk_addr + 1);
+            printf("   Logical block length=%" PRIu32 " bytes\n", block_size);
+            lbppbe = resp_buff[13] & 0xf;
+            printf("   Logical blocks per physical block exponent=%d",
+                   lbppbe);
+            if (lbppbe > 0)
+                printf(" [so physical block length=%u bytes]\n",
+                       block_size * (1 << lbppbe));
+            else
+                printf("\n");
+            printf("   Lowest aligned logical block address=%d\n",
+                   ((resp_buff[14] & 0x3f) << 8) + resp_buff[15]);
+            if (! op->do_pmi) {
+                uint64_t total_sz = llast_blk_addr + 1;
+                double sz_mb, sz_gb;
+
+                total_sz *= block_size;
+                sz_mb = ((double)(llast_blk_addr + 1) * block_size) /
+                        (double)(1048576);
+                sz_gb = ((double)(llast_blk_addr + 1) * block_size) /
+                        (double)(1000000000L);
+                printf("Hence:\n");
+#ifdef SG_LIB_MINGW
+                printf("   Device size: %" PRIu64 " bytes, %g MiB, %g GB\n",
+                       total_sz, sz_mb, sz_gb);
+#else
+                printf("   Device size: %" PRIu64 " bytes, %.1f MiB, %.2f "
+                       "GB\n", total_sz, sz_mb, sz_gb);
+#endif
+            }
+            goto good;
+        } else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+            pr2serr("bad field in READ CAPACITY (16) cdb including "
+                    "unsupported service action\n");
+        else if (res) {
+            sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose);
+            pr2serr("READ CAPACITY (16) failed: %s\n", b);
+        }
+    }
+    if (op->do_brief)
+        printf("0x0 0x0\n");
+
+good:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_reassign.c b/sg3_utils/src/sg_reassign.c
new file mode 100644
index 0000000..d6882df
--- /dev/null
+++ b/sg3_utils/src/sg_reassign.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2005-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <limits.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ * This utility invokes the REASSIGN BLOCKS SCSI command to reassign
+ * an existing (possibly damaged) lba on a direct access device (e.g.
+ * a disk) to a new physical location. The previous contents is
+ * recoverable then it is written to the remapped lba otherwise
+ * vendor specific data is written.
+ */
+
+static const char * version_str = "1.17 20151207";
+
+#define DEF_DEFECT_LIST_FORMAT 4        /* bytes from index */
+
+#define MAX_NUM_ADDR 1024
+
+
+static struct option long_options[] = {
+        {"address", 1, 0, 'a'},
+        {"dummy", 0, 0, 'd'},
+        {"eight", 1, 0, 'e'},
+        {"grown", 0, 0, 'g'},
+        {"help", 0, 0, 'h'},
+        {"hex", 0, 0, 'H'},
+        {"longlist", 1, 0, 'l'},
+        {"primary", 0, 0, 'p'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_reassign [--address=A,A...] [--dummy] [--eight=0|1] "
+            "[--grown]\n"
+            "                   [--help] [--hex] [--longlist=0|1] "
+            "[--primary] [--verbose]\n"
+            "                   [--version] DEVICE\n"
+            "  where:\n"
+            "    --address=A,A...|-a A,A...    comma separated logical block "
+            "addresses\n"
+            "                                  one or more, assumed to be "
+            "decimal\n"
+            "    --address=-|-a -    read stdin for logical block "
+            "addresses\n"
+            "    --dummy|-d          prepare but do not execute REASSIGN "
+            "BLOCKS command\n"
+            "    --eight=0|1\n"
+            "      -e 0|1            force eight byte (64 bit) lbas "
+            "when 1,\n"
+            "                        four byte (32 bit) lbas when 0 "
+            "(def)\n"
+            "    --grown|-g          fetch grown defect list length, "
+            "don't reassign\n"
+            "    --help|-h           print out usage message\n"
+            "    --hex|-H            print response in hex (for '-g' or "
+            "'-p')\n"
+            "    --longlist=0|1\n"
+            "       -l 0|1           use 4 byte list length when 1, safe to "
+            "ignore\n"
+            "                        (def: 0 (2 byte list length))\n"
+            "    --primary|-p        fetch primary defect list length, "
+            "don't reassign\n"
+            "    --verbose|-v        increase verbosity\n"
+            "    --version|-V        print version string and exit\n\n"
+            "Perform a SCSI REASSIGN BLOCKS command (or READ DEFECT LIST)\n");
+}
+
+/* Trying to decode multipliers as sg_get_llnum() [in sg_libs] does would
+ * only confuse things here, so use this local trimmed version */
+static int64_t
+get_llnum(const char * buf)
+{
+    int res, len;
+    int64_t num;
+    uint64_t unum;
+
+    if ((NULL == buf) || ('\0' == buf[0]))
+        return -1LL;
+    len = strspn(buf, "0123456789aAbBcCdDeEfFhHxX");
+    if (0 == len)
+        return -1LL;
+    if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
+        res = sscanf(buf + 2, "%" SCNx64 "", &unum);
+        num = unum;
+    } else if ('H' == toupper(buf[len - 1])) {
+        res = sscanf(buf, "%" SCNx64 "", &unum);
+        num = unum;
+    } else
+        res = sscanf(buf, "%" SCNd64 "", &num);
+    if (1 == res)
+        return num;
+    else
+        return -1LL;
+}
+
+/* Read numbers (up to 64 bits in size) from command line (comma (or
+ * (single) space) separated list) or from stdin (one per line, comma
+ * separated list or space separated list). Assumed decimal unless prefixed
+ * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
+ * Returns 0 if ok, or 1 if error. */
+static int
+build_lba_arr(const char * inp, uint64_t * lba_arr,
+              int * lba_arr_len, int max_arr_len)
+{
+    int in_len, k, j, m;
+    const char * lcp;
+    int64_t ll;
+    char * cp;
+    char * c2p;
+
+    if ((NULL == inp) || (NULL == lba_arr) ||
+        (NULL == lba_arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *lba_arr_len = 0;
+    if ('-' == inp[0]) {        /* read from stdin */
+        char line[1024];
+        int off = 0;
+
+        for (j = 0; j < 512; ++j) {
+            if (NULL == fgets(line, sizeof(line), stdin))
+                break;
+            // could improve with carry_over logic if sizeof(line) too small
+            in_len = strlen(line);
+            if (in_len > 0) {
+                if ('\n' == line[in_len - 1]) {
+                    --in_len;
+                    line[in_len] = '\0';
+                }
+            }
+            if (in_len < 1)
+                continue;
+            lcp = line;
+            m = strspn(lcp, " \t");
+            if (m == in_len)
+                continue;
+            lcp += m;
+            in_len -= m;
+            if ('#' == *lcp)
+                continue;
+            k = strspn(lcp, "0123456789aAbBcCdDeEfFhHxX ,\t");
+            if ((k < in_len) && ('#' != lcp[k])) {
+                pr2serr("build_lba_arr: syntax error at line %d, pos %d\n",
+                        j + 1, m + k + 1);
+                return 1;
+            }
+            for (k = 0; k < 1024; ++k) {
+                ll = get_llnum(lcp);
+                if (-1 != ll) {
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("build_lba_arr: array length exceeded\n");
+                        return 1;
+                    }
+                    lba_arr[off + k] = (uint64_t)ll;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if ('#' == *lcp) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("build_lba_arr: error in line %d, at pos %d\n",
+                            j + 1, (int)(lcp - line + 1));
+                    return 1;
+                }
+            }
+            off += (k + 1);
+        }
+        *lba_arr_len = off;
+    } else {        /* list of numbers (default decimal) on command line */
+        k = strspn(inp, "0123456789aAbBcCdDeEfFhHxX, ");
+        if (in_len != k) {
+            pr2serr("build_lba_arr: error at pos %d\n", k + 1);
+            return 1;
+        }
+        for (k = 0; k < max_arr_len; ++k) {
+            ll = get_llnum(lcp);
+            if (-1 != ll) {
+                lba_arr[k] = (uint64_t)ll;
+                cp = (char *)strchr(lcp, ',');
+                c2p = (char *)strchr(lcp, ' ');
+                if (NULL == cp)
+                    cp = c2p;
+                if (NULL == cp)
+                    break;
+                if (c2p && (c2p < cp))
+                    cp = c2p;
+                lcp = cp + 1;
+            } else {
+                pr2serr("build_lba_arr: error at pos %d\n",
+                        (int)(lcp - inp + 1));
+                return 1;
+            }
+        }
+        *lba_arr_len = k + 1;
+        if (k == max_arr_len) {
+            pr2serr("build_lba_arr: array length exceeded\n");
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, num, k, j;
+    int dummy = 0;
+    int got_addr = 0;
+    int eight = -1;
+    int addr_arr_len = 0;
+    int grown = 0;
+    int do_hex = 0;
+    int longlist = 0;
+    int primary = 0;
+    int verbose = 0;
+    const char * device_name = NULL;
+    uint64_t addr_arr[MAX_NUM_ADDR];
+    unsigned char param_arr[4 + (MAX_NUM_ADDR * 8)];
+    char b[80];
+    int param_len = 4;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "a:de:ghHl:pvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            memset(addr_arr, 0, sizeof(addr_arr));
+            if (0 != build_lba_arr(optarg, addr_arr, &addr_arr_len,
+                                   MAX_NUM_ADDR)) {
+                pr2serr("bad argument to '--address'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            got_addr = 1;
+            break;
+        case 'd':
+            dummy = 1;
+            break;
+        case 'e':
+            num = sscanf(optarg, "%d", &res);
+            if ((1 == num) && ((0 == res) || (1 == res)))
+                eight = res;
+            else {
+                pr2serr("value for '--eight=' must be 0 or 1\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'g':
+            grown = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'l':
+            num = sscanf(optarg, "%d", &res);
+            if ((1 == num) && ((0 == res) || (1 == res)))
+                longlist = res;
+            else {
+                pr2serr("value for '--longlist=' must be 0 or 1\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'p':
+            primary = 1;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (grown || primary) {
+        if (got_addr) {
+            pr2serr("can't have '--address=' with '--grown' or '--primary'\n");
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    } else if ((0 == got_addr) || (addr_arr_len < 1)) {
+        pr2serr("need at least one address (see '--address=')\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (got_addr) {
+        for (k = 0; k < addr_arr_len; ++k) {
+            if (addr_arr[k] >= ULONG_MAX) {
+                if (eight < 0) {
+                    eight = 1;
+                    break;
+                } else if (0 == eight) {
+                    pr2serr("address number %d exceeds 32 bits so "
+                            "'--eight=0' invalid\n", k + 1);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+        }
+        if (eight < 0)
+            eight = 0;
+
+        k = 4;
+        for (j = 0; j < addr_arr_len; ++j) {
+            if (eight) {
+                sg_put_unaligned_be64(addr_arr[j], param_arr + k);
+                k += 8;
+            } else {
+                sg_put_unaligned_be32((uint32_t)addr_arr[j], param_arr + k);
+                k += 4;
+            }
+        }
+        param_len = k;
+        k -= 4;
+        if (longlist)
+            sg_put_unaligned_be32((uint32_t)k, param_arr + 0);
+        else
+            sg_put_unaligned_be16((uint16_t)k, param_arr + 2);
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (got_addr) {
+        if (dummy) {
+            pr2serr(">>> dummy: REASSIGN BLOCKS not executed\n");
+            if (verbose) {
+                pr2serr("  Would have reassigned these blocks:\n");
+                for (j = 0; j < addr_arr_len; ++j)
+                    printf("    0x%" PRIx64 "\n", addr_arr[j]);
+            }
+            return 0;
+        }
+        res = sg_ll_reassign_blocks(sg_fd, eight, longlist, param_arr,
+                                    param_len, 1, verbose);
+        ret = res;
+        if (res) {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("REASSIGN BLOCKS: %s\n", b);
+            goto err_out;
+        }
+    } else /* if (grown || primary) */ {
+        int dl_format = DEF_DEFECT_LIST_FORMAT;
+        int div = 0;
+        int dl_len, got_grown, got_primary;
+        const char * lstp;
+
+        param_len = 4;
+        memset(param_arr, 0, param_len);
+        res = sg_ll_read_defect10(sg_fd, primary, grown, dl_format,
+                                  param_arr, param_len, 0, verbose);
+        ret = res;
+        if (res) {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("READ DEFECT DATA(10): %s\n", b);
+            goto err_out;
+        }
+        if (do_hex) {
+            dStrHex((const char *)param_arr, param_len, 1);
+            goto err_out;       /* ret is zero */
+        }
+        lstp = "";
+        got_grown = !!(param_arr[1] & 0x8);
+        got_primary = !!(param_arr[1] & 0x10);
+        if (got_grown && got_primary)
+            lstp = "grown and primary defect lists";
+        else if (got_grown)
+            lstp = "grown defect list";
+        else if (got_primary)
+            lstp = "primary defect list";
+        else {
+            pr2serr("didn't get grown or primary list in response\n");
+            goto err_out;
+        }
+        if (verbose)
+            pr2serr("asked for defect list format %d, got %d\n", dl_format,
+                    (param_arr[1] & 0x7));
+        dl_format = (param_arr[1] & 0x7);
+        switch (dl_format) {
+            case 0:     /* short block */
+                div = 4;
+                break;
+            case 3:     /* long block */
+            case 4:     /* bytes from index */
+            case 5:     /* physical sector */
+                div = 8;
+                break;
+            default:
+                pr2serr("defect list format %d unknown\n", dl_format);
+                break;
+        }
+        dl_len = sg_get_unaligned_be16(param_arr + 2);
+        if (0 == dl_len)
+            printf(">> Elements in %s: 0\n", lstp);
+        else {
+            if (0 == div)
+                printf(">> %s length=%d bytes [unknown number of elements]\n",
+                       lstp, dl_len);
+            else
+                printf(">> Elements in %s: %d\n", lstp,
+                       dl_len / div);
+        }
+    }
+
+err_out:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_referrals.c b/sg3_utils/src/sg_referrals.c
new file mode 100644
index 0000000..de65010
--- /dev/null
+++ b/sg3_utils/src/sg_referrals.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2010-2016 Hannes Reinecke.
+ * 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/*
+ * A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI REPORT REFERRALS command to the given
+ * SCSI device.
+ */
+
+static const char * version_str = "1.06 20160131";    /* sbc4r10 */
+
+#define MAX_REFER_BUFF_LEN (1024 * 1024)
+#define DEF_REFER_BUFF_LEN 256
+
+#define TPGS_STATE_OPTIMIZED 0x0
+#define TPGS_STATE_NONOPTIMIZED 0x1
+#define TPGS_STATE_STANDBY 0x2
+#define TPGS_STATE_UNAVAILABLE 0x3
+#define TPGS_STATE_LB_DEPENDENT 0x4
+#define TPGS_STATE_OFFLINE 0xe          /* SPC-4 rev 9 */
+#define TPGS_STATE_TRANSITIONING 0xf
+
+static unsigned char referralBuff[DEF_REFER_BUFF_LEN];
+static unsigned char * referralBuffp = referralBuff;
+
+static const char *decode_tpgs_state(const int st)
+{
+    switch (st) {
+    case TPGS_STATE_OPTIMIZED:
+        return "active/optimized";
+        break;
+    case TPGS_STATE_NONOPTIMIZED:
+        return "active/non optimized";
+        break;
+    case TPGS_STATE_STANDBY:
+        return "standby";
+        break;
+    case TPGS_STATE_UNAVAILABLE:
+        return "unavailable";
+        break;
+    case TPGS_STATE_LB_DEPENDENT:
+        return "logical block dependent";
+        break;
+    case TPGS_STATE_OFFLINE:
+        return "offline";
+        break;
+    case TPGS_STATE_TRANSITIONING:
+        return "transitioning between states";
+        break;
+    default:
+        return "unknown";
+        break;
+    }
+}
+
+static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"lba", required_argument, 0, 'l'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"one-segment", no_argument, 0, 's'},
+        {"raw", no_argument, 0, 'r'},
+        {"readonly", no_argument, 0, 'R'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_referrals  [--help] [--hex] [--lba=LBA] "
+            "[--maxlen=LEN]\n"
+            "                     [--one-segment] [--raw] [--readonly] "
+            "[--verbose]\n"
+            "                     [--version] DEVICE\n"
+            "  where:\n"
+            "    --help|-h         print out usage message\n"
+            "    --hex|-H          output in hexadecimal\n"
+            "    --lba=LBA|-l LBA    starting LBA (logical block address) "
+            "(def: 0)\n"
+            "    --maxlen=LEN|-m LEN    max response length (allocation "
+            "length in cdb)\n"
+            "                           (def: 0 -> %d bytes)\n",
+            DEF_REFER_BUFF_LEN );
+    pr2serr("    --one-segment|-s    return information about the specified "
+            "segment only\n"
+            "    --raw|-r          output in binary\n"
+            "    --verbose|-v      increase verbosity\n"
+            "    --version|-V      print version string and exit\n\n"
+            "Performs a SCSI REPORT REFERRALS command (SBC-3)\n"
+            );
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* Decodes given user data referral segment descriptor
+ * the number of blocks and returns the number of bytes processed,
+ * -1 for error.
+ */
+static int
+decode_referral_desc(const unsigned char * ucp, int bytes)
+{
+    int j, n;
+    uint64_t first, last;
+
+    if (NULL == ucp)
+        return -1;
+
+    if (bytes < 20)
+        return -1;
+
+    first = sg_get_unaligned_be64(ucp + 4);
+    last = sg_get_unaligned_be64(ucp + 12);
+
+    printf("    target port descriptors: %d\n", ucp[3]);
+    printf("    user data segment: first lba %" PRIu64 ", last lba %"
+          PRIu64 "\n", first, last);
+    n = 20;
+    bytes -= n;
+    for (j = 0; j < ucp[3]; j++) {
+        if (bytes < 4)
+            return -1;
+        printf("      target port descriptor %d:\n", j);
+        printf("        port group %x state (%s)\n",
+               sg_get_unaligned_be16(ucp + n + 2),
+               decode_tpgs_state(ucp[n] & 0xf));
+        n += 4;
+        bytes -= 4;
+    }
+    return n;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, res, c, rlen;
+    int do_hex = 0;
+    int do_one_segment = 0;
+    int o_readonly = 0;
+    int64_t ll;
+    uint64_t lba = 0;
+    int maxlen = DEF_REFER_BUFF_LEN;
+    int do_raw = 0;
+    int verbose = 0;
+    int desc = 0;
+    const char * device_name = NULL;
+    const unsigned char * ucp;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hHl:m:rRsvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'l':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            lba = (uint64_t)ll;
+            break;
+        case 'm':
+            maxlen = sg_get_num(optarg);
+            if ((maxlen < 0) || (maxlen > MAX_REFER_BUFF_LEN)) {
+                pr2serr("argument to '--maxlen' should be %d or less\n",
+                        MAX_REFER_BUFF_LEN);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 's':
+            ++do_one_segment;
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 'R':
+            ++o_readonly;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("No DEVICE argument given\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (maxlen > DEF_REFER_BUFF_LEN) {
+        referralBuffp = (unsigned char *)calloc(maxlen, 1);
+        if (NULL == referralBuffp) {
+            pr2serr("unable to allocate %d bytes on heap\n", maxlen);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            ret = SG_LIB_FILE_ERROR;
+            goto free_buff;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        ret = SG_LIB_FILE_ERROR;
+        goto free_buff;
+    }
+
+    res = sg_ll_report_referrals(sg_fd, lba, do_one_segment, referralBuffp,
+                                 maxlen, 1, verbose);
+    ret = res;
+    if (0 == res) {
+        if (maxlen >= 4)
+            /*
+             * This is strictly speaking incorrect. However, the
+             * spec reserved bytes 0 and 1, so some implementations
+             * might want to use them to increase the number of
+             * possible user segments.
+             * And maybe someone takes a pity and updates the spec ...
+             */
+            rlen = sg_get_unaligned_be32(referralBuffp + 0) + 4;
+        else
+            rlen = maxlen;
+        k = (rlen > maxlen) ? maxlen : rlen;
+        if (do_raw) {
+            dStrRaw((const char *)referralBuffp, k);
+            goto the_end;
+        }
+        if (do_hex) {
+            dStrHex((const char *)referralBuffp, k, 1);
+            goto the_end;
+        }
+        if (maxlen < 4) {
+            if (verbose)
+                pr2serr("Exiting because allocation length (maxlen)  less "
+                        "than 4\n");
+            goto the_end;
+        }
+        if ((verbose > 1) || (verbose && (rlen > maxlen))) {
+            pr2serr("response length %d bytes\n", rlen);
+            if (rlen > maxlen)
+                pr2serr("  ... which is greater than maxlen (allocation "
+                        "length %d), truncation\n", maxlen);
+        }
+        if (rlen > maxlen)
+            rlen = maxlen;
+
+        ucp = referralBuffp + 4;
+        k = 0;
+        printf("Report referrals:\n");
+        while (k < rlen - 4) {
+            printf("  descriptor %d:\n", desc);
+            res = decode_referral_desc(ucp + k, rlen - 4 - k);
+            if (res < 0) {
+                pr2serr("bad user data segment referral descriptor\n");
+                k = rlen - 4;
+                break;
+            }
+            k += res;
+            desc++;
+        }
+    } else {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Report Referrals command failed: %s\n", b);
+    }
+
+the_end:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            ret = SG_LIB_FILE_ERROR;
+    }
+free_buff:
+    if (referralBuffp && (referralBuffp != referralBuff))
+        free(referralBuffp);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_rep_zones.c b/sg3_utils/src/sg_rep_zones.c
new file mode 100644
index 0000000..bdb2d4a
--- /dev/null
+++ b/sg3_utils/src/sg_rep_zones.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2014-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI REPORT ZONES command to the given SCSI device
+ * and decodes the response. Based on zbc-r02.pdf
+ */
+
+static const char * version_str = "1.08 20160203";
+
+#define MAX_RZONES_BUFF_LEN (1024 * 1024)
+#define DEF_RZONES_BUFF_LEN (1024 * 8)
+
+#define SG_ZONING_IN_CMDLEN 16
+
+#define REPORT_ZONES_SA 0x0
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT  60      /* 60 seconds */
+
+
+static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"partial", no_argument, 0, 'p'},
+        {"raw", no_argument, 0, 'r'},
+        {"readonly", no_argument, 0, 'R'},
+        {"report", required_argument, 0, 'o'},
+        {"start", required_argument, 0, 's'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+            "sg_rep_zones  [--help] [--hex] [--maxlen=LEN] [--partial]\n"
+            "                     [--raw] [--readonly] [--report=OPT] "
+            "[--start=LBA]\n"
+            "                     [--verbose] [--version] DEVICE\n");
+    pr2serr("  where:\n"
+            "    --help|-h          print out usage message\n"
+            "    --hex|-H           output response in hexadecimal; used "
+            "twice\n"
+            "                       shows decoded values in hex\n"
+            "    --maxlen=LEN|-m LEN    max response length (allocation "
+            "length in cdb)\n"
+            "                           (def: 0 -> 8192 bytes)\n"
+            "    --partial|-p       sets PARTIAL bit in cdb\n"
+            "    --raw|-r           output response in binary\n"
+            "    --readonly|-R      open DEVICE read-only (def: read-write)\n"
+            "    --report=OPT|-o OP    reporting options (def: 0: all "
+            "zones)\n"
+            "    --start=LBA|-s LBA    report zones from the LBA (def: 0)\n"
+            "                          need not be a zone starting LBA\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "Performs a SCSI REPORT ZONES command.\n");
+}
+
+/* Invokes a SCSI REPORT ZONES command (ZBC).  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_report_zones(int sg_fd, uint64_t zs_lba, int partial, int report_opts,
+                   void * resp, int mx_resp_len, int * residp, int noisy,
+                   int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rzCmdBlk[SG_ZONING_IN_CMDLEN] =
+          {SG_ZONING_IN, REPORT_ZONES_SA, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be64(zs_lba, rzCmdBlk + 2);
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rzCmdBlk + 10);
+    rzCmdBlk[14] = report_opts & 0x3f;
+    if (partial)
+        rzCmdBlk[14] |= 0x80;
+    if (verbose) {
+        pr2serr("    Report zones cdb: ");
+        for (k = 0; k < SG_ZONING_IN_CMDLEN; ++k)
+            pr2serr("%02x ", rzCmdBlk[k]);
+        pr2serr("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("%s: out of memory\n", __func__);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rzCmdBlk, sizeof(rzCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "report zones", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    if (residp)
+        *residp = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+static const char *
+zone_type_str(int zt, char * b, int blen, int vb)
+{
+    const char * cp;
+
+    if (NULL == b)
+        return "zone_type_str: NULL ptr)";
+    switch (zt) {
+    case 1:
+        cp = "Conventional";
+        break;
+    case 2:
+        cp = "Sequential write required";
+        break;
+    case 3:
+        cp = "Sequential write preferred";
+        break;
+    default:
+        cp = NULL;
+        break;
+    }
+    if (cp) {
+        if (vb)
+            snprintf(b, blen, "%s [0x%x]", cp, zt);
+        else
+            snprintf(b, blen, "%s", cp);
+    } else
+        snprintf(b, blen, "Reserved [0x%x]", zt);
+    return b;
+}
+
+static const char *
+zone_condition_str(int zc, char * b, int blen, int vb)
+{
+    const char * cp;
+
+    if (NULL == b)
+        return "zone_condition_str: NULL ptr)";
+    switch (zc) {
+    case 0:
+        cp = "Not write pointer";
+        break;
+    case 1:
+        cp = "Empty";
+        break;
+    case 2:
+        cp = "Implicitly opened";
+        break;
+    case 3:
+        cp = "Explicitly opened";
+        break;
+    case 4:
+        cp = "Closed";
+        break;
+    case 0xd:
+        cp = "Read only";
+        break;
+    case 0xe:
+        cp = "Full";
+        break;
+    case 0xf:
+        cp = "Offline";
+        break;
+    default:
+        cp = NULL;
+        break;
+    }
+    if (cp) {
+        if (vb)
+            snprintf(b, blen, "%s [0x%x]", cp, zc);
+        else
+            snprintf(b, blen, "%s", cp);
+    } else
+        snprintf(b, blen, "Reserved [0x%x]", zc);
+    return b;
+}
+
+static const char * same_desc_arr[16] = {
+    "zone type and length may differ in each descriptor",
+    "zone type and length same in each descriptor",
+    "zone type and length same apart from length in last descriptor",
+    "zone type for each descriptor may be different",
+    "Reserved [0x4]", "Reserved [0x5]", "Reserved [0x6]", "Reserved [0x7]",
+    "Reserved [0x8]", "Reserved [0x9]", "Reserved [0xa]", "Reserved [0xb]",
+    "Reserved [0xc]", "Reserved [0xd]", "Reserved [0xe]", "Reserved [0xf]",
+};
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, res, c, zl_len, len, zones, resid, rlen, zt, zc, same;
+    int do_hex = 0;
+    int maxlen = 0;
+    int do_partial = 0;
+    int do_raw = 0;
+    int o_readonly = 0;
+    int reporting_opt = 0;
+    int verbose = 0;
+    uint64_t st_lba = 0;
+    int64_t ll;
+    const char * device_name = NULL;
+    unsigned char * reportZonesBuff = NULL;
+    unsigned char * ucp;
+    int ret = 0;
+    char b[80];
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hHm:o:prRs:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'm':
+            maxlen = sg_get_num(optarg);
+            if ((maxlen < 0) || (maxlen > MAX_RZONES_BUFF_LEN)) {
+                pr2serr("argument to '--maxlen' should be %d or "
+                        "less\n", MAX_RZONES_BUFF_LEN);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'o':
+           reporting_opt = sg_get_num(optarg);
+           if ((reporting_opt < 0) || (reporting_opt > 63)) {
+                pr2serr("bad argument to '--report=OPT', expect 0 to "
+                        "63\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'p':
+            ++do_partial;
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 'R':
+            ++o_readonly;
+            break;
+        case 's':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--start=LBA'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            st_lba = (uint64_t)ll;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (0 == maxlen)
+        maxlen = DEF_RZONES_BUFF_LEN;
+    reportZonesBuff = (unsigned char *)calloc(1, maxlen);
+    if (NULL == reportZonesBuff) {
+        pr2serr("unable to malloc %d bytes\n", maxlen);
+        return SG_LIB_CAT_OTHER;
+    }
+
+    res = sg_ll_report_zones(sg_fd, st_lba, do_partial, reporting_opt,
+                             reportZonesBuff, maxlen, &resid, 1, verbose);
+    ret = res;
+    if (0 == res) {
+        rlen = maxlen - resid;
+        if (rlen < 4) {
+            pr2serr("Response length (%d) too short\n", rlen);
+            ret = SG_LIB_CAT_MALFORMED;
+            goto the_end;
+        }
+        zl_len = sg_get_unaligned_be32(reportZonesBuff + 0) + 64;
+        if (zl_len > rlen) {
+            if (verbose)
+                pr2serr("zl_len available is %d, response length is %d\n",
+                        zl_len, rlen);
+            len = rlen;
+        } else
+            len = zl_len;
+        if (do_raw) {
+            dStrRaw((const char *)reportZonesBuff, len);
+            goto the_end;
+        }
+        if (do_hex && (2 != do_hex)) {
+            dStrHex((const char *)reportZonesBuff, len,
+                    ((1 == do_hex) ? 1 : -1));
+            goto the_end;
+        }
+        printf("Report zones response:\n");
+        if (len < 64) {
+            pr2serr("Zone length [%d] too short (perhaps after truncation\n)",
+                    len);
+            ret = SG_LIB_CAT_MALFORMED;
+            goto the_end;
+        }
+        same = reportZonesBuff[4] & 0xf;
+        printf("  Same=%d: %s\n\n", same, same_desc_arr[same]);
+        printf("  Maximum LBA: 0x%" PRIx64 "\n",
+               sg_get_unaligned_be64(reportZonesBuff + 8));
+        zones = (len - 64) / 64;
+        for (k = 0, ucp = reportZonesBuff + 64; k < zones; ++k, ucp += 64) {
+            printf(" Zone descriptor: %d\n", k);
+            if (do_hex) {
+                dStrHex((const char *)ucp, len, -1);
+                continue;
+            }
+            zt = ucp[0] & 0xf;
+            zc = (ucp[1] >> 4) & 0xf;
+            printf("   Zone type: %s\n", zone_type_str(zt, b, sizeof(b),
+                   verbose));
+            printf("   Zone condition: %s\n", zone_condition_str(zc, b,
+                   sizeof(b), verbose));
+            printf("   Non_seq: %d\n", !!(ucp[1] & 0x2));
+            printf("   Reset: %d\n", ucp[1] & 0x1);
+            printf("   Zone Length: 0x%" PRIx64 "\n",
+                   sg_get_unaligned_be64(ucp + 8));
+            printf("   Zone start LBA: 0x%" PRIx64 "\n",
+                   sg_get_unaligned_be64(ucp + 16));
+            printf("   Write pointer LBA: 0x%" PRIx64 "\n",
+                   sg_get_unaligned_be64(ucp + 24));
+        }
+        if ((64 + (64 * zones)) < zl_len)
+            printf("\n>>> Beware: Zone list truncated, may need another "
+                   "call\n");
+    } else if (SG_LIB_CAT_INVALID_OP == res)
+        pr2serr("Report zones command not supported\n");
+    else {
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Report zones command: %s\n", b);
+    }
+
+the_end:
+    if (reportZonesBuff)
+        free(reportZonesBuff);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_requests.c b/sg3_utils/src/sg_requests.c
new file mode 100644
index 0000000..29c611c
--- /dev/null
+++ b/sg3_utils/src/sg_requests.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2004-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pr2serr.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI command REQUEST SENSE to the given SCSI device.
+ */
+
+static const char * version_str = "1.26 20151219";
+
+#define MAX_REQS_RESP_LEN 255
+#define DEF_REQS_RESP_LEN 252
+
+/* Not all environments support the Unix sleep() */
+#if defined(MSC_VER) || defined(__MINGW32__)
+#define HAVE_MS_SLEEP
+#endif
+#ifdef HAVE_MS_SLEEP
+#include <windows.h>
+#define sleep_for(seconds)    Sleep( (seconds) * 1000)
+#else
+#define sleep_for(seconds)    sleep(seconds)
+#endif
+
+#define ME "sg_requests: "
+
+
+static struct option long_options[] = {
+        {"desc", no_argument, 0, 'd'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"num", required_argument, 0, 'n'},
+        {"progress", no_argument, 0, 'p'},
+        {"raw", no_argument, 0, 'r'},
+        {"status", no_argument, 0, 's'},
+        {"time", no_argument, 0, 't'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_requests [--desc] [--help] [--hex] [--maxlen=LEN] "
+            "[--num=NUM]\n"
+            "                   [--progress] [--raw] [--status] [--time] "
+            "[--verbose]\n"
+            "                   [--version] DEVICE\n"
+            "  where:\n"
+            "    --desc|-d         set flag for descriptor sense "
+            "format\n"
+            "    --help|-h         print out usage message\n"
+            "    --hex|-H          output in hexadecimal\n"
+            "    --maxlen=LEN|-m LEN    max response length (allocation "
+            "length in cdb)\n"
+            "                           (def: 0 -> 252 bytes)\n"
+            "    --num=NUM|-n NUM  number of REQUEST SENSE commands "
+            "to send (def: 1)\n"
+            "    --progress|-p     output a progress indication (percentage) "
+            "if available\n"
+            "    --raw|-r          output in binary (to stdout)\n"
+            "    --status|-s       set exit status from parameter data "
+            "(def: only set\n"
+            "                       exit status from autosense)\n"
+            "    --time|-t         time the transfer, calculate commands "
+            "per second\n"
+            "    --verbose|-v      increase verbosity\n"
+            "    --version|-V      print version string and exit\n\n"
+            "Performs a SCSI REQUEST SENSE command\n"
+            );
+
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, resp_len, k, progress;
+    unsigned char requestSenseBuff[MAX_REQS_RESP_LEN + 1];
+    int desc = 0;
+    int num_rs = 1;
+    int do_hex = 0;
+    int maxlen = 0;
+    int do_progress = 0;
+    int do_raw = 0;
+    int do_status = 0;
+    int verbose = 0;
+    const char * device_name = NULL;
+    int ret = 0;
+    char b[80];
+#ifndef SG_LIB_MINGW
+    int do_time = 0;
+    struct timeval start_tm, end_tm;
+#endif
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "dhHm:n:prstvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'd':
+            desc = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'm':
+            maxlen = sg_get_num(optarg);
+            if ((maxlen < 0) || (maxlen > MAX_REQS_RESP_LEN)) {
+                pr2serr("argument to '--maxlen' should be %d or less\n",
+                        MAX_REQS_RESP_LEN);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'n':
+           num_rs = sg_get_num(optarg);
+           if (num_rs < 1) {
+                pr2serr("bad argument to '--num'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'p':
+            ++do_progress;
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 's':
+            do_status = 1;
+            break;
+        case 't':
+#ifndef SG_LIB_MINGW
+            do_time = 1;
+#endif
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (0 == maxlen)
+        maxlen = DEF_REQS_RESP_LEN;
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 1 /* ro */, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (do_progress) {
+        for (k = 0; k < num_rs; ++k) {
+            if (k > 0)
+                sleep_for(30);
+            memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
+            res = sg_ll_request_sense(sg_fd, desc, requestSenseBuff, maxlen,
+                                      1, verbose);
+            if (res) {
+                ret = res;
+                if (SG_LIB_CAT_INVALID_OP == res)
+                    pr2serr("Request Sense command not supported\n");
+                else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+                    pr2serr("bad field in Request Sense cdb\n");
+                else if (SG_LIB_CAT_ABORTED_COMMAND == res)
+                    pr2serr("Request Sense, aborted command\n");
+                else {
+                    sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                    pr2serr("Request Sense command: %s\n", b);
+                }
+                break;
+            }
+            /* "Additional sense length" same in descriptor and fixed */
+            resp_len = requestSenseBuff[7] + 8;
+            if (verbose > 1) {
+                pr2serr("Parameter data in hex\n");
+                dStrHexErr((const char *)requestSenseBuff, resp_len, 1);
+            }
+            progress = -1;
+            sg_get_sense_progress_fld(requestSenseBuff, resp_len,
+                                      &progress);
+            if (progress < 0) {
+                ret = res;
+                if (verbose > 1)
+                     pr2serr("No progress indication found, iteration %d\n",
+                             k + 1);
+                /* N.B. exits first time there isn't a progress indication */
+                break;
+            } else
+                printf("Progress indication: %d.%02d%% done\n",
+                       (progress * 100) / 65536,
+                       ((progress * 100) % 65536) / 656);
+        }
+        goto finish;
+    }
+
+#ifndef SG_LIB_MINGW
+    if (do_time) {
+        start_tm.tv_sec = 0;
+        start_tm.tv_usec = 0;
+        gettimeofday(&start_tm, NULL);
+    }
+#endif
+
+    requestSenseBuff[0] = '\0';
+    requestSenseBuff[7] = '\0';
+    for (k = 0; k < num_rs; ++k) {
+        memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
+        res = sg_ll_request_sense(sg_fd, desc, requestSenseBuff, maxlen,
+                                  1, verbose);
+        ret = res;
+        if (0 == res) {
+            resp_len = requestSenseBuff[7] + 8;
+            if (do_raw)
+                dStrRaw((const char *)requestSenseBuff, resp_len);
+            else if (do_hex)
+                dStrHex((const char *)requestSenseBuff, resp_len, 1);
+            else if (1 == num_rs) {
+                pr2serr("Decode parameter data as sense data:\n");
+                sg_print_sense(NULL, requestSenseBuff, resp_len, 0);
+                if (verbose > 1) {
+                    pr2serr("\nParameter data in hex\n");
+                    dStrHexErr((const char *)requestSenseBuff, resp_len, 1);
+                }
+            }
+            continue;
+        } else if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr("Request Sense command not supported\n");
+        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+            pr2serr("bad field in Request Sense cdb\n");
+        else if (SG_LIB_CAT_ABORTED_COMMAND == res)
+            pr2serr("Request Sense, aborted command\n");
+        else {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("Request Sense command: %s\n", b);
+        }
+        break;
+    }
+    if ((0 == ret) && do_status) {
+        resp_len = requestSenseBuff[7] + 8;
+        ret = sg_err_category_sense(requestSenseBuff, resp_len);
+        if (SG_LIB_CAT_NO_SENSE == ret) {
+            struct sg_scsi_sense_hdr ssh;
+
+            if (sg_scsi_normalize_sense(requestSenseBuff, resp_len, &ssh)) {
+                if ((0 == ssh.asc) && (0 == ssh.ascq))
+                    ret = 0;
+            }
+        }
+    }
+#ifndef SG_LIB_MINGW
+    if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
+        struct timeval res_tm;
+        double a, b;
+
+        gettimeofday(&end_tm, NULL);
+        res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+        res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+        if (res_tm.tv_usec < 0) {
+            --res_tm.tv_sec;
+            res_tm.tv_usec += 1000000;
+        }
+        a = res_tm.tv_sec;
+        a += (0.000001 * res_tm.tv_usec);
+        b = (double)num_rs;
+        printf("time to perform commands was %d.%06d secs",
+               (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+        if (a > 0.00001)
+            printf("; %.2f operations/sec\n", b / a);
+        else
+            printf("\n");
+    }
+#endif
+
+finish:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_reset.c b/sg3_utils/src/sg_reset.c
new file mode 100644
index 0000000..59c5498
--- /dev/null
+++ b/sg3_utils/src/sg_reset.c
@@ -0,0 +1,303 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *  Copyright (C) 1999-2015 D. Gilbert
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ * This program send either device, bus or host resets to device,
+ * or bus or host associated with the given sg device.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_io_linux.h"
+
+
+#define ME "sg_reset: "
+
+static const char * version_str = "0.60 20151219";
+
+#ifndef SG_SCSI_RESET
+#define SG_SCSI_RESET 0x2284
+#endif
+
+#ifndef SG_SCSI_RESET_NOTHING
+#define SG_SCSI_RESET_NOTHING 0
+#define SG_SCSI_RESET_DEVICE 1
+#define SG_SCSI_RESET_BUS 2
+#define SG_SCSI_RESET_HOST 3
+#endif
+
+#ifndef SG_SCSI_RESET_TARGET
+#define SG_SCSI_RESET_TARGET 4
+#endif
+
+#ifndef SG_SCSI_RESET_NO_ESCALATE
+#define SG_SCSI_RESET_NO_ESCALATE 0x100
+#endif
+
+static struct option long_options[] = {
+        {"bus", no_argument, 0, 'b'},
+        {"device", no_argument, 0, 'd'},
+        {"help", no_argument, 0, 'z'},
+        {"host", no_argument, 0, 'H'},
+        {"no-esc", no_argument, 0, 'N'},
+        {"no-escalate", no_argument, 0, 'N'},
+        {"target", no_argument, 0, 't'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+#ifdef __GNUC__
+static int pr2serr(const char * fmt, ...)
+        __attribute__ ((format (printf, 1, 2)));
+#else
+static int pr2serr(const char * fmt, ...);
+#endif
+
+
+static int
+pr2serr(const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    va_start(args, fmt);
+    n = vfprintf(stderr, fmt, args);
+    va_end(args);
+    return n;
+}
+
+static void
+usage(int compat_mode)
+{
+    pr2serr("Usage: sg_reset [--bus] [--device] [--help] [--host] [--no-esc] "
+            "[--target]\n"
+            "                [--verbose] [--version] DEVICE\n"
+            "  where:\n"
+            "    --bus|-b        SCSI bus reset (SPI concept), might be all "
+            "targets\n"
+            "    --device|-d     device (logical unit) reset\n");
+    if (compat_mode) {
+        pr2serr("    --help|-z       print usage information then exit\n"
+                "    --host|-h|-H    host (bus adapter: HBA) reset\n");
+    } else {
+        pr2serr("    --help|-h       print usage information then exit\n"
+                "    --host|-H       host (bus adapter: HBA) reset\n");
+    }
+    pr2serr("    --no-esc|-N     overrides default action and only does "
+            "reset requested\n"
+            "    --target|-t     target reset. The target holds the DEVICE "
+            "and perhaps\n"
+            "                    other LUs\n"
+            "    --verbose|-v    increase the level of verbosity\n"
+            "    --version|-V    print version number then exit\n\n"
+            "Use SG_SCSI_RESET ioctl to send a reset to the "
+            "host/bus/target/device\nalong the DEVICE path. The DEVICE "
+            "itself is known as a logical unit (LU)\nin SCSI terminology.\n"
+            "Be warned: if the '-N' option is not given then if '-d' "
+            "fails then a\ntarget reset ('-t') is instigated. And it "
+            "'-t' fails then a bus reset\n('-b') is instigated. And if "
+            "'-b' fails then a host reset ('h') is\ninstigated. It is "
+            "recommended to use '-N' to stop the reset escalation.\n"
+           );
+}
+
+
+int main(int argc, char * argv[])
+{
+    int c, sg_fd, res, k, hold_errno;
+    int do_device_reset = 0;
+    int do_bus_reset = 0;
+    int do_host_reset = 0;
+    int no_escalate = 0;
+    int do_target_reset = 0;
+    int verbose = 0;
+    char * device_name = NULL;
+    char * cp = NULL;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (NULL == cp)
+        cp = getenv("SG_RESET_OLD_OPTS");
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "bdhHNtvVz", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            ++do_bus_reset;
+            break;
+        case 'd':
+            ++do_device_reset;
+            break;
+        case 'h':
+            if (cp) {
+                ++do_host_reset;
+                break;
+            } else {
+                usage(!!cp);
+                return 0;
+            }
+        case 'H':
+            ++do_host_reset;
+            break;
+        case 'N':
+            ++no_escalate;
+            break;
+        case 't':
+            ++do_target_reset;
+            break;
+
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        case 'z':
+            usage(!!cp);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage(!!cp);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage(!!cp);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (NULL == device_name) {
+        pr2serr("Missing DEVICE name. Use '--help' to see usage.\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (cp && (0 == verbose))
+        ++verbose;      // older behaviour was more verbose
+
+    if ((!!do_device_reset + !!do_target_reset + !!do_bus_reset +
+         !!do_host_reset) > 1) {
+        pr2serr("Can only request one type of reset per invocation\n");
+        return 1;
+    }
+
+    sg_fd = open(device_name, O_RDWR | O_NONBLOCK);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: ", device_name);
+        perror("");
+        return 1;
+    }
+
+    k = SG_SCSI_RESET_NOTHING;
+    if (do_device_reset) {
+        if (verbose)
+            printf(ME "starting device reset\n");
+        k = SG_SCSI_RESET_DEVICE;
+    }
+    else if (do_target_reset) {
+        if (verbose)
+            printf(ME "starting target reset\n");
+        k = SG_SCSI_RESET_TARGET;
+    }
+    else if (do_bus_reset) {
+        if (verbose)
+            printf(ME "starting bus reset\n");
+        k = SG_SCSI_RESET_BUS;
+    }
+    else if (do_host_reset) {
+        if (verbose)
+            printf(ME "starting host reset\n");
+        k = SG_SCSI_RESET_HOST;
+    }
+    if (no_escalate)
+        k += SG_SCSI_RESET_NO_ESCALATE;
+    if (verbose > 2)
+        pr2serr("    third argument to ioctl(SG_SCSI_RESET) is 0x%x\n", k);
+
+    res = ioctl(sg_fd, SG_SCSI_RESET, &k);
+    if (res < 0) {
+        hold_errno = errno;
+        switch (errno) {
+        case EBUSY:
+            pr2serr(ME "BUSY, may be resetting now\n");
+            break;
+        case ENODEV:
+            pr2serr(ME "'no device' error, may be temporary while device is "
+                    "resetting\n");
+            break;
+        case EAGAIN:
+            pr2serr(ME "try again later, may be resetting now\n");
+            break;
+        case EIO:
+            pr2serr(ME "reset (for value=0x%x) may not be available\n", k);
+            break;
+        case EPERM:
+        case EACCES:
+            pr2serr(ME "reset requires CAP_SYS_ADMIN (root) permission\n");
+            break;
+        case EINVAL:
+            pr2serr(ME "SG_SCSI_RESET not supported (for value=0x%x)\n", k);
+        default:
+            perror(ME "SG_SCSI_RESET failed");
+            break;
+        }
+        if (verbose > 1)
+            pr2serr(ME "ioctl(SG_SCSI_RESET) returned %d, errno=%d\n", res,
+                    hold_errno);
+        close(sg_fd);
+        return 1;
+    }
+
+    if (no_escalate)
+        k -= SG_SCSI_RESET_NO_ESCALATE;
+    if (verbose) {
+        if (SG_SCSI_RESET_NOTHING == k)
+            printf(ME "did nothing, device is normal mode\n");
+        else if (SG_SCSI_RESET_DEVICE == k)
+            printf(ME "completed device %sreset\n", (no_escalate ?
+                    "" : "(or target or bus or host) "));
+        else if (SG_SCSI_RESET_TARGET == k)
+            printf(ME "completed target %sreset\n", (no_escalate ?
+                    "" : "(or bus or host) "));
+        else if (SG_SCSI_RESET_BUS == k)
+            printf(ME "completed bus %sreset\n", (no_escalate ?
+                    "" : "(or host) "));
+        else if (SG_SCSI_RESET_HOST == k)
+            printf(ME "completed host reset\n");
+    }
+
+    if (close(sg_fd) < 0) {
+        perror(ME "close error");
+        return 1;
+    }
+    return 0;
+}
diff --git a/sg3_utils/src/sg_reset_wp.c b/sg3_utils/src/sg_reset_wp.c
new file mode 100644
index 0000000..1e24eba
--- /dev/null
+++ b/sg3_utils/src/sg_reset_wp.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2014-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI RESET WRITE POINTER command to the given SCSI
+ * device. Based on zbc-r04c.pdf .
+ */
+
+static const char * version_str = "1.04 20151219";
+
+#define SG_ZONING_OUT_CMDLEN 16
+#define RESET_WRITE_POINTER_SA 0x4
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT  60      /* 60 seconds */
+
+
+static struct option long_options[] = {
+        {"all", no_argument, 0, 'a'},
+        {"help", no_argument, 0, 'h'},
+        {"reset-all", no_argument, 0, 'R'},
+        {"reset_all", no_argument, 0, 'R'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {"zone", required_argument, 0, 'z'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+            "sg_reset_wp  [--all] [--help] [--verbose] [--version]\n"
+            "                    [--zone=ID] DEVICE\n");
+    pr2serr("  where:\n"
+            "    --all|-a           sets the ALL flag in the cdb\n"
+            "    --help|-h          print out usage message\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "    --zone=ID|-z ID    ID is the starting LBA of the zone "
+            "whose\n"
+            "                       write pointer is to be reset\n"
+            "Performs a SCSI RESET WRITE POINTER command. ID is decimal by "
+            "default,\nfor hex use a leading '0x' or a trailing 'h'. "
+            "Either the --zone=ID\nor --all option needs to be given.\n");
+}
+
+/* Invokes a SCSI RESET WRITE POINTER command (ZBC).  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_reset_write_pointer(int sg_fd, uint64_t zid, int all, int noisy,
+                          int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rwpCmdBlk[SG_ZONING_OUT_CMDLEN] =
+          {SG_ZONING_OUT, RESET_WRITE_POINTER_SA, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0,  0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be64(zid, rwpCmdBlk + 2);
+    if (all)
+        rwpCmdBlk[14] = 0x1;
+    if (verbose) {
+        pr2serr("    Reset write pointer cdb: ");
+        for (k = 0; k < SG_ZONING_OUT_CMDLEN; ++k)
+            pr2serr("%02x ", rwpCmdBlk[k]);
+        pr2serr("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("Reset write pointer: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rwpCmdBlk, sizeof(rwpCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "reset write pointer", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c;
+    int all = 0;
+    int verbose = 0;
+    int zid_given = 0;
+    uint64_t zid = 0;
+    int64_t ll;
+    const char * device_name = NULL;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ahRvVz:", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+        case 'R':
+            ++all;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        case 'z':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--zone=ID'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            zid = (uint64_t)ll;
+            ++zid_given;
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if ((! zid_given) && (0 == all)) {
+        pr2serr("either the --zone=ID or --all option is required\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 0, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    res = sg_ll_reset_write_pointer(sg_fd, zid, all, 1, verbose);
+    ret = res;
+    if (res) {
+        if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr("Reset write pointer command not supported\n");
+        else {
+            char b[80];
+
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("Reset write pointer command: %s\n", b);
+        }
+    }
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_rmsn.c b/sg3_utils/src/sg_rmsn.c
new file mode 100644
index 0000000..42c9b6f
--- /dev/null
+++ b/sg3_utils/src/sg_rmsn.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2005-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program was originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI command READ MEDIA SERIAL NUMBER
+ * to the given SCSI device.
+ */
+
+static const char * version_str = "1.11 20151219";
+
+#define SERIAL_NUM_SANITY_LEN (16 * 1024)
+
+
+static struct option long_options[] = {
+        {"help", 0, 0, 'h'},
+        {"raw", 0, 0, 'r'},
+        {"readonly", 0, 0, 'R'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void usage()
+{
+    pr2serr("Usage: sg_rmsn   [--help] [--raw] [--readonly] [--verbose] "
+            "[--version]\n"
+            "                 DEVICE\n"
+            "  where:\n"
+            "    --help|-h       print out usage message\n"
+            "    --raw|-r        output serial number to stdout "
+            "(potentially binary)\n"
+            "    --readonly|-R    open DEVICE read-only (def: open it "
+            "read-write)\n"
+            "    --verbose|-v    increase verbosity\n"
+            "    --version|-V    print version string and exit\n\n"
+            "Performs a SCSI READ MEDIA SERIAL NUMBER command\n");
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, res, c, sn_len, n;
+    unsigned char rmsn_buff[4];
+    unsigned char * ucp = NULL;
+    int raw = 0;
+    int readonly = 0;
+    int verbose = 0;
+    const char * device_name = NULL;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hrRvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'r':
+            ++raw;
+            break;
+        case 'R':
+            ++readonly;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    memset(rmsn_buff, 0x0, sizeof(rmsn_buff));
+
+    res = sg_ll_read_media_serial_num(sg_fd, rmsn_buff, sizeof(rmsn_buff),
+                                      1, verbose);
+    ret = res;
+    if (0 == res) {
+        sn_len = sg_get_unaligned_be32(rmsn_buff + 0);
+        if (! raw)
+            printf("Reported serial number length = %d\n", sn_len);
+        if (0 == sn_len) {
+            pr2serr("    This implies the media has no serial number\n");
+            goto err_out;
+        }
+        if (sn_len > SERIAL_NUM_SANITY_LEN) {
+            pr2serr("    That length (%d) seems too long for a serial "
+                    "number\n", sn_len);
+            goto err_out;
+        }
+        sn_len += 4;
+        ucp = (unsigned char *)malloc(sn_len);
+        if (NULL == ucp) {
+            pr2serr("    Out of memory (ram)\n");
+            goto err_out;
+        }
+        res = sg_ll_read_media_serial_num(sg_fd, ucp, sn_len, 1, verbose);
+        if (0 == res) {
+            sn_len = sg_get_unaligned_be32(ucp + 0);
+            if (raw) {
+                if (sn_len > 0) {
+                    n = fwrite(ucp + 4, 1, sn_len, stdout);
+                    if (n) { ; }  /* unused, dummy to suppress warning */
+                }
+            } else {
+                printf("Serial number:\n");
+                if (sn_len > 0)
+                    dStrHex((const char *)ucp + 4, sn_len, 0);
+            }
+        }
+    }
+    if (res) {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Read Media Serial Number: %s\n", b);
+        if (0 == verbose)
+            pr2serr("    try '-v' for more information\n");
+    }
+
+err_out:
+    if (ucp)
+        free(ucp);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_rtpg.c b/sg3_utils/src/sg_rtpg.c
new file mode 100644
index 0000000..a88dcba
--- /dev/null
+++ b/sg3_utils/src/sg_rtpg.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2004-2015 Christophe Varoqui and 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI command REPORT TARGET PORT GROUPS
+ * to the given SCSI device.
+ */
+
+static const char * version_str = "1.20 20151219";
+
+#define REPORT_TGT_GRP_BUFF_LEN 1024
+
+#define TPGS_STATE_OPTIMIZED 0x0
+#define TPGS_STATE_NONOPTIMIZED 0x1
+#define TPGS_STATE_STANDBY 0x2
+#define TPGS_STATE_UNAVAILABLE 0x3
+#define TPGS_STATE_LB_DEPENDENT 0x4
+#define TPGS_STATE_OFFLINE 0xe          /* SPC-4 rev 9 */
+#define TPGS_STATE_TRANSITIONING 0xf
+
+#define STATUS_CODE_NOSTATUS 0x0
+#define STATUS_CODE_CHANGED_BY_SET 0x1
+#define STATUS_CODE_CHANGED_BY_IMPLICIT 0x2
+
+static struct option long_options[] = {
+        {"decode", 0, 0, 'd'},
+        {"extended", 0, 0, 'e'},
+        {"help", 0, 0, 'h'},
+        {"hex", 0, 0, 'H'},
+        {"raw", 0, 0, 'r'},
+        {"readonly", 0, 0, 'R'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void usage()
+{
+    pr2serr("Usage: sg_rtpg   [--decode] [--extended] [--help] [--hex] "
+            "[--raw] [--readonly]\n"
+            "                 [--verbose] [--version] DEVICE\n"
+            "  where:\n"
+            "    --decode|-d        decode status and asym. access state\n"
+            "    --extended|-e      use extended header parameter data "
+            "format\n"
+            "    --help|-h          print out usage message\n"
+            "    --hex|-H           print out response in hex\n"
+            "    --raw|-r           output response in binary to stdout\n"
+            "    --readonly|-R      open DEVICE read-only (def: read-write)\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "Performs a SCSI REPORT TARGET PORT GROUPS command\n");
+
+}
+
+static void dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+static void decode_status(const int st)
+{
+    switch (st) {
+    case STATUS_CODE_NOSTATUS:
+        printf(" (no status available)");
+        break;
+    case STATUS_CODE_CHANGED_BY_SET:
+        printf(" (target port asym. state changed by SET TARGET PORT "
+               "GROUPS command)");
+        break;
+    case STATUS_CODE_CHANGED_BY_IMPLICIT:
+        printf(" (target port asym. state changed by implicit lu "
+               "behaviour)");
+        break;
+    default:
+        printf(" (unknown status code)");
+        break;
+    }
+}
+
+static void decode_tpgs_state(const int st)
+{
+    switch (st) {
+    case TPGS_STATE_OPTIMIZED:
+        printf(" (active/optimized)");
+        break;
+    case TPGS_STATE_NONOPTIMIZED:
+        printf(" (active/non optimized)");
+        break;
+    case TPGS_STATE_STANDBY:
+        printf(" (standby)");
+        break;
+    case TPGS_STATE_UNAVAILABLE:
+        printf(" (unavailable)");
+        break;
+    case TPGS_STATE_LB_DEPENDENT:
+        printf(" (logical block dependent)");
+        break;
+    case TPGS_STATE_OFFLINE:
+        printf(" (offline)");
+        break;
+    case TPGS_STATE_TRANSITIONING:
+        printf(" (transitioning between states)");
+        break;
+    default:
+        printf(" (unknown)");
+        break;
+    }
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, j, off, res, c, report_len, tgt_port_count;
+    unsigned char reportTgtGrpBuff[REPORT_TGT_GRP_BUFF_LEN];
+    unsigned char * ucp;
+    int decode = 0;
+    int hex = 0;
+    int raw = 0;
+    int o_readonly = 0;
+    int verbose = 0;
+    int extended = 0;
+    const char * device_name = NULL;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "dehHrRvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'd':
+            decode = 1;
+            break;
+        case 'e':
+             extended = 1;
+             break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            hex = 1;
+            break;
+        case 'r':
+            raw = 1;
+            break;
+        case 'R':
+            ++o_readonly;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("Version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    memset(reportTgtGrpBuff, 0x0, sizeof(reportTgtGrpBuff));
+    /* trunc = 0; */
+
+    res = sg_ll_report_tgt_prt_grp2(sg_fd, reportTgtGrpBuff,
+                                    sizeof(reportTgtGrpBuff),
+                                    extended, 1, verbose);
+    ret = res;
+    if (0 == res) {
+        report_len = sg_get_unaligned_be32(reportTgtGrpBuff + 0) + 4;
+        if (report_len > (int)sizeof(reportTgtGrpBuff)) {
+            /* trunc = 1; */
+            pr2serr("  <<report too long for internal buffer, output "
+                    "truncated\n");
+            report_len = (int)sizeof(reportTgtGrpBuff);
+        }
+        if (raw) {
+            dStrRaw((const char *)reportTgtGrpBuff, report_len);
+            goto err_out;
+        }
+        if (verbose)
+            printf("Report list length = %d\n", report_len);
+        if (hex) {
+            if (verbose)
+                printf("\nOutput response in hex:\n");
+            dStrHex((const char *)reportTgtGrpBuff, report_len, 1);
+            goto err_out;
+        }
+        printf("Report target port groups:\n");
+        ucp = reportTgtGrpBuff + 4;
+        if (extended) {
+             if (0x10 != (ucp[0] & 0x70)) {
+                  pr2serr("   <<invalid extended header format\n");
+                  goto err_out;
+             }
+             printf("  Implicit transition time: %d\n", ucp[1]);
+             ucp += 4;
+        }
+        for (k = ucp - reportTgtGrpBuff; k < report_len;
+             k += off, ucp += off) {
+
+            printf("  target port group id : 0x%x , Pref=%d, Rtpg_fmt=%d\n",
+                   sg_get_unaligned_be16(ucp + 2), !!(ucp[0] & 0x80),
+                   (ucp[0] >> 4) & 0x07);
+            printf("    target port group asymmetric access state : ");
+            printf("0x%02x", ucp[0] & 0x0f);
+            if (decode)
+                decode_tpgs_state(ucp[0] & 0x0f);
+            printf("\n");
+
+            printf("    T_SUP : %d, ", !!(ucp[1] & 0x80));
+            printf("O_SUP : %d, ", !!(ucp[1] & 0x40));
+            printf("LBD_SUP : %d, ", !!(ucp[1] & 0x10));
+            printf("U_SUP : %d, ", !!(ucp[1] & 0x08));
+            printf("S_SUP : %d, ", !!(ucp[1] & 0x04));
+            printf("AN_SUP : %d, ", !!(ucp[1] & 0x02));
+            printf("AO_SUP : %d\n", !!(ucp[1] & 0x01));
+
+            printf("    status code : ");
+            printf("0x%02x", ucp[5]);
+            if (decode)
+                decode_status(ucp[5]);
+            printf("\n");
+
+            printf("    vendor unique status : ");
+            printf("0x%02x\n", ucp[6]);
+
+            printf("    target port count : ");
+            tgt_port_count = ucp[7];
+            printf("%02x\n", tgt_port_count);
+
+            for (j = 0; j < tgt_port_count * 4; j += 4) {
+                if (0 == j)
+                    printf("    Relative target port ids:\n");
+                printf("      0x%02x\n",
+                       sg_get_unaligned_be16(ucp + 8 + j + 2));
+            }
+            off = 8 + j;
+        }
+    } else if (SG_LIB_CAT_INVALID_OP == res)
+        pr2serr("Report Target Port Groups command not supported\n");
+    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+        pr2serr("bad field in Report Target Port Groups cdb including "
+                "unsupported service action\n");
+    else {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Report Target Port Groups: %s\n", b);
+    }
+
+err_out:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_safte.c b/sg3_utils/src/sg_safte.c
new file mode 100644
index 0000000..8615bd7
--- /dev/null
+++ b/sg3_utils/src/sg_safte.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (c) 2004-2015 Hannes Reinecke and 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ *  This program accesses a processor device which operates according
+ *  to the 'SCSI Accessed Fault-Tolerant Enclosures' (SAF-TE) spec.
+ */
+
+static const char * version_str = "0.26 20151219";
+
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
+#define EBUFF_SZ 256
+
+#define RB_MODE_DESC 3
+#define RWB_MODE_DATA 2
+#define RWB_MODE_VENDOR 1
+#define RB_DESC_LEN 4
+
+#define SAFTE_CFG_FLAG_DOORLOCK 1
+#define SAFTE_CFG_FLAG_ALARM 2
+#define SAFTE_CFG_FLAG_CELSIUS 3
+
+struct safte_cfg_t {
+    int fans;
+    int psupplies;
+    int slots;
+    int temps;
+    int thermostats;
+    int vendor_specific;
+    int flags;
+};
+
+struct safte_cfg_t safte_cfg;
+
+static unsigned int buf_capacity = 64;
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* Buffer ID 0x0: Read Enclosure Configuration (mandatory) */
+static int
+read_safte_configuration(int sg_fd, unsigned char *rb_buff,
+                         unsigned int rb_len, int verbose)
+{
+    int res;
+
+    if (rb_len < buf_capacity) {
+        pr2serr("SCSI BUFFER size too small (%d/%d bytes)\n", rb_len,
+                buf_capacity);
+        return SG_LIB_CAT_ILLEGAL_REQ;
+    }
+
+    if (verbose > 1)
+        pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=0 to fetch "
+                "configuration\n");
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 0, 0,
+                            rb_buff, rb_len, 1, verbose);
+    if (res && res != SG_LIB_CAT_RECOVERED)
+        return res;
+
+    safte_cfg.fans = rb_buff[0];
+    safte_cfg.psupplies = rb_buff[1];
+    safte_cfg.slots = rb_buff[2];
+    safte_cfg.temps = rb_buff[4];
+    if (rb_buff[3])
+        safte_cfg.flags |= SAFTE_CFG_FLAG_DOORLOCK;
+    if (rb_buff[5])
+        safte_cfg.flags |= SAFTE_CFG_FLAG_ALARM;
+    if (rb_buff[6] & 0x80)
+        safte_cfg.flags |= SAFTE_CFG_FLAG_CELSIUS;
+
+    safte_cfg.thermostats = rb_buff[6] & 0x0f;
+    safte_cfg.vendor_specific = rb_buff[63];
+
+    return 0;
+}
+
+static int
+print_safte_configuration(void)
+{
+    printf("Enclosure Configuration:\n");
+    printf("\tNumber of Fans: %d\n", safte_cfg.fans);
+    printf("\tNumber of Power Supplies: %d\n", safte_cfg.psupplies);
+    printf("\tNumber of Device Slots: %d\n", safte_cfg.slots);
+    printf("\tNumber of Temperature Sensors: %d\n", safte_cfg.temps);
+    printf("\tNumber of Thermostats: %d\n", safte_cfg.thermostats);
+    printf("\tVendor unique bytes: %d\n", safte_cfg.vendor_specific);
+
+    return 0;
+}
+
+/* Buffer ID 0x01: Read Enclosure Status (mandatory) */
+static int
+do_safte_encl_status(int sg_fd, int do_hex, int do_raw, int verbose)
+{
+    int res, i, offset;
+    unsigned int rb_len;
+    unsigned char *rb_buff;
+
+    rb_len = safte_cfg.fans + safte_cfg.psupplies + safte_cfg.slots +
+        safte_cfg.temps + 5 + safte_cfg.vendor_specific;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+
+    if (verbose > 1)
+        pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=1 to read "
+                "enclosure status\n");
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 1, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res && res != SG_LIB_CAT_RECOVERED)
+        return res;
+
+    if (do_raw > 1) {
+        dStrRaw((const char *)rb_buff, buf_capacity);
+        return 0;
+    }
+    if (do_hex > 1) {
+        dStrHex((const char *)rb_buff, buf_capacity, 1);
+        return 0;
+    }
+    printf("Enclosure Status:\n");
+    offset = 0;
+    for (i = 0; i < safte_cfg.fans; i++) {
+        printf("\tFan %d status: ", i);
+        switch(rb_buff[i]) {
+            case 0:
+                printf("operational\n");
+                break;
+            case 1:
+                printf("malfunctioning\n");
+                break;
+            case 2:
+                printf("not installed\n");
+                break;
+            case 80:
+                printf("not reportable\n");
+                break;
+            default:
+                printf("unknown\n");
+                break;
+        }
+    }
+
+    offset += safte_cfg.fans;
+    for (i = 0; i < safte_cfg.psupplies; i++) {
+        printf("\tPower supply %d status: ", i);
+        switch(rb_buff[i + offset]) {
+            case 0:
+                printf("operational / on\n");
+                break;
+            case 1:
+                printf("operational / off\n");
+                break;
+            case 0x10:
+                printf("malfunctioning / on\n");
+                break;
+            case 0x11:
+                printf("malfunctioning / off\n");
+                break;
+            case 0x20:
+                printf("not present\n");
+                break;
+            case 0x21:
+                printf("present\n");
+                break;
+            case 0x80:
+                printf("not reportable\n");
+                break;
+            default:
+                printf("unknown\n");
+                break;
+        }
+    }
+
+    offset += safte_cfg.psupplies;
+    for (i = 0; i < safte_cfg.slots; i++) {
+        printf("\tDevice Slot %d: SCSI ID %d\n", i, rb_buff[i + offset]);
+    }
+
+    offset += safte_cfg.slots;
+    if (safte_cfg.flags & SAFTE_CFG_FLAG_DOORLOCK) {
+        switch(rb_buff[offset]) {
+            case 0x0:
+                printf("\tDoor lock status: locked\n");
+                break;
+            case 0x01:
+                printf("\tDoor lock status: unlocked\n");
+                break;
+            case 0x80:
+                printf("\tDoor lock status: not reportable\n");
+                break;
+        }
+    } else {
+        printf("\tDoor lock status: not installed\n");
+    }
+
+    offset++;
+    if (!(safte_cfg.flags & SAFTE_CFG_FLAG_ALARM)) {
+        printf("\tSpeaker status: not installed\n");
+    } else {
+        switch(rb_buff[offset]) {
+            case 0x0:
+                printf("\tSpeaker status: off\n");
+                break;
+            case 0x01:
+                printf("\tSpeaker status: on\n");
+                break;
+        }
+    }
+
+    offset++;
+    for (i = 0; i < safte_cfg.temps; i++) {
+        int temp = 0;
+
+        if (!(safte_cfg.flags & SAFTE_CFG_FLAG_CELSIUS))
+            temp -= 10;
+
+        printf("\tTemperature sensor %d: %d deg %c\n", i, rb_buff[i + offset],
+               safte_cfg.flags & SAFTE_CFG_FLAG_CELSIUS?'C':'F');
+    }
+
+    offset += safte_cfg.temps;
+    if (safte_cfg.thermostats) {
+        if (rb_buff[offset] & 0x80) {
+            printf("\tEnclosure Temperature alert status: abnormal\n");
+        } else {
+            printf("\tEnclosure Temperature alert status: normal\n");
+        }
+    }
+    return 0;
+}
+
+/* Buffer ID 0x02: Read Usage Statistics (optional) */
+static int
+do_safte_usage_statistics(int sg_fd, int do_hex, int do_raw, int verbose)
+{
+    int res;
+    unsigned int rb_len;
+    unsigned char *rb_buff;
+    unsigned int minutes;
+
+    rb_len = 16 + safte_cfg.vendor_specific;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+    if (verbose > 1)
+        pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=2 to read "
+                "usage statistics\n");
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 2, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res) {
+        if (res == SG_LIB_CAT_ILLEGAL_REQ) {
+            printf("Usage Statistics:\n\tNot implemented\n");
+            return 0;
+        }
+        if (res != SG_LIB_CAT_RECOVERED) {
+            free(rb_buff);
+            return res;
+        }
+    }
+
+    if (do_raw > 1) {
+        dStrRaw((const char *)rb_buff, buf_capacity);
+        return 0;
+    }
+    if (do_hex > 1) {
+        dStrHex((const char *)rb_buff, buf_capacity, 1);
+        return 0;
+    }
+    printf("Usage Statistics:\n");
+    minutes = sg_get_unaligned_be32(rb_buff + 0);
+    printf("\tPower on Minutes: %u\n", minutes);
+    minutes = sg_get_unaligned_be32(rb_buff + 4);
+    printf("\tPower on Cycles: %u\n", minutes);
+
+    free(rb_buff);
+    return 0;
+}
+
+/* Buffer ID 0x03: Read Device Insertions (optional) */
+static int
+do_safte_slot_insertions(int sg_fd, int do_hex, int do_raw, int verbose)
+{
+    int res, i;
+    unsigned int rb_len;
+    unsigned char *rb_buff, slot_status;
+
+    rb_len = safte_cfg.slots * 2;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+    if (verbose > 1)
+        pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=3 to read "
+                "device insertions\n");
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 3, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res ) {
+        if (res == SG_LIB_CAT_ILLEGAL_REQ) {
+                printf("Slot insertions:\n\tNot implemented\n");
+                return 0;
+        }
+        if (res != SG_LIB_CAT_RECOVERED) {
+                free(rb_buff);
+                return res;
+        }
+    }
+
+    if (do_raw > 1) {
+        dStrRaw((const char *)rb_buff, buf_capacity);
+        return 0;
+    }
+    if (do_hex > 1) {
+        dStrHex((const char *)rb_buff, buf_capacity, 1);
+        return 0;
+    }
+    printf("Slot insertions:\n");
+    for (i = 0; i < safte_cfg.slots; i++) {
+        slot_status = sg_get_unaligned_be16(rb_buff + (i * 2));
+        printf("\tSlot %d: %d insertions", i, slot_status);
+    }
+    free(rb_buff);
+    return 0;
+}
+
+/* Buffer ID 0x04: Read Device Slot Status (mandatory) */
+static int
+do_safte_slot_status(int sg_fd, int do_hex, int do_raw, int verbose)
+{
+    int res, i;
+    unsigned int rb_len;
+    unsigned char *rb_buff, slot_status;
+
+    rb_len = safte_cfg.slots * 4;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+    if (verbose > 1)
+        pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=4 to read "
+                "device slot status\n");
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 4, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res && res != SG_LIB_CAT_RECOVERED) {
+        free(rb_buff);
+        return res;
+    }
+
+    if (do_raw > 1) {
+        dStrRaw((const char *)rb_buff, buf_capacity);
+        return 0;
+    }
+    if (do_hex > 1) {
+        dStrHex((const char *)rb_buff, buf_capacity, 1);
+        return 0;
+    }
+    printf("Slot status:\n");
+    for (i = 0; i < safte_cfg.slots; i++) {
+        slot_status = rb_buff[i * 4 + 3];
+        printf("\tSlot %d: ", i);
+        if (slot_status & 0x7) {
+            if (slot_status & 0x1)
+                printf("inserted ");
+            if (slot_status & 0x2)
+                printf("ready ");
+            if (slot_status & 0x4)
+                printf("activated ");
+            printf("\n");
+        } else {
+            printf("empty\n");
+        }
+    }
+    free(rb_buff);
+    return 0;
+}
+
+/* Buffer ID 0x05: Read Global Flags (optional) */
+static int
+do_safte_global_flags(int sg_fd, int do_hex, int do_raw, int verbose)
+{
+    int res;
+    unsigned int rb_len;
+    unsigned char *rb_buff;
+
+    rb_len = 16;
+    rb_buff = (unsigned char *)malloc(rb_len);
+
+    if (verbose > 1)
+        pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=5 to read "
+                "global flags\n");
+    res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 5, 0,
+                            rb_buff, rb_len, 0, verbose);
+    if (res ) {
+        if (res == SG_LIB_CAT_ILLEGAL_REQ) {
+                printf("Global Flags:\n\tNot implemented\n");
+                return 0;
+        }
+        if (res != SG_LIB_CAT_RECOVERED) {
+                free(rb_buff);
+                return res;
+        }
+    }
+
+    if (do_raw > 1) {
+        dStrRaw((const char *)rb_buff, buf_capacity);
+        return 0;
+    }
+    if (do_hex > 1) {
+        dStrHex((const char *)rb_buff, buf_capacity, 1);
+        return 0;
+    }
+    printf("Global Flags:\n");
+    printf("\tAudible Alarm Control: %s\n",
+           rb_buff[0] & 0x1?"on":"off");
+    printf("\tGlobal Failure Indicator: %s\n",
+           rb_buff[0] & 0x2?"on":"off");
+    printf("\tGlobal Warning Indicator: %s\n",
+           rb_buff[0] & 0x4?"on":"off");
+    printf("\tEnclosure Power: %s\n",
+           rb_buff[0] & 0x8?"on":"off");
+    printf("\tCooling Failure: %s\n",
+           rb_buff[0] & 0x10?"yes":"no");
+    printf("\tPower Failure: %s\n",
+           rb_buff[0] & 0x20?"yes":"no");
+    printf("\tDrive Failure: %s\n",
+           rb_buff[0] & 0x40?"yes":"no");
+    printf("\tDrive Warning: %s\n",
+           rb_buff[0] & 0x80?"yes":"no");
+    printf("\tArray Failure: %s\n",
+           rb_buff[1] & 0x1?"yes":"no");
+    printf("\tArray Warning: %s\n",
+           rb_buff[0] & 0x2?"yes":"no");
+    printf("\tEnclosure Lock: %s\n",
+           rb_buff[0] & 0x4?"on":"off");
+    printf("\tEnclosure Identify: %s\n",
+           rb_buff[0] & 0x8?"on":"off");
+
+    free(rb_buff);
+    return 0;
+}
+
+static
+void usage()
+{
+    pr2serr("Usage:  sg_safte [--config] [--devstatus] [--encstatus] "
+            "[--flags] [--help]\n"
+            "                 [--hex] [--insertions] [--raw] [--usage] "
+            "[--verbose]\n"
+            "                 [--version] DEVICE\n"
+            "  where:\n"
+            "    --config|-c         output enclosure configuration\n"
+            "    --devstatus|-d      output device slot status\n"
+            "    --encstatus|-s      output enclosure status\n"
+            "    --flags|-f          output global flags\n"
+            "    --help|-h           output command usage message then "
+            "exit\n"
+            "    --hex|-H            output enclosure config in hex\n"
+            "    --insertions|-i     output insertion statistics\n"
+            "    --raw|-r            output enclosure config in binary "
+            "to stdout\n"
+            "    --usage|-u          output usage statistics\n"
+            "    --verbose|-v        increase verbosity\n"
+            "    --version|-v        output version then exit\n\n"
+            "Queries a SAF-TE processor device\n");
+}
+
+static struct option long_options[] = {
+    {"config", 0, 0, 'c'},
+    {"devstatus", 0, 0, 'd'},
+    {"encstatus", 0, 0, 's'},
+    {"flags", 0, 0, 'f'},
+    {"help", 0, 0, 'h'},
+    {"hex", 0, 0, 'H'},
+    {"insertions", 0, 0, 'i'},
+    {"raw", 0, 0, 'r'},
+    {"usage", 0, 0, 'u'},
+    {"verbose", 0, 0, 'v'},
+    {"version", 0, 0, 'V'},
+    {0, 0, 0, 0},
+};
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, c, ret, peri_type, no_hex_raw;
+    int res = SG_LIB_CAT_OTHER;
+    const char * device_name = NULL;
+    char ebuff[EBUFF_SZ];
+    unsigned char *rb_buff;
+    int do_config = 0;
+    int do_status = 0;
+    int do_slots = 0;
+    int do_flags = 0;
+    int do_usage = 0;
+    int do_hex = 0;
+    int do_raw = 0;
+    int verbose = 0;
+    int do_insertions = 0;
+    const char * cp;
+    char buff[48];
+    char b[80];
+    struct sg_simple_inquiry_resp inq_resp;
+    const char op_name[] = "READ BUFFER";
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "cdfhHirsuvV?", long_options,
+                        &option_index);
+
+        if (c == -1)
+            break;
+
+        switch (c) {
+            case 'c':
+                do_config = 1;
+                break;
+            case 'd':
+                do_slots = 1;
+                break;
+            case 'f':
+                do_flags = 1;
+                break;
+            case 'h':
+            case '?':
+                usage();
+                return 0;
+            case 'H':
+                ++do_hex;
+                break;
+            case 'i':
+                do_insertions = 1;
+                break;
+            case 'r':
+                ++do_raw;
+                break;
+            case 's':
+                do_status = 1;
+                break;
+            case 'u':
+                do_usage = 1;
+                break;
+            case 'v':
+                ++verbose;
+                break;
+            case 'V':
+                pr2serr("Version string: %s\n", version_str);
+                exit(0);
+            default:
+                pr2serr("unrecognised option code 0x%x ??\n", c);
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    if ((sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose)) < 0) {
+        snprintf(ebuff, EBUFF_SZ, "sg_safte: error opening file: %s (rw)",
+                 device_name);
+        perror(ebuff);
+        return SG_LIB_FILE_ERROR;
+    }
+    no_hex_raw = ((0 == do_hex) && (0 == do_raw));
+
+    if (no_hex_raw) {
+        if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, verbose)) {
+            printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor,
+                   inq_resp.product, inq_resp.revision);
+            peri_type = inq_resp.peripheral_type;
+            cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
+            if (strlen(cp) > 0)
+                printf("  Peripheral device type: %s\n", cp);
+            else
+                printf("  Peripheral device type: 0x%x\n", peri_type);
+        } else {
+            pr2serr("sg_safte: %s doesn't respond to a SCSI INQUIRY\n",
+                    device_name);
+            return SG_LIB_CAT_OTHER;
+        }
+    }
+
+    rb_buff = (unsigned char *)malloc(buf_capacity);
+    if (!rb_buff)
+        goto err_out;
+
+    memset(rb_buff, 0, buf_capacity);
+
+    res = read_safte_configuration(sg_fd, rb_buff, buf_capacity, verbose);
+    switch (res) {
+    case 0:
+    case SG_LIB_CAT_RECOVERED:
+        break;
+    default:
+        goto err_out;
+    }
+    if (1 == do_raw) {
+        dStrRaw((const char *)rb_buff, buf_capacity);
+        res = 0;
+        goto finish;
+    }
+    if (1 == do_hex) {
+        dStrHex((const char *)rb_buff, buf_capacity, 1);
+        res = 0;
+        goto finish;
+    }
+
+    if (do_config && no_hex_raw)
+        print_safte_configuration();
+
+    if (do_status) {
+        res = do_safte_encl_status(sg_fd, do_hex, do_raw, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+
+    if (do_usage) {
+        res = do_safte_usage_statistics(sg_fd, do_hex, do_raw, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+
+    if (do_insertions) {
+        res = do_safte_slot_insertions(sg_fd, do_hex, do_raw, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+
+    if (do_slots) {
+        res = do_safte_slot_status(sg_fd, do_hex, do_raw, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+
+    if (do_flags) {
+        res = do_safte_global_flags(sg_fd, do_hex, do_raw, verbose);
+        switch (res) {
+            case 0:
+            case SG_LIB_CAT_RECOVERED:
+                break;
+            default:
+                goto err_out;
+        }
+    }
+finish:
+    res = 0;
+
+err_out:
+    switch (res) {
+    case 0:
+    case SG_LIB_CAT_RECOVERED:
+        break;
+    default:
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("%s failed: %s\n", op_name, b);
+        break;
+    }
+    ret = res;
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_sanitize.c b/sg3_utils/src/sg_sanitize.c
new file mode 100644
index 0000000..bb9e866
--- /dev/null
+++ b/sg3_utils/src/sg_sanitize.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2011-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.00 20151219";
+
+/* Not all environments support the Unix sleep() */
+#if defined(MSC_VER) || defined(__MINGW32__)
+#define HAVE_MS_SLEEP
+#endif
+#ifdef HAVE_MS_SLEEP
+#include <windows.h>
+#define sleep_for(seconds)    Sleep( (seconds) * 1000)
+#else
+#define sleep_for(seconds)    sleep(seconds)
+#endif
+
+
+#define ME "sg_sanitize: "
+
+#define SANITIZE_OP 0x48
+#define SANITIZE_OP_LEN 10
+#define SANITIZE_SA_OVERWRITE 0x1
+#define SANITIZE_SA_BLOCK_ERASE 0x2
+#define SANITIZE_SA_CRYPTO_ERASE 0x3
+#define SANITIZE_SA_EXIT_FAIL_MODE 0x1f
+#define DEF_REQS_RESP_LEN 252
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define MAX_XFER_LEN 65535
+#define EBUFF_SZ 256
+
+#define SHORT_TIMEOUT 20   /* 20 seconds unless immed=0 ... */
+#define LONG_TIMEOUT (15 * 3600)       /* 15 hours ! */
+                /* Seagate ST32000444SS 2TB disk takes 9.5 hours to format */
+#define POLL_DURATION_SECS 60
+
+
+static struct option long_options[] = {
+    {"ause", no_argument, 0, 'A'},
+    {"block", no_argument, 0, 'B'},
+    {"count", required_argument, 0, 'c'},
+    {"crypto", no_argument, 0, 'C'},
+    {"desc", no_argument, 0, 'd'},
+    {"early", no_argument, 0, 'e'},
+    {"fail", no_argument, 0, 'F'},
+    {"help", no_argument, 0, 'h'},
+    {"invert", no_argument, 0, 'I'},
+    {"ipl", required_argument, 0, 'i'},
+    {"overwrite", no_argument, 0, 'O'},
+    {"pattern", required_argument, 0, 'p'},
+    {"quick", no_argument, 0, 'Q'},
+    {"test", required_argument, 0, 'T'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {"wait", no_argument, 0, 'w'},
+    {"zero", no_argument, 0, 'z'},
+    {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int ause;
+    int block;
+    int count;
+    int crypto;
+    int desc;
+    int early;
+    int fail;
+    int invert;
+    int ipl;    /* initialization pattern length */
+    int overwrite;
+    int test;
+    int quick;
+    int verbose;
+    int wait;
+    int zero;
+    int znr;
+    const char * pattern_fn;
+};
+
+
+static void
+usage()
+{
+  pr2serr("Usage: sg_sanitize [--ause] [--block] [--count=OC] [--crypto] "
+          "[--early]\n"
+          "                   [--fail] [--help] [--invert] [--ipl=LEN] "
+          "[--overwrite]\n"
+          "                   [--pattern=PF] [--quick] [--test=TE] "
+          "[--verbose]\n"
+          "                   [--version] [--wait] [--zero] [--znr] DEVICE\n"
+          "  where:\n"
+          "    --ause|-A            set AUSE bit in cdb\n"
+          "    --block|-B           do BLOCK ERASE sanitize\n"
+          "    --count=OC|-c OC     OC is overwrite count field (from 1 "
+          "(def) to 31)\n"
+          "    --crypto|-C          do CRYPTOGRAPHIC ERASE sanitize\n"
+          "    --desc|-d            polling request sense sets 'desc' "
+          "field\n"
+          "                         (def: clear 'desc' field)\n"
+          "    --early|-e           exit once sanitize started (IMMED set "
+          "in cdb)\n"
+          "                         user can monitor progress with REQUEST "
+          "SENSE\n"
+          "    --fail|-F            do EXIT FAILURE MODE sanitize\n"
+          "    --help|-h            print out usage message\n"
+          "    --invert|-I          set INVERT bit in OVERWRITE parameter "
+          "list\n"
+          "    --ipl=LEN|-i LEN     initialization pattern length (in "
+          "bytes)\n"
+          "    --overwrite|-O       do OVERWRITE sanitize\n"
+          "    --pattern=PF|-p PF    PF is file containing initialization "
+          "pattern\n"
+          "                          for OVERWRITE\n"
+          "    --quick|-Q           start sanitize without pause for user\n"
+          "                         intervention (i.e. no time to "
+          "reconsider)\n"
+          "    --test=TE|-T TE      TE is placed in TEST field of "
+          "OVERWRITE\n"
+          "                         parameter list (def: 0)\n"
+          "    --verbose|-v         increase verbosity\n"
+          "    --version|-V         print version string then exit\n"
+          "    --wait|-w            wait for command to finish (could "
+          "take hours)\n"
+          "    --zero|-z            use pattern of zeros for "
+          "OVERWRITE\n"
+          "    --znr|-Z             set ZNR (zone no reset) bit in cdb\n\n"
+          "Performs a SCSI SANITIZE command.\n    <<<WARNING>>>: all data "
+          "on DEVICE will be lost.\nDefault action is to give user time to "
+          "reconsider; then execute SANITIZE\ncommand with IMMED bit set; "
+          "then use REQUEST SENSE command every 60\nseconds to poll for a "
+          "progress indication; then exit when there is no\nmore progress "
+          "indication.\n"
+          );
+}
+
+/* Invoke SCSI SANITIZE command. Returns 0 if successful, otherwise error */
+static int
+do_sanitize(int sg_fd, const struct opts_t * op, const void * param_lstp,
+            int param_lst_len)
+{
+    int k, ret, res, sense_cat, immed;
+    unsigned char sanCmdBlk[SANITIZE_OP_LEN];
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (op->early || op->wait)
+        immed = op->early ? 1 : 0;
+    else
+        immed = 1;
+    memset(sanCmdBlk, 0, sizeof(sanCmdBlk));
+    sanCmdBlk[0] = SANITIZE_OP;
+    if (op->overwrite)
+        sanCmdBlk[1] = SANITIZE_SA_OVERWRITE;
+    else if (op->block)
+        sanCmdBlk[1] = SANITIZE_SA_BLOCK_ERASE;
+    else if (op->crypto)
+        sanCmdBlk[1] = SANITIZE_SA_CRYPTO_ERASE;
+    else if (op->fail)
+        sanCmdBlk[1] = SANITIZE_SA_EXIT_FAIL_MODE;
+    else
+        return SG_LIB_SYNTAX_ERROR;
+    if (immed)
+        sanCmdBlk[1] |= 0x80;
+    if (op->znr)        /* added sbc4r07 */
+        sanCmdBlk[1] |= 0x40;
+    if (op->ause)
+        sanCmdBlk[1] |= 0x20;
+    sg_put_unaligned_be16((uint16_t)param_lst_len, sanCmdBlk + 7);
+
+    if (op->verbose > 1) {
+        pr2serr("    Sanitize cmd: ");
+        for (k = 0; k < SANITIZE_OP_LEN; ++k)
+            pr2serr("%02x ", sanCmdBlk[k]);
+        pr2serr("\n");
+    }
+    if ((op->verbose > 2) && (param_lst_len > 0)) {
+        pr2serr("    Parameter list contents:\n");
+        dStrHexErr((const char *)param_lstp, param_lst_len, 1);
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("Sanitize: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, sanCmdBlk, sizeof(sanCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)param_lstp, param_lst_len);
+    res = do_scsi_pt(ptvp, sg_fd, (immed ? SHORT_TIMEOUT : LONG_TIMEOUT),
+                     op->verbose);
+    ret = sg_cmds_process_resp(ptvp, "Sanitize", res, 0, sense_b,
+                               1 /*noisy */, op->verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_MEDIUM_HARD:
+            {
+                int valid, slen;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                if (valid)
+                    pr2serr("Medium or hardware error starting at "
+                            "lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull);
+            }
+            ret = sense_cat;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+#define VPD_DEVICE_ID 0x83
+#define VPD_ASSOC_LU 0
+#define VPD_ASSOC_TPORT 1
+#define TPROTO_ISCSI 5
+
+static char *
+get_lu_name(const unsigned char * ucp, int u_len, char * b, int b_len)
+{
+    int len, off, sns_dlen, dlen, k;
+    unsigned char u_sns[512];
+    char * cp;
+
+    len = u_len - 4;
+    ucp += 4;
+    off = -1;
+    if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU,
+                                8 /* SCSI name string (sns) */,
+                                3 /* UTF-8 */)) {
+        sns_dlen = ucp[off + 3];
+        memcpy(u_sns, ucp + off + 4, sns_dlen);
+        /* now want to check if this is iSCSI */
+        off = -1;
+        if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_TPORT,
+                                    8 /* SCSI name string (sns) */,
+                                    3 /* UTF-8 */)) {
+            if ((0x80 & ucp[1]) && (TPROTO_ISCSI == (ucp[0] >> 4))) {
+                snprintf(b, b_len, "%.*s", sns_dlen, u_sns);
+                return b;
+            }
+        }
+    } else
+        sns_dlen = 0;
+    if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU,
+                                3 /* NAA */, 1 /* binary */)) {
+        dlen = ucp[off + 3];
+        if (! ((8 == dlen) || (16 ==dlen)))
+            return b;
+        cp = b;
+        for (k = 0; ((k < dlen) && (b_len > 1)); ++k) {
+            snprintf(cp, b_len, "%02x", ucp[off + 4 + k]);
+            cp += 2;
+            b_len -= 2;
+        }
+    } else if (0 == sg_vpd_dev_id_iter(ucp, len, &off, VPD_ASSOC_LU,
+                                       2 /* EUI */, 1 /* binary */)) {
+        dlen = ucp[off + 3];
+        if (! ((8 == dlen) || (12 == dlen) || (16 ==dlen)))
+            return b;
+        cp = b;
+        for (k = 0; ((k < dlen) && (b_len > 1)); ++k) {
+            snprintf(cp, b_len, "%02x", ucp[off + 4 + k]);
+            cp += 2;
+            b_len -= 2;
+        }
+    } else if (sns_dlen > 0)
+        snprintf(b, b_len, "%.*s", sns_dlen, u_sns);
+    return b;
+}
+
+#define SAFE_STD_INQ_RESP_LEN 36
+#define VPD_SUPPORTED_VPDS 0x0
+#define VPD_UNIT_SERIAL_NUM 0x80
+#define VPD_DEVICE_ID 0x83
+
+static int
+print_dev_id(int fd, unsigned char * sinq_resp, int max_rlen, int verbose)
+{
+    int res, k, n, verb, pdt, has_sn, has_di;
+    unsigned char b[256];
+    char a[256];
+    char pdt_name[64];
+
+    verb = (verbose > 1) ? verbose - 1 : 0;
+    memset(sinq_resp, 0, max_rlen);
+    res = sg_ll_inquiry(fd, 0, 0 /* evpd */, 0 /* pg_op */, b,
+                        SAFE_STD_INQ_RESP_LEN, 1, verb);
+    if (res)
+        return res;
+    n = b[4] + 5;
+    if (n > SAFE_STD_INQ_RESP_LEN)
+        n = SAFE_STD_INQ_RESP_LEN;
+    memcpy(sinq_resp, b, (n < max_rlen) ? n : max_rlen);
+    if (n == SAFE_STD_INQ_RESP_LEN) {
+        pdt = b[0] & 0x1f;
+        printf("    %.8s  %.16s  %.4s   peripheral_type: %s [0x%x]\n",
+               (const char *)(b + 8), (const char *)(b + 16),
+               (const char *)(b + 32),
+               sg_get_pdt_str(pdt, sizeof(pdt_name), pdt_name), pdt);
+        if (verbose)
+            printf("      PROTECT=%d\n", !!(b[5] & 1));
+        if (b[5] & 1)
+            printf("      << supports protection information>>\n");
+    } else {
+        pr2serr("Short INQUIRY response: %d bytes, expect at least 36\n", n);
+        return SG_LIB_CAT_OTHER;
+    }
+    res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_SUPPORTED_VPDS, b,
+                        SAFE_STD_INQ_RESP_LEN, 1, verb);
+    if (res) {
+        if (verbose)
+            pr2serr("VPD_SUPPORTED_VPDS gave res=%d\n", res);
+        return 0;
+    }
+    if (VPD_SUPPORTED_VPDS != b[1]) {
+        if (verbose)
+            pr2serr("VPD_SUPPORTED_VPDS corrupted\n");
+        return 0;
+    }
+    n = sg_get_unaligned_be16(b + 2);
+    if (n > (SAFE_STD_INQ_RESP_LEN - 4))
+        n = (SAFE_STD_INQ_RESP_LEN - 4);
+    for (k = 0, has_sn = 0, has_di = 0; k < n; ++k) {
+        if (VPD_UNIT_SERIAL_NUM == b[4 + k]) {
+            if (has_di) {
+                if (verbose)
+                    pr2serr("VPD_SUPPORTED_VPDS dis-ordered\n");
+                return 0;
+            }
+            ++has_sn;
+        } else if (VPD_DEVICE_ID == b[4 + k]) {
+            ++has_di;
+            break;
+        }
+    }
+    if (has_sn) {
+        res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_UNIT_SERIAL_NUM, b,
+                            sizeof(b), 1, verb);
+        if (res) {
+            if (verbose)
+                pr2serr("VPD_UNIT_SERIAL_NUM gave res=%d\n", res);
+            return 0;
+        }
+        if (VPD_UNIT_SERIAL_NUM != b[1]) {
+            if (verbose)
+                pr2serr("VPD_UNIT_SERIAL_NUM corrupted\n");
+            return 0;
+        }
+        n = sg_get_unaligned_be16(b + 2);
+        if (n > (int)(sizeof(b) - 4))
+            n = (sizeof(b) - 4);
+        printf("      Unit serial number: %.*s\n", n, (const char *)(b + 4));
+    }
+    if (has_di) {
+        res = sg_ll_inquiry(fd, 0, 1 /* evpd */, VPD_DEVICE_ID, b,
+                            sizeof(b), 1, verb);
+        if (res) {
+            if (verbose)
+                pr2serr("VPD_DEVICE_ID gave res=%d\n", res);
+            return 0;
+        }
+        if (VPD_DEVICE_ID != b[1]) {
+            if (verbose)
+                pr2serr("VPD_DEVICE_ID corrupted\n");
+            return 0;
+        }
+        n = sg_get_unaligned_be16(b + 2);
+        if (n > (int)(sizeof(b) - 4))
+            n = (sizeof(b) - 4);
+        n = strlen(get_lu_name(b, n + 4, a, sizeof(a)));
+        if (n > 0)
+            printf("      LU name: %.*s\n", n, a);
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, res, c, infd, progress, vb, n, resp_len;
+    int got_stdin = 0;
+    int param_lst_len = 0;
+    const char * device_name = NULL;
+    char ebuff[EBUFF_SZ];
+    char b[80];
+    unsigned char requestSenseBuff[DEF_REQS_RESP_LEN];
+    unsigned char * wBuff = NULL;
+    int ret = -1;
+    struct opts_t opts;
+    struct opts_t * op;
+    struct stat a_stat;
+    unsigned char inq_resp[SAFE_STD_INQ_RESP_LEN];
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->count = 1;
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ABc:CdeFhi:IOp:QT:vVwzZ", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'A':
+            ++op->ause;
+            break;
+        case 'B':
+            ++op->block;
+            break;
+        case 'c':
+            op->count = sg_get_num(optarg);
+            if ((op->count < 1) || (op->count > 31))  {
+                pr2serr("bad argument to '--count', expect 1 to 31\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'C':
+            ++op->crypto;
+            break;
+        case 'd':
+            ++op->desc;
+            break;
+        case 'e':
+            ++op->early;
+            break;
+        case 'F':
+            ++op->fail;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'i':
+            op->ipl = sg_get_num(optarg);
+            if ((op->ipl < 1) || (op->ipl > 65535))  {
+                pr2serr("bad argument to '--ipl', expect 1 to 65535\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'I':
+            ++op->invert;
+            break;
+        case 'O':
+            ++op->overwrite;
+            break;
+        case 'p':
+            op->pattern_fn = optarg;
+            break;
+        case 'Q':
+            ++op->quick;
+            break;
+        case 'T':
+            op->test = sg_get_num(optarg);
+            if ((op->test < 0) || (op->test > 3))  {
+                pr2serr("bad argument to '--test', expect 0 to 3\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        case 'w':
+            ++op->wait;
+            break;
+        case 'z':
+            ++op->zero;
+            break;
+        case 'Z':
+            ++op->znr;
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    vb = op->verbose;
+    n = !!op->block + !!op->crypto + !!op->fail + !!op->overwrite;
+    if (1 != n) {
+        pr2serr("one and only one of '--block', '--crypto', '--fail' or "
+                "'--overwrite' please\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->overwrite) {
+        if (op->zero) {
+            if (op->pattern_fn) {
+                pr2serr("confused: both '--pattern=PF' and '--zero' "
+                        "options\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->ipl = 4;
+        } else {
+            if (NULL == op->pattern_fn) {
+                pr2serr("'--overwrite' requires '--pattern=PF' or '--zero' "
+                        "option\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            got_stdin = (0 == strcmp(op->pattern_fn, "-")) ? 1 : 0;
+            if (! got_stdin) {
+                memset(&a_stat, 0, sizeof(a_stat));
+                if (stat(op->pattern_fn, &a_stat) < 0) {
+                    pr2serr("pattern file: unable to stat(%s): %s\n",
+                            op->pattern_fn, safe_strerror(errno));
+                    return SG_LIB_FILE_ERROR;
+                }
+                if (op->ipl <= 0) {
+                    op->ipl = (int)a_stat.st_size;
+                    if (op->ipl > MAX_XFER_LEN) {
+                        pr2serr("pattern file length exceeds 65535 bytes, "
+                                "need '--ipl=LEN' option\n");
+                         return SG_LIB_FILE_ERROR;
+                    }
+                }
+            }
+            if (op->ipl < 1) {
+                pr2serr("'--overwrite' requires '--ipl=LEN' option if can't "
+                        "get PF length\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, vb);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    ret = print_dev_id(sg_fd, inq_resp, sizeof(inq_resp), op->verbose);
+    if (ret)
+        goto err_out;
+
+    if (op->overwrite) {
+        param_lst_len = op->ipl + 4;
+        wBuff = (unsigned char*)calloc(op->ipl + 4, 1);
+        if (NULL == wBuff) {
+            pr2serr("unable to allocate %d bytes of memory with calloc()\n",
+                    op->ipl + 4);
+            ret = SG_LIB_SYNTAX_ERROR;
+            goto err_out;
+        }
+        if (op->zero) {
+            if (2 == op->zero)  /* treat -zz as fill with 0xff bytes */
+                memset(wBuff + 4, 0xff, op->ipl);
+            else
+                memset(wBuff + 4, 0, op->ipl);
+        } else {
+            if (got_stdin) {
+                infd = STDIN_FILENO;
+                if (sg_set_binary_mode(STDIN_FILENO) < 0)
+                    perror("sg_set_binary_mode");
+            } else {
+                if ((infd = open(op->pattern_fn, O_RDONLY)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
+                             "reading", op->pattern_fn);
+                    perror(ebuff);
+                    ret = SG_LIB_FILE_ERROR;
+                    goto err_out;
+                } else if (sg_set_binary_mode(infd) < 0)
+                    perror("sg_set_binary_mode");
+            }
+            res = read(infd, wBuff + 4, op->ipl);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
+                         op->pattern_fn);
+                perror(ebuff);
+                if (! got_stdin)
+                    close(infd);
+                ret = SG_LIB_FILE_ERROR;
+                goto err_out;
+            }
+            if (res < op->ipl) {
+                pr2serr("tried to read %d bytes from %s, got %d bytes\n",
+                         op->ipl, op->pattern_fn, res);
+                pr2serr("  so pad with 0x0 bytes and continue\n");
+            }
+            if (! got_stdin)
+                close(infd);
+        }
+        wBuff[0] = op->count & 0x1f;;
+        if (op->test)
+            wBuff[0] |= ((op->test & 0x3) << 5);
+        if (op->invert)
+            wBuff[0] |= 0x80;
+        sg_put_unaligned_be16((uint16_t)op->ipl, wBuff + 2);
+    }
+
+    if ((0 == op->quick) && (! op->fail)) {
+        printf("\nA SANITIZE will commence in 15 seconds\n");
+        printf("    ALL data on %s will be DESTROYED\n", device_name);
+        printf("        Press control-C to abort\n");
+        sleep_for(5);
+        printf("\nA SANITIZE will commence in 10 seconds\n");
+        printf("    ALL data on %s will be DESTROYED\n", device_name);
+        printf("        Press control-C to abort\n");
+        sleep_for(5);
+        printf("\nA SANITIZE will commence in 5 seconds\n");
+        printf("    ALL data on %s will be DESTROYED\n", device_name);
+        printf("        Press control-C to abort\n");
+        sleep_for(5);
+    }
+
+    ret = do_sanitize(sg_fd, op, wBuff, param_lst_len);
+    if (ret) {
+        sg_get_category_sense_str(ret, sizeof(b), b, vb);
+        pr2serr("Sanitize failed: %s\n", b);
+    }
+
+    if ((0 == ret) && (0 == op->early) && (0 == op->wait)) {
+        for (k = 0 ;; ++k) {
+            sleep_for(POLL_DURATION_SECS);
+            memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
+            res = sg_ll_request_sense(sg_fd, op->desc, requestSenseBuff,
+                                      sizeof(requestSenseBuff), 1, vb);
+            if (res) {
+                ret = res;
+                if (SG_LIB_CAT_INVALID_OP == res)
+                    pr2serr("Request Sense command not supported\n");
+                else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
+                    pr2serr("bad field in Request Sense cdb\n");
+                    if (1 == op->desc) {
+                        pr2serr("Descriptor type sense may not be supported, "
+                                "try again with fixed type\n");
+                        op->desc = 0;
+                        continue;
+                    }
+                } else {
+                    sg_get_category_sense_str(res, sizeof(b), b, vb);
+                    pr2serr("Request Sense: %s\n", b);
+                    if (0 == vb)
+                        pr2serr("    try the '-v' option for more "
+                                "information\n");
+                }
+                break;
+            }
+            /* "Additional sense length" same in descriptor and fixed */
+            resp_len = requestSenseBuff[7] + 8;
+            if (vb > 2) {
+                pr2serr("Parameter data in hex\n");
+                dStrHexErr((const char *)requestSenseBuff, resp_len, 1);
+            }
+            progress = -1;
+            sg_get_sense_progress_fld(requestSenseBuff, resp_len,
+                                      &progress);
+            if (progress < 0) {
+                ret = res;
+                if (vb > 1)
+                     pr2serr("No progress indication found, iteration %d\n",
+                             k + 1);
+                /* N.B. exits first time there isn't a progress indication */
+                break;
+            } else
+                printf("Progress indication: %d%% done\n",
+                       (progress * 100) / 65536);
+        }
+    }
+
+err_out:
+    if (wBuff)
+        free(wBuff);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_sat_identify.c b/sg3_utils/src/sg_sat_identify.c
new file mode 100644
index 0000000..f5f4de4
--- /dev/null
+++ b/sg3_utils/src/sg_sat_identify.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2006-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pr2serr.h"
+
+/* This program uses a ATA PASS-THROUGH SCSI command to package an
+ * ATA IDENTIFY (PACKAGE) DEVICE command. It is based on the SCSI to
+ * ATA Translation (SAT) drafts and standards. See http://www.t10.org
+ * for drafts. SAT is a standard: SAT ANSI INCITS 431-2007 (draft prior
+ * to that is sat-r09.pdf). SAT-2 is also a standard: SAT-2 ANSI INCITS
+ * 465-2010 and the draft prior to that is sat2r09.pdf . The SAT-3
+ * project has started and the most recent draft is sat3r01.pdf .
+ */
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_PASS_THROUGH12 0xa1     /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12_LEN 12
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
+#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
+
+#define ATA_IDENTIFY_DEVICE 0xec
+#define ATA_IDENTIFY_PACKET_DEVICE 0xa1
+#define ID_RESPONSE_LEN 512
+
+#define DEF_TIMEOUT 20
+
+#define EBUFF_SZ 256
+
+static const char * version_str = "1.12 20160126";
+
+static struct option long_options[] = {
+        {"ck_cond", no_argument, 0, 'c'},
+        {"extend", no_argument, 0, 'e'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"len", required_argument, 0, 'l'},
+        {"ident", no_argument, 0, 'i'},
+        {"packet", no_argument, 0, 'p'},
+        {"raw", no_argument, 0, 'r'},
+        {"readonly", no_argument, 0, 'R'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void usage()
+{
+    pr2serr("Usage: sg_sat_identify [--ck_cond] [--extend] [--help] [--hex] "
+            "[--ident]\n"
+            "                       [--len=16|12] [--packet] [--raw] "
+            "[--readonly]\n"
+            "                       [--verbose] [--version] DEVICE\n"
+            "  where:\n"
+            "    --ck_cond|-c     sets ck_cond bit in cdb (def: 0)\n"
+            "    --extend|-e      sets extend bit in cdb (def: 0)\n"
+            "    --help|-h        print out usage message then exit\n"
+            "    --hex|-H         output response in hex\n"
+            "    --ident|-i       output WWN prefixed by 0x, if not "
+            "available output\n"
+            "                     0x0000000000000000\n"
+            "    --len=16|12 | -l 16|12    cdb length: 16 or 12 bytes "
+            "(default: 16)\n"
+            "    --packet|-p      do IDENTIFY PACKET DEVICE (def: IDENTIFY "
+            "DEVICE) command\n"
+            "    --raw|-r         output response in binary to stdout\n"
+            "    --readonly|-R    open DEVICE read-only (def: read-write)\n"
+            "    --verbose|-v     increase verbosity\n"
+            "    --version|-V     print version string and exit\n\n"
+            "Performs a ATA IDENTIFY (PACKET) DEVICE command via a SAT "
+            "layer\n");
+}
+
+static void dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+static int do_identify_dev(int sg_fd, int do_packet, int cdb_len,
+                           int ck_cond, int extend, int do_indent,
+                           int do_hex, int do_raw, int verbose)
+{
+    int ok, j, res, ret;
+    /* Following for ATA READ/WRITE MULTIPLE (EXT) cmds, normally 0 */
+    int multiple_count = 0;
+    int protocol = 4;   /* PIO data-in */
+    int t_type = 0;     /* 0 -> 512 byte blocks, 1 -> device's LB size */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks (if t_type=0) */
+    int t_length = 2;   /* 0 -> no data transferred, 2 -> sector count */
+    int resid = 0;
+    int got_ard = 0;    /* got ATA result descriptor */
+    int got_fixsense = 0;    /* got ATA result in fixed format sense */
+    int sb_sz;
+    struct sg_scsi_sense_hdr ssh;
+    unsigned char inBuff[ID_RESPONSE_LEN];
+    unsigned char sense_buffer[64];
+    unsigned char ata_return_desc[16];
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char apt12CmdBlk[SAT_ATA_PASS_THROUGH12_LEN] =
+                {SAT_ATA_PASS_THROUGH12, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0};
+    const unsigned short * usp;
+    uint64_t ull;
+
+    sb_sz = sizeof(sense_buffer);
+    memset(sense_buffer, 0, sb_sz);
+    memset(ata_return_desc, 0, sizeof(ata_return_desc));
+    ok = 0;
+    if (SAT_ATA_PASS_THROUGH16_LEN == cdb_len) {
+        /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+        aptCmdBlk[6] = 1;   /* sector count */
+        aptCmdBlk[14] = (do_packet ? ATA_IDENTIFY_PACKET_DEVICE :
+                                     ATA_IDENTIFY_DEVICE);
+        aptCmdBlk[1] = (multiple_count << 5) | (protocol << 1) | extend;
+        aptCmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) |
+                       (byte_block << 2) | t_length;
+        res = sg_ll_ata_pt(sg_fd, aptCmdBlk, cdb_len, DEF_TIMEOUT, inBuff,
+                           NULL /* doutp */, ID_RESPONSE_LEN, sense_buffer,
+                           sb_sz, ata_return_desc,
+                           sizeof(ata_return_desc), &resid, verbose);
+    } else {
+        /* Prepare ATA PASS-THROUGH COMMAND (12) command */
+        apt12CmdBlk[4] = 1;   /* sector count */
+        apt12CmdBlk[9] = (do_packet ? ATA_IDENTIFY_PACKET_DEVICE :
+                                      ATA_IDENTIFY_DEVICE);
+        apt12CmdBlk[1] = (multiple_count << 5) | (protocol << 1);
+        apt12CmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) |
+                         (byte_block << 2) | t_length;
+        res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, cdb_len, DEF_TIMEOUT, inBuff,
+                           NULL /* doutp */, ID_RESPONSE_LEN, sense_buffer,
+                           sb_sz, ata_return_desc,
+                           sizeof(ata_return_desc), &resid, verbose);
+    }
+    if (0 == res) {
+        ok = 1;
+        if (verbose > 2)
+            pr2serr("command completed with SCSI GOOD status\n");
+    } else if ((res > 0) && (res & SAM_STAT_CHECK_CONDITION)) {
+        if (verbose > 1) {
+            pr2serr("ATA pass through:\n");
+            sg_print_sense(NULL, sense_buffer, sb_sz,
+                           ((verbose > 2) ? 1 : 0));
+        }
+        if (sg_scsi_normalize_sense(sense_buffer, sb_sz, &ssh)) {
+            switch (ssh.sense_key) {
+            case SPC_SK_ILLEGAL_REQUEST:
+                if ((0x20 == ssh.asc) && (0x0 == ssh.ascq)) {
+                    ret = SG_LIB_CAT_INVALID_OP;
+                    if (verbose < 2)
+                        pr2serr("ATA PASS-THROUGH (%d) not supported\n",
+                                cdb_len);
+                } else {
+                    ret = SG_LIB_CAT_ILLEGAL_REQ;
+                    if (verbose < 2)
+                        pr2serr("ATA PASS-THROUGH (%d), bad field in cdb\n",
+                                cdb_len);
+                }
+                return ret;
+            case SPC_SK_NO_SENSE:
+            case SPC_SK_RECOVERED_ERROR:
+                if ((0x0 == ssh.asc) &&
+                    (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
+                    if (0x72 == ssh.response_code) {
+                        if (SAT_ATA_RETURN_DESC != ata_return_desc[0]) {
+                            if (verbose)
+                                pr2serr("did not find ATA Return (sense) "
+                                        "Descriptor\n");
+                            return SG_LIB_CAT_RECOVERED;
+                        }
+                        got_ard = 1;
+                        break;
+                    } else if (0x70 == ssh.response_code) {
+                        got_fixsense = 1;
+                        break;
+                    } else {
+                        if (verbose < 2)
+                            pr2serr("ATA PASS-THROUGH (%d), unexpected  "
+                                    "response_code=0x%x\n", ssh.response_code,
+                                    cdb_len);
+                            return SG_LIB_CAT_RECOVERED;
+                    }
+                } else if (SPC_SK_RECOVERED_ERROR == ssh.sense_key)
+                    return SG_LIB_CAT_RECOVERED;
+                else {
+                    if ((0x0 == ssh.asc) && (0x0 == ssh.ascq))
+                        break;
+                    return SG_LIB_CAT_SENSE;
+                }
+            case SPC_SK_UNIT_ATTENTION:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), Unit Attention detected\n",
+                            cdb_len);
+                return SG_LIB_CAT_UNIT_ATTENTION;
+            case SPC_SK_NOT_READY:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), device not ready\n",
+                            cdb_len);
+                return SG_LIB_CAT_NOT_READY;
+            case SPC_SK_MEDIUM_ERROR:
+            case SPC_SK_HARDWARE_ERROR:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), medium or hardware "
+                            "error\n", cdb_len);
+                return SG_LIB_CAT_MEDIUM_HARD;
+            case SPC_SK_ABORTED_COMMAND:
+                if (0x10 == ssh.asc) {
+                    pr2serr("Aborted command: protection information\n");
+                    return SG_LIB_CAT_PROTECTION;
+                } else {
+                    pr2serr("Aborted command: try again with%s '-p' option\n",
+                            (do_packet ? "out" : ""));
+                    return SG_LIB_CAT_ABORTED_COMMAND;
+                }
+            case SPC_SK_DATA_PROTECT:
+                pr2serr("ATA PASS-THROUGH (%d): data protect, read only "
+                        "media?\n", cdb_len);
+                return SG_LIB_CAT_DATA_PROTECT;
+            default:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), some sense data, use "
+                            "'-v' for more information\n", cdb_len);
+                return SG_LIB_CAT_SENSE;
+            }
+        } else {
+            pr2serr("CHECK CONDITION without response code ??\n");
+            return SG_LIB_CAT_SENSE;
+        }
+        if (0x72 != (sense_buffer[0] & 0x7f)) {
+            pr2serr("expected descriptor sense format, response code=0x%x\n",
+                    sense_buffer[0]);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else if (res > 0) {
+        if (SAM_STAT_RESERVATION_CONFLICT == res) {
+            pr2serr("SCSI status: RESERVATION CONFLICT\n");
+            return SG_LIB_CAT_RES_CONFLICT;
+        } else {
+            pr2serr("Unexpected SCSI status=0x%x\n", res);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else {
+        pr2serr("ATA pass through (%d) failed\n", cdb_len);
+        if (verbose < 2)
+            pr2serr("    try adding '-v' for more information\n");
+        return -1;
+    }
+
+    if ((SAT_ATA_RETURN_DESC == ata_return_desc[0]) && (0 == got_ard))
+        pr2serr("Seem to have got ATA Result Descriptor but it was not "
+                "indicated\n");
+    if (got_ard) {
+        if (ata_return_desc[3] & 0x4) {
+                pr2serr("error indication in returned FIS: aborted command\n");
+                pr2serr("    try again with%s '-p' option\n",
+                        (do_packet ? "out" : ""));
+                return SG_LIB_CAT_ABORTED_COMMAND;
+        }
+        ok = 1;
+    }
+    if (got_fixsense) {
+        if (0x4 & sense_buffer[3]) { /* Error is MSB of Info field */
+                pr2serr("error indication in returned FIS: aborted command\n");
+                pr2serr("    try again with%s '-p' option\n",
+                        (do_packet ? "out" : ""));
+                return SG_LIB_CAT_ABORTED_COMMAND;
+        }
+        ok = 1;
+    }
+
+    if (ok) { /* output result if it is available */
+        if (do_raw)
+            dStrRaw((const char *)inBuff, 512);
+        else if (0 == do_hex) {
+            if (do_indent) {
+                usp = (const unsigned short *)inBuff;
+                ull = 0;
+                for (j = 0; j < 4; ++j) {
+                    if (j > 0)
+                        ull <<= 16;
+                    ull |= usp[108 + j];
+                }
+                printf("0x%016" PRIx64 "\n", ull);
+            } else {
+                printf("Response for IDENTIFY %sDEVICE ATA command:\n",
+                       (do_packet ? "PACKET " : ""));
+                dWordHex((const unsigned short *)inBuff, 256, 0,
+                         sg_is_big_endian());
+            }
+        } else if (1 == do_hex)
+            dStrHex((const char *)inBuff, 512, 0);
+        else if (2 == do_hex)
+            dWordHex((const unsigned short *)inBuff, 256, 0,
+                     sg_is_big_endian());
+        else if (3 == do_hex) /* '-HHH' suitable for "hdparm --Istdin" */
+            dWordHex((const unsigned short *)inBuff, 256, -2,
+                     sg_is_big_endian());
+        else     /* '-HHHH' hex bytes only */
+            dStrHex((const char *)inBuff, 512, -1);
+    }
+    return 0;
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, c, res;
+    const char * device_name = NULL;
+    int cdb_len = SAT_ATA_PASS_THROUGH16_LEN;
+    int do_packet = 0;
+    int do_hex = 0;
+    int do_indent = 0;
+    int do_raw = 0;
+    int o_readonly = 0;
+    int verbose = 0;
+    int ck_cond = 0;   /* set to 1 to read register(s) back */
+    int extend = 0;    /* set to 1 to send 48 bit LBA with command */
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "cehHil:prRvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            ++ck_cond;
+            break;
+        case 'e':
+            ++extend;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++do_hex;
+            break;
+        case 'i':
+            ++do_indent;
+            break;
+        case 'l':
+            cdb_len = sg_get_num(optarg);
+            if (! ((cdb_len == 12) || (cdb_len == 16))) {
+                pr2serr("argument to '--len' should be 12 or 16\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'p':
+            ++do_packet;
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 'R':
+            ++o_readonly;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return 1;
+    }
+    if (do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    if ((sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose)) < 0) {
+        pr2serr("error opening file: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    ret = do_identify_dev(sg_fd, do_packet, cdb_len, ck_cond, extend,
+                          do_indent, do_hex, do_raw, verbose);
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_sat_phy_event.c b/sg3_utils/src/sg_sat_phy_event.c
new file mode 100644
index 0000000..fcece2d
--- /dev/null
+++ b/sg3_utils/src/sg_sat_phy_event.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2006-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.07 20160126";
+
+/* This program uses a ATA PASS-THROUGH SCSI command. This usage is
+ * defined in the SCSI to ATA Translation (SAT) drafts and standards.
+ * See http://www.t10.org for drafts. SAT is a standard: SAT ANSI INCITS
+ * 431-2007 (draft prior to that is sat-r09.pdf). SAT-2 is also a
+ * standard: SAT-2 ANSI INCITS 465-2010 and the draft prior to that is
+ * sat2r09.pdf . The SAT-3 project has started and the most recent draft
+ * is sat3r01.pdf .
+ */
+
+/* This program uses a ATA PASS-THROUGH (16 or 12) SCSI command defined
+ * by SAT to package an ATA READ LOG EXT (2Fh) command to fetch
+ * log page 11h. That page contains SATA phy event counters.
+ * For ATA READ LOG EXT command see ATA-8/ACS at www.t13.org .
+ * For SATA phy counter definitions see SATA 2.5 .
+ *
+ * Invocation: see the usage() function below
+ */
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_PASS_THROUGH12 0xa1     /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12_LEN 12
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
+#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
+
+#define ATA_READ_LOG_EXT 0x2f
+#define SATA_PHY_EVENT_LPAGE 0x11
+#define READ_LOG_EXT_RESPONSE_LEN 512
+
+#define DEF_TIMEOUT 20
+
+#define EBUFF_SZ 256
+
+static struct option long_options[] = {
+        {"ck_cond", no_argument, 0, 'c'},
+        {"extend", no_argument, 0, 'e'},
+        {"hex", no_argument, 0, 'H'},
+        {"ignore", no_argument, 0, 'i'},
+        {"len", no_argument, 0, 'l'},
+        {"raw", no_argument, 0, 'r'},
+        {"reset", no_argument, 0, 'R'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+struct phy_event_t {
+    int id;
+    const char * desc;
+};
+
+static struct phy_event_t phy_event_arr[] = {   /* SATA 2.5 section 13.7.2 */
+    {0x1, "Command failed and ICRC error bit set in Error register"}, /* M */
+    {0x2, "R_ERR(p) response for data FIS"},
+    {0x3, "R_ERR(p) response for device-to-host data FIS"},
+    {0x4, "R_ERR(p) response for host-to-device data FIS"},
+    {0x5, "R_ERR(p) response for non-data FIS"},
+    {0x6, "R_ERR(p) response for device-to-host non-data FIS"},
+    {0x7, "R_ERR(p) response for host-to-device non-data FIS"},
+    {0x8, "Device-to-host non-data FIS retries"},
+    {0x9, "Transition from drive PHYRDY to drive PHYRDYn"},
+    {0xa, "Signature device-to-host register FISes due to COMRESET"}, /* M */
+    {0xb, "CRC errors within host-to-device FIS"},
+    {0xd, "non CRC errors within host-to-device FIS"},
+    {0xf, "R_ERR(p) response for host-to-device data FIS, CRC"},
+    {0x10, "R_ERR(p) response for host-to-device data FIS, non-CRC"},
+    {0x12, "R_ERR(p) response for host-to-device non-data FIS, CRC"},
+    {0x13, "R_ERR(p) response for host-to-device non-data FIS, non-CRC"},
+    {0xc00, "PM: host-to-device non-data FIS, R_ERR(p) due to collision"},
+    {0xc01, "PM: signature register - device-to-host FISes"},
+    {0xc02, "PM: corrupts CRC propagation of device-to-host FISes"},
+    {0x0, NULL},        /* end marker */        /* M(andatory) */
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_sat_phy_event [--ck_cond] [--extend] [--help] [--hex] "
+            "[--ignore]\n"
+            "                        [--len=16|12] [--raw] [--reset] "
+            "[--verbose]\n"
+            "                        [--version] DEVICE\n"
+            "  where:\n"
+            "    --ck_cond|-c    sets ck_cond bit in cdb (def: 0)\n"
+            "    --extend|-e     sets extend bit in cdb (def: 0)\n"
+            "    --help|-h       print this usage message then exit\n"
+            "    --hex|-H        output response in hex bytes, use twice for\n"
+            "                    hex words\n"
+            "    --ignore|-i     ignore identifier names, output id value "
+            "instead\n"
+            "    --len=16|12 | -l 16|12    cdb length: 16 or 12 bytes "
+            "(default: 16)\n"
+            "    --raw|-r        output response in binary to stdout\n"
+            "    --reset|-R      reset counters (after read)\n"
+            "    --verbose|-v    increase verbosity\n"
+            "    --version|-V    print version string then exit\n\n"
+            "Sends an ATA READ LOG EXT command via a SAT pass through to "
+            "fetch\nlog page 11h which contains SATA phy event counters\n");
+}
+
+static const char *
+find_phy_desc(int id)
+{
+    const struct phy_event_t * pep;
+
+    for (pep = phy_event_arr; pep->desc; ++pep) {
+        if ((id & 0xfff) == pep->id)
+            return pep->desc;
+    }
+    return NULL;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* ATA READ LOG EXT command [2Fh, PIO data-in] */
+/* N.B. "log_addr" is the log page number, "page_in_log" is usually zero */
+static int
+do_read_log_ext(int sg_fd, int log_addr, int page_in_log, int feature,
+                int blk_count, void * resp, int mx_resp_len, int cdb_len,
+                int ck_cond, int extend, int do_hex, int do_raw, int verbose)
+{
+    int ok, res, ret;
+    /* Following for ATA READ/WRITE MULTIPLE (EXT) cmds, normally 0 */
+    int multiple_count = 0;
+    int protocol = 4;   /* PIO data-in */
+    int t_type = 0;     /* 0 -> 512 byte blocks, 1 -> device's LB size */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks (if t_type=0) */
+    int t_length = 2;   /* 0 -> no data transferred, 2 -> sector count */
+    int resid = 0;
+    int got_ard = 0;    /* got ATA result descriptor */
+    int sb_sz;
+    struct sg_scsi_sense_hdr ssh;
+    unsigned char sense_buffer[64];
+    unsigned char ata_return_desc[16];
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char apt12CmdBlk[SAT_ATA_PASS_THROUGH12_LEN] =
+                {SAT_ATA_PASS_THROUGH12, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0};
+
+    sb_sz = sizeof(sense_buffer);
+    memset(sense_buffer, 0, sb_sz);
+    memset(ata_return_desc, 0, sizeof(ata_return_desc));
+    ok = 0;
+    if (SAT_ATA_PASS_THROUGH16_LEN == cdb_len) {
+        /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+        aptCmdBlk[3] = (feature >> 8) & 0xff;   /* feature(15:8) */
+        aptCmdBlk[4] = feature & 0xff;          /* feature(7:0) */
+        aptCmdBlk[5] = (blk_count >> 8) & 0xff; /* sector_count(15:8) */
+        aptCmdBlk[6] = blk_count & 0xff;        /* sector_count(7:0) */
+        aptCmdBlk[8] = log_addr & 0xff;  /* lba_low(7:0) == LBA(7:0) */
+        aptCmdBlk[9] = (page_in_log >> 8) & 0xff;
+                /* lba_mid(15:8) == LBA(39:32) */
+        aptCmdBlk[10] = page_in_log & 0xff; /* lba_mid(7:0) == LBA(15:8) */
+        aptCmdBlk[14] = ATA_READ_LOG_EXT;
+        aptCmdBlk[1] = (multiple_count << 5) | (protocol << 1) | extend;
+        aptCmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) |
+                       (byte_block << 2) | t_length;
+        res = sg_ll_ata_pt(sg_fd, aptCmdBlk, cdb_len, DEF_TIMEOUT, resp,
+                           NULL /* doutp */, mx_resp_len, sense_buffer,
+                           sb_sz, ata_return_desc,
+                           sizeof(ata_return_desc), &resid, verbose);
+    } else {
+        /* Prepare ATA PASS-THROUGH COMMAND (12) command */
+        apt12CmdBlk[3] = feature & 0xff;        /* feature(7:0) */
+        apt12CmdBlk[4] = blk_count & 0xff;        /* sector_count(7:0) */
+        apt12CmdBlk[5] = log_addr & 0xff;  /* lba_low(7:0) == LBA(7:0) */
+        apt12CmdBlk[6] = page_in_log & 0xff; /* lba_mid(7:0) == LBA(15:8) */
+        apt12CmdBlk[9] = ATA_READ_LOG_EXT;
+        apt12CmdBlk[1] = (multiple_count << 5) | (protocol << 1);
+        apt12CmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) |
+                         (byte_block << 2) | t_length;
+        res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, cdb_len, DEF_TIMEOUT, resp,
+                           NULL /* doutp */, mx_resp_len, sense_buffer,
+                           sb_sz, ata_return_desc,
+                           sizeof(ata_return_desc), &resid, verbose);
+    }
+    if (0 == res) {
+        ok = 1;
+        if (verbose > 2)
+            pr2serr("command completed with SCSI GOOD status\n");
+    } else if ((res > 0) && (res & SAM_STAT_CHECK_CONDITION)) {
+        if (verbose > 1) {
+            pr2serr("ATA pass through:\n");
+            sg_print_sense(NULL, sense_buffer, sb_sz,
+                           ((verbose > 2) ? 1 : 0));
+        }
+        if (sg_scsi_normalize_sense(sense_buffer, sb_sz, &ssh)) {
+            switch (ssh.sense_key) {
+            case SPC_SK_ILLEGAL_REQUEST:
+                if ((0x20 == ssh.asc) && (0x0 == ssh.ascq)) {
+                    ret = SG_LIB_CAT_INVALID_OP;
+                    if (verbose < 2)
+                        pr2serr("ATA PASS-THROUGH (%d) not supported\n",
+                                cdb_len);
+                } else {
+                    ret = SG_LIB_CAT_ILLEGAL_REQ;
+                    if (verbose < 2)
+                        pr2serr("ATA PASS-THROUGH (%d), bad field in cdb\n",
+                                cdb_len);
+                }
+                return ret;
+            case SPC_SK_NO_SENSE:
+            case SPC_SK_RECOVERED_ERROR:
+                if ((0x0 == ssh.asc) &&
+                    (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
+                    if (SAT_ATA_RETURN_DESC != ata_return_desc[0]) {
+                        if (verbose)
+                            pr2serr("did not find ATA Return (sense) "
+                                    "Descriptor\n");
+                        return SG_LIB_CAT_RECOVERED;
+                    }
+                    got_ard = 1;
+                    break;
+                } else if (SPC_SK_RECOVERED_ERROR == ssh.sense_key)
+                    return SG_LIB_CAT_RECOVERED;
+                else {
+                    if ((0x0 == ssh.asc) && (0x0 == ssh.ascq))
+                        break;
+                    return SG_LIB_CAT_SENSE;
+                }
+            case SPC_SK_UNIT_ATTENTION:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), Unit Attention detected\n",
+                            cdb_len);
+                return SG_LIB_CAT_UNIT_ATTENTION;
+            case SPC_SK_NOT_READY:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), device not ready\n",
+                            cdb_len);
+                return SG_LIB_CAT_NOT_READY;
+            case SPC_SK_MEDIUM_ERROR:
+            case SPC_SK_HARDWARE_ERROR:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), medium or hardware "
+                            "error\n", cdb_len);
+                return SG_LIB_CAT_MEDIUM_HARD;
+            case SPC_SK_ABORTED_COMMAND:
+                if (0x10 == ssh.asc) {
+                    pr2serr("Aborted command: protection information\n");
+                    return SG_LIB_CAT_PROTECTION;
+                } else {
+                    pr2serr("Aborted command\n");
+                    return SG_LIB_CAT_ABORTED_COMMAND;
+                }
+            case SPC_SK_DATA_PROTECT:
+                pr2serr("ATA PASS-THROUGH (%d): data protect, read only "
+                        "media?\n", cdb_len);
+                return SG_LIB_CAT_DATA_PROTECT;
+            default:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), some sense data, use "
+                            "'-v' for more information\n", cdb_len);
+                return SG_LIB_CAT_SENSE;
+            }
+        } else {
+            pr2serr("CHECK CONDITION without response code ??\n");
+            return SG_LIB_CAT_SENSE;
+        }
+        if (0x72 != (sense_buffer[0] & 0x7f)) {
+            pr2serr("expected descriptor sense format, response code=0x%x\n",
+                    sense_buffer[0]);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else if (res > 0) {
+        if (SAM_STAT_RESERVATION_CONFLICT == res) {
+            pr2serr("SCSI status: RESERVATION CONFLICT\n");
+            return SG_LIB_CAT_RES_CONFLICT;
+        } else {
+            pr2serr("Unexpected SCSI status=0x%x\n", res);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else {
+        pr2serr("ATA pass through (%d) failed\n", cdb_len);
+        if (verbose < 2)
+            pr2serr("    try adding '-v' for more information\n");
+        return -1;
+    }
+
+    if ((SAT_ATA_RETURN_DESC == ata_return_desc[0]) && (0 == got_ard))
+        pr2serr("Seem to have got ATA Result Descriptor but it was not "
+                "indicated\n");
+    if (got_ard) {
+        if (ata_return_desc[3] & 0x4) {
+                pr2serr("error indication in returned FIS: aborted command\n");
+                return SG_LIB_CAT_ABORTED_COMMAND;
+        }
+        ok = 1;
+    }
+
+    if (ok) { /* output result if ok and --hex or --raw given */
+        if (do_raw)
+            dStrRaw((const char *)resp, mx_resp_len);
+        else if (1 == do_hex)
+            dStrHex((const char *)resp, mx_resp_len, 0);
+        else if (do_hex > 1)
+            dWordHex((const unsigned short *)resp, mx_resp_len / 2, 0,
+                     sg_is_big_endian());
+    }
+    return 0;
+}
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, c, k, j, res, id, len, vendor;
+    char * device_name = 0;
+    char ebuff[EBUFF_SZ];
+    unsigned char inBuff[READ_LOG_EXT_RESPONSE_LEN];
+    int cdb_len = 16;
+    int hex = 0;
+    int ignore = 0;
+    int raw = 0;
+    int reset = 0;
+    int verbose = 0;
+    int ck_cond = 0;   /* set to 1 to read register(s) back */
+    int extend = 0;
+    int ret = 0;
+    uint64_t ull;
+    const char * cp;
+
+    memset(inBuff, 0, sizeof(inBuff));
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "cehHil:rRvV",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            ++ck_cond;
+            break;
+        case 'e':
+            ++extend;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            exit(0);
+        case 'H':
+            ++hex;
+            break;
+        case 'i':
+            ++ignore;
+            break;
+        case 'l':
+            cdb_len = sg_get_num(optarg);
+            if (! ((cdb_len == 12) || (cdb_len == 16))) {
+                pr2serr("argument to '--len' should be 12 or 16\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'r':
+            ++raw;
+            break;
+        case 'R':
+            ++reset;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            exit(0);
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (0 == device_name) {
+        pr2serr("no DEVICE name detected\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    if ((sg_fd = open(device_name, O_RDWR)) < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 "sg_sat_phy_event: error opening file: %s", device_name);
+        perror(ebuff);
+        return SG_LIB_FILE_ERROR;
+    }
+    ret = do_read_log_ext(sg_fd, SATA_PHY_EVENT_LPAGE, 0 /* page_in_log */,
+                          (reset ? 1 : 0) /* feature */,
+                          1 /* blk_count */, inBuff,
+                          READ_LOG_EXT_RESPONSE_LEN, cdb_len, ck_cond,
+                          extend, hex, raw, verbose);
+
+    if ((0 == ret) && (0 == hex) && (0 == raw)) {
+        printf("SATA phy event counters:\n");
+        for (k = 4; k < 512; k += (len + 2)) {
+            id = (inBuff[k + 1] << 8) + inBuff[k];
+            if (0 == id)
+                break;
+            len = ((id >> 12) & 0x7) * 2;
+            vendor = !!(id & 0x8000);
+            id = id & 0xfff;
+            ull = 0;
+            for (j = len - 1; j >= 0; --j) {
+                if (j < (len - 1))
+                    ull <<= 8;
+                ull |= inBuff[k + 2 + j];
+            }
+            cp = NULL;
+            if ((0 == vendor) && (0 == ignore))
+                cp = find_phy_desc(id);
+            if (cp)
+                printf("  %s: %" PRIu64 "\n", cp, ull);
+            else
+                printf("  id=0x%x, vendor=%d, data_len=%d, "
+                       "val=%" PRIu64 "\n", id, vendor, len, ull);
+        }
+    }
+
+    res = close(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_sat_read_gplog.c b/sg3_utils/src/sg_sat_read_gplog.c
new file mode 100644
index 0000000..df34de2
--- /dev/null
+++ b/sg3_utils/src/sg_sat_read_gplog.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2014-2016 Hannes Reinecke, SUSE Linux GmbH.
+ * 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* This program uses a ATA PASS-THROUGH SCSI command. This usage is
+ * defined in the SCSI to ATA Translation (SAT) drafts and standards.
+ * See http://www.t10.org for drafts. SAT is a standard: SAT ANSI INCITS
+ * 431-2007 (draft prior to that is sat-r09.pdf). SAT-2 is also a
+ * standard: SAT-2 ANSI INCITS 465-2010 and the draft prior to that is
+ * sat2r09.pdf . The SAT-3 project has started and the most recent draft
+ * is sat3r01.pdf .
+ */
+
+/* This program performs a ATA PASS-THROUGH (16) SCSI command in order
+ * to perform an ATA READ LOG EXT or ATA READ LOG DMA EXT command.
+ *
+ * See man page (sg_sat_read_gplog.8) for details.
+ */
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_PASS_THROUGH12 0xa1     /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12_LEN 12
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
+#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
+
+#define ATA_READ_LOG_EXT 0x2f
+#define ATA_READ_LOG_DMA_EXT 0x47
+
+#define DEF_TIMEOUT 20
+
+static const char * version_str = "1.13 20160126";
+
+struct opts_t {
+    int cdb_len;
+    int ck_cond;
+    int count;
+    int hex;
+    int la;             /* log address */
+    int pn;             /* page number within log address */
+    int rdonly;
+    int verbose;
+    const char * device_name;
+};
+
+static struct option long_options[] = {
+    {"count", required_argument, 0, 'c'},
+    {"ck_cond", no_argument, 0, 'C'},
+    {"dma", no_argument, 0, 'd'},
+    {"help", no_argument, 0, 'h'},
+    {"hex", no_argument, 0, 'H'},
+    {"len", required_argument, 0, 'l'},
+    {"log", required_argument, 0, 'L'},
+    {"page", required_argument, 0, 'p'},
+    {"readonly", no_argument, 0, 'r'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+          "sg_sat_read_gplog [--ck_cond] [--count=CO] [--dma] [--help]\n"
+          "                         [--hex] [--len=16|12] [--log=LA] "
+          "[--page=PN]\n"
+          "                         [--readonly] [--verbose] [--version] "
+          "DEVICE\n"
+          "  where:\n"
+          "    --ck_cond | -C          set ck_cond field in pass-through "
+          "(def: 0)\n"
+          "    --count=CO | -c CO      block count (def: 1)\n"
+          "    --dma | -d              Use READ LOG DMA EXT (def: READ LOG "
+          "EXT)\n"
+          "    --help | -h             output this usage message\n"
+          "    --hex | -H              output response in hex bytes, -HH "
+          "yields hex\n"
+          "                            words + ASCII (def), -HHH hex words "
+          "only\n"
+          "    --len=16|12 | -l 16|12    cdb length: 16 or 12 bytes "
+          "(def: 16)\n"
+          "    --log=LA | -L LA        Log address to be read (def: 0)\n"
+          "    --page=PN|-p PN         Log page number within address (def: "
+          "0)\n"
+          "    --readonly | -r         open DEVICE read-only (def: "
+          "read-write)\n"
+          "    --verbose | -v          increase verbosity\n"
+          "                            recommended if DEVICE is ATA disk\n"
+          "    --version | -V          print version string and exit\n\n"
+          "Sends an ATA READ LOG EXT (or READ LOG DMA EXT) command via a "
+          "SAT pass\nthrough to fetch a General Purpose (GP) log page. Each "
+          "page is accessed\nvia a log address and then a page number "
+          "within that address: LA,PN .\n"
+          "By default the output is the response in hex (16 bit) words.\n"
+           );
+}
+
+static int
+do_read_gplog(int sg_fd, int ata_cmd, unsigned char *inbuff,
+              const struct opts_t * op)
+{
+    int res, ret;
+    int extend = 1;
+    int protocol;
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+    int t_length = 2;   /* 0 -> no data transferred, 2 -> sector count */
+    int t_type = 0; /* 0 -> 512 byte blocks, 1 -> logical sectors */
+    int resid = 0;
+    int got_ard = 0;    /* got ATA result descriptor */
+    int sb_sz;
+    struct sg_scsi_sense_hdr ssh;
+    unsigned char sense_buffer[64];
+    unsigned char ata_return_desc[16];
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char apt12CmdBlk[SAT_ATA_PASS_THROUGH12_LEN] =
+                {SAT_ATA_PASS_THROUGH12, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0};
+    char cmd_name[32];
+
+    snprintf(cmd_name, sizeof(cmd_name), "ATA PASS-THROUGH (%d)",
+             op->cdb_len);
+    if (ata_cmd == ATA_READ_LOG_DMA_EXT) {
+        protocol = 6; /* DMA */
+    } else {
+        protocol = 4; /* PIO Data-In */
+    }
+    sb_sz = sizeof(sense_buffer);
+    memset(sense_buffer, 0, sb_sz);
+    memset(ata_return_desc, 0, sizeof(ata_return_desc));
+    memset(inbuff, 0, op->count * 512);
+    if (op->verbose > 1)
+        pr2serr("Building ATA READ LOG%s EXT command; la=0x%x, pn=0x%x\n",
+                ((ata_cmd == ATA_READ_LOG_DMA_EXT) ? " DMA" : ""), op->la,
+                op->pn);
+    if (op->cdb_len == 16) {
+        /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+        aptCmdBlk[14] = ata_cmd;
+        sg_put_unaligned_be16((uint16_t)op->count, aptCmdBlk + 5);
+        aptCmdBlk[8] = op->la;
+        sg_put_unaligned_be16((uint16_t)op->pn, aptCmdBlk + 9);
+        aptCmdBlk[1] = (protocol << 1) | extend;
+        aptCmdBlk[2] = (op->ck_cond << 5) | (t_type << 4) | (t_dir << 3) |
+                       (byte_block << 2) | t_length;
+        res = sg_ll_ata_pt(sg_fd, aptCmdBlk, op->cdb_len, DEF_TIMEOUT, inbuff,
+                           NULL, op->count * 512, sense_buffer,
+                           sb_sz, ata_return_desc,
+                           sizeof(ata_return_desc), &resid, op->verbose);
+    } else {
+        /* Prepare ATA PASS-THROUGH COMMAND (12) command */
+        /* Cannot map upper 8 bits of the pn since no LBA (39:32) field */
+        apt12CmdBlk[9] = ata_cmd;
+        apt12CmdBlk[4] = op->count;
+        apt12CmdBlk[5] = op->la;
+        apt12CmdBlk[6] = op->pn & 0xff;
+        /* apt12CmdBlk[7] = (op->pn >> 8) & 0xff; */
+        apt12CmdBlk[1] = (protocol << 1);
+        apt12CmdBlk[2] = (op->ck_cond << 5) | (t_type << 4) | (t_dir << 3) |
+                         (byte_block << 2) | t_length;
+        res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, op->cdb_len, DEF_TIMEOUT,
+                           inbuff, NULL, op->count * 512, sense_buffer,
+                           sb_sz, ata_return_desc,
+                           sizeof(ata_return_desc), &resid, op->verbose);
+    }
+    if (0 == res) {
+        if (op->verbose > 2)
+            pr2serr("command completed with SCSI GOOD status\n");
+        if ((0 == op->hex) || (2 == op->hex))
+            dWordHex((const unsigned short *)inbuff, op->count * 256, 0,
+                     sg_is_big_endian());
+        else if (1 == op->hex)
+            dStrHex((const char *)inbuff, 512, 0);
+        else if (3 == op->hex)  /* '-HHH' suitable for "hdparm --Istdin" */
+            dWordHex((const unsigned short *)inbuff, 256, -2,
+                     sg_is_big_endian());
+        else    /* '-HHHH' hex bytes only */
+            dStrHex((const char *)inbuff, 512, -1);
+    } else if ((res > 0) && (res & SAM_STAT_CHECK_CONDITION)) {
+        if (op->verbose > 1) {
+            pr2serr("ATA pass through:\n");
+            sg_print_sense(NULL, sense_buffer, sb_sz,
+                           ((op->verbose > 2) ? 1 : 0));
+        }
+        if (sg_scsi_normalize_sense(sense_buffer, sb_sz, &ssh)) {
+            switch (ssh.sense_key) {
+            case SPC_SK_ILLEGAL_REQUEST:
+                if ((0x20 == ssh.asc) && (0x0 == ssh.ascq)) {
+                    ret = SG_LIB_CAT_INVALID_OP;
+                    if (op->verbose < 2)
+                        pr2serr("%s not supported\n", cmd_name);
+                } else {
+                    ret = SG_LIB_CAT_ILLEGAL_REQ;
+                    if (op->verbose < 2)
+                        pr2serr("%s, bad field in cdb\n", cmd_name);
+                }
+                return ret;
+            case SPC_SK_NO_SENSE:
+            case SPC_SK_RECOVERED_ERROR:
+                if ((0x0 == ssh.asc) &&
+                    (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
+                    if (SAT_ATA_RETURN_DESC != ata_return_desc[0]) {
+                        if (op->verbose)
+                            pr2serr("did not find ATA Return (sense) "
+                                    "Descriptor\n");
+                        return SG_LIB_CAT_RECOVERED;
+                    }
+                    got_ard = 1;
+                    break;
+                } else if (SPC_SK_RECOVERED_ERROR == ssh.sense_key)
+                    return SG_LIB_CAT_RECOVERED;
+                else {
+                    if ((0x0 == ssh.asc) && (0x0 == ssh.ascq))
+                        break;
+                    return SG_LIB_CAT_SENSE;
+                }
+            case SPC_SK_UNIT_ATTENTION:
+                if (op->verbose < 2)
+                    pr2serr("%s, Unit Attention detected\n", cmd_name);
+                return SG_LIB_CAT_UNIT_ATTENTION;
+            case SPC_SK_NOT_READY:
+                if (op->verbose < 2)
+                    pr2serr("%s, device not ready\n", cmd_name);
+                return SG_LIB_CAT_NOT_READY;
+            case SPC_SK_MEDIUM_ERROR:
+            case SPC_SK_HARDWARE_ERROR:
+                if (op->verbose < 2)
+                    pr2serr("%s, medium or hardware error\n", cmd_name);
+                return SG_LIB_CAT_MEDIUM_HARD;
+            case SPC_SK_ABORTED_COMMAND:
+                if (0x10 == ssh.asc) {
+                    pr2serr("Aborted command: protection information\n");
+                    return SG_LIB_CAT_PROTECTION;
+                } else {
+                    pr2serr("Aborted command\n");
+                    return SG_LIB_CAT_ABORTED_COMMAND;
+                }
+            case SPC_SK_DATA_PROTECT:
+                pr2serr("%s: data protect, read only media?\n", cmd_name);
+                return SG_LIB_CAT_DATA_PROTECT;
+            default:
+                if (op->verbose < 2)
+                    pr2serr("%s, some sense data, use '-v' for more "
+                            "information\n", cmd_name);
+                return SG_LIB_CAT_SENSE;
+            }
+        } else {
+            pr2serr("CHECK CONDITION without response code ??\n");
+            return SG_LIB_CAT_SENSE;
+        }
+        if (0x72 != (sense_buffer[0] & 0x7f)) {
+            pr2serr("expected descriptor sense format, response "
+                    "code=0x%x\n", sense_buffer[0]);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else if (res > 0) {
+        if (SAM_STAT_RESERVATION_CONFLICT == res) {
+            pr2serr("SCSI status: RESERVATION CONFLICT\n");
+            return SG_LIB_CAT_RES_CONFLICT;
+        } else {
+            pr2serr("Unexpected SCSI status=0x%x\n", res);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else {
+        pr2serr("%s failed\n", cmd_name);
+        if (op->verbose < 2)
+            pr2serr("    try adding '-v' for more information\n");
+        return -1;
+    }
+
+    if ((SAT_ATA_RETURN_DESC == ata_return_desc[0]) && (0 == got_ard))
+        pr2serr("Seem to have got ATA Result Descriptor but it was not "
+                "indicated\n");
+    if (got_ard) {
+        if (ata_return_desc[3] & 0x4) {
+                pr2serr("error indication in returned FIS: aborted "
+                        "command\n");
+                return SG_LIB_CAT_ABORTED_COMMAND;
+        }
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, c, ret, res, n;
+    int ata_cmd = ATA_READ_LOG_EXT;
+    unsigned char *inbuff;
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->cdb_len = SAT_ATA_PASS_THROUGH16_LEN;
+    op->count = 1;
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "c:CdhHl:L:p:rvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            op->count = sg_get_num(optarg);
+            if ((op->count < 1) || (op->count > 0xffff)) {
+                pr2serr("bad argument for '--count'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'C':
+            op->ck_cond = 1;
+            break;
+        case 'd':
+            ata_cmd = ATA_READ_LOG_DMA_EXT;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++op->hex;
+            break;
+        case 'l':
+           op->cdb_len = sg_get_num(optarg);
+           if (! ((op->cdb_len == 12) || (op->cdb_len == 16))) {
+                pr2serr("argument to '--len' should be 12 or 16\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'L':
+            op->la = sg_get_num(optarg);
+            if (op->la < 0 || op->la > 0xff) {
+                pr2serr("bad argument for '--log'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'p':
+            op->pn = sg_get_num(optarg);
+            if ((op->pn < 0) || (op->pn > 0xffff)) {
+                pr2serr("bad argument for '--page'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'r':
+            ++op->rdonly;
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == op->device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return 1;
+    }
+
+    if ((op->count > 0xff) && (12 == op->cdb_len)) {
+        op->cdb_len = 16;
+        if (op->verbose)
+            pr2serr("Since count > 0xff, forcing cdb length to "
+                    "16\n");
+    }
+
+    n = op->count * 512;
+    inbuff = (unsigned char *)malloc(n);
+    if (!inbuff) {
+        pr2serr("Cannot allocate output buffer of size %d\n", n);
+        return SG_LIB_CAT_OTHER;
+    }
+
+    if ((sg_fd = sg_cmds_open_device(op->device_name, op->rdonly,
+                                     op->verbose)) < 0) {
+        pr2serr("error opening file: %s: %s\n", op->device_name,
+                safe_strerror(-sg_fd));
+        ret = SG_LIB_FILE_ERROR;
+        goto fini;
+    }
+
+    ret = do_read_gplog(sg_fd, ata_cmd, inbuff, op);
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            ret = SG_LIB_FILE_ERROR;
+    }
+fini:
+    free(inbuff);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_sat_set_features.c b/sg3_utils/src/sg_sat_set_features.c
new file mode 100644
index 0000000..9f31503
--- /dev/null
+++ b/sg3_utils/src/sg_sat_set_features.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2006-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pr2serr.h"
+
+/* This program uses a ATA PASS-THROUGH SCSI command. This usage is
+ * defined in the SCSI to ATA Translation (SAT) drafts and standards.
+ * See http://www.t10.org for drafts. SAT is a standard: SAT ANSI INCITS
+ * 431-2007 (draft prior to that is sat-r09.pdf). SAT-2 is also a
+ * standard: SAT-2 ANSI INCITS 465-2010 and the draft prior to that is
+ * sat2r09.pdf . The SAT-3 project has started and the most recent draft
+ * is sat3r01.pdf .
+ */
+
+/* This program performs a ATA PASS-THROUGH (16) SCSI command in order
+ * to perform an ATA SET FEATURES command.
+ *
+ * See man page (sg_sat_set_features.8) for details.
+ */
+
+#define SAT_ATA_PASS_THROUGH16 0x85
+#define SAT_ATA_PASS_THROUGH16_LEN 16
+#define SAT_ATA_PASS_THROUGH12 0xa1     /* clashes with MMC BLANK comand */
+#define SAT_ATA_PASS_THROUGH12_LEN 12
+#define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
+#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d
+
+#define ATA_SET_FEATURES 0xef
+
+#define DEF_TIMEOUT 20
+
+static const char * version_str = "1.12 20160126";
+
+static struct option long_options[] = {
+    {"count", required_argument, 0, 'c'},
+    {"ck_cond", no_argument, 0, 'C'},
+    {"extended", no_argument, 0, 'e'},
+    {"feature", required_argument, 0, 'f'},
+    {"help", no_argument, 0, 'h'},
+    {"len", required_argument, 0, 'l'},
+    {"lba", required_argument, 0, 'L'},
+    {"readonly", no_argument, 0, 'r'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_sat_set_features [--count=CO] [--ck_cond] [--extended] "
+            "[--feature=FEA]\n"
+            "                           [--help] [--lba=LBA] [--len=16|12] "
+            "[--readonly]\n"
+            "                           [--verbose] [--version] DEVICE\n"
+            "  where:\n"
+            "    --count=CO | -c CO      count field contents (def: 0)\n"
+            "    --ck_cond | -C          set ck_cond field in pass-through "
+            "(def: 0)\n"
+            "    --extended | -e         enable extended lba values\n"
+            "    --feature=FEA|-f FEA    feature field contents\n"
+            "                            (def: 0 (which is reserved))\n"
+            "    --help | -h             output this usage message\n"
+            "    --lba=LBA | -L LBA      LBA field contents (def: 0)\n"
+            "                            meaning depends on sub-command "
+            "(feature)\n"
+            "    --len=16|12 | -l 16|12    cdb length: 16 or 12 bytes "
+            "(def: 16)\n"
+            "    --verbose | -v          increase verbosity\n"
+            "    --readonly | -r         open DEVICE read-only (def: "
+            "read-write)\n"
+            "                            recommended if DEVICE is ATA disk\n"
+            "    --version | -V          print version string and exit\n\n"
+            "Sends an ATA SET FEATURES command via a SAT pass through.\n"
+            "Primary feature code is placed in '--feature=FEA' with "
+            "'--count=CO' and\n"
+            "'--lba=LBA' being auxiliaries for some features.  The arguments "
+            "CO, FEA\n"
+            "and LBA are decimal unless prefixed by '0x' or have a trailing "
+            "'h'.\n"
+            "Example enabling write cache: 'sg_sat_set_feature --feature=2 "
+            "/dev/sdc'\n");
+}
+
+static int
+do_set_features(int sg_fd, int feature, int count, uint64_t lba,
+                int cdb_len, int ck_cond, int extend, int verbose)
+{
+    int res, ret;
+    /* Following for ATA READ/WRITE MULTIPLE (EXT) cmds, normally 0 */
+    int multiple_count = 0;
+    int protocol = 3;   /* non-data */
+    int t_type = 0;     /* 0 -> 512 byte blocks, 1 -> device's LB size */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks (if t_type=0) */
+    int t_length = 0;   /* 0 -> no data transferred, 2 -> sector count */
+    int resid = 0;
+    int got_ard = 0;    /* got ATA result descriptor */
+    int sb_sz;
+    struct sg_scsi_sense_hdr ssh;
+    unsigned char sense_buffer[64];
+    unsigned char ata_return_desc[16];
+    unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
+                {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char apt12CmdBlk[SAT_ATA_PASS_THROUGH12_LEN] =
+                {SAT_ATA_PASS_THROUGH12, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0};
+
+    sb_sz = sizeof(sense_buffer);
+    memset(sense_buffer, 0, sb_sz);
+    memset(ata_return_desc, 0, sizeof(ata_return_desc));
+    if (16 == cdb_len) {
+        /* Prepare ATA PASS-THROUGH COMMAND (16) command */
+        aptCmdBlk[14] = ATA_SET_FEATURES;
+        aptCmdBlk[4] = feature;
+        aptCmdBlk[6] = count;
+        aptCmdBlk[8] = lba & 0xff;
+        aptCmdBlk[10] = (lba >> 8) & 0xff;
+        aptCmdBlk[12] = (lba >> 16) & 0xff;
+        aptCmdBlk[7] = (lba >> 24) & 0xff;
+        aptCmdBlk[9] = (lba >> 32) & 0xff;
+        aptCmdBlk[11] = (lba >> 40) & 0xff;
+        aptCmdBlk[1] = (multiple_count << 5) | (protocol << 1) | extend;
+        aptCmdBlk[2] = (ck_cond << 5) | (t_type << 4)| (t_dir << 3) |
+                       (byte_block << 2) | t_length;
+        res = sg_ll_ata_pt(sg_fd, aptCmdBlk, cdb_len, DEF_TIMEOUT, NULL,
+                           NULL /* doutp */, 0, sense_buffer,
+                           sb_sz, ata_return_desc,
+                           sizeof(ata_return_desc), &resid, verbose);
+    } else {
+        /* Prepare ATA PASS-THROUGH COMMAND (12) command */
+        apt12CmdBlk[9] = ATA_SET_FEATURES;
+        apt12CmdBlk[3] = feature;
+        apt12CmdBlk[4] = count;
+        apt12CmdBlk[5] = lba & 0xff;
+        apt12CmdBlk[6] = (lba >> 8) & 0xff;
+        apt12CmdBlk[7] = (lba >> 16) & 0xff;
+        apt12CmdBlk[1] = (multiple_count << 5) | (protocol << 1);
+        apt12CmdBlk[2] = (ck_cond << 5) | (t_type << 4) | (t_dir << 3) |
+                         (byte_block << 2) | t_length;
+        res = sg_ll_ata_pt(sg_fd, apt12CmdBlk, cdb_len, DEF_TIMEOUT, NULL,
+                           NULL /* doutp */, 0, sense_buffer,
+                           sb_sz, ata_return_desc,
+                           sizeof(ata_return_desc), &resid, verbose);
+    }
+    if (0 == res) {
+        if (verbose > 2)
+            pr2serr("command completed with SCSI GOOD status\n");
+    } else if ((res > 0) && (res & SAM_STAT_CHECK_CONDITION)) {
+        if (verbose > 1) {
+            pr2serr("ATA pass through:\n");
+            sg_print_sense(NULL, sense_buffer, sb_sz,
+                           ((verbose > 2) ? 1 : 0));
+        }
+        if (sg_scsi_normalize_sense(sense_buffer, sb_sz, &ssh)) {
+            switch (ssh.sense_key) {
+            case SPC_SK_ILLEGAL_REQUEST:
+                if ((0x20 == ssh.asc) && (0x0 == ssh.ascq)) {
+                    ret = SG_LIB_CAT_INVALID_OP;
+                    if (verbose < 2)
+                        pr2serr("ATA PASS-THROUGH (%d) not supported\n",
+                                cdb_len);
+                } else {
+                    ret = SG_LIB_CAT_ILLEGAL_REQ;
+                    if (verbose < 2)
+                        pr2serr("ATA PASS-THROUGH (%d), bad field in cdb\n",
+                                cdb_len);
+                }
+                return ret;
+            case SPC_SK_NO_SENSE:
+            case SPC_SK_RECOVERED_ERROR:
+                if ((0x0 == ssh.asc) &&
+                    (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
+                    if (SAT_ATA_RETURN_DESC != ata_return_desc[0]) {
+                        if (verbose)
+                            pr2serr("did not find ATA Return (sense) "
+                                    "Descriptor\n");
+                        return SG_LIB_CAT_RECOVERED;
+                    }
+                    got_ard = 1;
+                    break;
+                } else if (SPC_SK_RECOVERED_ERROR == ssh.sense_key)
+                    return SG_LIB_CAT_RECOVERED;
+                else {
+                    if ((0x0 == ssh.asc) && (0x0 == ssh.ascq))
+                        break;
+                    return SG_LIB_CAT_SENSE;
+                }
+            case SPC_SK_UNIT_ATTENTION:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), Unit Attention detected\n",
+                            cdb_len);
+                return SG_LIB_CAT_UNIT_ATTENTION;
+            case SPC_SK_NOT_READY:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), device not ready\n",
+                            cdb_len);
+                return SG_LIB_CAT_NOT_READY;
+            case SPC_SK_MEDIUM_ERROR:
+            case SPC_SK_HARDWARE_ERROR:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), medium or hardware "
+                            "error\n", cdb_len);
+                return SG_LIB_CAT_MEDIUM_HARD;
+            case SPC_SK_ABORTED_COMMAND:
+                if (0x10 == ssh.asc) {
+                    pr2serr("Aborted command: protection information\n");
+                    return SG_LIB_CAT_PROTECTION;
+                } else {
+                    pr2serr("Aborted command\n");
+                    return SG_LIB_CAT_ABORTED_COMMAND;
+                }
+            case SPC_SK_DATA_PROTECT:
+                pr2serr("ATA PASS-THROUGH (%d): data protect, read only "
+                        "media?\n", cdb_len);
+                return SG_LIB_CAT_DATA_PROTECT;
+            default:
+                if (verbose < 2)
+                    pr2serr("ATA PASS-THROUGH (%d), some sense data, use "
+                            "'-v' for more information\n", cdb_len);
+                return SG_LIB_CAT_SENSE;
+            }
+        } else {
+            pr2serr("CHECK CONDITION without response code ??\n");
+            return SG_LIB_CAT_SENSE;
+        }
+        if (0x72 != (sense_buffer[0] & 0x7f)) {
+            pr2serr("expected descriptor sense format, response code=0x%x\n",
+                    sense_buffer[0]);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else if (res > 0) {
+        if (SAM_STAT_RESERVATION_CONFLICT == res) {
+            pr2serr("SCSI status: RESERVATION CONFLICT\n");
+            return SG_LIB_CAT_RES_CONFLICT;
+        } else {
+            pr2serr("Unexpected SCSI status=0x%x\n", res);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else {
+        pr2serr("ATA pass through (%d) failed\n", cdb_len);
+        if (verbose < 2)
+            pr2serr("    try adding '-v' for more information\n");
+        return -1;
+    }
+
+    if ((SAT_ATA_RETURN_DESC == ata_return_desc[0]) && (0 == got_ard))
+        pr2serr("Seem to have got ATA Result Descriptor but it was not "
+                "indicated\n");
+    if (got_ard) {
+        if (ata_return_desc[3] & 0x4) {
+                pr2serr("error indication in returned FIS: aborted command\n");
+                return SG_LIB_CAT_ABORTED_COMMAND;
+        }
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, c, ret, res;
+    const char * device_name = NULL;
+    int count = 0;
+    int extend = 0;
+    int rdonly = 0;
+    int feature = 0;
+    uint64_t lba = 0;
+    int verbose = 0;
+    int ck_cond = 0;
+    int cdb_len = SAT_ATA_PASS_THROUGH16_LEN;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "c:Cef:hl:L:rvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            count = sg_get_num(optarg);
+            if ((count < 0) || (count > 255)) {
+                pr2serr("bad argument for '--count'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'C':
+            ck_cond = 1;
+            break;
+        case 'e':
+            extend = 1;
+            break;
+        case 'f':
+            feature = sg_get_num(optarg);
+            if ((feature < 0) || (feature > 255)) {
+                pr2serr("bad argument for '--feature'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'l':
+            cdb_len = sg_get_num(optarg);
+            if (! ((cdb_len == 12) || (cdb_len == 16))) {
+                pr2serr("argument to '--len' should be 12 or 16\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'L':       /* up to 32 bits, allow for 48 bits (less -1) */
+            lba = sg_get_llnum(optarg);
+            if ((uint64_t)-1 == lba) {
+                pr2serr("bad argument for '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if (lba > 0xffffffff)
+                extend = 1;
+            break;
+        case 'r':
+            ++rdonly;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return 1;
+    }
+
+    if ((lba > 0xffffff) && (12 == cdb_len)) {
+        cdb_len = 16;
+        if (verbose)
+            pr2serr("Since lba > 0xffffff, forcing cdb length to 16\n");
+    }
+
+    if ((sg_fd = sg_cmds_open_device(device_name, rdonly, verbose)) < 0) {
+        pr2serr("error opening file: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    ret = do_set_features(sg_fd, feature, count, lba, cdb_len, ck_cond,
+                          extend, verbose);
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_scan_linux.c b/sg3_utils/src/sg_scan_linux.c
new file mode 100644
index 0000000..3d405e1
--- /dev/null
+++ b/sg3_utils/src/sg_scan_linux.c
@@ -0,0 +1,613 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *  Copyright (C) 1999 - 2016 D. Gilbert
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ * This program scans the "sg" device space (ie actual + simulated SCSI
+ * generic devices). Optionally sg_scan can be given other device names
+ * to scan (in place of the sg devices).
+ * Options: -a   alpha scan: scan /dev/sga,b,c, ....
+ *          -i   do SCSI inquiry on device (implies -w)
+ *          -n   numeric scan: scan /dev/sg0,1,2, ....
+ *          -V   output version string and exit
+ *          -w   open writable (new driver opens readable unless -i)
+ *          -x   extra information output
+ *
+ * By default this program will look for /dev/sg0 first (i.e. numeric scan)
+ *
+ * Note: This program is written to work under both the original and
+ * the new sg driver.
+ *
+ * F. Jansen - modification to extend beyond 26 sg devices.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <scsi/scsi_ioctl.h>
+
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "4.12 20160121";
+
+#define ME "sg_scan: "
+
+#define NUMERIC_SCAN_DEF 1   /* change to 0 to make alpha scan default */
+
+#define INQ_REPLY_LEN 36
+#define INQ_CMD_LEN 6
+#define MAX_ERRORS 4
+
+#define EBUFF_SZ 256
+#define FNAME_SZ 64
+#define PRESENT_ARRAY_SIZE 8192
+
+static const char * sysfs_sg_dir = "/sys/class/scsi_generic";
+static int * gen_index_arr;
+
+typedef struct my_scsi_idlun {
+/* why can't userland see this structure ??? */
+    int dev_id;
+    int host_unique_id;
+} My_scsi_idlun;
+
+typedef struct my_sg_scsi_id {
+    int host_no;        /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
+    int channel;
+    int scsi_id;        /* scsi id of target device */
+    int lun;
+    int scsi_type;      /* TYPE_... defined in scsi/scsi.h */
+    short h_cmd_per_lun;/* host (adapter) maximum commands per lun */
+    short d_queue_depth;/* device (or adapter) maximum queue length */
+    int unused1;        /* probably find a good use, set 0 for now */
+    int unused2;        /* ditto */
+} My_sg_scsi_id;
+
+int sg3_inq(int sg_fd, unsigned char * inqBuff, int do_extra);
+int scsi_inq(int sg_fd, unsigned char * inqBuff);
+int try_ata_identity(const char * file_namep, int ata_fd, int do_inq);
+
+static unsigned char inqCmdBlk[INQ_CMD_LEN] =
+                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
+
+
+void usage()
+{
+    printf("Usage: sg_scan [-a] [-i] [-n] [-v] [-V] [-w] [-x] "
+           "[DEVICE]*\n");
+    printf("  where:\n");
+    printf("    -a    do alpha scan (ie sga, sgb, sgc)\n");
+    printf("    -i    do SCSI INQUIRY, output results\n");
+    printf("    -n    do numeric scan (ie sg0, sg1...) [default]\n");
+    printf("    -v    increase verbosity\n");
+    printf("    -V    output version string then exit\n");
+    printf("    -w    force open with read/write flag\n");
+    printf("    -x    extra information output about queuing\n");
+    printf("   DEVICE    name of device\n");
+}
+
+static int scandir_select(const struct dirent * s)
+{
+    int k;
+
+    if (1 == sscanf(s->d_name, "sg%d", &k)) {
+        if ((k >= 0) && (k < PRESENT_ARRAY_SIZE)) {
+            gen_index_arr[k] = 1;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int sysfs_sg_scan(const char * dir_name)
+{
+    struct dirent ** namelist;
+    int num, k;
+
+    num = scandir(dir_name, &namelist, scandir_select, NULL);
+    if (num < 0)
+        return -errno;
+    for (k = 0; k < num; ++k)
+        free(namelist[k]);
+    free(namelist);
+    return num;
+}
+
+void make_dev_name(char * fname, int k, int do_numeric)
+{
+    char buff[FNAME_SZ];
+    int  big,little;
+
+    strcpy(fname, "/dev/sg");
+    if (do_numeric) {
+        snprintf(buff, sizeof(buff), "%d", k);
+        strcat(fname, buff);
+    }
+    else {
+        if (k < 26) {
+            buff[0] = 'a' + (char)k;
+            buff[1] = '\0';
+            strcat(fname, buff);
+        }
+        else if (k <= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */
+            big    = k/26;
+            little = k - (26 * big);
+            big    = big - 1;
+
+            buff[0] = 'a' + (char)big;
+            buff[1] = 'a' + (char)little;
+            buff[2] = '\0';
+            strcat(fname, buff);
+        }
+        else
+            strcat(fname, "xxxx");
+    }
+}
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, res, k, j, f, plen, jmp_out;
+    unsigned char inqBuff[INQ_REPLY_LEN];
+    int do_numeric = NUMERIC_SCAN_DEF;
+    int do_inquiry = 0;
+    int do_extra = 0;
+    int verbose = 0;
+    int writeable = 0;
+    int num_errors = 0;
+    int num_silent = 0;
+    int sg_ver3 = -1;
+    int eacces_err = 0;
+    char fname[FNAME_SZ];
+    char * file_namep;
+    char ebuff[EBUFF_SZ];
+    My_scsi_idlun my_idlun;
+    int host_no;
+    int flags;
+    int emul = -1;
+    int has_file_args = 0;
+    int has_sysfs_sg = 0;
+    const int max_file_args = PRESENT_ARRAY_SIZE;
+    const char * cp;
+    struct stat a_stat;
+
+    if (NULL == (gen_index_arr =
+                 (int *)calloc(max_file_args + 1, sizeof(int)))) {
+        printf(ME "Out of memory\n");
+        return SG_LIB_CAT_OTHER;
+    }
+
+    for (k = 1, j = 0; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case 'a':
+                    do_numeric = 0;
+                    break;
+                case 'h':
+                case '?':
+                    printf("Scan sg device names and optionally do an "
+                           "INQUIRY\n\n");
+                    usage();
+                    return 0;
+                case 'i':
+                    do_inquiry = 1;
+                    break;
+                case 'n':
+                    do_numeric = 1;
+                    break;
+                case 'v':
+                    ++verbose;
+                    break;
+                case 'V':
+                    pr2serr("Version string: %s\n", version_str);
+                    exit(0);
+                case 'w':
+                    writeable = 1;
+                    break;
+                case 'x':
+                    do_extra = 1;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else {
+            if (j < max_file_args) {
+                has_file_args = 1;
+                gen_index_arr[j++] = k;
+            } else {
+                printf("Too many command line arguments\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        }
+    }
+
+    if ((! has_file_args) && (stat(sysfs_sg_dir, &a_stat) >= 0) &&
+        (S_ISDIR(a_stat.st_mode)))
+        has_sysfs_sg = sysfs_sg_scan(sysfs_sg_dir);
+
+    flags = O_NONBLOCK | (writeable ? O_RDWR : O_RDONLY);
+
+    for (k = 0, res = 0, j = 0, sg_fd = -1;
+         (k < max_file_args)  && (has_file_args || (num_errors < MAX_ERRORS));
+         ++k, res = ((sg_fd >= 0) ? close(sg_fd) : 0)) {
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ", fname);
+            perror(ebuff);
+            return SG_LIB_FILE_ERROR;
+        }
+        if (has_file_args) {
+            if (gen_index_arr[j])
+                file_namep = argv[gen_index_arr[j++]];
+            else
+                break;
+        } else if (has_sysfs_sg) {
+            if (0 == gen_index_arr[k]) {
+                sg_fd = -1;
+                continue;
+            }
+            make_dev_name(fname, k, 1);
+            file_namep = fname;
+        } else {
+            make_dev_name(fname, k, do_numeric);
+            file_namep = fname;
+        }
+
+        sg_fd = open(file_namep, flags);
+        if (sg_fd < 0) {
+            if (EBUSY == errno) {
+                printf("%s: device busy (O_EXCL lock), skipping\n",
+                       file_namep);
+                continue;
+            }
+            else if ((ENODEV == errno) || (ENOENT == errno) ||
+                     (ENXIO == errno)) {
+                if (verbose)
+                    pr2serr("Unable to open: %s, errno=%d\n", file_namep,
+                            errno);
+                ++num_errors;
+                ++num_silent;
+                continue;
+            }
+            else {
+                if (EACCES == errno)
+                    eacces_err = 1;
+                snprintf(ebuff, EBUFF_SZ, ME "Error opening %s ", file_namep);
+                perror(ebuff);
+                ++num_errors;
+                continue;
+            }
+        }
+        res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
+        if (res < 0) {
+            res = try_ata_identity(file_namep, sg_fd, do_inquiry);
+            if (res == 0)
+                continue;
+            snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi+ata "
+                     "ioctl, skip", file_namep);
+            perror(ebuff);
+            ++num_errors;
+            continue;
+        }
+        res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi "
+                     "ioctl(2), skip", file_namep);
+            perror(ebuff);
+            ++num_errors;
+            continue;
+        }
+        res = ioctl(sg_fd, SG_EMULATED_HOST, &emul);
+        if (res < 0)
+            emul = -1;
+        printf("%s: scsi%d channel=%d id=%d lun=%d", file_namep, host_no,
+               (my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff,
+               (my_idlun.dev_id >> 8) & 0xff);
+        if (1 == emul)
+            printf(" [em]");
+#if 0
+        printf(", huid=%d", my_idlun.host_unique_id);
+#endif
+        if (! has_file_args) {
+            My_sg_scsi_id m_id; /* compatible with sg_scsi_id_t in sg.h */
+
+            res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "device %s failed "
+                         "SG_GET_SCSI_ID ioctl(4), skip", file_namep);
+                perror(ebuff);
+                ++num_errors;
+                continue;
+            }
+            /* printf("  type=%d", m_id.scsi_type); */
+            if (do_extra)
+                printf("  cmd_per_lun=%hd queue_depth=%hd\n",
+                       m_id.h_cmd_per_lun, m_id.d_queue_depth);
+            else
+                printf("\n");
+        }
+        else
+            printf("\n");
+        if (do_inquiry) {
+            if (-1 == sg_ver3) {
+                sg_ver3 = 0;
+                if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &f) >= 0) &&
+                    (f >= 30000))
+                    sg_ver3 = 1;
+            }
+            if (1 == sg_ver3)
+                res = sg3_inq(sg_fd, inqBuff, do_extra);
+        }
+    }
+    if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors) &&
+        (! has_file_args)) {
+        printf("Stopping because there are too many error\n");
+        if (eacces_err)
+            printf("    root access may be required\n");
+    }
+    return 0;
+}
+
+int sg3_inq(int sg_fd, unsigned char * inqBuff, int do_extra)
+{
+    struct sg_io_hdr io_hdr;
+    unsigned char sense_buffer[32];
+    int ok, err, sg_io;
+
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    memset(inqBuff, 0, INQ_REPLY_LEN);
+    inqBuff[0] = 0x7f;
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sizeof(inqCmdBlk);
+    io_hdr.mx_sb_len = sizeof(sense_buffer);
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = INQ_REPLY_LEN;
+    io_hdr.dxferp = inqBuff;
+    io_hdr.cmdp = inqCmdBlk;
+    io_hdr.sbp = sense_buffer;
+    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
+
+    ok = 1;
+    sg_io = 0;
+    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+        if ((err = scsi_inq(sg_fd, inqBuff)) < 0) {
+            perror(ME "Inquiry SG_IO + SCSI_IOCTL_SEND_COMMAND ioctl error");
+            return 1;
+        } else if (err) {
+            printf(ME "SCSI_IOCTL_SEND_COMMAND ioctl error=0x%x\n", err);
+            return 1;
+        }
+    } else {
+        sg_io = 1;
+        /* now for the error processing */
+        switch (sg_err_category3(&io_hdr)) {
+        case SG_LIB_CAT_RECOVERED:
+            sg_chk_n_print3("Inquiry, continuing", &io_hdr, 1);
+            /* fall through */
+        case SG_LIB_CAT_CLEAN:
+            break;
+        default: /* won't bother decoding other categories */
+            ok = 0;
+            sg_chk_n_print3("INQUIRY command error", &io_hdr, 1);
+            break;
+        }
+    }
+
+    if (ok) { /* output result if it is available */
+        char * p = (char *)inqBuff;
+
+        printf("    %.8s  %.16s  %.4s ", p + 8, p + 16, p + 32);
+        printf("[rmb=%d cmdq=%d pqual=%d pdev=0x%x] ",
+               !!(p[1] & 0x80), !!(p[7] & 2), (p[0] & 0xe0) >> 5,
+               (p[0] & 0x1f));
+        if (do_extra && sg_io)
+            printf("dur=%ums\n", io_hdr.duration);
+        else
+            printf("\n");
+    }
+    return 0;
+}
+
+struct lscsi_ioctl_command {
+        unsigned int inlen;  /* _excluding_ scsi command length */
+        unsigned int outlen;
+        unsigned char data[1];  /* was 0 but that's not ISO C!! */
+                /* on input, scsi command starts here then opt. data */
+};
+
+/* fallback INQUIRY using scsi mid-level's SCSI_IOCTL_SEND_COMMAND ioctl */
+int scsi_inq(int sg_fd, unsigned char * inqBuff)
+{
+    int res;
+    unsigned char buff[512];
+    struct lscsi_ioctl_command * sicp = (struct lscsi_ioctl_command *)buff;
+
+    memset(buff, 0, sizeof(buff));
+    sicp->inlen = 0;
+    sicp->outlen = INQ_REPLY_LEN;
+    memcpy(sicp->data, inqCmdBlk, INQ_CMD_LEN);
+    res = ioctl(sg_fd, SCSI_IOCTL_SEND_COMMAND, sicp);
+    if (0 == res)
+        memcpy(inqBuff, sicp->data, INQ_REPLY_LEN);
+    return res;
+}
+
+/* Following code permits ATA IDENTIFY commands to be performed on
+   ATA non "Packet Interface" devices (e.g. ATA disks).
+   GPL-ed code borrowed from smartmontools (smartmontools.sf.net).
+   Copyright (C) 2002-4 Bruce Allen
+                <smartmontools-support@lists.sourceforge.net>
+ */
+#ifndef ATA_IDENTIFY_DEVICE
+#define ATA_IDENTIFY_DEVICE 0xec
+#endif
+#ifndef HDIO_DRIVE_CMD
+#define HDIO_DRIVE_CMD    0x031f
+#endif
+
+/* Needed parts of the ATA DRIVE IDENTIFY Structure. Those labeled
+ * word* are NOT used.
+ */
+struct ata_identify_device {
+  unsigned short words000_009[10];
+  unsigned char  serial_no[20];
+  unsigned short words020_022[3];
+  unsigned char  fw_rev[8];
+  unsigned char  model[40];
+  unsigned short words047_079[33];
+  unsigned short major_rev_num;
+  unsigned short minor_rev_num;
+  unsigned short command_set_1;
+  unsigned short command_set_2;
+  unsigned short command_set_extension;
+  unsigned short cfs_enable_1;
+  unsigned short word086;
+  unsigned short csf_default;
+  unsigned short words088_255[168];
+};
+
+/* Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
+ * bytes.
+ */
+void swapbytes(char *out, const char *in, size_t n)
+{
+    size_t k;
+
+    if (n > 1) {
+        for (k = 0; k < (n - 1); k += 2) {
+            out[k] = in[k + 1];
+            out[k + 1] = in[k];
+        }
+    }
+}
+
+/* Copies in to out, but removes leading and trailing whitespace. */
+void trim(char *out, const char *in)
+{
+    int k, first, last;
+
+    /* Find the first non-space character (maybe none). */
+    first = -1;
+    for (k = 0; in[k]; k++) {
+        if (! isspace((int)in[k])) {
+            first = k;
+            break;
+        }
+    }
+
+    if (first == -1) {
+        /* There are no non-space characters. */
+        out[0] = '\0';
+        return;
+    }
+
+    /* Find the last non-space character. */
+    for (k = strlen(in) - 1; k >= first && isspace((int)in[k]); k--)
+        ;
+    last = k;
+    strncpy(out, in + first, last - first + 1);
+    out[last - first + 1] = '\0';
+}
+
+/* Convenience function for formatting strings from ata_identify_device */
+void formatdriveidstring(char *out, const char *in, int n)
+{
+    char tmp[65];
+
+    n = n > 64 ? 64 : n;
+    swapbytes(tmp, in, n);
+    tmp[n] = '\0';
+    trim(out, tmp);
+}
+
+/* Function for printing ASCII byte-swapped strings, skipping white
+ * space. Please note that this is needed on both big- and
+ * little-endian hardware.
+ */
+void printswap(char *output, char *in, unsigned int n)
+{
+    formatdriveidstring(output, in, n);
+    if (*output)
+        printf("%.*s   ", (int)n, output);
+    else
+        printf("%.*s   ", (int)n, "[No Information Found]\n");
+}
+
+#define ATA_IDENTIFY_BUFF_SZ  sizeof(struct ata_identify_device)
+#define HDIO_DRIVE_CMD_OFFSET 4
+
+int ata_command_interface(int device, char *data)
+{
+    unsigned char buff[ATA_IDENTIFY_BUFF_SZ + HDIO_DRIVE_CMD_OFFSET];
+    int retval;
+
+    buff[0] = ATA_IDENTIFY_DEVICE;
+    buff[3] = 1;
+    /* We are now doing the HDIO_DRIVE_CMD type ioctl. */
+    if ((retval = ioctl(device, HDIO_DRIVE_CMD, buff)))
+        return retval;
+
+    /* if the command returns data, copy it back */
+    memcpy(data, buff + HDIO_DRIVE_CMD_OFFSET, ATA_IDENTIFY_BUFF_SZ);
+    return 0;
+}
+
+int try_ata_identity(const char * file_namep, int ata_fd, int do_inq)
+{
+    struct ata_identify_device ata_ident;
+    char model[64];
+    char serial[64];
+    char firm[64];
+    int res;
+
+    res = ata_command_interface(ata_fd, (char *)&ata_ident);
+    if (res)
+        return res;
+    printf("%s: ATA device\n", file_namep);
+    if (do_inq) {
+        printf("    ");
+        printswap(model, (char *)ata_ident.model, 40);
+        printswap(serial, (char *)ata_ident.serial_no, 20);
+        printswap(firm, (char *)ata_ident.fw_rev, 8);
+        printf("\n");
+    }
+    return res;
+}
diff --git a/sg3_utils/src/sg_scan_win32.c b/sg3_utils/src/sg_scan_win32.c
new file mode 100644
index 0000000..a45b038
--- /dev/null
+++ b/sg3_utils/src/sg_scan_win32.c
@@ -0,0 +1,773 @@
+/*
+ * Copyright (c) 2006-2015 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.
+ */
+
+/*
+ * This utility shows the relationship between various device names and
+ * volumes in Windows OSes (Windows 2000, 2003, XP and Vista). There is
+ * an optional scsi adapter scan.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <errno.h>
+
+#include "sg_lib.h"
+#include "sg_pr2serr.h"
+
+#ifdef _WIN32_WINNT
+ #if _WIN32_WINNT < 0x0602
+ #undef _WIN32_WINNT
+ #define _WIN32_WINNT 0x0602
+ #endif
+#else
+#define _WIN32_WINNT 0x0602
+/* claim its W8 */
+#endif
+
+#include "sg_pt_win32.h"
+
+static const char * version_str = "1.15 (win32) 20140827";
+
+#define MAX_SCSI_ELEMS 1024
+#define MAX_ADAPTER_NUM 64
+#define MAX_PHYSICALDRIVE_NUM 512
+#define MAX_CDROM_NUM 512
+#define MAX_TAPE_NUM 512
+#define MAX_HOLE_COUNT 8
+
+// IOCTL_STORAGE_QUERY_PROPERTY
+
+#define FILE_DEVICE_MASS_STORAGE    0x0000002d
+#define IOCTL_STORAGE_BASE          FILE_DEVICE_MASS_STORAGE
+#define FILE_ANY_ACCESS             0
+
+// #define METHOD_BUFFERED             0
+
+#define IOCTL_STORAGE_QUERY_PROPERTY \
+    CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+
+#ifndef _DEVIOCTL_
+typedef enum _STORAGE_BUS_TYPE {
+    BusTypeUnknown      = 0x00,
+    BusTypeScsi         = 0x01,
+    BusTypeAtapi        = 0x02,
+    BusTypeAta          = 0x03,
+    BusType1394         = 0x04,
+    BusTypeSsa          = 0x05,
+    BusTypeFibre        = 0x06,
+    BusTypeUsb          = 0x07,
+    BusTypeRAID         = 0x08,
+    BusTypeiScsi        = 0x09,
+    BusTypeSas          = 0x0A,
+    BusTypeSata         = 0x0B,
+    BusTypeSd           = 0x0C,
+    BusTypeMmc          = 0x0D,
+    BusTypeVirtual             = 0xE,
+    BusTypeFileBackedVirtual   = 0xF,
+    BusTypeMax,
+    BusTypeMaxReserved  = 0x7F
+} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
+
+typedef struct _STORAGE_DEVICE_DESCRIPTOR {
+    ULONG Version;
+    ULONG Size;
+    UCHAR DeviceType;
+    UCHAR DeviceTypeModifier;
+    BOOLEAN RemovableMedia;
+    BOOLEAN CommandQueueing;
+    ULONG VendorIdOffset;       /* 0 if not available */
+    ULONG ProductIdOffset;      /* 0 if not available */
+    ULONG ProductRevisionOffset;/* 0 if not available */
+    ULONG SerialNumberOffset;   /* -1 if not available ?? */
+    STORAGE_BUS_TYPE BusType;
+    ULONG RawPropertiesLength;
+    UCHAR RawDeviceProperties[1];
+} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
+#endif
+
+typedef struct _STORAGE_DEVICE_UNIQUE_IDENTIFIER {
+    ULONG  Version;
+    ULONG  Size;
+    ULONG  StorageDeviceIdOffset;
+    ULONG  StorageDeviceOffset;
+    ULONG  DriveLayoutSignatureOffset;
+} STORAGE_DEVICE_UNIQUE_IDENTIFIER, *PSTORAGE_DEVICE_UNIQUE_IDENTIFIER;
+
+// Use CompareStorageDuids(PSTORAGE_DEVICE_UNIQUE_IDENTIFIER duid1, duid2)
+// to test for equality
+
+#ifndef _DEVIOCTL_
+typedef enum _STORAGE_QUERY_TYPE {
+    PropertyStandardQuery = 0,
+    PropertyExistsQuery,
+    PropertyMaskQuery,
+    PropertyQueryMaxDefined
+} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
+
+typedef enum _STORAGE_PROPERTY_ID {
+    StorageDeviceProperty = 0,
+    StorageAdapterProperty,
+    StorageDeviceIdProperty,
+    StorageDeviceUniqueIdProperty,
+    StorageDeviceWriteCacheProperty,
+    StorageMiniportProperty,
+    StorageAccessAlignmentProperty
+} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
+
+typedef struct _STORAGE_PROPERTY_QUERY {
+    STORAGE_PROPERTY_ID PropertyId;
+    STORAGE_QUERY_TYPE QueryType;
+    UCHAR AdditionalParameters[1];
+} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+union STORAGE_DEVICE_DESCRIPTOR_DATA {
+    STORAGE_DEVICE_DESCRIPTOR desc;
+    char raw[256];
+};
+
+union STORAGE_DEVICE_UID_DATA {
+    STORAGE_DEVICE_UNIQUE_IDENTIFIER desc;
+    char raw[1060];
+};
+
+struct storage_elem {
+    char    name[32];
+    char    volume_letters[32];
+    int qp_descriptor_valid;
+    int qp_uid_valid;
+    union STORAGE_DEVICE_DESCRIPTOR_DATA qp_descriptor;
+    union STORAGE_DEVICE_UID_DATA qp_uid;
+};
+
+
+static struct storage_elem * storage_arr;
+static int next_unused_elem = 0;
+static int verbose = 0;
+
+static struct option long_options[] = {
+        {"bus", 0, 0, 'b'},
+        {"help", 0, 0, 'h'},
+        {"letter", 1, 0, 'l'},
+        {"verbose", 0, 0, 'v'},
+        {"scsi", 0, 0, 's'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_scan  [--bus] [--help] [--letter=VL] [--scsi] "
+            "[--verbose] [--version]\n");
+    pr2serr("       --bus|-b        output bus type\n"
+            "       --help|-h       output this usage message then exit\n"
+            "       --letter=VL|-l VL    volume letter (e.g. 'F' for F:) "
+            "to match\n"
+            "       --scsi|-s       show SCSI adapter (tuple) scan\n"
+            "       --verbose|-v    increase verbosity\n"
+            "       --version|-V    print version string and exit\n\n"
+            "Scan for storage and related device names\n");
+}
+
+static char *
+get_err_str(DWORD err, int max_b_len, char * b)
+{
+    LPVOID lpMsgBuf;
+    int k, num, ch;
+
+    if (max_b_len < 2) {
+        if (1 == max_b_len)
+            b[0] = '\0';
+        return b;
+    }
+    memset(b, 0, max_b_len);
+    FormatMessage(
+        FORMAT_MESSAGE_ALLOCATE_BUFFER |
+        FORMAT_MESSAGE_FROM_SYSTEM,
+        NULL, err,
+        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        (LPTSTR) &lpMsgBuf, 0, NULL );
+    num = lstrlen((LPCTSTR)lpMsgBuf);
+    if (num < 1)
+        return b;
+    num = (num < max_b_len) ? num : (max_b_len - 1);
+    for (k = 0; k < num; ++k) {
+        ch = *((LPCTSTR)lpMsgBuf + k);
+        if ((ch >= 0x0) && (ch < 0x7f))
+            b[k] = ch & 0x7f;
+        else
+            b[k] = '?';
+    }
+    return b;
+}
+
+static const char *
+get_bus_type(int bt)
+{
+    switch (bt)
+    {
+    case BusTypeUnknown:
+        return "Unkno";
+    case BusTypeScsi:
+        return "Scsi ";
+    case BusTypeAtapi:
+        return "Atapi";
+    case BusTypeAta:
+        return "Ata  ";
+    case BusType1394:
+        return "1394 ";
+    case BusTypeSsa:
+        return "Ssa  ";
+    case BusTypeFibre:
+        return "Fibre";
+    case BusTypeUsb:
+        return "Usb  ";
+    case BusTypeRAID:
+        return "RAID ";
+    case BusTypeiScsi:
+        return "iScsi";
+    case BusTypeSas:
+        return "Sas  ";
+    case BusTypeSata:
+        return "Sata ";
+    case BusTypeSd:
+        return "Sd   ";
+    case BusTypeMmc:
+        return "Mmc  ";
+    case BusTypeVirtual:
+        return "Virt ";
+    case BusTypeFileBackedVirtual:
+        return "FBVir";
+    case BusTypeMax:
+        return "Max  ";
+    default:
+        return "_unkn";
+    }
+}
+
+static int
+query_dev_property(HANDLE hdevice,
+                   union STORAGE_DEVICE_DESCRIPTOR_DATA * data)
+{
+    DWORD num_out, err;
+    char b[256];
+    STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty,
+                                    PropertyStandardQuery, {0} };
+
+    memset(data, 0, sizeof(*data));
+    if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
+                          &query, sizeof(query), data, sizeof(*data),
+                          &num_out, NULL)) {
+        if (verbose > 2) {
+            err = GetLastError();
+            pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(Devprop) failed, "
+                    "Error=%u %s\n", (unsigned int)err,
+                    get_err_str(err, sizeof(b), b));
+        }
+        return -ENOSYS;
+    }
+
+    if (verbose > 3)
+        pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(DevProp) num_out=%u\n",
+                (unsigned int)num_out);
+    return 0;
+}
+
+static int
+query_dev_uid(HANDLE hdevice, union STORAGE_DEVICE_UID_DATA * data)
+{
+    DWORD num_out, err;
+    char b[256];
+    STORAGE_PROPERTY_QUERY query = {StorageDeviceUniqueIdProperty,
+                                    PropertyStandardQuery, {0} };
+
+    memset(data, 0, sizeof(*data));
+    num_out = 0;
+    query.QueryType = PropertyExistsQuery;
+    if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
+                          &query, sizeof(query), NULL, 0, &num_out, NULL)) {
+        if (verbose > 2) {
+            err = GetLastError();
+            pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(DevUid(exists)) failed, "
+                    "Error=%u %s\n", (unsigned int)err,
+                    get_err_str(err, sizeof(b), b));
+        }
+        if (verbose > 3)
+            pr2serr("      num_out=%u\n", (unsigned int)num_out);
+        /* interpret any error to mean this property doesn't exist */
+        return 0;
+    }
+
+    query.QueryType = PropertyStandardQuery;
+    if (! DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
+                          &query, sizeof(query), data, sizeof(*data),
+                          &num_out, NULL)) {
+        if (verbose > 2) {
+            err = GetLastError();
+            pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(DevUid) failed, Error=%u "
+                    "%s\n", (unsigned int)err,
+                    get_err_str(err, sizeof(b), b));
+        }
+        return -ENOSYS;
+    }
+    if (verbose > 3)
+        pr2serr("  IOCTL_STORAGE_QUERY_PROPERTY(DevUid) num_out=%u\n",
+                (unsigned int)num_out);
+    return 0;
+}
+
+/* Updates storage_arr based on sep. Returns 1 if update occurred, 0 if
+ * no update occured. */
+static int
+check_devices(const struct storage_elem * sep)
+{
+    int k, j;
+    struct storage_elem * sarr = storage_arr;
+
+    for (k = 0; k < next_unused_elem; ++k, ++sarr) {
+        if ('\0' == sarr->name[0])
+            continue;
+        if (sep->qp_uid_valid && sarr->qp_uid_valid) {
+            if (0 == memcmp(&sep->qp_uid, &sarr->qp_uid,
+                            sizeof(sep->qp_uid))) {
+                for (j = 0; j < (int)sizeof(sep->volume_letters); ++j) {
+                    if ('\0' == sarr->volume_letters[j]) {
+                        sarr->volume_letters[j] = sep->name[0];
+                        break;
+                    }
+                }
+                return 1;
+            }
+        } else if (sep->qp_descriptor_valid && sarr->qp_descriptor_valid) {
+            if (0 == memcmp(&sep->qp_descriptor, &sarr->qp_descriptor,
+                            sizeof(sep->qp_descriptor))) {
+                for (j = 0; j < (int)sizeof(sep->volume_letters); ++j) {
+                    if ('\0' == sarr->volume_letters[j]) {
+                        sarr->volume_letters[j] = sep->name[0];
+                        break;
+                    }
+                }
+                return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+static int
+enum_scsi_adapters(void)
+{
+    int k, j;
+    int hole_count = 0;
+    HANDLE fh;
+    ULONG dummy;
+    DWORD err;
+    BYTE bus;
+    BOOL success;
+    char adapter_name[64];
+    char inqDataBuff[8192];
+    PSCSI_ADAPTER_BUS_INFO  ai;
+    char b[256];
+
+    for (k = 0; k < MAX_ADAPTER_NUM; ++k) {
+        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\SCSI%d:", k);
+        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                        OPEN_EXISTING, 0, NULL);
+        if (fh != INVALID_HANDLE_VALUE) {
+            hole_count = 0;
+            success = DeviceIoControl(fh, IOCTL_SCSI_GET_INQUIRY_DATA,
+                                      NULL, 0, inqDataBuff,
+                                      sizeof(inqDataBuff), &dummy, NULL);
+            if (success) {
+                PSCSI_BUS_DATA pbd;
+                PSCSI_INQUIRY_DATA pid;
+                int num_lus, off;
+
+                ai = (PSCSI_ADAPTER_BUS_INFO)inqDataBuff;
+                for (bus = 0; bus < ai->NumberOfBusses; bus++) {
+                    pbd = ai->BusData + bus;
+                    num_lus = pbd->NumberOfLogicalUnits;
+                    off = pbd->InquiryDataOffset;
+                    for (j = 0; j < num_lus; ++j) {
+                        if ((off < (int)sizeof(SCSI_ADAPTER_BUS_INFO)) ||
+                            (off > ((int)sizeof(inqDataBuff) -
+                                    (int)sizeof(SCSI_INQUIRY_DATA))))
+                            break;
+                        pid = (PSCSI_INQUIRY_DATA)(inqDataBuff + off);
+                        snprintf(b, sizeof(b) - 1, "SCSI%d:%d,%d,%d ", k,
+                                 pid->PathId, pid->TargetId, pid->Lun);
+                        printf("%-15s", b);
+                        snprintf(b, sizeof(b) - 1, "claimed=%d pdt=%xh %s ",
+                                 pid->DeviceClaimed,
+                                 pid->InquiryData[0] % 0x3f,
+                                 ((0 == pid->InquiryData[4]) ? "dubious" :
+                                                               ""));
+                        printf("%-26s", b);
+                        printf("%.8s  %.16s  %.4s\n", pid->InquiryData + 8,
+                               pid->InquiryData + 16, pid->InquiryData + 32);
+                        off = pid->NextInquiryDataOffset;
+                    }
+                }
+            } else {
+                err = GetLastError();
+                pr2serr("%s: IOCTL_SCSI_GET_INQUIRY_DATA failed err=%u\n\t%s",
+                        adapter_name, (unsigned int)err,
+                        get_err_str(err, sizeof(b), b));
+            }
+            CloseHandle(fh);
+        } else {
+            err = GetLastError();
+            if (ERROR_SHARING_VIOLATION == err)
+                pr2serr("%s: in use by other process (sharing violation "
+                        "[34])\n", adapter_name);
+            else if (verbose > 3)
+                pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name,
+                        (unsigned int)err, get_err_str(err, sizeof(b), b));
+            if (++hole_count >= MAX_HOLE_COUNT)
+                break;
+        }
+    }
+    return 0;
+}
+
+static int
+enum_volumes(char letter)
+{
+    int k;
+    HANDLE fh;
+    char adapter_name[64];
+    struct storage_elem tmp_se;
+
+    if (verbose > 2)
+        pr2serr("%s: enter\n", __FUNCTION__ );
+    for (k = 0; k < 24; ++k) {
+        memset(&tmp_se, 0, sizeof(tmp_se));
+        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\%c:", 'C' + k);
+        tmp_se.name[0] = 'C' + k;
+        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                        OPEN_EXISTING, 0, NULL);
+        if (fh != INVALID_HANDLE_VALUE) {
+            if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0)
+                pr2serr("%s: query_dev_property failed\n", __FUNCTION__ );
+            else
+                tmp_se.qp_descriptor_valid = 1;
+            if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) {
+                if (verbose > 2)
+                    pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ );
+            } else
+                tmp_se.qp_uid_valid = 1;
+            if (('\0' == letter) || (letter == tmp_se.name[0]))
+                check_devices(&tmp_se);
+            CloseHandle(fh);
+        }
+    }
+    return 0;
+}
+
+static int
+enum_pds(void)
+{
+    int k;
+    int hole_count = 0;
+    HANDLE fh;
+    DWORD err;
+    char adapter_name[64];
+    char b[256];
+    struct storage_elem tmp_se;
+
+    if (verbose > 2)
+        pr2serr("%s: enter\n", __FUNCTION__ );
+    for (k = 0; k < MAX_PHYSICALDRIVE_NUM; ++k) {
+        memset(&tmp_se, 0, sizeof(tmp_se));
+        snprintf(adapter_name, sizeof (adapter_name),
+                 "\\\\.\\PhysicalDrive%d", k);
+        snprintf(tmp_se.name, sizeof(tmp_se.name), "PD%d", k);
+        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                        OPEN_EXISTING, 0, NULL);
+        if (fh != INVALID_HANDLE_VALUE) {
+            if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0)
+                pr2serr("%s: query_dev_property failed\n", __FUNCTION__ );
+            else
+                tmp_se.qp_descriptor_valid = 1;
+            if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) {
+                if (verbose > 2)
+                    pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ );
+            } else
+                tmp_se.qp_uid_valid = 1;
+            hole_count = 0;
+            memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se));
+            CloseHandle(fh);
+        } else {
+            err = GetLastError();
+            if (ERROR_SHARING_VIOLATION == err)
+                pr2serr("%s: in use by other process (sharing violation "
+                        "[34])\n", adapter_name);
+            else if (verbose > 3)
+                pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name,
+                        (unsigned int)err, get_err_str(err, sizeof(b), b));
+            if (++hole_count >= MAX_HOLE_COUNT)
+                break;
+        }
+    }
+    return 0;
+}
+
+static int
+enum_cdroms(void)
+{
+    int k;
+    int hole_count = 0;
+    HANDLE fh;
+    DWORD err;
+    char adapter_name[64];
+    char b[256];
+    struct storage_elem tmp_se;
+
+    if (verbose > 2)
+        pr2serr("%s: enter\n", __FUNCTION__ );
+    for (k = 0; k < MAX_CDROM_NUM; ++k) {
+        memset(&tmp_se, 0, sizeof(tmp_se));
+        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\CDROM%d", k);
+        snprintf(tmp_se.name, sizeof(tmp_se.name), "CDROM%d", k);
+        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                        OPEN_EXISTING, 0, NULL);
+        if (fh != INVALID_HANDLE_VALUE) {
+            if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0)
+                pr2serr("%s: query_dev_property failed\n", __FUNCTION__ );
+            else
+                tmp_se.qp_descriptor_valid = 1;
+            if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) {
+                if (verbose > 2)
+                    pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ );
+            } else
+                tmp_se.qp_uid_valid = 1;
+            hole_count = 0;
+            memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se));
+            CloseHandle(fh);
+        } else {
+            err = GetLastError();
+            if (ERROR_SHARING_VIOLATION == err)
+                pr2serr("%s: in use by other process (sharing violation "
+                        "[34])\n", adapter_name);
+            else if (verbose > 3)
+                pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name,
+                        (unsigned int)err, get_err_str(err, sizeof(b), b));
+            if (++hole_count >= MAX_HOLE_COUNT)
+                break;
+        }
+    }
+    return 0;
+}
+
+static int
+enum_tapes(void)
+{
+    int k;
+    int hole_count = 0;
+    HANDLE fh;
+    DWORD err;
+    char adapter_name[64];
+    char b[256];
+    struct storage_elem tmp_se;
+
+    if (verbose > 2)
+        pr2serr("%s: enter\n", __FUNCTION__ );
+    for (k = 0; k < MAX_TAPE_NUM; ++k) {
+        memset(&tmp_se, 0, sizeof(tmp_se));
+        snprintf(adapter_name, sizeof (adapter_name), "\\\\.\\TAPE%d", k);
+        snprintf(tmp_se.name, sizeof(tmp_se.name), "TAPE%d", k);
+        fh = CreateFile(adapter_name, GENERIC_READ | GENERIC_WRITE,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                        OPEN_EXISTING, 0, NULL);
+        if (fh != INVALID_HANDLE_VALUE) {
+            if (query_dev_property(fh, &tmp_se.qp_descriptor) < 0)
+                pr2serr("%s: query_dev_property failed\n", __FUNCTION__ );
+            else
+                tmp_se.qp_descriptor_valid = 1;
+            if (query_dev_uid(fh, &tmp_se.qp_uid) < 0) {
+                if (verbose > 2)
+                    pr2serr("%s: query_dev_uid failed\n", __FUNCTION__ );
+            } else
+                tmp_se.qp_uid_valid = 1;
+            hole_count = 0;
+            memcpy(&storage_arr[next_unused_elem++], &tmp_se, sizeof(tmp_se));
+            CloseHandle(fh);
+        } else {
+            err = GetLastError();
+            if (ERROR_SHARING_VIOLATION == err)
+                pr2serr("%s: in use by other process (sharing violation "
+                        "[34])\n", adapter_name);
+            else if (verbose > 3)
+                pr2serr("%s: CreateFile failed err=%u\n\t%s", adapter_name,
+                        (unsigned int)err, get_err_str(err, sizeof(b), b));
+            if (++hole_count >= MAX_HOLE_COUNT)
+                break;
+        }
+    }
+    return 0;
+}
+
+static int
+sg_do_wscan(char letter, int show_bt, int scsi_scan)
+{
+    int k, j, n;
+    struct storage_elem * sp;
+
+    if (scsi_scan < 2) {
+        k = enum_pds();
+        if (k)
+            return k;
+        k = enum_cdroms();
+        if (k)
+            return k;
+        k = enum_tapes();
+        if (k)
+            return k;
+        k = enum_volumes(letter);
+        if (k)
+            return k;
+
+        for (k = 0; k < next_unused_elem; ++k) {
+            sp = storage_arr + k;
+            if ('\0' == sp->name[0])
+                continue;
+            printf("%-7s ", sp->name);
+            n = strlen(sp->volume_letters);
+            if (0 == n)
+                printf("        ");
+            else if (1 == n)
+                printf("[%s]     ", sp->volume_letters);
+            else if (2 == n)
+                printf("[%s]    ", sp->volume_letters);
+            else if (3 == n)
+                printf("[%s]   ", sp->volume_letters);
+            else if (4 == n)
+                printf("[%s]  ", sp->volume_letters);
+            else
+                printf("[%4s+] ", sp->volume_letters);
+            if (sp->qp_descriptor_valid) {
+                if (show_bt)
+                    printf("<%s>  ",
+                           get_bus_type(sp->qp_descriptor.desc.BusType));
+                j = sp->qp_descriptor.desc.VendorIdOffset;
+                if (j > 0)
+                    printf("%s  ", sp->qp_descriptor.raw + j);
+                j = sp->qp_descriptor.desc.ProductIdOffset;
+                if (j > 0)
+                    printf("%s  ", sp->qp_descriptor.raw + j);
+                j = sp->qp_descriptor.desc.ProductRevisionOffset;
+                if (j > 0)
+                    printf("%s  ", sp->qp_descriptor.raw + j);
+                j = sp->qp_descriptor.desc.SerialNumberOffset;
+                if (j > 0)
+                    printf("%s", sp->qp_descriptor.raw + j);
+                printf("\n");
+                if (verbose > 2)
+                    dStrHexErr(sp->qp_descriptor.raw, 144, 0);
+            } else
+                printf("\n");
+            if ((verbose > 3) && sp->qp_uid_valid) {
+                printf("  UID valid, in hex:\n");
+                dStrHexErr(sp->qp_uid.raw, sizeof(sp->qp_uid.raw), 1);
+            }
+        }
+    }
+
+    if (scsi_scan) {
+        if (scsi_scan < 2)
+            printf("\n");
+        enum_scsi_adapters();
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int c, ret;
+    int vol_letter = 0;
+    int show_bt = 0;
+    int scsi_scan = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "bhHl:svV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            ++show_bt;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'l':
+            vol_letter = toupper(optarg[0]);
+            if ((vol_letter < 'C') || (vol_letter > 'Z')) {
+                pr2serr("'--letter=' expects a letter in the 'C' to 'Z' "
+                        "range\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 's':
+            ++scsi_scan;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    storage_arr = calloc(sizeof(struct storage_elem) * MAX_SCSI_ELEMS, 1);
+    if (storage_arr) {
+        ret = sg_do_wscan(vol_letter, show_bt, scsi_scan);
+        free(storage_arr);
+    } else {
+        pr2serr("Failed to allocate storage_arr on heap\n");
+        ret = SG_LIB_SYNTAX_ERROR;
+    }
+    return ret;
+}
diff --git a/sg3_utils/src/sg_senddiag.c b/sg3_utils/src/sg_senddiag.c
new file mode 100644
index 0000000..5e5de56
--- /dev/null
+++ b/sg3_utils/src/sg_senddiag.c
@@ -0,0 +1,867 @@
+/* A utility program originally written for the Linux OS SCSI subsystem
+*  Copyright (C) 2003-2015 D. Gilbert
+*  This program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+
+   This program issues the SCSI SEND DIAGNOSTIC command and in one case
+   the SCSI RECEIVE DIAGNOSTIC command to list supported diagnostic pages.
+*/
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "0.47 20151219";
+
+#define ME "sg_senddiag: "
+
+#define DEF_ALLOC_LEN (1024 * 4)
+
+static struct option long_options[] = {
+        {"doff", no_argument, 0, 'd'},
+        {"extdur", no_argument, 0, 'e'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"list", no_argument, 0, 'l'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"new", no_argument, 0, 'N'},
+        {"old", no_argument, 0, 'O'},
+        {"page", required_argument, 0, 'P'},
+        {"pf", no_argument, 0, 'p'},
+        {"raw", required_argument, 0, 'r'},
+        {"selftest", required_argument, 0, 's'},
+        {"test", no_argument, 0, 't'},
+        {"uoff", no_argument, 0, 'u'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_doff;
+    int do_extdur;
+    int do_help;
+    int do_hex;
+    int do_list;
+    int maxlen;
+    int page_code;
+    int do_pf;
+    int do_raw;
+    int do_selftest;
+    int do_deftest;
+    int do_uoff;
+    int do_verbose;
+    int do_version;
+    const char * device_name;
+    const char * raw_arg;
+    int opt_new;
+};
+
+
+static void
+usage()
+{
+    printf("Usage: sg_senddiag [--doff] [--extdur] [--help] [--hex] "
+           "[--list]\n"
+           "                   [--maxlen=LEN] [--page=PG] [--pf] "
+           "[--raw=H,H...]\n"
+           "                   [--selftest=ST] [--test] [--uoff] "
+           "[--verbose] [--version]\n"
+           "                   [DEVICE]\n"
+           "  where:\n"
+           "    --doff|-d       device online (def: 0, only with '--test')\n"
+           "    --extdur|-e     duration of an extended self-test (from mode "
+           "page 0xa)\n"
+           "    --help|-h       print usage message then exit\n"
+           "    --hex|-H        output RDR in hex; twice: plus ASCII; thrice: "
+           "suitable\n"
+           "                    for '--raw=-' with later invocation\n"
+           "    --list|-l       list supported page codes (with or without "
+           "DEVICE)\n"
+           "    --maxlen=LEN|-m LEN    parameter list length or maximum "
+           "allocation\n"
+           "                           length (default: 4096 bytes)\n"
+           "    --page=PG|-P PG    do RECEIVE DIAGNOSTIC RESULTS only, set "
+           "PCV\n"
+           "    --pf|-p         set PF bit (def: 0)\n"
+           "    --raw=H,H...|-r H,H...    sequence of hex bytes to form "
+           "diag page to send\n"
+           "    --raw=-|-r -    read stdin for sequence of bytes to send\n"
+           "    --selftest=ST|-s ST    self-test code, default: 0 "
+           "(inactive)\n"
+           "                           1->background short, 2->background "
+           "extended\n"
+           "                           4->abort test\n"
+           "                           5->foreground short, 6->foreground "
+           "extended\n"
+           "    --test|-t       default self-test\n"
+           "    --uoff|-u       unit offline (def: 0, only with '--test')\n"
+           "    --verbose|-v    increase verbosity\n"
+           "    --version|-V    output version string then exit\n\n"
+           "Performs a SCSI SEND DIAGNOSTIC (and/or a RECEIVE DIAGNOSTIC "
+           "RESULTS) command\n"
+        );
+}
+
+static void
+usage_old()
+{
+    printf("Usage: sg_senddiag [-doff] [-e] [-h] [-H] [-l] [-pf]"
+           " [-raw=H,H...]\n"
+           "                   [-s=SF] [-t] [-uoff] [-v] [-V] "
+           "[DEVICE]\n"
+           "  where:\n"
+           "    -doff   device online (def: 0, only with '-t')\n"
+           "    -e      duration of an extended self-test (from mode page "
+           "0xa)\n"
+           "    -h      output in hex\n"
+           "    -H      output in hex (same as '-h')\n"
+           "    -l      list supported page codes\n"
+           "    -pf     set PF bit (def: 0)\n"
+           "    -raw=H,H...    sequence of bytes to form diag page to "
+           "send\n"
+           "    -raw=-  read stdin for sequence of bytes to send\n"
+           "    -s=SF   self-test code (def: 0)\n"
+           "            1->background short, 2->background extended,"
+           " 4->abort test\n"
+           "            5->foreground short, 6->foreground extended\n"
+           "    -t      default self-test\n"
+           "    -uoff   unit offline (def: 0, only with '-t')\n"
+           "    -v      increase verbosity (print issued SCSI cmds)\n"
+           "    -V      output version string\n"
+           "    -?      output this usage message\n\n"
+           "Performs a SCSI SEND DIAGNOSTIC (and/or a RECEIVE DIAGNOSTIC "
+           "RESULTS) command\n"
+        );
+}
+
+static int
+process_cl_new(struct opts_t * op, int argc, char * argv[])
+{
+    int c, n;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "dehHlm:NOpP:r:s:tuvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'd':
+            op->do_doff = 1;
+            break;
+        case 'e':
+            op->do_extdur = 1;
+            break;
+        case 'h':
+        case '?':
+            ++op->do_help;
+            break;
+        case 'H':
+            ++op->do_hex;
+            break;
+        case 'l':
+            ++op->do_list;
+            break;
+        case 'm':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 0xffff)) {
+                pr2serr("bad argument to '--maxlen=' or greater than 65535 "
+                        "[0xffff]\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->maxlen = n;
+            break;
+        case 'N':
+            break;      /* ignore */
+        case 'O':
+            op->opt_new = 0;
+            return 0;
+        case 'p':
+            op->do_pf = 1;
+            break;
+        case 'P':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 0xff)) {
+                pr2serr("bad argument to '--page=' or greater than 255 "
+                        "[0xff]\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->page_code = n;
+            break;
+        case 'r':
+            op->raw_arg = optarg;
+            op->do_raw = 1;
+            break;
+        case 's':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 7)) {
+                pr2serr("bad argument to '--selftest='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->do_selftest = n;
+            break;
+        case 't':
+            op->do_deftest = 1;
+            break;
+        case 'u':
+            op->do_uoff = 1;
+            break;
+        case 'v':
+            ++op->do_verbose;
+            break;
+        case 'V':
+            ++op->do_version;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (op->do_help)
+                break;
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int
+process_cl_old(struct opts_t * op, int argc, char * argv[])
+{
+    int k, jmp_out, plen, num;
+    unsigned int u;
+    const char * cp;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case 'd':
+                    if (0 == strncmp("doff", cp, 4)) {
+                        op->do_doff = 1;
+                        cp += 3;
+                        plen -= 3;
+                    } else
+                        jmp_out = 1;
+                    break;
+                case 'e':
+                    op->do_extdur = 1;
+                    break;
+                case 'h':
+                case 'H':
+                    ++op->do_hex;
+                    break;
+                case 'l':
+                    ++op->do_list;
+                    break;
+                case 'N':
+                    op->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case 'p':
+                    if (0 == strncmp("pf", cp, 2)) {
+                        op->do_pf = 1;
+                        ++cp;
+                        --plen;
+                    } else
+                        jmp_out = 1;
+                    break;
+                case 't':
+                    op->do_deftest = 1;
+                    break;
+                case 'u':
+                    if (0 == strncmp("uoff", cp, 4)) {
+                        op->do_uoff = 1;
+                        cp += 3;
+                        plen -= 3;
+                    } else
+                        jmp_out = 1;
+                    break;
+                case 'v':
+                    ++op->do_verbose;
+                    break;
+                case 'V':
+                    ++op->do_version;
+                    break;
+                case '?':
+                    ++op->do_help;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            if (0 == strncmp("raw=", cp, 4)) {
+                op->raw_arg = cp + 4;
+                op->do_raw = 1;
+            } else if (0 == strncmp("s=", cp, 2)) {
+                num = sscanf(cp + 2, "%x", &u);
+                if ((1 != num) || (u > 7)) {
+                    printf("Bad page code after '-s=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->do_selftest = u;
+            } else if (0 == strncmp("-old", cp, 5))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage_old();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == op->device_name)
+            op->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not expecting: %s\n",
+                    op->device_name, cp);
+            usage_old();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int
+process_cl(struct opts_t * op, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        op->opt_new = 0;
+        res = process_cl_old(op, argc, argv);
+        if ((0 == res) && op->opt_new)
+            res = process_cl_new(op, argc, argv);
+    } else {
+        op->opt_new = 1;
+        res = process_cl_new(op, argc, argv);
+        if ((0 == res) && (0 == op->opt_new))
+            res = process_cl_old(op, argc, argv);
+    }
+    return res;
+}
+
+/* Return of 0 -> success, otherwise see sg_ll_send_diag() */
+static int
+do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit, int devofl_bit,
+            int unitofl_bit, void * outgoing_pg, int outgoing_len,
+            int noisy, int verbose)
+{
+    int long_duration = 0;
+
+    if ((0 == sf_bit) && ((5 == sf_code) || (6 == sf_code)))
+        long_duration = 1;      /* foreground self-tests */
+    return sg_ll_send_diag(sg_fd, sf_code, pf_bit, sf_bit, devofl_bit,
+                           unitofl_bit, long_duration, outgoing_pg,
+                           outgoing_len, noisy, verbose);
+}
+
+/* Get expected extended self-test time from mode page 0xa (for '-e') */
+static int
+do_modes_0a(int sg_fd, void * resp, int mx_resp_len, int noisy, int mode6,
+            int verbose)
+{
+    int res;
+
+    if (mode6)
+        res = sg_ll_mode_sense6(sg_fd, 1 /* dbd */, 0 /* pc */, 0xa /* page */,
+                                0, resp, mx_resp_len, noisy, verbose);
+    else
+        res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, 1 /* dbd */, 0, 0xa, 0,
+                                 resp, mx_resp_len, noisy, verbose);
+    if (res) {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Mode sense (%s): %s\n", (mode6 ? "6" : "10"), b);
+    }
+    return res;
+}
+
+/* Read hex numbers from command line (comma separated list) or from */
+/* stdin (one per line, comma separated list or space separated list). */
+/* Returns 0 if ok, or 1 if error. */
+static int
+build_diag_page(const char * inp, unsigned char * mp_arr, int * mp_arr_len,
+                int max_arr_len)
+{
+    int in_len, k, j, m;
+    unsigned int h;
+    const char * lcp;
+    char * cp;
+    char * c2p;
+
+    if ((NULL == inp) || (NULL == mp_arr) ||
+        (NULL == mp_arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *mp_arr_len = 0;
+    if ('-' == inp[0]) {        /* read from stdin */
+        char line[512];
+        char carry_over[4];
+        int off = 0;
+        int split_line;
+
+        carry_over[0] = 0;
+        for (j = 0; j < 512; ++j) {
+            if (NULL == fgets(line, sizeof(line), stdin))
+                break;
+            in_len = strlen(line);
+            if (in_len > 0) {
+                if ('\n' == line[in_len - 1]) {
+                    --in_len;
+                    line[in_len] = '\0';
+                    split_line = 0;
+                } else
+                    split_line = 1;
+            }
+            if (in_len < 1) {
+                carry_over[0] = 0;
+                continue;
+            }
+            if (carry_over[0]) {
+                if (isxdigit(line[0])) {
+                    carry_over[1] = line[0];
+                    carry_over[2] = '\0';
+                    if (1 == sscanf(carry_over, "%x", &h))
+                        mp_arr[off - 1] = h;       /* back up and overwrite */
+                    else {
+                        pr2serr("build_diag_page: carry_over error ['%s'] "
+                                "around line %d\n", carry_over, j + 1);
+                        return 1;
+                    }
+                    lcp = line + 1;
+                    --in_len;
+                } else
+                    lcp = line;
+                carry_over[0] = 0;
+            } else
+                lcp = line;
+            m = strspn(lcp, " \t");
+            if (m == in_len)
+                continue;
+            lcp += m;
+            in_len -= m;
+            if ('#' == *lcp)
+                continue;
+            k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+            if ((k < in_len) && ('#' != lcp[k])) {
+                pr2serr("build_diag_page: syntax error at line %d, pos %d\n",
+                        j + 1, m + k + 1);
+                return 1;
+            }
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("build_diag_page: hex number larger than "
+                                "0xff in line %d, pos %d\n", j + 1,
+                                (int)(lcp - line + 1));
+                        return 1;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("build_diag_page: array length exceeded\n");
+                        return 1;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if ('#' == *lcp) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("build_diag_page: error in line %d, at pos %d\n",
+                            j + 1, (int)(lcp - line + 1));
+                    return 1;
+                }
+            }
+            off += (k + 1);
+        }
+        *mp_arr_len = off;
+    } else {        /* hex string on command line */
+        k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
+        if (in_len != k) {
+            pr2serr("build_diag_page: error at pos %d\n", k + 1);
+            return 1;
+        }
+        for (k = 0; k < max_arr_len; ++k) {
+            if (1 == sscanf(lcp, "%x", &h)) {
+                if (h > 0xff) {
+                    pr2serr("build_diag_page: hex number larger than 0xff at "
+                            "pos %d\n", (int)(lcp - inp + 1));
+                    return 1;
+                }
+                mp_arr[k] = h;
+                cp = (char *)strchr(lcp, ',');
+                c2p = (char *)strchr(lcp, ' ');
+                if (NULL == cp)
+                    cp = c2p;
+                if (NULL == cp)
+                    break;
+                if (c2p && (c2p < cp))
+                    cp = c2p;
+                lcp = cp + 1;
+            } else {
+                pr2serr("build_diag_page: error at pos %d\n",
+                        (int)(lcp - inp + 1));
+                return 1;
+            }
+        }
+        *mp_arr_len = k + 1;
+        if (k == max_arr_len) {
+            pr2serr("build_diag_page: array length exceeded\n");
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+struct page_code_desc {
+        int page_code;
+        const char * desc;
+};
+static struct page_code_desc pc_desc_arr[] = {
+        {0x0, "Supported diagnostic pages"},
+        {0x1, "Configuration (SES)"},
+        {0x2, "Enclosure status/control (SES)"},
+        {0x3, "Help text (SES)"},
+        {0x4, "String In/Out (SES)"},
+        {0x5, "Threshold In/Out (SES)"},
+        {0x6, "Array Status/Control (SES, obsolete)"},
+        {0x7, "Element descriptor (SES)"},
+        {0x8, "Short enclosure status (SES)"},
+        {0x9, "Enclosure busy (SES-2)"},
+        {0xa, "Additional (device) element status (SES-2)"},
+        {0xb, "Subenclosure help text (SES-2)"},
+        {0xc, "Subenclosure string In/Out (SES-2)"},
+        {0xd, "Supported SES diagnostic pages (SES-2)"},
+        {0xe, "Download microcode diagnostic pages (SES-2)"},
+        {0xf, "Subenclosure nickname diagnostic pages (SES-2)"},
+        {0x3f, "Protocol specific (SAS transport)"},
+        {0x40, "Translate address (direct access)"},
+        {0x41, "Device status (direct access)"},
+        {0x42, "Rebuild assist (direct access)"}, /* sbc3r31 */
+};
+
+static const char *
+find_page_code_desc(int page_num)
+{
+    int k;
+    int num = sizeof(pc_desc_arr) / sizeof(pc_desc_arr[0]);
+    const struct page_code_desc * pcdp = &pc_desc_arr[0];
+
+    for (k = 0; k < num; ++k, ++pcdp) {
+        if (page_num == pcdp->page_code)
+            return pcdp->desc;
+        else if (page_num < pcdp->page_code)
+            return NULL;
+    }
+    return NULL;
+}
+
+static void
+list_page_codes()
+{
+    int k;
+    int num = sizeof(pc_desc_arr) / sizeof(pc_desc_arr[0]);
+    const struct page_code_desc * pcdp = &pc_desc_arr[0];
+
+    printf("Page_Code  Description\n");
+    for (k = 0; k < num; ++k, ++pcdp)
+        printf(" 0x%02x      %s\n", pcdp->page_code,
+               (pcdp->desc ? pcdp->desc : "<unknown>"));
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, num, rsp_len, res, rsp_buff_size, pg;
+    int read_in_len = 0;
+    int ret = 0;
+    struct opts_t opts;
+    struct opts_t * op;
+    unsigned char * rsp_buff = NULL;
+    const char * cp;
+    unsigned char * read_in = NULL;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->maxlen = DEF_ALLOC_LEN;
+    op->page_code = -1;
+    res = process_cl(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        if (op->opt_new)
+            usage();
+        else
+            usage_old();
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+    rsp_buff_size = op->maxlen;
+
+    if (NULL == op->device_name) {
+        if (op->do_list) {
+            list_page_codes();
+            return 0;
+        }
+        pr2serr("No DEVICE argument given\n");
+        if (op->opt_new)
+            usage();
+        else
+            usage_old();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_raw) {
+        read_in = (unsigned char *)calloc(op->maxlen, 1);
+        if (NULL == read_in) {
+            pr2serr("unable to allocate %d bytes\n", op->maxlen);
+            return SG_LIB_CAT_OTHER;
+        }
+        if (build_diag_page(op->raw_arg, read_in, &read_in_len, op->maxlen)) {
+            if (op->opt_new) {
+                printf("Bad sequence after '--raw=' option\n");
+                usage();
+            } else {
+                printf("Bad sequence after '-raw=' option\n");
+                usage_old();
+            }
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if ((op->do_doff || op->do_uoff) && (! op->do_deftest)) {
+        if (op->opt_new) {
+            printf("setting --doff or --uoff only useful when -t is set\n");
+            usage();
+        } else {
+            printf("setting -doff or -uoff only useful when -t is set\n");
+            usage_old();
+        }
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((op->do_selftest > 0) && op->do_deftest) {
+        if (op->opt_new) {
+            printf("either set --selftest=SF or --test (not both)\n");
+            usage();
+        } else {
+            printf("either set -s=SF or -t (not both)\n");
+            usage_old();
+        }
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_raw) {
+        if ((op->do_selftest > 0) || op->do_deftest || op->do_extdur ||
+            op->do_list) {
+            if (op->opt_new) {
+                printf("'--raw=' cannot be used with self-tests, '-e' or "
+                       "'-l'\n");
+                usage();
+            } else {
+                printf("'-raw=' cannot be used with self-tests, '-e' or "
+                       "'-l'\n");
+                usage_old();
+            }
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (! op->do_pf) {
+            if (op->opt_new)
+                printf(">>> warning, '--pf' probably should be used with "
+                       "'--raw='\n");
+            else
+                printf(">>> warning, '-pf' probably should be used with "
+                       "'-raw='\n");
+        }
+    }
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+    if (op->do_verbose > 4)
+        pr2serr("Initial win32 SPT interface state: %s\n",
+                scsi_pt_win32_spt_state() ? "direct" : "indirect");
+    if (op->maxlen >= 16384)
+        scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
+#endif
+#endif
+
+    if ((sg_fd = sg_cmds_open_device(op->device_name, 0 /* rw */,
+                                     op->do_verbose)) < 0) {
+        pr2serr(ME "error opening file: %s: %s\n", op->device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    rsp_buff = (unsigned char *)calloc(op->maxlen, 1);
+    if (NULL == rsp_buff) {
+        pr2serr("unable to allocate %d bytes (2)\n", op->maxlen);
+        return SG_LIB_CAT_OTHER;
+    }
+    if (op->do_extdur) {
+        res = do_modes_0a(sg_fd, rsp_buff, 32, 1, 0, op->do_verbose);
+        if (0 == res) {
+            /* Assume mode sense(10) response without block descriptors */
+            num = sg_get_unaligned_be16(rsp_buff) - 6;
+            if (num >= 0xc) {
+                int secs;
+
+                secs = sg_get_unaligned_be16(rsp_buff + 18);
+#ifdef SG_LIB_MINGW
+                printf("Expected extended self-test duration=%d seconds "
+                       "(%g minutes)\n", secs, secs / 60.0);
+#else
+                printf("Expected extended self-test duration=%d seconds "
+                       "(%.2f minutes)\n", secs, secs / 60.0);
+#endif
+            } else
+                printf("Extended self-test duration not available\n");
+        } else {
+            ret = res;
+            printf("Extended self-test duration (mode page 0xa) failed\n");
+            goto err_out9;
+        }
+    } else if ((op->do_list) || (op->page_code >= 0x0)) {
+        pg = op->page_code;
+        if (pg < 0)
+            res = do_senddiag(sg_fd, 0, 1 /* pf */, 0, 0, 0, rsp_buff, 4, 1,
+                              op->do_verbose);
+        else
+            res = 0;
+        if (0 == res) {
+            if (0 == sg_ll_receive_diag(sg_fd, (pg >= 0x0),
+                                        ((pg >= 0x0) ? pg : 0), rsp_buff,
+                                        rsp_buff_size, 1, op->do_verbose)) {
+                rsp_len = sg_get_unaligned_be16(rsp_buff + 2) + 4;
+                if (op->do_hex > 1)
+                    dStrHex((const char *)rsp_buff, rsp_len,
+                            (2 == op->do_hex) ? 0 : -1);
+                else if (pg < 0x1) {
+                    printf("Supported diagnostic pages response:\n");
+                    if (op->do_hex)
+                        dStrHex((const char *)rsp_buff, rsp_len, 1);
+                    else {
+                        for (k = 0; k < (rsp_len - 4); ++k) {
+                            cp = find_page_code_desc(rsp_buff[k + 4]);
+                            printf("  0x%02x  %s\n", rsp_buff[k + 4],
+                                   (cp ? cp : "<unknown>"));
+                        }
+                    }
+                } else {
+                    cp = find_page_code_desc(pg);
+                    if (cp)
+                        printf("%s diagnostic page [0x%x] response in "
+                               "hex:\n", cp, pg);
+                    else
+                        printf("diagnostic page 0x%x response in hex:\n", pg);
+                    dStrHex((const char *)rsp_buff, rsp_len, 1);
+                }
+            } else {
+                ret = res;
+                pr2serr("RECEIVE DIAGNOSTIC RESULTS command failed\n");
+                goto err_out9;
+            }
+        } else {
+            ret = res;
+            goto err_out;
+        }
+    } else if (op->do_raw) {
+        res = do_senddiag(sg_fd, 0, op->do_pf, 0, 0, 0, read_in,
+                          read_in_len, 1, op->do_verbose);
+        if (res) {
+            ret = res;
+            goto err_out;
+        }
+    } else {
+        res = do_senddiag(sg_fd, op->do_selftest, op->do_pf, op->do_deftest,
+                          op->do_doff, op->do_uoff, NULL, 0, 1,
+                          op->do_verbose);
+        if (0 == res) {
+            if ((5 == op->do_selftest) || (6 == op->do_selftest))
+                printf("Foreground self-test returned GOOD status\n");
+            else if (op->do_deftest && (! op->do_doff) && (! op->do_uoff))
+                printf("Default self-test returned GOOD status\n");
+        } else {
+            ret = res;
+            goto err_out;
+        }
+    }
+    res = sg_cmds_close_device(sg_fd);
+    if ((res < 0) && (0 == ret))
+        return SG_LIB_SYNTAX_ERROR;
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+
+err_out:
+    if (SG_LIB_CAT_UNIT_ATTENTION == res)
+        pr2serr("SEND DIAGNOSTIC, unit attention\n");
+    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
+        pr2serr("SEND DIAGNOSTIC, aborted command\n");
+    else if (SG_LIB_CAT_NOT_READY == res)
+        pr2serr("SEND DIAGNOSTIC, device not ready\n");
+    else
+        pr2serr("SEND DIAGNOSTIC command, failed\n");
+err_out9:
+    if (op->do_verbose < 2)
+        pr2serr("  try again with '-vv' for more information\n");
+    res = sg_cmds_close_device(sg_fd);
+    if ((res < 0) && (0 == ret))
+        return SG_LIB_FILE_ERROR;
+    if (read_in)
+        free(read_in);
+    if (rsp_buff)
+        free(rsp_buff);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_ses.c b/sg3_utils/src/sg_ses.c
new file mode 100644
index 0000000..6499664
--- /dev/null
+++ b/sg3_utils/src/sg_ses.c
@@ -0,0 +1,4488 @@
+/*
+ * Copyright (c) 2004-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
+#include "sg_pr2serr.h"
+
+/*
+ * This program issues SCSI SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC RESULTS
+ * commands tailored for SES (enclosure) devices.
+ */
+
+static const char * version_str = "2.07 20160201";    /* ses3r08->11 */
+
+#define MX_ALLOC_LEN ((64 * 1024) - 4)  /* max allowable for big enclosures */
+#define MX_ELEM_HDR 1024
+#define MX_DATA_IN 2048
+#define MX_JOIN_ROWS 260
+#define NUM_ACTIVE_ET_AESP_ARR 32
+
+#define TEMPERAT_OFF 20         /* 8 bits represents -19 C to +235 C */
+                                /* value of 0 (would imply -20 C) reserved */
+
+/* Send Diagnostic and Receive Diagnostic Results page codes */
+#define DPC_SUPPORTED 0x0
+#define DPC_CONFIGURATION 0x1
+#define DPC_ENC_CONTROL 0x2
+#define DPC_ENC_STATUS 0x2
+#define DPC_HELP_TEXT 0x3
+#define DPC_STRING 0x4
+#define DPC_THRESHOLD 0x5
+#define DPC_ARRAY_CONTROL 0x6   /* obsolete */
+#define DPC_ARRAY_STATUS 0x6    /* obsolete */
+#define DPC_ELEM_DESC 0x7
+#define DPC_SHORT_ENC_STATUS 0x8
+#define DPC_ENC_BUSY 0x9
+#define DPC_ADD_ELEM_STATUS 0xa
+#define DPC_SUBENC_HELP_TEXT 0xb
+#define DPC_SUBENC_STRING 0xc
+#define DPC_SUPPORTED_SES 0xd
+#define DPC_DOWNLOAD_MICROCODE 0xe
+#define DPC_SUBENC_NICKNAME 0xf
+
+/* Element Type codes */
+#define UNSPECIFIED_ETC 0x0
+#define DEVICE_ETC 0x1
+#define POWER_SUPPLY_ETC 0x2
+#define COOLING_ETC 0x3
+#define TEMPERATURE_ETC 0x4
+#define DOOR_ETC 0x5    /* prior to ses3r05 was DOOR_LOCK_ETC */
+#define AUD_ALARM_ETC 0x6
+#define ENC_ELECTRONICS_ETC 0x7
+#define SCC_CELECTR_ETC 0x8
+#define NV_CACHE_ETC 0x9
+#define INV_OP_REASON_ETC 0xa
+#define UI_POWER_SUPPLY_ETC 0xb
+#define DISPLAY_ETC 0xc
+#define KEY_PAD_ETC 0xd
+#define ENCLOSURE_ETC 0xe
+#define SCSI_PORT_TRAN_ETC 0xf
+#define LANGUAGE_ETC 0x10
+#define COMM_PORT_ETC 0x11
+#define VOLT_SENSOR_ETC 0x12
+#define CURR_SENSOR_ETC 0x13
+#define SCSI_TPORT_ETC 0x14
+#define SCSI_IPORT_ETC 0x15
+#define SIMPLE_SUBENC_ETC 0x16
+#define ARRAY_DEV_ETC 0x17
+#define SAS_EXPANDER_ETC 0x18
+#define SAS_CONNECTOR_ETC 0x19
+#define LAST_ETC SAS_CONNECTOR_ETC      /* adjust as necessary */
+
+#define NUM_ETC (LAST_ETC + 1)
+
+
+struct element_type_t {
+    int elem_type_code;
+    const char * abbrev;
+    const char * desc;
+};
+
+struct opts_t {
+    int byte1;
+    int byte1_given;
+    int do_control;
+    int do_data;
+    int dev_slot_num;
+    int enumerate;
+    int eiioe_auto;
+    int eiioe_force;
+    int do_filter;
+    int do_help;
+    int do_hex;
+    int ind_given;
+    int ind_th;         /* type header index */
+    int ind_indiv;      /* individual element index; -1 for overall */
+    int ind_et_inst;    /* ETs can have multiple type header instances */
+    int inner_hex;
+    int do_join;
+    int do_list;
+    int mask_ign;       /* element read-mask-modify-write actions */
+    int maxlen;
+    int seid;
+    int seid_given;
+    int page_code;
+    int page_code_given;
+    int do_raw;
+    int o_readonly;
+    int do_status;
+    int verbose;
+    int do_version;
+    int warn;
+    int num_cgs;
+    int arr_len;
+    unsigned char sas_addr[8];
+    unsigned char data_arr[MX_DATA_IN + 16];
+    const char * clear_str;
+    const char * desc_name;
+    const char * get_str;
+    const char * set_str;
+    const char * dev_name;
+    const char * index_str;
+    const char * nickname_str;
+    const struct element_type_t * ind_etp;
+};
+
+struct diag_page_code {
+    int page_code;
+    const char * desc;
+};
+
+struct diag_page_abbrev {
+    const char * abbrev;
+    int page_code;
+};
+
+/* The Configuration diagnostic page contains one or more of these. The
+ * elements of the Enclosure Control/Status and Threshold In/ Out page follow
+ * this format. The additional element status page is closely related to
+ * this format (with some element types and all overall elements excluded). */
+struct type_desc_hdr_t {
+    unsigned char etype;        /* element type code (0: unspecified) */
+    unsigned char num_elements; /* number of possible elements, excluding
+                                 * overall element */
+    unsigned char se_id;        /* subenclosure id (0 for primary enclosure) */
+    unsigned char txt_len;      /* type descriptor text length; (unused) */
+};
+
+/* A SQL-like join of the Enclosure Status, Threshold In and Additional
+ * Element Status pages based of the format indicated in the Configuration
+ * page. */
+struct join_row_t {
+    int el_ind_th;              /* type header index (origin 0) */
+    int el_ind_indiv;           /* individual element index, -1 for overall
+                                 * instance, otherwise origin 0 */
+    unsigned char etype;        /* element type */
+    unsigned char se_id;        /* subenclosure id (0 for primary enclosure) */
+    int ei_asc;                 /* element index used by Additional Element
+                                 * Status page, -1 for not applicable */
+    int ei_asc2;                /* some vendors get ei_asc wrong, this is
+                                 * their broken version */
+    /* following point into Element Descriptor, Enclosure Status, Threshold
+     * In and Additional element status diagnostic pages. enc_statp only
+     * NULL past last, other pointers can be NULL . */
+    unsigned char * elem_descp;
+    unsigned char * enc_statp;  /* NULL indicates past last */
+    unsigned char * thresh_inp;
+    unsigned char * add_elem_statp;
+    int dev_slot_num;           /* if not available, set to -1 */
+    unsigned char sas_addr[8];  /* if not available, set to 0 */
+};
+
+/* Representation of <acronym>[=<value>] or
+ * <start_byte>:<start_bit>[:<num_bits>][=<value>]. */
+struct tuple_acronym_val {
+    const char * acron;
+    const char * val_str;
+    int start_byte;     /* -1 indicates no start_byte */
+    int start_bit;
+    int num_bits;
+    int64_t val;
+};
+
+/* Mapping from <acronym> to <start_byte>:<start_bit>:<num_bits> for a
+ * given element type. */
+struct acronym2tuple {
+    const char * acron; /* element name or acronym, NULL for past end */
+    int etype;          /* -1 for all element types */
+    int start_byte;     /* origin 0, normally 0 to 3 */
+    int start_bit;      /* 7 (MSB or rightmost in SES drafts) to 0 (LSB) */
+    int num_bits;       /* usually 1 */
+    const char * info;  /* optional, set to NULL if not used */
+};
+
+/* Structure for holding (sub-)enclosure information found in the
+ * Configuration diagnostic page. */
+struct enclosure_info {
+    int have_info;
+    int rel_esp_id;     /* relative enclosure services process id (origin 1) */
+    int num_esp;        /* number of enclosure services processes */
+    unsigned char enc_log_id[8];        /* 8 byte NAA */
+    unsigned char enc_vendor_id[8];     /* may differ from INQUIRY response */
+    unsigned char product_id[16];       /* may differ from INQUIRY response */
+    unsigned char product_rev_level[4]; /* may differ from INQUIRY response */
+};
+
+
+static struct type_desc_hdr_t type_desc_hdr_arr[MX_ELEM_HDR];
+
+static struct join_row_t join_arr[MX_JOIN_ROWS];
+static struct join_row_t * join_arr_lastp = join_arr + MX_JOIN_ROWS - 1;
+
+#ifdef SG_LIB_FREEBSD
+
+#include <sys/param.h>  /* contains PAGE_SIZE */
+
+static unsigned char enc_stat_rsp[MX_ALLOC_LEN]
+        __attribute__ ((aligned (PAGE_SIZE)));
+static unsigned char elem_desc_rsp[MX_ALLOC_LEN]
+        __attribute__ ((aligned (PAGE_SIZE)));
+static unsigned char add_elem_rsp[MX_ALLOC_LEN]
+        __attribute__ ((aligned (PAGE_SIZE)));
+static unsigned char threshold_rsp[MX_ALLOC_LEN]
+        __attribute__ ((aligned (PAGE_SIZE)));
+
+#else
+
+static unsigned char enc_stat_rsp[MX_ALLOC_LEN];
+static unsigned char elem_desc_rsp[MX_ALLOC_LEN];
+static unsigned char add_elem_rsp[MX_ALLOC_LEN];
+static unsigned char threshold_rsp[MX_ALLOC_LEN];
+
+#endif
+
+static int enc_stat_rsp_len;
+static int elem_desc_rsp_len;
+static int add_elem_rsp_len;
+static int threshold_rsp_len;
+
+
+/* Diagnostic page names, control and/or status (in and/or out) */
+static struct diag_page_code dpc_arr[] = {
+    {DPC_SUPPORTED, "Supported Diagnostic Pages"},  /* 0 */
+    {DPC_CONFIGURATION, "Configuration (SES)"},
+    {DPC_ENC_STATUS, "Enclosure Status/Control (SES)"},
+    {DPC_HELP_TEXT, "Help Text (SES)"},
+    {DPC_STRING, "String In/Out (SES)"},
+    {DPC_THRESHOLD, "Threshold In/Out (SES)"},
+    {DPC_ARRAY_STATUS, "Array Status/Control (SES, obsolete)"},
+    {DPC_ELEM_DESC, "Element Descriptor (SES)"},
+    {DPC_SHORT_ENC_STATUS, "Short Enclosure Status (SES)"},  /* 8 */
+    {DPC_ENC_BUSY, "Enclosure Busy (SES-2)"},
+    {DPC_ADD_ELEM_STATUS, "Additional Element Status (SES-2)"},
+    {DPC_SUBENC_HELP_TEXT, "Subenclosure Help Text (SES-2)"},
+    {DPC_SUBENC_STRING, "Subenclosure String In/Out (SES-2)"},
+    {DPC_SUPPORTED_SES, "Supported SES Diagnostic Pages (SES-2)"},
+    {DPC_DOWNLOAD_MICROCODE, "Download Microcode (SES-2)"},
+    {DPC_SUBENC_NICKNAME, "Subenclosure Nickname (SES-2)"},
+    {0x3f, "Protocol Specific (SAS transport)"},
+    {0x40, "Translate Address (SBC)"},
+    {0x41, "Device Status (SBC)"},
+    {0x42, "Rebuild Assist (SBC)"},     /* sbc3r31 */
+    {-1, NULL},
+};
+
+/* Diagnostic page names, for status (or in) pages */
+static struct diag_page_code in_dpc_arr[] = {
+    {DPC_SUPPORTED, "Supported Diagnostic Pages"},  /* 0 */
+    {DPC_CONFIGURATION, "Configuration (SES)"},
+    {DPC_ENC_STATUS, "Enclosure Status (SES)"},
+    {DPC_HELP_TEXT, "Help Text (SES)"},
+    {DPC_STRING, "String In (SES)"},
+    {DPC_THRESHOLD, "Threshold In (SES)"},
+    {DPC_ARRAY_STATUS, "Array Status (SES, obsolete)"},
+    {DPC_ELEM_DESC, "Element Descriptor (SES)"},
+    {DPC_SHORT_ENC_STATUS, "Short Enclosure Status (SES)"},  /* 8 */
+    {DPC_ENC_BUSY, "Enclosure Busy (SES-2)"},
+    {DPC_ADD_ELEM_STATUS, "Additional Element Status (SES-2)"},
+    {DPC_SUBENC_HELP_TEXT, "Subenclosure Help Text (SES-2)"},
+    {DPC_SUBENC_STRING, "Subenclosure String In (SES-2)"},
+    {DPC_SUPPORTED_SES, "Supported SES Diagnostic Pages (SES-2)"},
+    {DPC_DOWNLOAD_MICROCODE, "Download Microcode (SES-2)"},
+    {DPC_SUBENC_NICKNAME, "Subenclosure Nickname (SES-2)"},
+    {0x3f, "Protocol Specific (SAS transport)"},
+    {0x40, "Translate Address (SBC)"},
+    {0x41, "Device Status (SBC)"},
+    {0x42, "Rebuild Assist Input (SBC)"},
+    {-1, NULL},
+};
+
+/* Diagnostic page names, for control (or out) pages */
+static struct diag_page_code out_dpc_arr[] = {
+    {DPC_SUPPORTED, "?? [Supported Diagnostic Pages]"},  /* 0 */
+    {DPC_CONFIGURATION, "?? [Configuration (SES)]"},
+    {DPC_ENC_CONTROL, "Enclosure Control (SES)"},
+    {DPC_HELP_TEXT, "Help Text (SES)"},
+    {DPC_STRING, "String Out (SES)"},
+    {DPC_THRESHOLD, "Threshold Out (SES)"},
+    {DPC_ARRAY_CONTROL, "Array Control (SES, obsolete)"},
+    {DPC_ELEM_DESC, "?? [Element Descriptor (SES)]"},
+    {DPC_SHORT_ENC_STATUS, "?? [Short Enclosure Status (SES)]"},  /* 8 */
+    {DPC_ENC_BUSY, "?? [Enclosure Busy (SES-2)]"},
+    {DPC_ADD_ELEM_STATUS, "?? [Additional Element Status (SES-2)]"},
+    {DPC_SUBENC_HELP_TEXT, "?? [Subenclosure Help Text (SES-2)]"},
+    {DPC_SUBENC_STRING, "Subenclosure String Out (SES-2)"},
+    {DPC_SUPPORTED_SES, "?? [Supported SES Diagnostic Pages (SES-2)]"},
+    {DPC_DOWNLOAD_MICROCODE, "Download Microcode (SES-2)"},
+    {DPC_SUBENC_NICKNAME, "Subenclosure Nickname (SES-2)"},
+    {0x3f, "Protocol Specific (SAS transport)"},
+    {0x40, "Translate Address (SBC)"},
+    {0x41, "Device Status (SBC)"},
+    {0x42, "Rebuild Assist Output (SBC)"},
+    {-1, NULL},
+};
+
+static struct diag_page_abbrev dp_abbrev[] = {
+    {"ac", DPC_ARRAY_CONTROL},
+    {"aes", DPC_ADD_ELEM_STATUS},
+    {"as", DPC_ARRAY_STATUS},
+    {"cf", DPC_CONFIGURATION},
+    {"dm", DPC_DOWNLOAD_MICROCODE},
+    {"eb", DPC_ENC_BUSY},
+    {"ec", DPC_ENC_CONTROL},
+    {"ed", DPC_ELEM_DESC},
+    {"es", DPC_ENC_STATUS},
+    {"ht", DPC_HELP_TEXT},
+    {"sdp", DPC_SUPPORTED},
+    {"ses", DPC_SHORT_ENC_STATUS},
+    {"sht", DPC_SUBENC_HELP_TEXT},
+    {"snic", DPC_SUBENC_NICKNAME},
+    {"ssp", DPC_SUPPORTED_SES},
+    {"sstr", DPC_SUBENC_STRING},
+    {"str", DPC_STRING},
+    {"th", DPC_THRESHOLD},
+    {NULL, -1},
+};
+
+/* Names of element types used by the Enclosure Control/Status diagnostic
+ * page. */
+static struct element_type_t element_type_arr[] = {
+    {UNSPECIFIED_ETC, "un", "Unspecified"},
+    {DEVICE_ETC, "dev", "Device slot"},
+    {POWER_SUPPLY_ETC, "ps", "Power supply"},
+    {COOLING_ETC, "coo", "Cooling"},
+    {TEMPERATURE_ETC, "ts", "Temperature sensor"},
+    {DOOR_ETC, "do", "Door"},   /* prior to ses3r05 was 'dl' (for Door Lock)
+                                   but the "Lock" has been dropped */
+    {AUD_ALARM_ETC, "aa", "Audible alarm"},
+    {ENC_ELECTRONICS_ETC, "esc", "Enclosure services controller electronics"},
+    {SCC_CELECTR_ETC, "sce", "SCC controller electronics"},
+    {NV_CACHE_ETC, "nc", "Nonvolatile cache"},
+    {INV_OP_REASON_ETC, "ior", "Invalid operation reason"},
+    {UI_POWER_SUPPLY_ETC, "ups", "Uninterruptible power supply"},
+    {DISPLAY_ETC, "dis", "Display"},
+    {KEY_PAD_ETC, "kpe", "Key pad entry"},
+    {ENCLOSURE_ETC, "enc", "Enclosure"},
+    {SCSI_PORT_TRAN_ETC, "sp", "SCSI port/transceiver"},
+    {LANGUAGE_ETC, "lan", "Language"},
+    {COMM_PORT_ETC, "cp", "Communication port"},
+    {VOLT_SENSOR_ETC, "vs", "Voltage sensor"},
+    {CURR_SENSOR_ETC, "cs", "Current sensor"},
+    {SCSI_TPORT_ETC, "stp", "SCSI target port"},
+    {SCSI_IPORT_ETC, "sip", "SCSI initiator port"},
+    {SIMPLE_SUBENC_ETC, "ss", "Simple subenclosure"},
+    {ARRAY_DEV_ETC, "arr", "Array device slot"},
+    {SAS_EXPANDER_ETC, "sse", "SAS expander"},
+    {SAS_CONNECTOR_ETC, "ssc", "SAS connector"},
+    {-1, NULL, NULL},
+};
+
+static struct element_type_t element_type_by_code =
+    {0, NULL, "element type code form"};
+
+/* Many control element names below have "RQST" in front in drafts.
+   These are for the Enclosure Control/Status diagnostic page */
+static struct acronym2tuple ecs_a2t_arr[] = {
+    {"ac_fail", UI_POWER_SUPPLY_ETC, 2, 4, 1, NULL},
+    {"ac_hi", UI_POWER_SUPPLY_ETC, 2, 6, 1, NULL},
+    {"ac_lo", UI_POWER_SUPPLY_ETC, 2, 7, 1, NULL},
+    {"ac_qual", UI_POWER_SUPPLY_ETC, 2, 5, 1, NULL},
+    {"active", DEVICE_ETC, 2, 7, 1, NULL},     /* for control only */
+    {"active", ARRAY_DEV_ETC, 2, 7, 1, NULL},  /* for control only */
+    {"batt_fail", UI_POWER_SUPPLY_ETC, 3, 1, 1, NULL},
+    {"bpf", UI_POWER_SUPPLY_ETC, 3, 0, 1, NULL},
+    {"bypa", DEVICE_ETC, 3, 3, 1, "bypass port A"},
+    {"bypa", ARRAY_DEV_ETC, 3, 3, 1, "bypass port A"},
+    {"bypb", DEVICE_ETC, 3, 2, 1, "bypass port B"},
+    {"bypb", ARRAY_DEV_ETC, 3, 2, 1, "bypass port B"},
+    {"conscheck", ARRAY_DEV_ETC, 1, 4, 1, "consistency check"},
+    {"ctr_link", SAS_CONNECTOR_ETC, 2, 7, 8, "connector physical link"},
+    {"ctr_type", SAS_CONNECTOR_ETC, 1, 6, 7, "connector type"},
+    {"current", CURR_SENSOR_ETC, 2, 7, 16, "current in centiamps"},
+    {"dc_fail", UI_POWER_SUPPLY_ETC, 2, 3, 1, NULL},
+    {"disable", -1, 0, 5, 1, NULL},        /* -1 is for all element types */
+    {"disable_elm", SCSI_PORT_TRAN_ETC, 3, 4, 1, "disable port/transceiver"},
+    {"disable_elm", COMM_PORT_ETC, 3, 0, 1, "disable communication port"},
+    {"devoff", DEVICE_ETC, 3, 4, 1, NULL},     /* device off */
+    {"devoff", ARRAY_DEV_ETC, 3, 4, 1, NULL},
+    {"disp_mode", DISPLAY_ETC, 1, 1, 2, NULL},
+    {"disp_char", DISPLAY_ETC, 2, 7, 16, NULL},
+    {"dnr", ARRAY_DEV_ETC, 2, 6, 1, "do not remove"},
+    {"dnr", COOLING_ETC, 1, 6, 1, "do not remove"},
+    {"dnr", DEVICE_ETC, 2, 6, 1, "do not remove"},
+    {"dnr", ENC_ELECTRONICS_ETC, 1, 5, 1, "do not remove"},
+    {"dnr", POWER_SUPPLY_ETC, 1, 6, 1, "do not remove"},
+    {"dnr", UI_POWER_SUPPLY_ETC, 3, 3, 1, "do not remove"},
+    {"enable", SCSI_IPORT_ETC, 3, 0, 1, NULL},
+    {"enable", SCSI_TPORT_ETC, 3, 0, 1, NULL},
+    {"fail", AUD_ALARM_ETC, 1, 6, 1, NULL},
+    {"fail", COMM_PORT_ETC, 1, 7, 1, NULL},
+    {"fail", COOLING_ETC, 3, 6, 1, NULL},
+    {"fail", CURR_SENSOR_ETC, 3, 6, 1, NULL},
+    {"fail", DISPLAY_ETC, 1, 6, 1, NULL},
+    {"fail", DOOR_ETC, 1, 6, 1, NULL},
+    {"fail", ENC_ELECTRONICS_ETC, 1, 6, 1, NULL},
+    {"fail", KEY_PAD_ETC, 1, 6, 1, NULL},
+    {"fail", NV_CACHE_ETC, 3, 6, 1, NULL},
+    {"fail", POWER_SUPPLY_ETC, 3, 6, 1, NULL},
+    {"fail", SAS_CONNECTOR_ETC, 3, 6, 1, NULL},
+    {"fail", SAS_EXPANDER_ETC, 1, 6, 1, NULL},
+    {"fail", SCC_CELECTR_ETC, 3, 6, 1, NULL},
+    {"fail", SCSI_IPORT_ETC, 1, 6, 1, NULL},
+    {"fail", SCSI_PORT_TRAN_ETC, 1, 6, 1, NULL},
+    {"fail", SCSI_TPORT_ETC, 1, 6, 1, NULL},
+    {"fail", SIMPLE_SUBENC_ETC, 1, 6, 1, NULL},
+    {"fail", TEMPERATURE_ETC, 3, 6, 1, NULL},
+    {"fail", UI_POWER_SUPPLY_ETC, 3, 6, 1, NULL},
+    {"fail", VOLT_SENSOR_ETC, 1, 6, 1, NULL},
+    {"failure_ind", ENCLOSURE_ETC, 2, 1, 1, NULL},
+    {"failure", ENCLOSURE_ETC, 3, 1, 1, NULL},
+    {"fault", DEVICE_ETC, 3, 5, 1, NULL},
+    {"fault", ARRAY_DEV_ETC, 3, 5, 1, NULL},
+    {"hotspare", ARRAY_DEV_ETC, 1, 5, 1, NULL},
+    {"hotswap", COOLING_ETC, 3, 7, 1, NULL},
+    {"hotswap", ENC_ELECTRONICS_ETC, 3, 7, 1, NULL},
+    {"ident", DEVICE_ETC, 2, 1, 1, "flash LED"},
+    {"ident", ARRAY_DEV_ETC, 2, 1, 1, "flash LED"},
+    {"ident", POWER_SUPPLY_ETC, 1, 7, 1, "flash LED"},
+    {"ident", COMM_PORT_ETC, 1, 7, 1, "flash LED"},
+    {"ident", COOLING_ETC, 1, 7, 1, "flash LED"},
+    {"ident", CURR_SENSOR_ETC, 1, 7, 1, "flash LED"},
+    {"ident", DISPLAY_ETC, 1, 7, 1, "flash LED"},
+    {"ident", DOOR_ETC, 1, 7, 1, "flash LED"},
+    {"ident", ENC_ELECTRONICS_ETC, 1, 7, 1, "flash LED"},
+    {"ident", ENCLOSURE_ETC, 1, 7, 1, "flash LED"},
+    {"ident", KEY_PAD_ETC, 1, 7, 1, "flash LED"},
+    {"ident", LANGUAGE_ETC, 1, 7, 1, "flash LED"},
+    {"ident", AUD_ALARM_ETC, 1, 7, 1, NULL},
+    {"ident", NV_CACHE_ETC, 1, 7, 1, "flash LED"},
+    {"ident", SAS_CONNECTOR_ETC, 1, 7, 1, "flash LED"},
+    {"ident", SAS_EXPANDER_ETC, 1, 7, 1, "flash LED"},
+    {"ident", SCC_CELECTR_ETC, 1, 7, 1, "flash LED"},
+    {"ident", SCSI_IPORT_ETC, 1, 7, 1, "flash LED"},
+    {"ident", SCSI_PORT_TRAN_ETC, 1, 7, 1, "flash LED"},
+    {"ident", SCSI_TPORT_ETC, 1, 7, 1, "flash LED"},
+    {"ident", SIMPLE_SUBENC_ETC, 1, 7, 1, "flash LED"},
+    {"ident", TEMPERATURE_ETC, 1, 7, 1, "flash LED"},
+    {"ident", UI_POWER_SUPPLY_ETC, 3, 7, 1, "flash LED"},
+    {"ident", VOLT_SENSOR_ETC, 1, 7, 1, "flash LED"},
+    {"incritarray", ARRAY_DEV_ETC, 1, 3, 1, NULL},
+    {"infailedarray", ARRAY_DEV_ETC, 1, 2, 1, NULL},
+    {"info", AUD_ALARM_ETC, 3, 3, 1, "emits warning tone when set"},
+    {"insert", DEVICE_ETC, 2, 3, 1, NULL},
+    {"insert", ARRAY_DEV_ETC, 2, 3, 1, NULL},
+    {"intf_fail", UI_POWER_SUPPLY_ETC, 2, 0, 1, NULL},
+    {"language", LANGUAGE_ETC, 2, 7, 16, "language code"},
+    {"locate", DEVICE_ETC, 2, 1, 1, "flash LED"},
+    {"locate", ARRAY_DEV_ETC, 2, 1, 1, "flash LED"},
+    {"locate", POWER_SUPPLY_ETC, 1, 7, 1, "flash LED"},
+    {"locate", COMM_PORT_ETC, 1, 7, 1, "flash LED"},
+    {"locate", COOLING_ETC, 1, 7, 1, "flash LED"},
+    {"locate", CURR_SENSOR_ETC, 1, 7, 1, "flash LED"},
+    {"locate", DISPLAY_ETC, 1, 7, 1, "flash LED"},
+    {"locate", DOOR_ETC, 1, 7, 1, "flash LED"},
+    {"locate", ENC_ELECTRONICS_ETC, 1, 7, 1, "flash LED"},
+    {"locate", ENCLOSURE_ETC, 1, 7, 1, "flash LED"},
+    {"locate", KEY_PAD_ETC, 1, 7, 1, "flash LED"},
+    {"locate", LANGUAGE_ETC, 1, 7, 1, "flash LED"},
+    {"locate", AUD_ALARM_ETC, 1, 7, 1, NULL},
+    {"locate", NV_CACHE_ETC, 1, 7, 1, "flash LED"},
+    {"locate", SAS_CONNECTOR_ETC, 1, 7, 1, "flash LED"},
+    {"locate", SAS_EXPANDER_ETC, 1, 7, 1, "flash LED"},
+    {"locate", SCC_CELECTR_ETC, 1, 7, 1, "flash LED"},
+    {"locate", SCSI_IPORT_ETC, 1, 7, 1, "flash LED"},
+    {"locate", SCSI_PORT_TRAN_ETC, 1, 7, 1, "flash LED"},
+    {"locate", SCSI_TPORT_ETC, 1, 7, 1, "flash LED"},
+    {"locate", SIMPLE_SUBENC_ETC, 1, 7, 1, "flash LED"},
+    {"locate", TEMPERATURE_ETC, 1, 7, 1, "flash LED"},
+    {"locate", UI_POWER_SUPPLY_ETC, 3, 7, 1, "flash LED"},
+    {"locate", VOLT_SENSOR_ETC, 1, 7, 1, "flash LED"},
+    {"lol", SCSI_PORT_TRAN_ETC, 3, 1, 1, "Loss of Link"},
+    {"mated", SAS_CONNECTOR_ETC, 3, 7, 1, NULL},
+    {"missing", DEVICE_ETC, 2, 4, 1, NULL},
+    {"missing", ARRAY_DEV_ETC, 2, 4, 1, NULL},
+    {"mute", AUD_ALARM_ETC, 3, 6, 1, "control only: mute the alarm"},
+    {"muted", AUD_ALARM_ETC, 3, 6, 1, "status only: alarm is muted"},
+    {"off", POWER_SUPPLY_ETC, 3, 4, 1, "Not providing power"},
+    {"off", COOLING_ETC, 3, 4, 1, "Not providing cooling"},
+    {"ok", ARRAY_DEV_ETC, 1, 7, 1, NULL},
+    {"on", COOLING_ETC, 3, 5, 1, NULL},
+    {"on", POWER_SUPPLY_ETC, 3, 5, 1, "0: turn (remain) off; 1: turn on"},
+    {"open", DOOR_ETC, 3, 1, 1, NULL},
+    {"overcurrent", CURR_SENSOR_ETC, 1, 1, 1, "overcurrent"},
+    {"overcurrent", POWER_SUPPLY_ETC, 2, 1, 1, "DC overcurrent"},
+    {"overcurrent", SAS_CONNECTOR_ETC, 3, 5, 1, NULL},  /* added ses3r07 */
+    {"overcurrent_warn", CURR_SENSOR_ETC, 1, 3, 1, "overcurrent warning"},
+    {"overtemp_fail", TEMPERATURE_ETC, 3, 3, 1, "Overtemperature failure"},
+    {"overtemp_warn", TEMPERATURE_ETC, 3, 2, 1, "Overtemperature warning"},
+    {"overvoltage", POWER_SUPPLY_ETC, 2, 3, 1, "DC overvoltage"},
+    {"overvoltage", VOLT_SENSOR_ETC, 1, 1, 1, "overvoltage"},
+    {"overvoltage_warn", POWER_SUPPLY_ETC, 1, 3, 1, "DC overvoltage warning"},
+    {"remind", AUD_ALARM_ETC, 3, 4, 1, NULL},
+    {"report", ENC_ELECTRONICS_ETC, 2, 0, 1, NULL},
+    {"report", SCC_CELECTR_ETC, 2, 0, 1, NULL},
+    {"report", SCSI_IPORT_ETC, 2, 0, 1, NULL},
+    {"report", SCSI_TPORT_ETC, 2, 0, 1, NULL},
+    {"rqst_mute", AUD_ALARM_ETC, 3, 7, 1,
+     "status only: alarm was manually muted"},
+    {"pow_cycle", ENCLOSURE_ETC, 2, 7, 2,
+     "0: no; 1: start in pow_c_delay minutes; 2: cancel"},
+    {"pow_c_delay", ENCLOSURE_ETC, 2, 5, 6,
+     "delay in minutes before starting power cycle"},
+    {"pow_c_duration", ENCLOSURE_ETC, 3, 7, 6, NULL},
+    {"pow_c_time", ENCLOSURE_ETC, 2, 7, 6,
+     "time in minutes remaining until starting power cycle"},
+    {"prdfail", -1, 0, 6, 1, "predict failure"},
+    {"rebuildremap", ARRAY_DEV_ETC, 1, 1, 1, NULL},
+    {"remove", DEVICE_ETC, 2, 2, 1, NULL},
+    {"remove", ARRAY_DEV_ETC, 2, 2, 1, NULL},
+    {"rrabort", ARRAY_DEV_ETC, 1, 0, 1, "rebuild/remap abort"},
+    {"rsvddevice", ARRAY_DEV_ETC, 1, 6, 1, "reserved device"},
+    {"select_element", ENC_ELECTRONICS_ETC, 2, 0, 1, NULL},
+    {"short_stat", SIMPLE_SUBENC_ETC, 3, 7, 8, "short enclosure status"},
+    {"size", NV_CACHE_ETC, 2, 7, 16, NULL},
+    {"speed_act", COOLING_ETC, 1, 2, 11, "actual speed (rpm / 10)"},
+    {"speed_code", COOLING_ETC, 3, 2, 3,
+     "0: leave; 1: lowest... 7: highest"},
+    {"size_mult", NV_CACHE_ETC, 1, 1, 2, NULL},
+    {"swap", -1, 0, 4, 1, NULL},               /* Reset swap */
+    {"unlock", DOOR_ETC, 3, 0, 1, NULL},
+    {"undertemp_fail", TEMPERATURE_ETC, 3, 1, 1, "Undertemperature failure"},
+    {"undertemp_warn", TEMPERATURE_ETC, 3, 0, 1, "Undertemperature warning"},
+    {"undervoltage", POWER_SUPPLY_ETC, 2, 2, 1, "DC undervoltage"},
+    {"undervoltage", VOLT_SENSOR_ETC, 1, 0, 1, "undervoltage"},
+    {"undervoltage_warn", POWER_SUPPLY_ETC, 1, 2, 1,
+     "DC undervoltage warning"},
+    {"ups_fail", UI_POWER_SUPPLY_ETC, 2, 2, 1, NULL},
+    {"urgency", AUD_ALARM_ETC, 3, 3, 4, NULL},
+    {"voltage", VOLT_SENSOR_ETC, 2, 7, 16, "voltage in centivolts"},
+    {"warning", UI_POWER_SUPPLY_ETC, 2, 1, 1, NULL},
+    {"warning", ENCLOSURE_ETC, 3, 0, 1, NULL},
+    {"warning_ind", ENCLOSURE_ETC, 2, 0, 1, NULL},
+    {"xmit_fail", SCSI_PORT_TRAN_ETC, 3, 0, 1, "Transmitter failure"},
+    {NULL, 0, 0, 0, 0, NULL},
+};
+
+/* These are for the Threshold in/out diagnostic page */
+static struct acronym2tuple th_a2t_arr[] = {
+    {"high_crit", -1, 0, 7, 8, NULL},
+    {"high_warn", -1, 1, 7, 8, NULL},
+    {"low_crit", -1, 2, 7, 8, NULL},
+    {"low_warn", -1, 3, 7, 8, NULL},
+    {NULL, 0, 0, 0, 0, NULL},
+};
+
+/* These are for the Additional element status diagnostic page for SAS with
+ * the EIP bit set. First phy only. Index from start of AES descriptor */
+static struct acronym2tuple ae_sas_a2t_arr[] = {
+    {"at_sas_addr", -1, 12, 7, 64, NULL},  /* best viewed with --hex --get= */
+        /* typically this is the expander's SAS address */
+    {"dev_type", -1, 8, 6, 3, "1: SAS/SATA dev, 2: expander"},
+    {"dsn", -1, 7, 7, 8, "device slot number (255: none)"},
+    {"num_phys", -1, 4, 7, 8, "number of phys"},
+    {"phy_id", -1, 28, 7, 8, NULL},
+    {"sas_addr", -1, 20, 7, 64, NULL},  /* should be disk or tape ... */
+    {"sata_dev", -1, 11, 0, 1, NULL},
+    {"sata_port_sel", -1, 11, 7, 1, NULL},
+    {"smp_init", -1, 10, 1, 1, NULL},
+    {"smp_targ", -1, 11, 1, 1, NULL},
+    {"ssp_init", -1, 10, 3, 1, NULL},
+    {"ssp_targ", -1, 11, 3, 1, NULL},
+    {"stp_init", -1, 10, 2, 1, NULL},
+    {"stp_targ", -1, 11, 2, 1, NULL},
+    {NULL, 0, 0, 0, 0, NULL},
+};
+
+/* Boolean array of element types of interest to the Additional Element
+ * Status page. Indexed by element type (0 <= et <= 32). */
+static int active_et_aesp_arr[NUM_ACTIVE_ET_AESP_ARR] = {
+    0, 1 /* dev */, 0, 0,  0, 0, 0, 1 /* esce */,
+    0, 0, 0, 0,  0, 0, 0, 0,
+    0, 0, 0, 0,  1 /* starg */, 1 /* sinit */, 0, 1 /* arr */,
+    1 /* sas exp */, 0, 0, 0,  0, 0, 0, 0,
+};
+
+/* Command line long option names with corresponding short letter. */
+static struct option long_options[] = {
+    {"byte1", required_argument, 0, 'b'},
+    {"clear", required_argument, 0, 'C'},
+    {"control", no_argument, 0, 'c'},
+    {"data", required_argument, 0, 'd'},
+    {"descriptor", required_argument, 0, 'D'},
+    {"dev-slot-num", required_argument, 0, 'x'},
+    {"dsn", required_argument, 0, 'x'},
+    {"eiioe", required_argument, 0, 'E'},
+    {"enumerate", no_argument, 0, 'e'},
+    {"filter", no_argument, 0, 'f'},
+    {"get", required_argument, 0, 'G'},
+    {"help", no_argument, 0, 'h'},
+    {"hex", no_argument, 0, 'H'},
+    {"index", required_argument, 0, 'I'},
+    {"inner-hex", no_argument, 0, 'i'},
+    {"join", no_argument, 0, 'j'},
+    {"list", no_argument, 0, 'l'},
+    {"nickid", required_argument, 0, 'N'},
+    {"nickname", required_argument, 0, 'n'},
+    {"mask", required_argument, 0, 'M'},
+    {"maxlen", required_argument, 0, 'm'},
+    {"page", required_argument, 0, 'p'},
+    {"raw", no_argument, 0, 'r'},
+    {"readonly", no_argument, 0, 'R'},
+    {"sas-addr", required_argument, 0, 'A'},
+    {"set", required_argument, 0, 'S'},
+    {"status", no_argument, 0, 's'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {0, 0, 0, 0},
+};
+
+/* For overzealous SES device servers that don't like some status elements
+ * sent back as control elements. This table is as per ses3r06. */
+static uint8_t ses3_element_cmask_arr[NUM_ETC][4] = {
+                                /* Element type code (ETC) names; comment */
+    {0x40, 0xff, 0xff, 0xff},   /* [0] unspecified */
+    {0x40, 0, 0x4e, 0x3c},      /* DEVICE */
+    {0x40, 0x80, 0, 0x60},      /* POWER_SUPPLY */
+    {0x40, 0x80, 0, 0x60},      /* COOLING; requested speed as is unless */
+    {0x40, 0xc0, 0, 0},         /* TEMPERATURE */
+    {0x40, 0xc0, 0, 0x1},       /* DOOR */
+    {0x40, 0xc0, 0, 0x5f},      /* AUD_ALARM */
+    {0x40, 0xc0, 0x1, 0},       /* ENC_ELECTRONICS */
+    {0x40, 0xc0, 0, 0},         /* SCC_CELECTR */
+    {0x40, 0xc0, 0, 0},         /* NV_CACHE */
+    {0x40, 0, 0, 0},            /* [10] INV_OP_REASON */
+    {0x40, 0, 0, 0xc0},         /* UI_POWER_SUPPLY */
+    {0x40, 0xc0, 0xff, 0xff},   /* DISPLAY */
+    {0x40, 0xc3, 0, 0},         /* KEY_PAD */
+    {0x40, 0x80, 0, 0xff},      /* ENCLOSURE */
+    {0x40, 0xc0, 0, 0x10},      /* SCSI_PORT_TRAN */
+    {0x40, 0x80, 0xff, 0xff},   /* LANGUAGE */
+    {0x40, 0xc0, 0, 0x1},       /* COMM_PORT */
+    {0x40, 0xc0, 0, 0},         /* VOLT_SENSOR */
+    {0x40, 0xc0, 0, 0},         /* CURR_SENSOR */
+    {0x40, 0xc0, 0, 0x1},       /* [20] SCSI_TPORT */
+    {0x40, 0xc0, 0, 0x1},       /* SCSI_IPORT */
+    {0x40, 0xc0, 0, 0},         /* SIMPLE_SUBENC */
+    {0x40, 0xff, 0x4e, 0x3c},   /* ARRAY */
+    {0x40, 0xc0, 0, 0},         /* SAS_EXPANDER */
+    {0x40, 0x80, 0, 0x40},      /* SAS_CONNECTOR */
+};
+
+
+static int read_hex(const char * inp, unsigned char * arr, int * arr_len,
+                    int verb);
+static int strcase_eq(const char * s1p, const char * s2p);
+static void enumerate_diag_pages(void);
+static int saddr_non_zero(const unsigned char * ucp);
+
+
+static void
+usage(int help_num)
+{
+    if (1 == help_num) {
+        pr2serr(
+            "Usage: sg_ses [--descriptor=DN] [--dev-slot-num=SN] "
+            "[--eiioe=A_F]\n"
+            "              [--filter] [--get=STR] [--hex] "
+            "[--index=IIA | =TIA,II]\n"
+            "              [--inner-hex] [--join] [--maxlen=LEN] "
+            "[--page=PG]\n"
+            "              [--raw] [--sas-addr=SA] [--status] [--verbose] "
+            "[--warn]\n"
+            "              DEVICE\n\n"
+            "       sg_ses [--byte1=B1] [--clear=STR] [--control] "
+            "[--data=H,H...]\n"
+            "              [--descriptor=DN] [--dev-slot-num=SN] "
+            "[--index=IIA | =TIA,II]\n"
+            "              [--mask] [--maxlen=LEN] [--nickname=SEN] "
+            "[--nickid=SEID]\n"
+            "              [--page=PG] [--sas-addr=SA] [--set=STR] "
+            "[--verbose]\n"
+            "              DEVICE\n\n"
+            "       sg_ses [--enumerate] [--help] [--list] [--version]\n\n"
+            "  where the main options are:\n"
+            "    --clear=STR|-C STR    clear field by acronym or position\n"
+            "    --control|-c        send control information (def: fetch "
+            "status)\n"
+            "    --descriptor=DN|-D DN    descriptor name (for indexing)\n"
+            "    --dev-slot-num=SN|--dsn=SN|-x SN    device slot number "
+            "(for indexing)\n"
+            "    --filter|-f         filter out enclosure status flags that "
+            "are clear\n"
+            "                        use twice for status=okay entries "
+            "only\n"
+            "    --get=STR|-G STR    get value of field by acronym or "
+            "position\n"
+            "    --help|-h           print out usage message, use twice for "
+            "additional\n"
+            "    --index=IIA|-I IIA    individual index ('-1' for overall) "
+            "or element\n"
+            "                          type abbreviation (e.g. 'arr')\n"
+            "    --index=TIA,II|-I TIA,II    comma separated pair: TIA is "
+            "type header\n"
+            "                                index or element type "
+            "abbreviation;\n"
+            "                                II is individual index ('-1' "
+            "for overall)\n"
+            );
+        pr2serr(
+            "    --join|-j           group Enclosure Status, Element "
+            "Descriptor\n"
+            "                        and Additional Element Status pages. "
+            "Use twice\n"
+            "                        to add Threshold In page\n"
+            "    --page=PG|-p PG     diagnostic page code (abbreviation "
+            "or number)\n"
+            "                        (def: 'ssp' [0x0] (supported diagnostic "
+            "pages))\n"
+            "    --sas-addr=SA|-A SA    SAS address in hex (for indexing)\n"
+            "    --set=STR|-S STR    set value of field by acronym or "
+            "position\n"
+            "    --status|-s         fetch status information (default "
+            "action)\n\n"
+            "First usage above is for fetching pages or fields from a SCSI "
+            "enclosure.\nThe second usage is for changing a page or field in "
+            "an enclosure. Use\n'-hh' for more help, including the options "
+            "not explained above.\n");
+    } else {    /* for '-hh' or '--help --help' */
+        pr2serr(
+            "  where the remaining sg_ses options are:\n"
+            "    --byte1=B1|-b B1    byte 1 (2nd byte) of control page set "
+            "to B1\n"
+            "    --data=H,H...|-d H,H...    string of ASCII hex bytes for "
+            "control pages\n"
+            "    --data=- | -d -     fetch string of ASCII hex bytes from "
+            "stdin\n"
+            "    --data=@FN | -d @FN    fetch string of ASCII hex bytes from "
+            "file: FN\n"
+            "    --eiioe=A_F|-E A_F    where A_F is either 'auto' or 'force'."
+            "'force'\n"
+            "                          acts as if EIIOE is set, 'auto' tries "
+            "to guess\n"
+            "    --enumerate|-e      enumerate page names + element types "
+            "(ignore\n"
+            "                        DEVICE). Use twice for clear,get,set "
+            "acronyms\n"
+            "    --hex|-H            print page response (or field) in hex\n"
+            "    --inner-hex|-i      print innermost level of a"
+            " status page in hex\n"
+            "    --list|-l           same as '--enumerate' option\n"
+            "    --mask|-M           ignore status element mask in modify "
+            "actions\n"
+            "                        (e.g.--set= and --clear=) (def: apply "
+            "mask)\n"
+            "    --maxlen=LEN|-m LEN    max response length (allocation "
+            "length in cdb)\n"
+            "    --nickname=SEN|-n SEN   SEN is new subenclosure nickname\n"
+            "    --nickid=SEID|-N SEID   SEID is subenclosure identifier "
+            "(def: 0)\n"
+            "                            used to specify which nickname to "
+            "change\n"
+            "    --raw|-r            print status page in ASCII hex suitable "
+            "for '-d';\n"
+            "                        when used twice outputs page in binary "
+            "to stdout\n"
+            "    --readonly|-R       open DEVICE read-only (def: "
+            "read-write)\n"
+            "    --verbose|-v        increase verbosity\n"
+            "    --version|-V        print version string and exit\n"
+            "    --warn|-w           warn about join (and other) issues\n\n"
+            "If no options are given then DEVICE's supported diagnostic "
+            "pages are\nlisted. STR can be '<start_byte>:<start_bit>"
+            "[:<num_bits>][=<val>]'\nor '<acronym>[=val]'. Element type "
+            "abbreviations may be followed by a\nnumber (e.g. 'ps1' is "
+            "the second power supply element type). Use\n'sg_ses -e' and "
+            "'sg_ses -ee' for more information.\n\n"
+            );
+        pr2serr(
+            "Low level indexing can be done with one of the two '--index=' "
+            "options.\nAlternatively, medium level indexing can be done "
+            "with either the\n'--descriptor=', 'dev-slot-num=' or "
+            "'--sas-addr=' options. Support for\nthe medium level options "
+            "in the SES device is itself optional.\n"
+            );
+    }
+}
+
+/* Return 0 for okay, else an error */
+static int
+parse_index(struct opts_t *op)
+{
+    int n;
+    const char * cp;
+    char * mallcp;
+    char * c2p;
+    const struct element_type_t * etp;
+    char b[64];
+
+    op->ind_given = 1;
+    if ((cp = strchr(op->index_str, ','))) {
+        if (0 == strcmp("-1", cp + 1))
+            n = -1;
+        else {
+            n = sg_get_num(cp + 1);
+            if ((n < 0) || (n > 255)) {
+                pr2serr("bad argument to '--index', after comma expect "
+                        "number from -1 to 255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        }
+        op->ind_indiv = n;
+        n = cp - op->index_str;
+        if (n >= (int)sizeof(b)) {
+            pr2serr("bad argument to '--index', string prior to comma too "
+                    "long\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    } else {
+        n = strlen(op->index_str);
+        if (n >= (int)sizeof(b)) {
+            pr2serr("bad argument to '--index', string too long\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    strncpy(b, op->index_str, n);
+    b[n] = '\0';
+    if (0 == strcmp("-1", b)) {
+        if (cp) {
+            pr2serr("bad argument to '--index', unexpected '-1' type header "
+                    "index\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        op->ind_th = 0;
+        op->ind_indiv = -1;
+    } else if (isdigit(b[0])) {
+        n = sg_get_num(b);
+        if ((n < 0) || (n > 255)) {
+            pr2serr("bad numeric argument to '--index', expect number from 0 "
+                    "to 255\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (cp)
+            op->ind_th = n;
+        else {
+            op->ind_th = 0;
+            op->ind_indiv = n;
+        }
+    } else if ('_' == b[0]) {
+        if ((c2p = strchr(b + 1, '_')))
+            *c2p = '\0';
+        n = sg_get_num(b + 1);
+        if ((n < 0) || (n > 255)) {
+            pr2serr("bad element type code for '--index', expect value from "
+                    "0 to 255\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        element_type_by_code.elem_type_code = n;
+        mallcp = (char *)malloc(8);  /* willfully forget about freeing this */
+        mallcp[0] = '_';
+        snprintf(mallcp + 1, 6, "%d", n);
+        element_type_by_code.abbrev = mallcp;
+        if (c2p) {
+            n = sg_get_num(c2p + 1);
+            if ((n < 0) || (n > 255)) {
+                pr2serr("bad element type code <num> for '--index', expect "
+                        "<num> from 0 to 255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->ind_et_inst = n;
+        }
+        op->ind_etp = &element_type_by_code;
+        if (NULL == cp)
+            op->ind_indiv = -1;
+    } else { /* element type abbreviation perhaps followed by <num> */
+        int blen = strlen(b);
+
+        for (etp = element_type_arr; etp->desc; ++etp) {
+            n = strlen(etp->abbrev);
+            if ((n == blen) && (0 == strncmp(b, etp->abbrev, n)))
+                break;
+        }
+        if (NULL == etp->desc) {
+            pr2serr("bad element type abbreviation [%s] for '--index'\n"
+                    "use '--enumerate' to see possibles\n", b);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if ((int)strlen(b) > n) {
+            n = sg_get_num(b + n);
+            if ((n < 0) || (n > 255)) {
+                pr2serr("bad element type abbreviation <num> for '--index', "
+                        "expect <num> from 0 to 255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->ind_et_inst = n;
+        }
+        op->ind_etp = etp;
+        if (NULL == cp)
+            op->ind_indiv = -1;
+    }
+    if (op->verbose > 1) {
+        if (op->ind_etp)
+            pr2serr("   element type abbreviation: %s, etp_num=%d, "
+                    "individual index=%d\n", op->ind_etp->abbrev,
+                    op->ind_et_inst, op->ind_indiv);
+        else
+            pr2serr("   type header index=%d, individual index=%d\n",
+                    op->ind_th, op->ind_indiv);
+    }
+    return 0;
+}
+
+
+/* process command line options and argument. Returns 0 if ok. */
+static int
+cl_process(struct opts_t *op, int argc, char *argv[])
+{
+    int c, j, ret, ff;
+    const char * data_arg = NULL;
+    uint64_t saddr;
+    const char * cp;
+
+    op->dev_slot_num = -1;
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "A:b:cC:d:D:eE:fG:hHiI:jln:N:m:Mp:rRsS:v"
+                        "Vwx:", long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'A':       /* SAS address, assumed to be hex */
+            cp = optarg;
+            if ((strlen(optarg) > 2) && ('X' == toupper(optarg[1])))
+                cp = optarg + 2;
+            if (1 != sscanf(cp, "%" SCNx64 "", &saddr)) {
+                pr2serr("bad argument to '--sas-addr'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            for (j = 7, ff = 1; j >= 0; --j) {
+                if (ff & (0xff != (saddr & 0xff)))
+                    ff = 0;
+                op->sas_addr[j] = (saddr & 0xff);
+                saddr >>= 8;
+            }
+            if (ff) {
+                pr2serr("decode error from argument to '--sas-addr'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'b':
+            op->byte1 = sg_get_num(optarg);
+            if ((op->byte1 < 0) || (op->byte1 > 255)) {
+                pr2serr("bad argument to '--byte1' (0 to 255 inclusive)\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++op->byte1_given;
+            break;
+        case 'c':
+            ++op->do_control;
+            break;
+        case 'C':
+            op->clear_str = optarg;
+            ++op->num_cgs;
+            break;
+        case 'd':
+            data_arg = optarg;
+            op->do_data = 1;
+            break;
+        case 'D':
+            op->desc_name = optarg;
+            break;
+        case 'e':
+            ++op->enumerate;
+            break;
+        case 'E':
+            if (0 == strcmp("auto", optarg))
+                ++op->eiioe_auto;
+            else if (0 == strcmp("force", optarg))
+                ++op->eiioe_force;
+            else {
+                pr2serr("--eiioe option expects 'auto' or 'force' as an "
+                        "argument\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'f':
+            ++op->do_filter;
+            break;
+        case 'G':
+            op->get_str = optarg;
+            ++op->num_cgs;
+            break;
+        case 'h':
+        case '?':
+            ++op->do_help;
+            break;
+        case 'H':
+            ++op->do_hex;
+            break;
+        case 'i':
+            ++op->inner_hex;
+            break;
+        case 'I':
+            op->index_str = optarg;
+            break;
+        case 'j':
+            ++op->do_join;
+            break;
+        case 'l':
+            ++op->do_list;
+            break;
+        case 'n':
+            op->nickname_str = optarg;
+            break;
+        case 'N':
+            op->seid = sg_get_num(optarg);
+            if ((op->seid < 0) || (op->seid > 255)) {
+                pr2serr("bad argument to '--nick_id' (0 to 255 inclusive)\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++op->seid_given;
+            break;
+        case 'm':
+            op->maxlen = sg_get_num(optarg);
+            if ((op->maxlen < 0) || (op->maxlen > 65535)) {
+                pr2serr("bad argument to '--maxlen' (0 to 65535 "
+                        "inclusive expected)\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'M':
+            ++op->mask_ign;
+            break;
+        case 'p':
+            if (isdigit(optarg[0])) {
+                op->page_code = sg_get_num(optarg);
+                if ((op->page_code < 0) || (op->page_code > 255)) {
+                    pr2serr("bad argument to '--page' (0 to 255 "
+                            "inclusive)\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else {
+                const struct diag_page_abbrev * ap;
+
+                for (ap = dp_abbrev; ap->abbrev; ++ap) {
+                    if (strcase_eq(ap->abbrev, optarg)) {
+                        op->page_code = ap->page_code;
+                        break;
+                    }
+                }
+                if (NULL == ap->abbrev) {
+                    pr2serr("'--page' abbreviation %s not found\nHere are "
+                            "the choices:\n", optarg);
+                    enumerate_diag_pages();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+            ++op->page_code_given;
+            break;
+        case 'r':
+            ++op->do_raw;
+            break;
+        case 'R':
+            ++op->o_readonly;
+            break;
+        case 's':
+            ++op->do_status;
+            break;
+        case 'S':
+            op->set_str = optarg;
+            ++op->num_cgs;
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            ++op->do_version;
+            return 0;
+        case 'w':
+            ++op->warn;
+            break;
+        case 'x':
+            op->dev_slot_num = sg_get_num(optarg);
+            if ((op->dev_slot_num < 0) || (op->dev_slot_num > 255)) {
+                pr2serr("bad argument to '--dev-slot-num' (0 to 255 "
+                        "inclusive)\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            goto err_help;
+        }
+    }
+    if (op->do_help)
+        return 0;
+    if (optind < argc) {
+        if (NULL == op->dev_name) {
+            op->dev_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            goto err_help;
+        }
+    }
+    if (data_arg) {
+        memset(op->data_arr, 0, sizeof(op->data_arr));
+        if (read_hex(data_arg, op->data_arr + 4, &op->arr_len, op->verbose)) {
+            pr2serr("bad argument to '--data'\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (op->maxlen <= 0)
+        op->maxlen = MX_ALLOC_LEN;
+    if (op->do_join && (op->do_control)) {
+        pr2serr("cannot have '--join' and '--control'\n");
+        goto err_help;
+    }
+    if (op->num_cgs > 1) {
+        pr2serr("can only be one of '--clear', '--get' and '--set'\n");
+        goto err_help;
+    }
+    if (op->index_str) {
+        ret = parse_index(op);
+        if (ret) {
+            pr2serr("  For more information use '--help'\n");
+            return ret;
+        }
+    }
+    if (op->desc_name || (op->dev_slot_num >= 0) ||
+        saddr_non_zero(op->sas_addr)) {
+        if (op->ind_given) {
+            pr2serr("cannot have --index with either --descriptor, "
+                    "--dev-slot-num or --sas-addr\n");
+            goto err_help;
+        }
+        if (((!! op->desc_name) + (op->dev_slot_num >= 0) +
+             saddr_non_zero(op->sas_addr)) > 1) {
+            pr2serr("can only have one of --descriptor, "
+                    "--dev-slot-num and --sas-addr\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if ((0 == op->do_join) && (0 == op->do_control) &&
+            (0 == op->num_cgs) && (0 == op->page_code_given)) {
+            ++op->do_join;      /* implicit --join */
+            if (op->verbose)
+                pr2serr("assume --join option is set\n");
+        }
+    }
+    if (op->ind_given) {
+        if ((0 == op->do_join) && (0 == op->do_control) &&
+            (0 == op->num_cgs) && (0 == op->page_code_given)) {
+            ++op->page_code_given;
+            op->page_code = 2;  /* implicit status page */
+            if (op->verbose)
+                pr2serr("assume --page=2 (es) option is set\n");
+        }
+    }
+    if (op->do_list || op->enumerate)
+        return 0;
+    if (op->do_control && op->do_status) {
+        pr2serr("cannot have both '--control' and '--status'\n");
+        goto err_help;
+    } else if (op->do_control) {
+        if (op->nickname_str || op->seid_given)
+            ;
+        else if (! op->do_data) {
+            pr2serr("need to give '--data' in control mode\n");
+            goto err_help;
+        }
+    } else if (0 == op->do_status)
+        op->do_status = 1;  /* default to receiving status pages */
+
+    if (op->nickname_str) {
+        if (! op->do_control) {
+            pr2serr("since '--nickname=' implies control mode, require "
+                    "'--control' as well\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->page_code_given) {
+            if (DPC_SUBENC_NICKNAME != op->page_code) {
+                pr2serr("since '--nickname=' assume or expect "
+                        "'--page=snic'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else
+            op->page_code = DPC_SUBENC_NICKNAME;
+    } else if (op->seid_given) {
+        pr2serr("'--nickid=' must be used together with '--nickname='\n");
+        return SG_LIB_SYNTAX_ERROR;
+
+    }
+    if ((op->verbose > 4) && saddr_non_zero(op->sas_addr)) {
+        pr2serr("    SAS address (in hex): ");
+        for (j = 0; j < 8; ++j)
+            pr2serr("%02x", op->sas_addr[j]);
+        pr2serr("\n");
+    }
+
+    if (NULL == op->dev_name) {
+        pr2serr("missing DEVICE name!\n");
+        goto err_help;
+    }
+    return 0;
+
+err_help:
+    pr2serr("  For more information use '--help'\n");
+    return SG_LIB_SYNTAX_ERROR;
+}
+
+/* Returns 64 bit signed integer given in either decimal or in hex. The
+ * hex number is either preceded by "0x" or followed by "h". Returns -1
+ * on error (so check for "-1" string before using this function). */
+static int64_t
+get_llnum(const char * buf)
+{
+    int res, len;
+    int64_t num;
+    uint64_t unum;
+
+    if ((NULL == buf) || ('\0' == buf[0]))
+        return -1;
+    len = strlen(buf);
+    if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
+        res = sscanf(buf + 2, "%" SCNx64 "", &unum);
+        num = unum;
+    } else if ('H' == toupper(buf[len - 1])) {
+        res = sscanf(buf, "%" SCNx64 "", &unum);
+        num = unum;
+    } else
+        res = sscanf(buf, "%" SCNd64 "", &num);
+    return (1 == res) ? num : -1;
+}
+
+/* Parse clear/get/set string. Uses 'buff' for scratch area. Returns 0
+ * on success, else -1. */
+static int
+parse_cgs_str(char * buff, struct tuple_acronym_val * tavp)
+{
+    char * esp;
+    char * colp;
+    char * cp;
+    unsigned int ui;
+
+    tavp->acron = NULL;
+    tavp->val_str = NULL;
+    tavp->start_byte = -1;
+    tavp->num_bits = 1;
+    if ((esp = strchr(buff, '='))) {
+        tavp->val_str = esp + 1;
+        *esp = '\0';
+        if (0 == strcmp("-1", esp + 1))
+            tavp->val = -1;
+        else {
+            tavp->val = get_llnum(esp + 1);
+            if (-1 == tavp->val) {
+                pr2serr("unable to decode: %s value\n", esp + 1);
+                pr2serr("    expected: <acronym>[=<val>]\n");
+                return -1;
+            }
+        }
+    }
+    if (isalpha(buff[0]))
+        tavp->acron = buff;
+    else {
+        colp = strchr(buff, ':');
+        if ((NULL == colp) || (buff == colp))
+            return -1;
+        *colp = '\0';
+        if (('0' == buff[0]) && ('X' == toupper(buff[1]))) {
+            if (1 != sscanf(buff + 2, "%x", &ui))
+                return -1;
+            tavp->start_byte = ui;
+        } else if ('H' == toupper(*(colp - 1))) {
+            if (1 != sscanf(buff, "%x", &ui))
+                return -1;
+            tavp->start_byte = ui;
+        } else {
+            if (1 != sscanf(buff, "%d", &tavp->start_byte))
+                return -1;
+        }
+        if ((tavp->start_byte < 0) || (tavp->start_byte > 127)) {
+            pr2serr("<start_byte> needs to be between 0 and 127\n");
+            return -1;
+        }
+        cp = colp + 1;
+        colp = strchr(cp, ':');
+        if (cp == colp)
+            return -1;
+        if (colp)
+            *colp = '\0';
+        if (1 != sscanf(cp, "%d", &tavp->start_bit))
+            return -1;
+        if ((tavp->start_bit < 0) || (tavp->start_bit > 7)) {
+            pr2serr("<start_bit> needs to be between 0 and 7\n");
+            return -1;
+        }
+        if (colp) {
+            if (1 != sscanf(colp + 1, "%d", &tavp->num_bits))
+                return -1;
+        }
+        if ((tavp->num_bits < 1) || (tavp->num_bits > 64)) {
+            pr2serr("<num_bits> needs to be between 1 and 64\n");
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/* Fetch diagnostic page name (control or out). Returns NULL if not found. */
+static const char *
+find_out_diag_page_desc(int page_num)
+{
+    const struct diag_page_code * pcdp;
+
+    for (pcdp = out_dpc_arr; pcdp->desc; ++pcdp) {
+        if (page_num == pcdp->page_code)
+            return pcdp->desc;
+        else if (page_num < pcdp->page_code)
+            return NULL;
+    }
+    return NULL;
+}
+
+/* Return of 0 -> success, SG_LIB_CAT_* positive values or -1 -> other
+ * failures */
+static int
+do_senddiag(int sg_fd, int pf_bit, void * outgoing_pg, int outgoing_len,
+            int noisy, int verbose)
+{
+    const char * cp;
+    int page_num;
+
+    if (outgoing_pg && (verbose > 2)) {
+        page_num = ((const char *)outgoing_pg)[0];
+        cp = find_out_diag_page_desc(page_num);
+        if (cp)
+            pr2serr("    Send diagnostic cmd name: %s\n", cp);
+        else
+            pr2serr("    Send diagnostic cmd number: 0x%x\n", page_num);
+    }
+    return sg_ll_send_diag(sg_fd, 0 /* sf_code */, pf_bit, 0 /* sf_bit */,
+                           0 /* devofl_bit */, 0 /* unitofl_bit */,
+                           0 /* long_duration */, outgoing_pg, outgoing_len,
+                           noisy, verbose);
+}
+
+/* Fetch diagnostic page name (status and/or control). Returns NULL if not
+ * found. */
+static const char *
+find_diag_page_desc(int page_num)
+{
+    const struct diag_page_code * pcdp;
+
+    for (pcdp = dpc_arr; pcdp->desc; ++pcdp) {
+        if (page_num == pcdp->page_code)
+            return pcdp->desc;
+        else if (page_num < pcdp->page_code)
+            return NULL;
+    }
+    return NULL;
+}
+
+/* Fetch diagnostic page name (status or in). Returns NULL if not found. */
+static const char *
+find_in_diag_page_desc(int page_num)
+{
+    const struct diag_page_code * pcdp;
+
+    for (pcdp = in_dpc_arr; pcdp->desc; ++pcdp) {
+        if (page_num == pcdp->page_code)
+            return pcdp->desc;
+        else if (page_num < pcdp->page_code)
+            return NULL;
+    }
+    return NULL;
+}
+
+/* Fetch element type name. Returns NULL if not found. */
+static char *
+find_element_tname(int elem_type_code, char * b, int mlen_b)
+{
+    const struct element_type_t * etp;
+    int len;
+
+    if ((NULL == b) || (mlen_b < 1))
+        return b;
+    for (etp = element_type_arr; etp->desc; ++etp) {
+        if (elem_type_code == etp->elem_type_code) {
+            len = strlen(etp->desc);
+            if (len < mlen_b)
+                strcpy(b, etp->desc);
+            else {
+                strncpy(b, etp->desc, mlen_b - 1);
+                b[mlen_b - 1] = '\0';
+            }
+            return b;
+        } else if (elem_type_code < etp->elem_type_code)
+            break;
+    }
+    if (elem_type_code < 0x80)
+        snprintf(b, mlen_b - 1, "[0x%x]", elem_type_code);
+    else
+        snprintf(b, mlen_b - 1, "vendor specific [0x%x]", elem_type_code);
+    b[mlen_b - 1] = '\0';
+    return b;
+}
+
+/* Returns 1 if el_type (element type) is of interest to the Additional
+ * Element Status page. Otherwise return 0. */
+static int
+active_et_aesp(int el_type)
+{
+    if ((el_type >= 0) && (el_type < NUM_ACTIVE_ET_AESP_ARR))
+        return active_et_aesp_arr[el_type];
+    else
+        return 0;
+}
+
+/* Return of 0 -> success, SG_LIB_CAT_* positive values or -1 -> other
+ * failures */
+static int
+do_rec_diag(int sg_fd, int page_code, unsigned char * rsp_buff,
+            int rsp_buff_size, const struct opts_t * op, int * rsp_lenp)
+{
+    int rsp_len, res;
+    const char * cp;
+    char b[80];
+
+    memset(rsp_buff, 0, rsp_buff_size);
+    if (rsp_lenp)
+        *rsp_lenp = 0;
+    cp = find_in_diag_page_desc(page_code);
+    if (op->verbose > 1) {
+        if (cp)
+            pr2serr("    Receive diagnostic results cmd for %s page\n", cp);
+        else
+            pr2serr("    Receive diagnostic results cmd for page 0x%x\n",
+                    page_code);
+    }
+    res = sg_ll_receive_diag(sg_fd, 1 /* pcv */, page_code, rsp_buff,
+                             rsp_buff_size, 1, op->verbose);
+    if (0 == res) {
+        rsp_len = sg_get_unaligned_be16(rsp_buff + 2) + 4;
+        if (rsp_len > rsp_buff_size) {
+            if (rsp_buff_size > 8) /* tried to get more than header */
+                pr2serr("<<< warning response buffer too small [%d but need "
+                        "%d]>>>\n", rsp_buff_size, rsp_len);
+            rsp_len = rsp_buff_size;
+        }
+        if (rsp_lenp)
+            *rsp_lenp = rsp_len;
+        if (page_code != rsp_buff[0]) {
+            if ((0x9 == rsp_buff[0]) && (1 & rsp_buff[1])) {
+                pr2serr("Enclosure busy, try again later\n");
+                if (op->do_hex)
+                    dStrHexErr((const char *)rsp_buff, rsp_len, 0);
+            } else if (0x8 == rsp_buff[0]) {
+                pr2serr("Enclosure only supports Short Enclosure Status: "
+                        "0x%x\n", rsp_buff[1]);
+            } else {
+                pr2serr("Invalid response, wanted page code: 0x%x but got "
+                        "0x%x\n", page_code, rsp_buff[0]);
+                dStrHexErr((const char *)rsp_buff, rsp_len, 0);
+            }
+            return -2;
+        }
+        return 0;
+    } else if (op->verbose) {
+        if (cp)
+            pr2serr("Attempt to fetch %s diagnostic page failed\n", cp);
+        else
+            pr2serr("Attempt to fetch status diagnostic page [0x%x] failed\n",
+                    page_code);
+        sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+        pr2serr("    %s\n", b);
+    }
+    return res;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* DPC_CONFIGURATION [0x1]
+ * Display Configuration diagnostic page. */
+static void
+ses_configuration_sdg(const unsigned char * resp, int resp_len)
+{
+    int j, k, el, num_subs, sum_elem_types;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+    const unsigned char * text_ucp;
+    char b[64];
+
+    printf("Configuration diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
+    sum_elem_types = 0;
+    last_ucp = resp + resp_len - 1;
+    printf("  number of secondary subenclosures: %d\n",
+            num_subs - 1);
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    printf("  enclosure descriptor list\n");
+    ucp = resp + 8;
+    for (k = 0; k < num_subs; ++k, ucp += el) {
+        if ((ucp + 3) > last_ucp)
+            goto truncated;
+        el = ucp[3] + 4;
+        sum_elem_types += ucp[2];
+        printf("    Subenclosure identifier: %d%s\n", ucp[1],
+               (ucp[1] ? "" : " [primary]"));
+        printf("      relative ES process id: %d, number of ES processes"
+               ": %d\n", ((ucp[0] & 0x70) >> 4), (ucp[0] & 0x7));
+        printf("      number of type descriptor headers: %d\n", ucp[2]);
+        if (el < 40) {
+            pr2serr("      enc descriptor len=%d ??\n", el);
+            continue;
+        }
+        printf("      enclosure logical identifier (hex): ");
+        for (j = 0; j < 8; ++j)
+            printf("%02x", ucp[4 + j]);
+        printf("\n      enclosure vendor: %.8s  product: %.16s  rev: %.4s\n",
+               ucp + 12, ucp + 20, ucp + 36);
+        if (el > 40) {
+            printf("      vendor-specific data:\n");
+            /* dStrHex((const char *)(ucp + 40), el - 40, 0); */
+            printf("        ");
+            for (j = 0; j < (el - 40); ++j) {
+                if ((j > 0) && (0 == (j % 16)))
+                    printf("\n        ");
+                printf("%02x ", *(ucp + 40 + j));
+            }
+            printf("\n");
+        }
+    }
+    /* printf("\n"); */
+    printf("  type descriptor header/text list\n");
+    text_ucp = ucp + (sum_elem_types * 4);
+    for (k = 0; k < sum_elem_types; ++k, ucp += 4) {
+        if ((ucp + 3) > last_ucp)
+            goto truncated;
+        printf("    Element type: %s, subenclosure id: %d\n",
+               find_element_tname(ucp[0], b, sizeof(b)), ucp[2]);
+        printf("      number of possible elements: %d\n", ucp[1]);
+        if (ucp[3] > 0) {
+            if (text_ucp > last_ucp)
+                goto truncated;
+            printf("      text: %.*s\n", ucp[3], text_ucp);
+            text_ucp += ucp[3];
+        }
+    }
+    return;
+truncated:
+    pr2serr("    <<<ses_configuration_sdg: response too short>>>\n");
+    return;
+}
+
+/* DPC_CONFIGURATION
+ * Returns total number of type descriptor headers written to 'tdhp' or -1
+ * if there is a problem */
+static int
+populate_type_desc_hdr_arr(int fd, struct type_desc_hdr_t * tdhp,
+                           uint32_t * generationp,
+                           struct enclosure_info * primary_ip,
+                           struct opts_t * op)
+{
+    int resp_len, k, el, num_subs, sum_type_dheaders, res, n;
+    int ret = 0;
+    uint32_t gen_code;
+    unsigned char * resp;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+
+    resp = (unsigned char *)calloc(op->maxlen, 1);
+    if (NULL == resp) {
+        pr2serr("%s: unable to allocate %d bytes on heap\n", __func__,
+                op->maxlen);
+        ret = -1;
+        goto the_end;
+    }
+    res = do_rec_diag(fd, DPC_CONFIGURATION, resp, op->maxlen, op, &resp_len);
+    if (res) {
+        pr2serr("%s: couldn't read config page, res=%d\n", __func__, res);
+        ret = -1;
+        goto the_end;
+    }
+    if (resp_len < 4) {
+        ret = -1;
+        goto the_end;
+    }
+    num_subs = resp[1] + 1;
+    sum_type_dheaders = 0;
+    last_ucp = resp + resp_len - 1;
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    if (generationp)
+        *generationp = gen_code;
+    ucp = resp + 8;
+    for (k = 0; k < num_subs; ++k, ucp += el) {
+        if ((ucp + 3) > last_ucp)
+            goto p_truncated;
+        el = ucp[3] + 4;
+        sum_type_dheaders += ucp[2];
+        if (el < 40) {
+            pr2serr("%s: short enc descriptor len=%d ??\n", __func__, el);
+            continue;
+        }
+        if ((0 == k) && primary_ip) {
+            ++primary_ip->have_info;
+            primary_ip->rel_esp_id = (ucp[0] & 0x70) >> 4;
+            primary_ip->num_esp = (ucp[0] & 0x7);
+            memcpy(primary_ip->enc_log_id, ucp + 4, 8);
+            memcpy(primary_ip->enc_vendor_id, ucp + 12, 8);
+            memcpy(primary_ip->product_id, ucp + 20, 16);
+            memcpy(primary_ip->product_rev_level, ucp + 36, 4);
+        }
+    }
+    for (k = 0; k < sum_type_dheaders; ++k, ucp += 4) {
+        if ((ucp + 3) > last_ucp)
+            goto p_truncated;
+        if (k >= MX_ELEM_HDR) {
+            pr2serr("%s: too many elements\n", __func__);
+            ret = -1;
+            goto the_end;
+        }
+        tdhp[k].etype = ucp[0];
+        tdhp[k].num_elements = ucp[1];
+        tdhp[k].se_id = ucp[2];
+        tdhp[k].txt_len = ucp[3];
+    }
+    if (op->ind_given && op->ind_etp) {
+        n = op->ind_et_inst;
+        for (k = 0; k < sum_type_dheaders; ++k) {
+            if (op->ind_etp->elem_type_code == tdhp[k].etype) {
+                if (0 == n)
+                    break;
+                else
+                    --n;
+            }
+        }
+        if (k < sum_type_dheaders)
+            op->ind_th = k;
+        else {
+            if (op->ind_et_inst)
+                pr2serr("%s: unable to find element type '%s%d'\n", __func__,
+                        op->ind_etp->abbrev, op->ind_et_inst);
+            else
+                pr2serr("%s: unable to find element type '%s'\n", __func__,
+                        op->ind_etp->abbrev);
+            ret = -1;
+            goto the_end;
+        }
+    }
+    ret = sum_type_dheaders;
+    goto the_end;
+
+p_truncated:
+    pr2serr("%s: config too short\n", __func__);
+    ret = -1;
+
+the_end:
+    if (resp)
+        free(resp);
+    return ret;
+}
+
+static char *
+find_sas_connector_type(int conn_type, char * buff, int buff_len)
+{
+    switch (conn_type) {
+    case 0x0:
+        snprintf(buff, buff_len, "No information");
+        break;
+    case 0x1:
+        snprintf(buff, buff_len, "SAS 4x receptacle (SFF-8470) "
+                 "[max 4 phys]");
+        break;
+    case 0x2:
+        snprintf(buff, buff_len, "Mini SAS 4x receptacle (SFF-8088) "
+                 "[max 4 phys]");
+        break;
+    case 0x3:
+        snprintf(buff, buff_len, "QSFP+ receptacle (SFF-8436) "
+                 "[max 4 phys]");
+        break;
+    case 0x4:
+        snprintf(buff, buff_len, "Mini SAS 4x active receptacle (SFF-8088) "
+                 "[max 4 phys]");
+        break;
+    case 0x5:
+        snprintf(buff, buff_len, "Mini SAS HD 4x receptacle (SFF-8644) "
+                 "[max 4 phys]");
+        break;
+    case 0x6:
+        snprintf(buff, buff_len, "Mini SAS HD 8x receptacle (SFF-8644) "
+                 "[max 8 phys]");
+        break;
+    case 0x7:
+        snprintf(buff, buff_len, "Mini SAS HD 16x receptacle (SFF-8644) "
+                 "[max 16 phys]");
+        break;
+    case 0xf:
+        snprintf(buff, buff_len, "Vendor specific external connector");
+        break;
+    case 0x10:
+        snprintf(buff, buff_len, "SAS 4i plug (SFF-8484) [max 4 phys]");
+        break;
+    case 0x11:
+        snprintf(buff, buff_len, "Mini SAS 4i receptacle (SFF-8087) "
+                 "[max 4 phys]");
+        break;
+    case 0x12:
+        snprintf(buff, buff_len, "Mini SAS HD 4i receptacle (SFF-8643) "
+                 "[max 4 phys]");
+        break;
+    case 0x13:
+        snprintf(buff, buff_len, "Mini SAS HD 8i receptacle (SFF-8643) "
+                 "[max 8 phys]");
+        break;
+    case 0x20:
+        snprintf(buff, buff_len, "SAS Drive backplane receptacle (SFF-8482) "
+                 "[max 2 phys]");
+        break;
+    case 0x21:
+        snprintf(buff, buff_len, "SATA host plug [max 1 phy]");
+        break;
+    case 0x22:
+        snprintf(buff, buff_len, "SAS Drive plug (SFF-8482) [max 2 phys]");
+        break;
+    case 0x23:
+        snprintf(buff, buff_len, "SATA device plug [max 1 phy]");
+        break;
+    case 0x24:
+        snprintf(buff, buff_len, "Micro SAS receptacle [max 2 phys]");
+        break;
+    case 0x25:
+        snprintf(buff, buff_len, "Micro SATA device plug [max 1 phy]");
+        break;
+    case 0x26:
+        snprintf(buff, buff_len, "Micro SAS plug (SFF-8486) [max 2 phys]");
+        break;
+    case 0x27:
+        snprintf(buff, buff_len, "Micro SAS/SATA plug (SFF-8486) "
+                 "[max 2 phys]");
+        break;
+    case 0x28:
+        snprintf(buff, buff_len, "12 Gb/s SAS drive backplane receptacle "
+                 "(SFF-8680) [max 2 phys]");
+        break;
+    case 0x29:
+        snprintf(buff, buff_len, "12 Gb/s SAS drive plug (SFF-8680) [max 2 "
+                 "phys]");
+        break;
+    case 0x2a:
+        snprintf(buff, buff_len, "Multifunction 12 Gb/s 6x unshielded "
+                 "receptacle (SFF-8639)");
+        break;
+    case 0x2b:
+        snprintf(buff, buff_len, "Multifunction 12 Gb/s 6x unshielded plug "
+                 "(SFF-8639)");
+        break;
+    case 0x2f:
+        snprintf(buff, buff_len, "SAS virtual connector [max 1 phy]");
+        break;
+    case 0x3f:
+        snprintf(buff, buff_len, "Vendor specific internal connector");
+        break;
+    default:
+        if (conn_type < 0x10)
+            snprintf(buff, buff_len, "unknown external connector type: 0x%x",
+                     conn_type);
+        else if (conn_type < 0x20)
+            snprintf(buff, buff_len, "unknown internal wide connector type: "
+                     "0x%x", conn_type);
+        else if (conn_type < 0x30)
+            snprintf(buff, buff_len, "unknown internal connector to end "
+                     "device, type: 0x%x", conn_type);
+        else if (conn_type < 0x3f)
+            snprintf(buff, buff_len, "reserved for internal connector, "
+                     "type: 0x%x", conn_type);
+        else if (conn_type < 0x70)
+            snprintf(buff, buff_len, "reserved connector type: 0x%x",
+                     conn_type);
+        else if (conn_type < 0x80)
+            snprintf(buff, buff_len, "vendor specific connector type: 0x%x",
+                     conn_type);
+        else    /* conn_type is a 7 bit field, so this is impossible */
+            snprintf(buff, buff_len, "unexpected connector type: 0x%x",
+                     conn_type);
+        break;
+    }
+    return buff;
+}
+
+static const char * elem_status_code_desc[] = {
+    "Unsupported", "OK", "Critical", "Noncritical",
+    "Unrecoverable", "Not installed", "Unknown", "Not available",
+    "No access allowed", "reserved [9]", "reserved [10]", "reserved [11]",
+    "reserved [12]", "reserved [13]", "reserved [14]", "reserved [15]",
+};
+
+static const char * actual_speed_desc[] = {
+    "stopped", "at lowest speed", "at second lowest speed",
+    "at third lowest speed", "at intermediate speed",
+    "at third highest speed", "at second highest speed", "at highest speed"
+};
+
+static const char * nv_cache_unit[] = {
+    "Bytes", "KiB", "MiB", "GiB"
+};
+
+static const char * invop_type_desc[] = {
+    "SEND DIAGNOSTIC page code error", "SEND DIAGNOSTIC page format error",
+    "Reserved", "Vendor specific error"
+};
+
+static void
+enc_status_helper(const char * pad, const unsigned char * statp, int etype,
+                  const struct opts_t * op)
+{
+    int res, a, b;
+    char bb[128];
+    int nofilter = ! op->do_filter;
+
+
+    if (op->inner_hex) {
+        printf("%s%02x %02x %02x %02x\n", pad, statp[0], statp[1], statp[2],
+               statp[3]);
+        return;
+    }
+    printf("%sPredicted failure=%d, Disabled=%d, Swap=%d, status: %s\n",
+           pad, !!(statp[0] & 0x40), !!(statp[0] & 0x20),
+           !!(statp[0] & 0x10), elem_status_code_desc[statp[0] & 0xf]);
+    switch (etype) { /* element types */
+    case UNSPECIFIED_ETC:
+        if (op->verbose)
+            printf("%sstatus in hex: %02x %02x %02x %02x\n",
+                   pad, statp[0], statp[1], statp[2], statp[3]);
+        break;
+    case DEVICE_ETC:
+        printf("%sSlot address: %d\n", pad, statp[1]);
+        if (nofilter || (0xe0 & statp[2]))
+            printf("%sApp client bypassed A=%d, Do not remove=%d, Enc "
+                   "bypassed A=%d\n", pad, !!(statp[2] & 0x80),
+                   !!(statp[2] & 0x40), !!(statp[2] & 0x20));
+        if (nofilter || (0x1c & statp[2]))
+            printf("%sEnc bypassed B=%d, Ready to insert=%d, RMV=%d, Ident="
+                   "%d\n", pad, !!(statp[2] & 0x10), !!(statp[2] & 0x8),
+                   !!(statp[2] & 0x4), !!(statp[2] & 0x2));
+        if (nofilter || ((1 & statp[2]) || (0xe0 & statp[3])))
+            printf("%sReport=%d, App client bypassed B=%d, Fault sensed=%d, "
+                   "Fault requested=%d\n", pad, !!(statp[2] & 0x1),
+                   !!(statp[3] & 0x80), !!(statp[3] & 0x40),
+                   !!(statp[3] & 0x20));
+        if (nofilter || (0x1e & statp[3]))
+            printf("%sDevice off=%d, Bypassed A=%d, Bypassed B=%d, Device "
+                   "bypassed A=%d\n", pad, !!(statp[3] & 0x10),
+                   !!(statp[3] & 0x8), !!(statp[3] & 0x4), !!(statp[3] & 0x2));
+        if (nofilter || (0x1 & statp[3]))
+            printf("%sDevice bypassed B=%d\n", pad, !!(statp[3] & 0x1));
+        break;
+    case POWER_SUPPLY_ETC:
+        if (nofilter || ((0xc0 & statp[1]) || (0xe & statp[2])))
+            printf("%sIdent=%d, Do not remove=%d, DC overvoltage=%d, "
+                   "DC undervoltage=%d\n", pad, !!(statp[1] & 0x80),
+                   !!(statp[1] & 0x40), !!(statp[2] & 0x8),
+                   !!(statp[2] & 0x4));
+            printf("%s DC overcurrent=%d\n", pad, !!(statp[2] & 0x2));
+        if (nofilter || (0xf8 & statp[3]))
+            printf("%sHot swap=%d, Fail=%d, Requested on=%d, Off=%d, "
+                   "Overtmp fail=%d\n", pad, !!(statp[3] & 0x80),
+                   !!(statp[3] & 0x40), !!(statp[3] & 0x20),
+                   !!(statp[3] & 0x10), !!(statp[3] & 0x8));
+        if (nofilter || (0x7 & statp[3]))
+            printf("%sTemperature warn=%d, AC fail=%d, DC fail=%d\n",
+                   pad, !!(statp[3] & 0x4), !!(statp[3] & 0x2),
+                   !!(statp[3] & 0x1));
+        break;
+    case COOLING_ETC:
+        if (nofilter || ((0xc0 & statp[1]) || (0xf0 & statp[3])))
+            printf("%sIdent=%d, Do not remove=%d, Hot swap=%d, Fail=%d, "
+                   "Requested on=%d\n", pad, !!(statp[1] & 0x80),
+                   !!(statp[1] & 0x40), !!(statp[3] & 0x80),
+                   !!(statp[3] & 0x40), !!(statp[3] & 0x20));
+        printf("%sOff=%d, Actual speed=%d rpm, Fan %s\n", pad,
+               !!(statp[3] & 0x10), (((0x7 & statp[1]) << 8) + statp[2]) * 10,
+               actual_speed_desc[7 & statp[3]]);
+        break;
+    case TEMPERATURE_ETC:     /* temperature sensor */
+        if (nofilter || ((0xc0 & statp[1]) || (0xf & statp[3]))) {
+            printf("%sIdent=%d, Fail=%d, OT failure=%d, OT warning=%d, "
+                   "UT failure=%d\n", pad, !!(statp[1] & 0x80),
+                   !!(statp[1] & 0x40), !!(statp[3] & 0x8),
+                   !!(statp[3] & 0x4), !!(statp[3] & 0x2));
+            printf("%sUT warning=%d\n", pad, !!(statp[3] & 0x1));
+        }
+        if (statp[2])
+            printf("%sTemperature=%d C\n", pad,
+                   (int)statp[2] - TEMPERAT_OFF);
+        else
+            printf("%sTemperature: <reserved>\n", pad);
+        break;
+    case DOOR_ETC:      /* OPEN field added in ses3r05 */
+        if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[3])))
+            printf("%sIdent=%d, Fail=%d, Open=%d, Unlock=%d\n", pad,
+                   !!(statp[1] & 0x80), !!(statp[1] & 0x40),
+                   !!(statp[3] & 0x2), !!(statp[3] & 0x1));
+        break;
+    case AUD_ALARM_ETC:     /* audible alarm */
+        if (nofilter || ((0xc0 & statp[1]) || (0xd0 & statp[3])))
+            printf("%sIdent=%d, Fail=%d, Request mute=%d, Mute=%d, "
+                   "Remind=%d\n", pad, !!(statp[1] & 0x80),
+                   !!(statp[1] & 0x40), !!(statp[3] & 0x80),
+                   !!(statp[3] & 0x40), !!(statp[3] & 0x10));
+        if (nofilter || (0xf & statp[3]))
+            printf("%sTone indicator: Info=%d, Non-crit=%d, Crit=%d, "
+                   "Unrecov=%d\n", pad, !!(statp[3] & 0x8), !!(statp[3] & 0x4),
+                   !!(statp[3] & 0x2), !!(statp[3] & 0x1));
+        break;
+    case ENC_ELECTRONICS_ETC: /* enclosure services controller electronics */
+        if (nofilter || (0xe0 & statp[1]) || (0x1 & statp[2]) ||
+            (0x80 & statp[3]))
+            printf("%sIdent=%d, Fail=%d, Do not remove=%d, Report=%d, "
+                   "Hot swap=%d\n", pad, !!(statp[1] & 0x80),
+                   !!(statp[1] & 0x40), !!(statp[1] & 0x20),
+                   !!(statp[2] & 0x1), !!(statp[3] & 0x80));
+        break;
+    case SCC_CELECTR_ETC:     /* SCC controller electronics */
+        if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[2])))
+            printf("%sIdent=%d, Fail=%d, Report=%d\n", pad,
+                   !!(statp[1] & 0x80), !!(statp[1] & 0x40),
+                   !!(statp[2] & 0x1));
+        break;
+    case NV_CACHE_ETC:     /* Non volatile cache */
+        res = sg_get_unaligned_be16(statp + 2);
+        printf("%sIdent=%d, Fail=%d, Size multiplier=%d, Non volatile cache "
+               "size=0x%x\n", pad, !!(statp[1] & 0x80), !!(statp[1] & 0x40),
+               (statp[1] & 0x3), res);
+        printf("%sHence non volatile cache size: %d %s\n", pad, res,
+               nv_cache_unit[statp[1] & 0x3]);
+        break;
+    case INV_OP_REASON_ETC:   /* Invalid operation reason */
+        res = ((statp[1] >> 6) & 3);
+        printf("%sInvop type=%d   %s\n", pad, res, invop_type_desc[res]);
+        switch (res) {
+        case 0:
+            printf("%sPage not supported=%d\n", pad, (statp[1] & 1));
+            break;
+        case 1:
+            printf("%sByte offset=%d, bit number=%d\n", pad,
+                   sg_get_unaligned_be16(statp + 2), (statp[1] & 7));
+            break;
+        case 2:
+        case 3:
+            printf("%slast 3 bytes (hex): %02x %02x %02x\n", pad, statp[1],
+                   statp[2], statp[3]);
+            break;
+        default:
+            break;
+        }
+        break;
+    case UI_POWER_SUPPLY_ETC:   /* Uninterruptible power supply */
+        if (0 == statp[1])
+            printf("%sBattery status: discharged or unknown\n", pad);
+        else if (255 == statp[1])
+            printf("%sBattery status: 255 or more minutes remaining\n", pad);
+        else
+            printf("%sBattery status: %d minutes remaining\n", pad, statp[1]);
+        if (nofilter || (0xf8 & statp[2]))
+            printf("%sAC low=%d, AC high=%d, AC qual=%d, AC fail=%d, DC fail="
+                   "%d\n", pad, !!(statp[2] & 0x80), !!(statp[2] & 0x40),
+                   !!(statp[2] & 0x20), !!(statp[2] & 0x10),
+                   !!(statp[2] & 0x8));
+        if (nofilter || ((0x7 & statp[2]) || (0xe3 & statp[3]))) {
+            printf("%sUPS fail=%d, Warn=%d, Intf fail=%d, Ident=%d, Fail=%d, "
+                   "Do not remove=%d\n", pad, !!(statp[2] & 0x4),
+                   !!(statp[2] & 0x2), !!(statp[2] & 0x1),
+                   !!(statp[3] & 0x80), !!(statp[3] & 0x40),
+                   !!(statp[3] & 0x20));
+            printf("%sBatt fail=%d, BPF=%d\n", pad, !!(statp[3] & 0x2),
+                   !!(statp[3] & 0x1));
+        }
+        break;
+    case DISPLAY_ETC:   /* Display (ses2r15) */
+        if (nofilter || (0xc0 & statp[1])) {
+            int dms = statp[1] & 0x3;
+            uint16_t dcs;
+
+            printf("%sIdent=%d, Fail=%d, Display mode status=%d", pad,
+                   !!(statp[1] & 0x80), !!(statp[1] & 0x40), dms);
+            if ((1 == dms) || (2 == dms)) {
+                dcs = sg_get_unaligned_be16(statp + 2);
+                printf(", Display character status=0x%x", dcs);
+                if (statp[2] && (0 == statp[3]))
+                    printf(" ['%c']", statp[2]);
+            }
+            printf("\n");
+        }
+        break;
+    case KEY_PAD_ETC:   /* Key pad entry */
+        if (nofilter || (0xc0 & statp[1]))
+            printf("%sIdent=%d, Fail=%d\n", pad, !!(statp[1] & 0x80),
+                   !!(statp[1] & 0x40));
+        break;
+    case ENCLOSURE_ETC:
+        a = ((statp[2] >> 2) & 0x3f);
+        if (nofilter || ((0x80 & statp[1]) || a || (0x2 & statp[2])))
+            printf("%sIdent=%d, Time until power cycle=%d, "
+                   "Failure indication=%d\n", pad, !!(statp[1] & 0x80),
+                   a, !!(statp[2] & 0x2));
+        b = ((statp[3] >> 2) & 0x3f);
+        if (nofilter || (0x1 & statp[2]) || a || b)
+            printf("%sWarning indication=%d, Requested power off "
+                   "duration=%d\n", pad, !!(statp[2] & 0x1), b);
+        if (nofilter || (0x3 & statp[3]))
+            printf("%sFailure requested=%d, Warning requested=%d\n",
+                   pad, !!(statp[3] & 0x2), !!(statp[3] & 0x1));
+        break;
+    case SCSI_PORT_TRAN_ETC:   /* SCSI port/transceiver */
+        if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[2]) ||
+                           (0x13 & statp[3])))
+            printf("%sIdent=%d, Fail=%d, Report=%d, Disabled=%d, Loss of "
+                   "link=%d, Xmit fail=%d\n", pad, !!(statp[1] & 0x80),
+                   !!(statp[1] & 0x40), !!(statp[2] & 0x1),
+                   !!(statp[3] & 0x10), !!(statp[3] & 0x2),
+                   !!(statp[3] & 0x1));
+        break;
+    case LANGUAGE_ETC:
+        printf("%sIdent=%d, Language code: %.2s\n", pad, !!(statp[1] & 0x80),
+               statp + 2);
+        break;
+    case COMM_PORT_ETC:   /* Communication port */
+        if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[3])))
+            printf("%sIdent=%d, Fail=%d, Disabled=%d\n", pad,
+                   !!(statp[1] & 0x80), !!(statp[1] & 0x40),
+                   !!(statp[3] & 0x1));
+        break;
+    case VOLT_SENSOR_ETC:   /* Voltage sensor */
+        if (nofilter || (0xcf & statp[1])) {
+            printf("%sIdent=%d, Fail=%d,  Warn Over=%d, Warn Under=%d, "
+                   "Crit Over=%d\n", pad, !!(statp[1] & 0x80),
+                   !!(statp[1] & 0x40), !!(statp[1] & 0x8),
+                   !!(statp[1] & 0x4), !!(statp[1] & 0x2));
+            printf("%sCrit Under=%d\n", pad, !!(statp[1] & 0x1));
+        }
+#ifdef SG_LIB_MINGW
+        printf("%sVoltage: %g volts\n", pad,
+               ((int)(short)sg_get_unaligned_be16(statp + 2) / 100.0));
+#else
+        printf("%sVoltage: %.2f volts\n", pad,
+               ((int)(short)sg_get_unaligned_be16(statp + 2) / 100.0));
+#endif
+        break;
+    case CURR_SENSOR_ETC:   /* Current sensor */
+        if (nofilter || (0xca & statp[1]))
+            printf("%sIdent=%d, Fail=%d, Warn Over=%d, Crit Over=%d\n",
+                    pad, !!(statp[1] & 0x80), !!(statp[1] & 0x40),
+                    !!(statp[1] & 0x8), !!(statp[1] & 0x2));
+#ifdef SG_LIB_MINGW
+        printf("%sCurrent: %g amps\n", pad,
+               ((int)(short)sg_get_unaligned_be16(statp + 2) / 100.0));
+#else
+        printf("%sCurrent: %.2f amps\n", pad,
+               ((int)(short)sg_get_unaligned_be16(statp + 2) / 100.0));
+#endif
+        break;
+    case SCSI_TPORT_ETC:   /* SCSI target port */
+        if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[2]) ||
+                           (0x1 & statp[3])))
+            printf("%sIdent=%d, Fail=%d, Report=%d, Enabled=%d\n", pad,
+                   !!(statp[1] & 0x80), !!(statp[1] & 0x40),
+                   !!(statp[2] & 0x1), !!(statp[3] & 0x1));
+        break;
+    case SCSI_IPORT_ETC:   /* SCSI initiator port */
+        if (nofilter || ((0xc0 & statp[1]) || (0x1 & statp[2]) ||
+                           (0x1 & statp[3])))
+            printf("%sIdent=%d, Fail=%d, Report=%d, Enabled=%d\n", pad,
+                   !!(statp[1] & 0x80), !!(statp[1] & 0x40),
+                   !!(statp[2] & 0x1), !!(statp[3] & 0x1));
+        break;
+    case SIMPLE_SUBENC_ETC:   /* Simple subenclosure */
+        printf("%sIdent=%d, Fail=%d, Short enclosure status: 0x%x\n", pad,
+               !!(statp[1] & 0x80), !!(statp[1] & 0x40), statp[3]);
+        break;
+    case ARRAY_DEV_ETC:   /* Array device */
+        if (nofilter || (0xf0 & statp[1]))
+            printf("%sOK=%d, Reserved device=%d, Hot spare=%d, Cons check="
+                   "%d\n", pad, !!(statp[1] & 0x80), !!(statp[1] & 0x40),
+                   !!(statp[1] & 0x20), !!(statp[1] & 0x10));
+        if (nofilter || (0xf & statp[1]))
+            printf("%sIn crit array=%d, In failed array=%d, Rebuild/remap=%d"
+                   ", R/R abort=%d\n", pad, !!(statp[1] & 0x8),
+                   !!(statp[1] & 0x4), !!(statp[1] & 0x2),
+                   !!(statp[1] & 0x1));
+        if (nofilter || (0xf0 & statp[2]))
+            printf("%sApp client bypass A=%d, Do not remove=%d, Enc bypass "
+                   "A=%d, Enc bypass B=%d\n", pad, !!(statp[2] & 0x80),
+                   !!(statp[2] & 0x40), !!(statp[2] & 0x20),
+                   !!(statp[2] & 0x10));
+        if (nofilter || (0xf & statp[2]))
+            printf("%sReady to insert=%d, RMV=%d, Ident=%d, Report=%d\n",
+                   pad, !!(statp[2] & 0x8), !!(statp[2] & 0x4),
+                   !!(statp[2] & 0x2), !!(statp[2] & 0x1));
+        if (nofilter || (0xf0 & statp[3]))
+            printf("%sApp client bypass B=%d, Fault sensed=%d, Fault reqstd="
+                   "%d, Device off=%d\n", pad, !!(statp[3] & 0x80),
+                   !!(statp[3] & 0x40), !!(statp[3] & 0x20),
+                   !!(statp[3] & 0x10));
+        if (nofilter || (0xf & statp[3]))
+            printf("%sBypassed A=%d, Bypassed B=%d, Dev bypassed A=%d, "
+                   "Dev bypassed B=%d\n",
+                   pad, !!(statp[3] & 0x8), !!(statp[3] & 0x4),
+                   !!(statp[3] & 0x2), !!(statp[3] & 0x1));
+        break;
+    case SAS_EXPANDER_ETC:
+        printf("%sIdent=%d, Fail=%d\n", pad, !!(statp[1] & 0x80),
+               !!(statp[1] & 0x40));
+        break;
+    case SAS_CONNECTOR_ETC:     /* OC (overcurrent) added in ses3r07 */
+        printf("%sIdent=%d, %s\n", pad, !!(statp[1] & 0x80),
+               find_sas_connector_type((statp[1] & 0x7f), bb, sizeof(bb)));
+        printf("%sConnector physical link=0x%x, Mated=%d, Fail=%d, OC=%d\n",
+               pad, statp[2], !!(statp[3] & 0x80), !!(statp[3] & 0x40),
+               !!(statp[3] & 0x20));
+        break;
+    default:
+        if (etype < 0x80)
+            printf("%sUnknown element type, status in hex: %02x %02x %02x "
+                   "%02x\n", pad, statp[0], statp[1], statp[2], statp[3]);
+        else
+            printf("%sVendor specific element type, status in hex: %02x "
+                   "%02x %02x %02x\n", pad, statp[0], statp[1], statp[2],
+                   statp[3]);
+        break;
+    }
+}
+
+/* DPC_ENC_STATUS [0x2]
+ * Display enclosure status diagnostic page. */
+static void
+ses_enc_status_dp(const struct type_desc_hdr_t * tdhp, int num_telems,
+                  uint32_t ref_gen_code, const unsigned char * resp,
+                  int resp_len, const struct opts_t * op)
+{
+    int j, k, elem_ind, match_ind_th, got1;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+    char b[64];
+
+    printf("Enclosure Status diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    printf("  INVOP=%d, INFO=%d, NON-CRIT=%d, CRIT=%d, UNRECOV=%d\n",
+           !!(resp[1] & 0x10), !!(resp[1] & 0x8), !!(resp[1] & 0x4),
+           !!(resp[1] & 0x2), !!(resp[1] & 0x1));
+    last_ucp = resp + resp_len - 1;
+    if (resp_len < 8)
+        goto truncated;
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%x\n", gen_code);
+    if (ref_gen_code != gen_code) {
+        pr2serr("  <<state of enclosure changed, please try again>>\n");
+        return;
+    }
+    printf("  status descriptor list\n");
+    ucp = resp + 8;
+    for (k = 0, got1 = 0; k < num_telems; ++k, ++tdhp) {
+        if ((ucp + 3) > last_ucp)
+            goto truncated;
+        match_ind_th = (op->ind_given && (k == op->ind_th));
+        if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
+            printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
+                   find_element_tname(tdhp->etype, b, sizeof(b)),
+                   tdhp->se_id, k);
+            printf("      Overall descriptor:\n");
+            enc_status_helper("        ", ucp, tdhp->etype, op);
+            ++got1;
+        }
+        for (ucp += 4, j = 0, elem_ind = 0; j < tdhp->num_elements;
+             ++j, ucp += 4, ++elem_ind) {
+            if (op->ind_given) {
+                if ((! match_ind_th) || (-1 == op->ind_indiv) ||
+                    (elem_ind != op->ind_indiv))
+                    continue;
+            }
+            printf("      Element %d descriptor:\n", elem_ind);
+            enc_status_helper("        ", ucp, tdhp->etype, op);
+            ++got1;
+        }
+    }
+    if (op->ind_given && (0 == got1))
+        printf("      >>> no match on --index=%d,%d\n", op->ind_th,
+               op->ind_indiv);
+    return;
+truncated:
+    pr2serr("    <<<enc: response too short>>>\n");
+    return;
+}
+
+static char *
+reserved_or_num(char * buff, int buff_len, int num, int reserve_num)
+{
+    if (num == reserve_num)
+        strncpy(buff, "<res>", buff_len);
+    else
+        snprintf(buff, buff_len, "%d", num);
+    if (buff_len > 0)
+        buff[buff_len - 1] = '\0';
+    return buff;
+}
+
+static void
+ses_threshold_helper(const char * pad, const unsigned char *tp, int etype,
+                     const struct opts_t * op)
+{
+    char b[128];
+    char b2[128];
+
+    if (op->inner_hex) {
+        printf("%s%02x %02x %02x %02x\n", pad, tp[0], tp[1], tp[2], tp[3]);
+        return;
+    }
+    switch (etype) {
+    case 0x4:  /*temperature */
+        printf("%shigh critical=%s, high warning=%s\n", pad,
+               reserved_or_num(b, 128, tp[0] - TEMPERAT_OFF, -TEMPERAT_OFF),
+               reserved_or_num(b2, 128, tp[1] - TEMPERAT_OFF, -TEMPERAT_OFF));
+        printf("%slow warning=%s, low critical=%s (in Celsius)\n", pad,
+               reserved_or_num(b, 128, tp[2] - TEMPERAT_OFF, -TEMPERAT_OFF),
+               reserved_or_num(b2, 128, tp[3] - TEMPERAT_OFF, -TEMPERAT_OFF));
+        break;
+    case 0xb:  /* UPS */
+        if (0 == tp[2])
+            strcpy(b, "<vendor>");
+        else
+            snprintf(b, sizeof(b), "%d", tp[2]);
+        printf("%slow warning=%s, ", pad, b);
+        if (0 == tp[3])
+            strcpy(b, "<vendor>");
+        else
+            snprintf(b, sizeof(b), "%d", tp[3]);
+        printf("low critical=%s (in minutes)\n", b);
+        break;
+    case 0x12: /* voltage */
+#ifdef SG_LIB_MINGW
+        printf("%shigh critical=%g %%, high warning=%g %%\n", pad,
+               0.5 * tp[0], 0.5 * tp[1]);
+        printf("%slow warning=%g %%, low critical=%g %% (from nominal "
+               "voltage)\n", pad, 0.5 * tp[2], 0.5 * tp[3]);
+#else
+        printf("%shigh critical=%.1f %%, high warning=%.1f %%\n", pad,
+               0.5 * tp[0], 0.5 * tp[1]);
+        printf("%slow warning=%.1f %%, low critical=%.1f %% (from nominal "
+               "voltage)\n", pad, 0.5 * tp[2], 0.5 * tp[3]);
+#endif
+        break;
+    case 0x13: /* current */
+#ifdef SG_LIB_MINGW
+        printf("%shigh critical=%g %%, high warning=%g %%", pad,
+               0.5 * tp[0], 0.5 * tp[1]);
+#else
+        printf("%shigh critical=%.1f %%, high warning=%.1f %%", pad,
+               0.5 * tp[0], 0.5 * tp[1]);
+#endif
+        printf(" (above nominal current)\n");
+        break;
+    default:
+        if (op->verbose)
+            printf("%s<< no thresholds for this element type >>\n", pad);
+        break;
+    }
+}
+
+/* DPC_THRESHOLD [0x5] */
+static void
+ses_threshold_sdg(const struct type_desc_hdr_t * tdhp, int num_telems,
+                  uint32_t ref_gen_code, const unsigned char * resp,
+                  int resp_len, const struct opts_t * op)
+{
+    int j, k, elem_ind, match_ind_th, got1;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+    char b[64];
+
+    printf("Threshold In diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    printf("  INVOP=%d\n", !!(resp[1] & 0x10));
+    last_ucp = resp + resp_len - 1;
+    if (resp_len < 8)
+        goto truncated;
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    if (ref_gen_code != gen_code) {
+        pr2serr("  <<state of enclosure changed, please try again>>\n");
+        return;
+    }
+    printf("  Threshold status descriptor list\n");
+    ucp = resp + 8;
+    for (k = 0, got1 = 0; k < num_telems; ++k, ++tdhp) {
+        if ((ucp + 3) > last_ucp)
+            goto truncated;
+        match_ind_th = (op->ind_given && (k == op->ind_th));
+        if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
+            printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
+                   find_element_tname(tdhp->etype, b, sizeof(b)),
+                   tdhp->se_id, k);
+            printf("      Overall descriptor:\n");
+            ses_threshold_helper("        ", ucp, tdhp->etype, op);
+            ++got1;
+        }
+        for (ucp += 4, j = 0, elem_ind = 0; j < tdhp->num_elements;
+             ++j, ucp += 4, ++elem_ind) {
+            if (op->ind_given) {
+                if ((! match_ind_th) || (-1 == op->ind_indiv) ||
+                    (elem_ind != op->ind_indiv))
+                    continue;
+            }
+            printf("      Element %d descriptor:\n", elem_ind);
+            ses_threshold_helper("        ", ucp, tdhp->etype, op);
+            ++got1;
+        }
+    }
+    if (op->ind_given && (0 == got1))
+        printf("      >>> no match on --index=%d,%d\n", op->ind_th,
+               op->ind_indiv);
+    return;
+truncated:
+    pr2serr("    <<<thresh: response too short>>>\n");
+    return;
+}
+
+/* DPC_ELEM_DESC [0x7]
+ * This page essentially contains names of overall and individual
+ * elements. */
+static void
+ses_element_desc_sdg(const struct type_desc_hdr_t * tdhp, int num_telems,
+                     uint32_t ref_gen_code, const unsigned char * resp,
+                     int resp_len, const struct opts_t * op)
+{
+    int j, k, desc_len, elem_ind, match_ind_th, got1;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+    const struct type_desc_hdr_t * tp;
+    char b[64];
+
+    printf("Element Descriptor In diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    last_ucp = resp + resp_len - 1;
+    if (resp_len < 8)
+        goto truncated;
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    if (ref_gen_code != gen_code) {
+        pr2serr("  <<state of enclosure changed, please try again>>\n");
+        return;
+    }
+    printf("  element descriptor list (grouped by type):\n");
+    ucp = resp + 8;
+    for (k = 0, got1 = 0, tp = tdhp; k < num_telems; ++k, ++tp) {
+        if ((ucp + 3) > last_ucp)
+            goto truncated;
+        desc_len = sg_get_unaligned_be16(ucp + 2) + 4;
+        match_ind_th = (op->ind_given && (k == op->ind_th));
+        if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
+            printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
+                   find_element_tname(tp->etype, b, sizeof(b)), tp->se_id, k);
+            if (desc_len > 4)
+                printf("      Overall descriptor: %.*s\n", desc_len - 4,
+                       ucp + 4);
+            else
+                printf("      Overall descriptor: <empty>\n");
+            ++got1;
+        }
+        for (ucp += desc_len, j = 0, elem_ind = 0; j < tp->num_elements;
+             ++j, ucp += desc_len, ++elem_ind) {
+            desc_len = sg_get_unaligned_be16(ucp + 2) + 4;
+            if (op->ind_given) {
+                if ((! match_ind_th) || (-1 == op->ind_indiv) ||
+                    (elem_ind != op->ind_indiv))
+                    continue;
+            }
+            if (desc_len > 4)
+                printf("      Element %d descriptor: %.*s\n", j,
+                       desc_len - 4, ucp + 4);
+            else
+                printf("      Element %d descriptor: <empty>\n", j);
+            ++got1;
+        }
+    }
+    if (op->ind_given && (0 == got1))
+        printf("      >>> no match on --index=%d,%d\n", op->ind_th,
+               op->ind_indiv);
+    return;
+truncated:
+    pr2serr("    <<<element: response too short>>>\n");
+    return;
+}
+
+static int
+saddr_non_zero(const unsigned char * ucp)
+{
+    int k;
+
+    for (k = 0; k < 8; ++k) {
+        if (ucp[k])
+            return 1;
+    }
+    return 0;
+}
+
+static const char * sas_device_type[] = {
+    "no SAS device attached",   /* but might be SATA device */
+    "end device",
+    "expander device",  /* in SAS-1.1 this was a "edge expander device */
+    "expander device (fanout, SAS-1.1)",  /* marked obsolete in SAS-2 */
+    "reserved [4]", "reserved [5]", "reserved [6]", "reserved [7]"
+};
+
+static void
+additional_elem_helper(const char * pad, const unsigned char * ucp, int len,
+                       int elem_type, const struct opts_t * op)
+{
+    int ports, phys, j, m, desc_type, eip_offset, print_sas_addr, saddr_nz;
+    int nofilter = ! op->do_filter;
+    uint16_t pcie_vid;
+    int pcie_pt, psn_valid, bdf_valid, cid_valid;
+    const unsigned char * per_ucp;
+    char b[64];
+
+    if (op->inner_hex) {
+        for (j = 0; j < len; ++j) {
+            if (0 == (j % 16))
+                printf("%s%s", ((0 == j) ? "" : "\n"), pad);
+            printf("%02x ", ucp[j]);
+        }
+        printf("\n");
+        return;
+    }
+    eip_offset = (0x10 & ucp[0]) ? 2 : 0;
+    switch (0xf & ucp[0]) {
+    case TPROTO_FCP:
+        printf("%sTransport protocol: FCP\n", pad);
+        if (len < (12 + eip_offset))
+            break;
+        ports = ucp[2 + eip_offset];
+        printf("%snumber of ports: %d\n", pad, ports);
+        printf("%snode_name: ", pad);
+        for (m = 0; m < 8; ++m)
+            printf("%02x", ucp[6 + eip_offset + m]);
+        if (eip_offset)
+            printf(", device slot number: %d", ucp[5 + eip_offset]);
+        printf("\n");
+        per_ucp = ucp + 14 + eip_offset;
+        for (j = 0; j < ports; ++j, per_ucp += 16) {
+            printf("%s  port index: %d, port loop position: %d, port "
+                   "bypass reason: 0x%x\n", pad, j, per_ucp[0], per_ucp[1]);
+            printf("%srequested hard address: %d, n_port identifier: "
+                   "%02x%02x%02x\n", pad, per_ucp[4], per_ucp[5],
+                   per_ucp[6], per_ucp[7]);
+            printf("%s  n_port name: ", pad);
+            for (m = 0; m < 8; ++m)
+                printf("%02x", per_ucp[8 + m]);
+            printf("\n");
+        }
+        break;
+    case TPROTO_SAS:
+        printf("%sTransport protocol: SAS\n", pad);
+        if (len < (4 + eip_offset))
+            break;
+        desc_type = (ucp[3 + eip_offset] >> 6) & 0x3;
+        if (op->verbose > 1)
+            printf("%sdescriptor_type: %d\n", pad, desc_type);
+        if (0 == desc_type) {
+            phys = ucp[2 + eip_offset];
+            printf("%snumber of phys: %d, not all phys: %d", pad, phys,
+                   ucp[3 + eip_offset] & 1);
+            if (eip_offset)
+                printf(", device slot number: %d", ucp[5 + eip_offset]);
+            printf("\n");
+            per_ucp = ucp + 4 + eip_offset + eip_offset;
+            for (j = 0; j < phys; ++j, per_ucp += 28) {
+                printf("%sphy index: %d\n", pad, j);
+                printf("%s  SAS device type: %s\n", pad,
+                       sas_device_type[(0x70 & per_ucp[0]) >> 4]);
+                if (nofilter || (0xe & per_ucp[2]))
+                    printf("%s  initiator port for:%s%s%s\n", pad,
+                           ((per_ucp[2] & 8) ? " SSP" : ""),
+                           ((per_ucp[2] & 4) ? " STP" : ""),
+                           ((per_ucp[2] & 2) ? " SMP" : ""));
+                if (nofilter || (0x8f & per_ucp[3]))
+                    printf("%s  target port for:%s%s%s%s%s\n", pad,
+                           ((per_ucp[3] & 0x80) ? " SATA_port_selector" : ""),
+                           ((per_ucp[3] & 8) ? " SSP" : ""),
+                           ((per_ucp[3] & 4) ? " STP" : ""),
+                           ((per_ucp[3] & 2) ? " SMP" : ""),
+                           ((per_ucp[3] & 1) ? " SATA_device" : ""));
+                print_sas_addr = 0;
+                saddr_nz = saddr_non_zero(per_ucp + 4);
+                if (nofilter || saddr_nz) {
+                    ++print_sas_addr;
+                    printf("%s  attached SAS address: 0x", pad);
+                    if (saddr_nz) {
+                        for (m = 0; m < 8; ++m)
+                            printf("%02x", per_ucp[4 + m]);
+                    } else
+                        printf("0");
+                }
+                saddr_nz = saddr_non_zero(per_ucp + 12);
+                if (nofilter || saddr_nz) {
+                    ++print_sas_addr;
+                    printf("\n%s  SAS address: 0x", pad);
+                    if (saddr_nz) {
+                        for (m = 0; m < 8; ++m)
+                            printf("%02x", per_ucp[12 + m]);
+                    } else
+                        printf("0");
+                }
+                if (print_sas_addr)
+                    printf("\n%s  phy identifier: 0x%x\n", pad, per_ucp[20]);
+            }
+        } else if (1 == desc_type) {
+            phys = ucp[2 + eip_offset];
+            if (SAS_EXPANDER_ETC == elem_type) {
+                printf("%snumber of phys: %d\n", pad, phys);
+                printf("%sSAS address: 0x", pad);
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", ucp[6 + eip_offset + m]);
+                printf("\n");
+                per_ucp = ucp + 14 + eip_offset;
+                for (j = 0; j < phys; ++j, per_ucp += 2) {
+                    printf("%s  [%d] ", pad, j);
+                    if (0xff == per_ucp[0])
+                        printf("no attached connector");
+                    else
+                        printf("connector element index: %d", per_ucp[0]);
+                    if (0xff != per_ucp[1])
+                        printf(", other element index: %d", per_ucp[1]);
+                    printf("\n");
+                }
+            } else if ((SCSI_TPORT_ETC == elem_type) ||
+                       (SCSI_IPORT_ETC == elem_type) ||
+                       (ENC_ELECTRONICS_ETC == elem_type)) {
+                printf("%snumber of phys: %d\n", pad, phys);
+                per_ucp = ucp + 6 + eip_offset;
+                for (j = 0; j < phys; ++j, per_ucp += 12) {
+                    printf("%sphy index: %d\n", pad, j);
+                    printf("%s  phy identifier: 0x%x\n", pad, per_ucp[0]);
+                    if (0xff == per_ucp[2])
+                        printf("%s  no attached connector", pad);
+                    else
+                        printf("%s  connector element index: %d", pad,
+                               per_ucp[2]);
+                    if (0xff != per_ucp[3])
+                        printf(", other element index: %d", per_ucp[3]);
+                    printf("\n");
+                    printf("%s  SAS address: 0x", pad);
+                    for (m = 0; m < 8; ++m)
+                        printf("%02x", per_ucp[4 + m]);
+                    printf("\n");
+                }
+            } else
+                printf("%sunrecognised element type [%d] for desc_type "
+                       "1\n", pad, elem_type);
+        } else
+            printf("%sunrecognised descriptor type [%d]\n", pad, desc_type);
+        break;
+    case TPROTO_PCIE: /* added in ses3r08 */
+        printf("%sTransport protocol: PCIe\n", pad);
+        if (0 == eip_offset) {
+            printf("%sfor this protocol EIP must be set (it isn't)\n", pad);
+            break;
+        }
+        if (len < 6)
+            break;
+        pcie_pt = (ucp[5] >> 5) & 0x7;
+        if (1 == pcie_pt)
+            printf("%sPCIe protocol type: NVMe\n", pad);
+        else {
+            printf("%sTransport protocol: PCIe subprotocol=0x%x not "
+                   "decoded\n", pad, pcie_pt);
+            if (op->verbose)
+                dStrHex((const char *)ucp, len, 0);
+            break;
+        }
+        phys = ucp[4];
+        printf("%snumber of ports: %d, not all ports: %d", pad, phys,
+               ucp[5] & 1);
+        printf(", device slot number: %d\n", ucp[7]);
+
+        pcie_vid = sg_get_unaligned_le16(ucp + 10);
+        printf("%svendor id: 0x%" PRIx16 "%s\n", pad, pcie_vid,
+               (0xffff == pcie_vid) ? " (not reported)" : "");
+        printf("%sserial number: %.20s\n", pad, ucp + 12);
+        printf("%smodel number: %.40s\n", pad, ucp + 32);
+        per_ucp = ucp + 72;
+        for (j = 0; j < phys; ++j, per_ucp += 8) {
+            printf("%sport index: %d\n", pad, j);
+            psn_valid = !!(0x4 & per_ucp[0]);
+            bdf_valid = !!(0x2 & per_ucp[0]);
+            cid_valid = !!(0x1 & per_ucp[0]);
+            printf("%s  PSN_VALID=%d, BDF_VALID=%d, CID_VALID=%d\n", pad,
+                   psn_valid, bdf_valid, cid_valid);
+            if (cid_valid)
+                printf("%s  controller id: 0x%" PRIx16 "\n", pad,
+                       sg_get_unaligned_le16(per_ucp + 1));
+            if (bdf_valid)
+                printf("%s  bus number: 0x%x, device number: 0x%x, "
+                       "function number: 0x%x\n", pad, per_ucp[4],
+                       (per_ucp[5] >> 3) & 0x1f, 0x7 & per_ucp[5]);
+            if (psn_valid)
+                printf("%s  physical slot number: 0x%" PRIx16 "\n", pad,
+                       0x1fff & sg_get_unaligned_le16(per_ucp + 6));
+        }
+        break;
+    default:
+        printf("%sTransport protocol: %s not decoded\n", pad,
+               sg_get_trans_proto_str((0xf & ucp[0]), sizeof(b), b));
+        if (op->verbose)
+            dStrHex((const char *)ucp, len, 0);
+        break;
+    }
+}
+
+/* DPC_ADD_ELEM_STATUS [0xa]
+ * Previously called "Device element status descriptor". Changed "device"
+ * to "additional" to allow for SAS expander and SATA devices */
+static void
+ses_additional_elem_sdg(const struct type_desc_hdr_t * tdhp, int num_telems,
+                        uint32_t ref_gen_code, const unsigned char * resp,
+                        int resp_len, const struct opts_t * op)
+{
+    int j, k, desc_len, elem_type, invalid, el_num, eip, ind, match_ind_th;
+    int elem_count, ei, eiioe, my_eiioe_force, num_elems, skip;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+    const struct type_desc_hdr_t * tp;
+    char b[64];
+
+    printf("Additional element status diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    last_ucp = resp + resp_len - 1;
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    if (ref_gen_code != gen_code) {
+        pr2serr("  <<state of enclosure changed, please try again>>\n");
+        return;
+    }
+    printf("  additional element status descriptor list\n");
+    ucp = resp + 8;
+    my_eiioe_force = op->eiioe_force;
+    for (k = 0, tp = tdhp, elem_count = 0; k < num_telems; ++k, ++tp) {
+        elem_type = tp->etype;
+        num_elems = tp->num_elements;
+        if (! active_et_aesp(elem_type)) {
+            elem_count += num_elems;
+            continue;   /* skip if not element type of interest */
+        }
+        if ((ucp + 1) > last_ucp)
+            goto truncated;
+
+        /* if eip is 1, do bounds check on the element index */
+        if (ucp[0] & 0x10)  /* eip=1 */ {
+            ei = ucp[3];
+            skip = 0;
+            if ((0 == k) && op->eiioe_auto && (1 == ei)) {
+                /* heuristic: if first element index in this page is 1
+                 * then act as if the EIIOE bit is set. */
+                my_eiioe_force = 1;
+            }
+            eiioe = my_eiioe_force ? 1 : (ucp[2] & 1);
+            if (eiioe) {
+                if ((ei < (elem_count + k)) ||
+                    (ei > (elem_count + k + num_elems))) {
+                    elem_count += num_elems;
+                    skip = 1;
+                }
+            } else {
+                if ((ei < elem_count) || (ei > elem_count + num_elems)) {
+                    elem_count += num_elems;
+                    skip = 1;
+                }
+            }
+            if (skip) {
+                if (op->verbose > 2)
+                    pr2serr("skipping elem_type=0x%x, k=%d due to "
+                            "element_index=%d bounds\n  effective eiioe=%d, "
+                            "elem_count=%d, num_elems=%d\n", elem_type, k,
+                            ei, eiioe, elem_count, num_elems);
+                continue;
+            }
+        }
+        match_ind_th = (op->ind_given && (k == op->ind_th));
+        if ((! op->ind_given) || (match_ind_th && (-1 == op->ind_indiv))) {
+            printf("    Element type: %s, subenclosure id: %d [ti=%d]\n",
+                   find_element_tname(elem_type, b, sizeof(b)), tp->se_id, k);
+        }
+        el_num = 0;
+        for (j = 0; j < num_elems; ++j, ucp += desc_len, ++el_num) {
+            invalid = !!(ucp[0] & 0x80);
+            desc_len = ucp[1] + 2;
+            eip = ucp[0] & 0x10;
+            eiioe = eip ? (ucp[2] & 1) : 0;
+            ind = eip ? ucp[3] : el_num;
+            if (op->ind_given) {
+                if ((! match_ind_th) || (-1 == op->ind_indiv) ||
+                    (el_num != op->ind_indiv))
+                    continue;
+            }
+            if (eip)
+                printf("      Element index: %d  eiioe=%d%s\n", ind, eiioe,
+                       (((! eiioe) && my_eiioe_force) ?
+                        " but overridden" : ""));
+            else
+                printf("      Element %d descriptor\n", ind);
+            if (invalid && (0 == op->inner_hex))
+                printf("        flagged as invalid (no further "
+                       "information)\n");
+            else
+                additional_elem_helper("        ", ucp, desc_len, elem_type,
+                                       op);
+        }
+        elem_count += tp->num_elements;
+    }
+    return;
+truncated:
+    pr2serr("    <<<additional: response too short>>>\n");
+    return;
+}
+
+/* DPC_SUBENC_HELP_TEXT [0xb] */
+static void
+ses_subenc_help_sdg(const unsigned char * resp, int resp_len)
+{
+    int k, el, num_subs;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+
+    printf("Subenclosure help text diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
+    last_ucp = resp + resp_len - 1;
+    printf("  number of secondary subenclosures: %d\n",
+            num_subs - 1);
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    ucp = resp + 8;
+    for (k = 0; k < num_subs; ++k, ucp += el) {
+        if ((ucp + 3) > last_ucp)
+            goto truncated;
+        el = sg_get_unaligned_be16(ucp + 2) + 4;
+        printf("   subenclosure identifier: %d\n", ucp[1]);
+        if (el > 4)
+            printf("    %.*s\n", el - 4, ucp + 4);
+        else
+            printf("    <empty>\n");
+    }
+    return;
+truncated:
+    pr2serr("    <<<subenc: response too short>>>\n");
+    return;
+}
+
+/* DPC_SUBENC_STRING [0xc] */
+static void
+ses_subenc_string_sdg(const unsigned char * resp, int resp_len)
+{
+    int k, j, el, num_subs;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+
+    printf("Subenclosure string in diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
+    last_ucp = resp + resp_len - 1;
+    printf("  number of secondary subenclosures: %d\n", num_subs - 1);
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    ucp = resp + 8;
+    for (k = 0; k < num_subs; ++k, ucp += el) {
+        if ((ucp + 3) > last_ucp)
+            goto truncated;
+        el = sg_get_unaligned_be16(ucp + 2) + 4;
+        printf("   subenclosure identifier: %d\n", ucp[1]);
+        if (el > 4) {
+            /* dStrHex((const char *)(ucp + 4), el - 4, 0); */
+            printf("    ");
+            for (j = 0; j < (el - 4); ++j) {
+                if ((j > 0) && (0 == (j % 16)))
+                    printf("\n    ");
+                printf("%02x ", *(ucp + 4 + j));
+            }
+            printf("\n");
+        } else
+            printf("    <empty>\n");
+    }
+    return;
+truncated:
+    pr2serr("    <<<subence str: response too short>>>\n");
+    return;
+}
+
+/* DPC_SUBENC_NICKNAME [0xf] */
+static void
+ses_subenc_nickname_sdg(const unsigned char * resp, int resp_len)
+{
+    int k, el, num_subs;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+
+    printf("Subenclosure nickname status diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
+    last_ucp = resp + resp_len - 1;
+    printf("  number of secondary subenclosures: %d\n",
+            num_subs - 1);
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    ucp = resp + 8;
+    el = 40;
+    for (k = 0; k < num_subs; ++k, ucp += el) {
+        if ((ucp + el - 1) > last_ucp)
+            goto truncated;
+        printf("   subenclosure identifier: %d\n", ucp[1]);
+        printf("   nickname status: 0x%x\n", ucp[2]);
+        printf("   nickname additional status: 0x%x\n", ucp[3]);
+        printf("   nickname language code: %.2s\n", ucp + 6);
+        printf("   nickname: %.*s\n", 32, ucp + 8);
+    }
+    return;
+truncated:
+    pr2serr("    <<<subence str: response too short>>>\n");
+    return;
+}
+
+/* DPC_SUPPORTED_SES [0xd] */
+static void
+ses_supported_pages_sdg(const char * leadin, const unsigned char * resp,
+                        int resp_len)
+{
+    int k, code, prev, got1;
+    const char * cp;
+    const struct diag_page_abbrev * ap;
+
+    printf("%s:\n", leadin);
+    for (k = 0, prev = 0; k < (resp_len - 4); ++k, prev = code) {
+        code = resp[k + 4];
+        if (code < prev)
+            break;      /* assume to be padding at end */
+        cp = find_diag_page_desc(code);
+        if (cp) {
+            printf("  %s [", cp);
+            for (ap = dp_abbrev, got1 = 0; ap->abbrev; ++ap) {
+                if (ap->page_code == code) {
+                    printf("%s%s", (got1 ? "," : ""), ap->abbrev);
+                    ++got1;
+                }
+            }
+            printf("] [0x%x]\n", code);
+        } else
+            printf("  <unknown> [0x%x]\n", code);
+    }
+}
+
+/* An array of Download microcode status field values and descriptions */
+static struct diag_page_code mc_status_arr[] = {
+    {0x0, "No download microcode operation in progress"},
+    {0x1, "Download in progress, awaiting more"},
+    {0x2, "Download complete, updating storage"},
+    {0x3, "Updating storage with deferred microcode"},
+    {0x10, "Complete, no error, starting now"},
+    {0x11, "Complete, no error, start after hard reset or power cycle"},
+    {0x12, "Complete, no error, start after power cycle"},
+    {0x13, "Complete, no error, start after activate_mc, hard reset or "
+           "power cycle"},
+    {0x80, "Error, discarded, see additional status"},
+    {0x81, "Error, discarded, image error"},
+    {0x82, "Timeout, discarded"},
+    {0x83, "Internal error, need new microcode before reset"},
+    {0x84, "Internal error, need new microcode, reset safe"},
+    {0x85, "Unexpected activate_mc received"},
+    {0x1000, NULL},
+};
+
+static const char *
+get_mc_status(unsigned char status_val)
+{
+    const struct diag_page_code * mcsp;
+
+    for (mcsp = mc_status_arr; mcsp->desc; ++mcsp) {
+        if (status_val == mcsp->page_code)
+            return mcsp->desc;
+    }
+    return "";
+}
+
+/* DPC_DOWNLOAD_MICROCODE [0xe] */
+static void
+ses_download_code_sdg(const unsigned char * resp, int resp_len)
+{
+    int k, num_subs;
+    uint32_t gen_code;
+    const unsigned char * ucp;
+    const unsigned char * last_ucp;
+    const char * cp;
+
+    printf("Download microcode status diagnostic page:\n");
+    if (resp_len < 4)
+        goto truncated;
+    num_subs = resp[1] + 1;  /* number of subenclosures (add 1 for primary) */
+    last_ucp = resp + resp_len - 1;
+    printf("  number of secondary subenclosures: %d\n",
+            num_subs - 1);
+    gen_code = sg_get_unaligned_be32(resp + 4);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    ucp = resp + 8;
+    for (k = 0; k < num_subs; ++k, ucp += 16) {
+        if ((ucp + 3) > last_ucp)
+            goto truncated;
+        cp = (0 == ucp[1]) ? " [primary]" : "";
+        printf("   subenclosure identifier: %d%s\n", ucp[1], cp);
+        cp = get_mc_status(ucp[2]);
+        if (strlen(cp) > 0) {
+            printf("     download microcode status: %s [0x%x]\n", cp, ucp[2]);
+            printf("     download microcode additional status: 0x%x\n",
+                   ucp[3]);
+        } else
+            printf("     download microcode status: 0x%x [additional "
+                   "status: 0x%x]\n", ucp[2], ucp[3]);
+        printf("     download microcode maximum size: %d bytes\n",
+               sg_get_unaligned_be32(ucp + 4));
+        printf("     download microcode expected buffer id: 0x%x\n", ucp[11]);
+        printf("     download microcode expected buffer id offset: %d\n",
+               sg_get_unaligned_be32(ucp + 12));
+    }
+    return;
+truncated:
+    pr2serr("    <<<download: response too short>>>\n");
+    return;
+}
+
+/* Reads hex data from command line, stdin or a file. Returns 0 on success,
+ * 1 otherwise. */
+static int
+read_hex(const char * inp, unsigned char * arr, int * arr_len, int verb)
+{
+    int in_len, k, j, m, off, split_line;
+    unsigned int h;
+    const char * lcp;
+    char * cp;
+    char * c2p;
+    char line[512];
+    char carry_over[4];
+    FILE * fp = NULL;
+
+    if ((NULL == inp) || (NULL == arr) || (NULL == arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *arr_len = 0;
+    if (('-' == inp[0]) || ('@' == inp[0])) {    /* read from stdin or file */
+        if ('-' == inp[0])
+            fp = stdin;
+        else {
+            fp = fopen(inp + 1, "r");
+            if (NULL == fp) {
+                pr2serr("%s: unable to open file: %s\n", __func__, inp + 1);
+                return 1;
+            }
+        }
+        carry_over[0] = 0;
+        for (j = 0, off = 0; j < MX_DATA_IN; ++j) {
+            /* limit lines read to MX_DATA_IN */
+            if (NULL == fgets(line, sizeof(line), fp))
+                break;
+            in_len = strlen(line);
+            if (in_len > 0) {
+                if ('\n' == line[in_len - 1]) {
+                    --in_len;
+                    line[in_len] = '\0';
+                    split_line = 0;
+                } else
+                    split_line = 1;
+            }
+            if (in_len < 1) {
+                carry_over[0] = 0;
+                continue;
+            }
+            if (carry_over[0]) {
+                if (isxdigit(line[0])) {
+                    carry_over[1] = line[0];
+                    carry_over[2] = '\0';
+                    if (1 == sscanf(carry_over, "%x", &h))
+                        arr[off - 1] = h;       /* back up and overwrite */
+                    else {
+                        pr2serr("%s: carry_over error ['%s'] around line "
+                                "%d\n", __func__, carry_over, j + 1);
+                        goto err_with_fp;
+                    }
+                    lcp = line + 1;
+                    --in_len;
+                } else
+                    lcp = line;
+                carry_over[0] = 0;
+            } else
+                lcp = line;
+            m = strspn(lcp, " \t");
+            if (m == in_len)
+                continue;
+            lcp += m;
+            in_len -= m;
+            if ('#' == *lcp)
+                continue;
+            k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+            if (in_len != k) {
+                pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
+                        j + 1, m + k + 1);
+                goto err_with_fp;
+            }
+            for (k = 0; k < (MX_DATA_IN - off); ++k) {
+                if (1 == sscanf(lcp, "%x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("%s: hex number larger than 0xff in line %d, "
+                                "pos %d\n", __func__, j + 1,
+                                (int)(lcp - line + 1));
+                        goto err_with_fp;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    pr2serr("%s: error in line %d, at pos %d\n", __func__,
+                            j + 1, (int)(lcp - line + 1));
+                    goto err_with_fp;
+                }
+            }
+            off += k + 1;
+            if (off >= MX_DATA_IN)
+                break;
+        }
+        *arr_len = off;
+    } else {        /* hex string on command line */
+        k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
+        if (in_len != k) {
+            pr2serr("%s: error at pos %d\n", __func__, k + 1);
+            goto err_with_fp;
+        }
+        for (k = 0; k < MX_DATA_IN; ++k) {
+            if (1 == sscanf(lcp, "%x", &h)) {
+                if (h > 0xff) {
+                    pr2serr("%s: hex number larger than 0xff at pos %d\n",
+                            __func__, (int)(lcp - inp + 1));
+                    goto err_with_fp;
+                }
+                arr[k] = h;
+                cp = (char *)strchr(lcp, ',');
+                c2p = (char *)strchr(lcp, ' ');
+                if (NULL == cp)
+                    cp = c2p;
+                if (NULL == cp)
+                    break;
+                if (c2p && (c2p < cp))
+                    cp = c2p;
+                lcp = cp + 1;
+            } else {
+                pr2serr("%s: error at pos %d\n", __func__,
+                        (int)(lcp - inp + 1));
+                goto err_with_fp;
+            }
+        }
+        *arr_len = k + 1;
+    }
+    if (verb > 3)
+        dStrHex((const char *)arr, *arr_len, 0);
+    if (fp && (fp != stdin))
+        fclose(fp);
+    return 0;
+
+err_with_fp:
+    if (fp && (fp != stdin))
+        fclose(fp);
+    return 1;
+}
+
+/* Display "status" page (op->page_code). Return 0 for success. */
+static int
+ses_process_status_page(int sg_fd, struct opts_t * op)
+{
+    int j, resp_len, res;
+    int ret = 0;
+    uint32_t ref_gen_code;
+    unsigned char *  resp;
+    const char * cp;
+    struct enclosure_info primary_info;
+
+    resp = (unsigned char *)calloc(op->maxlen, 1);
+    if (NULL == resp) {
+        pr2serr("%s: unable to allocate %d bytes on heap\n", __func__,
+                op->maxlen);
+        ret = -1;
+        goto fini;
+    }
+    cp = find_in_diag_page_desc(op->page_code);
+    ret = do_rec_diag(sg_fd, op->page_code, resp, op->maxlen, op, &resp_len);
+    if (ret)
+        goto fini;
+    if (op->do_raw) {
+        if (1 == op->do_raw)
+            dStrHex((const char *)resp + 4, resp_len - 4, -1);
+        else {
+            if (sg_set_binary_mode(STDOUT_FILENO) < 0)
+                perror("sg_set_binary_mode");
+            dStrRaw((const char *)resp, resp_len);
+        }
+    } else if (op->do_hex) {
+        if (op->do_hex > 2)
+            dStrHex((const char *)resp, resp_len, -1);
+         else {
+            if (cp)
+                printf("Response in hex from diagnostic page: %s\n", cp);
+            else
+                printf("Response in hex from unknown diagnostic page "
+                       "[0x%x]\n", op->page_code);
+            dStrHex((const char *)resp, resp_len, (2 == op->do_hex));
+        }
+    } else {
+        memset(&primary_info, 0, sizeof(primary_info));
+        switch (op->page_code) {
+        case DPC_SUPPORTED:
+            ses_supported_pages_sdg("Supported diagnostic pages",
+                                    resp, resp_len);
+            break;
+        case DPC_CONFIGURATION:
+            ses_configuration_sdg(resp, resp_len);
+            break;
+        case DPC_ENC_STATUS:
+            res = populate_type_desc_hdr_arr(sg_fd, type_desc_hdr_arr,
+                                             &ref_gen_code, &primary_info,
+                                             op);
+            if (res < 0) {
+                ret = res;
+                goto fini;
+            }
+            if (primary_info.have_info) {
+                printf("  Primary enclosure logical identifier (hex): ");
+                for (j = 0; j < 8; ++j)
+                    printf("%02x", primary_info.enc_log_id[j]);
+                printf("\n");
+            }
+            ses_enc_status_dp(type_desc_hdr_arr, res, ref_gen_code,
+                              resp, resp_len, op);
+            break;
+        case DPC_HELP_TEXT:
+            printf("Help text diagnostic page (for primary "
+                   "subenclosure):\n");
+            if (resp_len > 4)
+                printf("  %.*s\n", resp_len - 4, resp + 4);
+            else
+                printf("  <empty>\n");
+            break;
+        case DPC_STRING:
+            printf("String In diagnostic page (for primary "
+                   "subenclosure):\n");
+            if (resp_len > 4)
+                dStrHex((const char *)(resp + 4), resp_len - 4, 0);
+            else
+                printf("  <empty>\n");
+            break;
+        case DPC_THRESHOLD:
+            res = populate_type_desc_hdr_arr(sg_fd, type_desc_hdr_arr,
+                                             &ref_gen_code, &primary_info,
+                                             op);
+            if (res < 0) {
+                ret = res;
+                goto fini;
+            }
+            if (primary_info.have_info) {
+                printf("  Primary enclosure logical identifier (hex): ");
+                for (j = 0; j < 8; ++j)
+                    printf("%02x", primary_info.enc_log_id[j]);
+                printf("\n");
+            }
+            ses_threshold_sdg(type_desc_hdr_arr, res, ref_gen_code,
+                              resp, resp_len, op);
+            break;
+        case DPC_ELEM_DESC:
+            res = populate_type_desc_hdr_arr(sg_fd, type_desc_hdr_arr,
+                                             &ref_gen_code, &primary_info,
+                                             op);
+            if (res < 0) {
+                ret = res;
+                goto fini;
+            }
+            if (primary_info.have_info) {
+                printf("  Primary enclosure logical identifier (hex): ");
+                for (j = 0; j < 8; ++j)
+                    printf("%02x", primary_info.enc_log_id[j]);
+                printf("\n");
+            }
+            ses_element_desc_sdg(type_desc_hdr_arr, res, ref_gen_code,
+                                 resp, resp_len, op);
+            break;
+        case DPC_SHORT_ENC_STATUS:
+            printf("Short enclosure status diagnostic page, "
+                   "status=0x%x\n", resp[1]);
+            break;
+        case DPC_ENC_BUSY:
+            printf("Enclosure Busy diagnostic page, "
+                   "busy=%d [vendor specific=0x%x]\n",
+                   resp[1] & 1, (resp[1] >> 1) & 0xff);
+            break;
+        case DPC_ADD_ELEM_STATUS:
+            res = populate_type_desc_hdr_arr(sg_fd, type_desc_hdr_arr,
+                                             &ref_gen_code, &primary_info,
+                                             op);
+            if (res < 0) {
+                ret = res;
+                goto fini;
+            }
+            if (primary_info.have_info) {
+                printf("  Primary enclosure logical identifier (hex): ");
+                for (j = 0; j < 8; ++j)
+                    printf("%02x", primary_info.enc_log_id[j]);
+                printf("\n");
+            }
+            ses_additional_elem_sdg(type_desc_hdr_arr, res, ref_gen_code,
+                                    resp, resp_len, op);
+            break;
+        case DPC_SUBENC_HELP_TEXT:
+            ses_subenc_help_sdg(resp, resp_len);
+            break;
+        case DPC_SUBENC_STRING:
+            ses_subenc_string_sdg(resp, resp_len);
+            break;
+        case DPC_SUPPORTED_SES:
+            ses_supported_pages_sdg("Supported SES diagnostic pages",
+                                    resp, resp_len);
+            break;
+        case DPC_DOWNLOAD_MICROCODE:
+            ses_download_code_sdg(resp, resp_len);
+            break;
+        case DPC_SUBENC_NICKNAME:
+            ses_subenc_nickname_sdg(resp, resp_len);
+            break;
+        default:
+            printf("Cannot decode response from diagnostic "
+                   "page: %s\n", (cp ? cp : "<unknown>"));
+            dStrHex((const char *)resp, resp_len, 0);
+        }
+    }
+    ret = 0;
+
+fini:
+    if (resp)
+        free(resp);
+    return ret;
+}
+
+static void
+devslotnum_and_sasaddr(struct join_row_t * jrp, unsigned char * ae_ucp)
+{
+    int m;
+
+    if ((0 == jrp) || (0 == ae_ucp) || (0 == (0x10 & ae_ucp[0])))
+        return; /* sanity and expect EIP=1 */
+    switch (0xf & ae_ucp[0]) {
+    case TPROTO_FCP:
+        jrp->dev_slot_num = ae_ucp[7];
+        break;
+    case TPROTO_SAS:
+        if (0 == (0xc0 & ae_ucp[5])) {
+            /* only for device slot and array device slot elements */
+            jrp->dev_slot_num = ae_ucp[7];
+            if (ae_ucp[4] > 0) {        /* number of phys */
+                /* Use the first phy's "SAS ADDRESS" field */
+                for (m = 0; m < 8; ++m)
+                    jrp->sas_addr[m] = ae_ucp[(4 + 4 + 12) + m];
+            }
+        }
+        break;
+    case TPROTO_PCIE:
+        jrp->dev_slot_num = ae_ucp[7];
+        break;
+    default:
+        ;
+    }
+}
+
+/* Fetch Configuration, Enclosure Status, Element Descriptor, Additional
+ * Element Status and optionally Threshold In pages, place in static arrays.
+ * Collate (join) overall and individual elements into the static join_arr[].
+ * Returns 0 for success, any other return value is an error. */
+static int
+join_work(int sg_fd, struct opts_t * op, int display)
+{
+    int k, j, res, num_t_hdrs, elem_ind, ei, desc_len, dn_len;
+    int et4aes, broken_ei, ei2, got1, jr_max_ind, mlen;
+    uint32_t ref_gen_code, gen_code;
+    struct join_row_t * jrp;
+    struct join_row_t * jr2p;
+    unsigned char * es_ucp;
+    unsigned char * ed_ucp;
+    unsigned char * ae_ucp;
+    unsigned char * t_ucp;
+    /* const unsigned char * es_last_ucp; */
+    /* const unsigned char * ed_last_ucp; */
+    const unsigned char * ae_last_ucp;
+    /* const unsigned char * t_last_ucp; */
+    const char * cp;
+    const char * enc_state_changed = "  <<state of enclosure changed, "
+                                     "please try again>>\n";
+    const struct type_desc_hdr_t * tdhp;
+    struct enclosure_info primary_info;
+    char b[64];
+
+    memset(&primary_info, 0, sizeof(primary_info));
+    num_t_hdrs = populate_type_desc_hdr_arr(sg_fd, type_desc_hdr_arr,
+                                            &ref_gen_code, &primary_info,
+                                            op);
+    if (num_t_hdrs < 0)
+        return num_t_hdrs;
+    if (display && primary_info.have_info) {
+        printf("  Primary enclosure logical identifier (hex): ");
+        for (j = 0; j < 8; ++j)
+            printf("%02x", primary_info.enc_log_id[j]);
+        printf("\n");
+    }
+    mlen = sizeof(enc_stat_rsp);
+    if (mlen > op->maxlen)
+        mlen = op->maxlen;
+    res = do_rec_diag(sg_fd, DPC_ENC_STATUS, enc_stat_rsp, mlen, op,
+                      &enc_stat_rsp_len);
+    if (res)
+        return res;
+    if (enc_stat_rsp_len < 8) {
+        pr2serr("Enclosure Status response too short\n");
+        return -1;
+    }
+    gen_code = sg_get_unaligned_be32(enc_stat_rsp + 4);
+    if (ref_gen_code != gen_code) {
+        pr2serr("%s", enc_state_changed);
+        return -1;
+    }
+    es_ucp = enc_stat_rsp + 8;
+    /* es_last_ucp = enc_stat_rsp + enc_stat_rsp_len - 1; */
+
+    mlen = sizeof(elem_desc_rsp);
+    if (mlen > op->maxlen)
+        mlen = op->maxlen;
+    res = do_rec_diag(sg_fd, DPC_ELEM_DESC, elem_desc_rsp, mlen, op,
+                      &elem_desc_rsp_len);
+    if (0 == res) {
+        if (elem_desc_rsp_len < 8) {
+            pr2serr("Element Descriptor response too short\n");
+            return -1;
+        }
+        gen_code = sg_get_unaligned_be32(elem_desc_rsp + 4);
+        if (ref_gen_code != gen_code) {
+            pr2serr("%s", enc_state_changed);
+            return -1;
+        }
+        ed_ucp = elem_desc_rsp + 8;
+        /* ed_last_ucp = elem_desc_rsp + elem_desc_rsp_len - 1; */
+    } else {
+        elem_desc_rsp_len = 0;
+        ed_ucp = NULL;
+        res = 0;
+        if (op->verbose)
+            pr2serr("  Element Descriptor page not available\n");
+    }
+
+    if (display || (DPC_ADD_ELEM_STATUS == op->page_code) ||
+        (op->dev_slot_num >= 0) || saddr_non_zero(op->sas_addr)) {
+        mlen = sizeof(add_elem_rsp);
+        if (mlen > op->maxlen)
+            mlen = op->maxlen;
+        res = do_rec_diag(sg_fd, DPC_ADD_ELEM_STATUS, add_elem_rsp, mlen, op,
+                          &add_elem_rsp_len);
+        if (0 == res) {
+            if (add_elem_rsp_len < 8) {
+                pr2serr("Additional Element Status response too short\n");
+                return -1;
+            }
+            gen_code = sg_get_unaligned_be32(add_elem_rsp + 4);
+            if (ref_gen_code != gen_code) {
+                pr2serr("%s", enc_state_changed);
+                return -1;
+            }
+            ae_ucp = add_elem_rsp + 8;
+            ae_last_ucp = add_elem_rsp + add_elem_rsp_len - 1;
+            if (op->eiioe_auto && (add_elem_rsp_len > 11)) {
+                /* heuristic: if first element index in this page is 1
+                 * then act as if the EIIOE bit is set. */
+                if ((ae_ucp[0] & 0x10) && (1 == ae_ucp[3]))
+                    op->eiioe_force = 1;
+            }
+        } else {
+            add_elem_rsp_len = 0;
+            ae_ucp = NULL;
+            ae_last_ucp = NULL;
+            res = 0;
+            if (op->verbose)
+                pr2serr("  Additional Element Status page not available\n");
+        }
+    } else {
+        ae_ucp = NULL;
+        ae_last_ucp = NULL;
+    }
+
+    if ((op->do_join > 1) ||
+        ((0 == display) && (DPC_THRESHOLD == op->page_code))) {
+        mlen = sizeof(threshold_rsp);
+        if (mlen > op->maxlen)
+            mlen = op->maxlen;
+        res = do_rec_diag(sg_fd, DPC_THRESHOLD, threshold_rsp, mlen, op,
+                          &threshold_rsp_len);
+        if (0 == res) {
+            if (threshold_rsp_len < 8) {
+                pr2serr("Threshold In response too short\n");
+                return -1;
+            }
+            gen_code = sg_get_unaligned_be32(threshold_rsp + 4);
+            if (ref_gen_code != gen_code) {
+                pr2serr("%s", enc_state_changed);
+                return -1;
+            }
+            t_ucp = threshold_rsp + 8;
+            /* t_last_ucp = threshold_rsp + threshold_rsp_len - 1; */
+        } else {
+            threshold_rsp_len = 0;
+            t_ucp = NULL;
+            res = 0;
+            if (op->verbose)
+                pr2serr("  Threshold In page not available\n");
+        }
+    } else {
+        threshold_rsp_len = 0;
+        t_ucp = NULL;
+    }
+
+    jrp = join_arr;
+    tdhp = type_desc_hdr_arr;
+    jr_max_ind = 0;
+    for (k = 0, ei = 0, ei2 = 0; k < num_t_hdrs; ++k, ++tdhp) {
+        jrp->el_ind_th = k;
+        jrp->el_ind_indiv = -1;
+        jrp->etype = tdhp->etype;
+        jrp->ei_asc = -1;
+        et4aes = active_et_aesp(tdhp->etype);
+        jrp->ei_asc2 = -1;
+        jrp->se_id = tdhp->se_id;
+        /* check es_ucp < es_last_ucp still in range */
+        jrp->enc_statp = es_ucp;
+        es_ucp += 4;
+        jrp->elem_descp = ed_ucp;
+        if (ed_ucp)
+            ed_ucp += sg_get_unaligned_be16(ed_ucp + 2) + 4;
+        jrp->add_elem_statp = NULL;
+        jrp->thresh_inp = t_ucp;
+        jrp->dev_slot_num = -1;
+        /* assume sas_addr[8] zeroed since it's static file scope */
+        if (t_ucp)
+            t_ucp += 4;
+        ++jrp;
+        for (j = 0, elem_ind = 0; j < tdhp->num_elements;
+             ++j, ++jrp, ++elem_ind) {
+            if (jrp >= join_arr_lastp)
+                break;
+            jrp->el_ind_th = k;
+            jrp->el_ind_indiv = elem_ind;
+            jrp->ei_asc = ei++;
+            if (et4aes)
+                jrp->ei_asc2 = ei2++;
+            else
+                jrp->ei_asc2 = -1;
+            jrp->etype = tdhp->etype;
+            jrp->se_id = tdhp->se_id;
+            jrp->enc_statp = es_ucp;
+            es_ucp += 4;
+            jrp->elem_descp = ed_ucp;
+            if (ed_ucp)
+                ed_ucp += sg_get_unaligned_be16(ed_ucp + 2) + 4;
+            jrp->thresh_inp = t_ucp;
+            jrp->dev_slot_num = -1;
+            /* assume sas_addr[8] zeroed since it's static file scope */
+            if (t_ucp)
+                t_ucp += 4;
+            jrp->add_elem_statp = NULL;
+            ++jr_max_ind;
+        }
+        if (jrp >= join_arr_lastp) {
+            ++k;
+            break;      /* leave last row all zeros */
+        }
+    }
+
+    broken_ei = 0;
+    if (ae_ucp) {
+        int eip, eiioe;
+        int aes_i = 0;
+        int get_out = 0;
+
+        jrp = join_arr;
+        tdhp = type_desc_hdr_arr;
+        for (k = 0; k < num_t_hdrs; ++k, ++tdhp) {
+            if (active_et_aesp(tdhp->etype)) {
+                /* only consider element types that AES element are permiited
+                 * to refer to, then loop over those number of elements */
+                for (j = 0; j < tdhp->num_elements; ++j) {
+                    if ((ae_ucp + 1) > ae_last_ucp) {
+                        get_out = 1;
+                        if (op->verbose || op->warn)
+                            pr2serr("warning: %s: off end of ae page\n",
+                                    __func__);
+                        break;
+                    }
+                    eip = !!(ae_ucp[0] & 0x10); /* element index present */
+                    if (eip)
+                        eiioe = op->eiioe_force ? 1 : (ae_ucp[2] & 1);
+                    else
+                        eiioe = 0;
+                    if (eip && eiioe) {
+                        ei = ae_ucp[3];
+                        jr2p = join_arr + ei;
+                        if ((ei >= jr_max_ind) || (NULL == jr2p->enc_statp)) {
+                            get_out = 1;
+                            pr2serr("%s: oi=%d, ei=%d [max_ind=%d], eiioe=1 "
+                                    "not in join_arr\n", __func__, k, ei,
+                                    jr_max_ind);
+                            break;
+                        }
+                        devslotnum_and_sasaddr(jr2p, ae_ucp);
+                        if (jr2p->add_elem_statp) {
+                            if (op->warn || op->verbose)
+                                pr2serr("warning: aes slot busy [oi=%d, "
+                                        "ei=%d, aes_i=%d]\n", k, ei, aes_i);
+                        } else
+                            jr2p->add_elem_statp = ae_ucp;
+                    } else if (eip) {     /* and EIIOE=0 */
+                        ei = ae_ucp[3];
+try_again:
+                        for (jr2p = join_arr; jr2p->enc_statp; ++jr2p) {
+                            if (broken_ei) {
+                                if (ei == jr2p->ei_asc2)
+                                    break;
+                            } else {
+                                if (ei == jr2p->ei_asc)
+                                    break;
+                            }
+                        }
+                        if (NULL == jr2p->enc_statp) {
+                            get_out = 1;
+                            pr2serr("warning: %s: oi=%d, ei=%d (broken_ei=%d) "
+                                    "not in join_arr\n", __func__, k, ei,
+                                    broken_ei);
+                            break;
+                        }
+                        if (! active_et_aesp(jr2p->etype)) {
+                            /* broken_ei must be 0 for that to be false */
+                            ++broken_ei;
+                            goto try_again;
+                        }
+                        devslotnum_and_sasaddr(jr2p, ae_ucp);
+                        if (jr2p->add_elem_statp) {
+                            if (op->warn || op->verbose)
+                                pr2serr("warning: aes slot busy [oi=%d, "
+                                        "ei=%d, aes_i=%d]\n", k, ei, aes_i);
+                        } else
+                            jr2p->add_elem_statp = ae_ucp;
+                    } else {    /* EIP=0 */
+                        while (jrp->enc_statp && ((-1 == jrp->el_ind_indiv) ||
+                                                  jrp->add_elem_statp))
+                            ++jrp;
+                        if (NULL == jrp->enc_statp) {
+                            get_out = 1;
+                            pr2serr("warning: %s: join_arr has no space for "
+                                    "ae\n", __func__);
+                            break;
+                        }
+                        jrp->add_elem_statp = ae_ucp;
+                        ++jrp;
+                    }
+                    ae_ucp += ae_ucp[1] + 2;
+                    ++aes_i;
+                }
+            } else {    /* element type not relevant to ae status */
+                /* step over overall and individual elements */
+                for (j = 0; j <= tdhp->num_elements; ++j, ++jrp) {
+                    if (NULL == jrp->enc_statp) {
+                        get_out = 1;
+                        pr2serr("warning: %s: join_arr has no space\n",
+                                __func__);
+                        break;
+                    }
+                }
+            }
+            if (get_out)
+                break;
+        }
+    }
+
+    if (op->verbose > 3) {
+        jrp = join_arr;
+        for (k = 0; ((k < MX_JOIN_ROWS) && jrp->enc_statp); ++k, ++jrp) {
+            pr2serr("el_ind_th=%d el_ind_indiv=%d etype=%d se_id=%d ei=%d "
+                    "ei2=%d dsn=%d sa=0x", jrp->el_ind_th, jrp->el_ind_indiv,
+                    jrp->etype, jrp->se_id, jrp->ei_asc, jrp->ei_asc2,
+                    jrp->dev_slot_num);
+            if (saddr_non_zero(jrp->sas_addr)) {
+                for (j = 0; j < 8; ++j)
+                    pr2serr("%02x", jrp->sas_addr[j]);
+            } else
+                pr2serr("0");
+            pr2serr(" %s %s %s %s\n", (jrp->enc_statp ? "ES" : ""),
+                    (jrp->elem_descp ? "ED" : ""),
+                    (jrp->add_elem_statp ? "AES" : ""),
+                    (jrp->thresh_inp ? "TI" : ""));
+        }
+        pr2serr(">> elements in join_arr: %d, broken_ei=%d\n", k, broken_ei);
+    }
+
+    if (! display)      /* probably wanted join_arr[] built only */
+        return 0;
+
+    /* Display contents of join_arr */
+    dn_len = op->desc_name ? (int)strlen(op->desc_name) : 0;
+    for (k = 0, jrp = join_arr, got1 = 0;
+         ((k < MX_JOIN_ROWS) && jrp->enc_statp); ++k, ++jrp) {
+        if (op->ind_given) {
+            if (op->ind_th != jrp->el_ind_th)
+                continue;
+            if (op->ind_indiv != jrp->el_ind_indiv)
+                continue;
+        }
+        ed_ucp = jrp->elem_descp;
+        if (op->desc_name) {
+            if (NULL == ed_ucp)
+                continue;
+            desc_len = sg_get_unaligned_be16(ed_ucp + 2);
+            /* some element descriptor strings have trailing NULLs and
+             * count them in their length; adjust */
+            while (desc_len && ('\0' == ed_ucp[4 + desc_len - 1]))
+                --desc_len;
+            if (desc_len != dn_len)
+                continue;
+            if (0 != strncmp(op->desc_name, (const char *)(ed_ucp + 4),
+                             desc_len))
+                continue;
+        } else if (op->dev_slot_num >= 0) {
+            if (op->dev_slot_num != jrp->dev_slot_num)
+                continue;
+        } else if (saddr_non_zero(op->sas_addr)) {
+            for (j = 0; j < 8; ++j) {
+                if (op->sas_addr[j] != jrp->sas_addr[j])
+                    break;
+            }
+            if (j < 8)
+                continue;
+        }
+        ++got1;
+        if ((op->do_filter > 1) && (1 != (0xf & jrp->enc_statp[0])))
+            continue;   /* when '-ff' and status!=OK, skip */
+        cp = find_element_tname(jrp->etype, b, sizeof(b));
+        if (ed_ucp) {
+            desc_len = sg_get_unaligned_be16(ed_ucp + 2) + 4;
+            if (desc_len > 4)
+                printf("%.*s [%d,%d]  Element type: %s\n", desc_len - 4,
+                       (const char *)(ed_ucp + 4), jrp->el_ind_th,
+                       jrp->el_ind_indiv, cp);
+            else
+                printf("[%d,%d]  Element type: %s\n", jrp->el_ind_th,
+                       jrp->el_ind_indiv, cp);
+        } else
+            printf("[%d,%d]  Element type: %s\n", jrp->el_ind_th,
+                   jrp->el_ind_indiv, cp);
+        printf("  Enclosure Status:\n");
+        enc_status_helper("    ", jrp->enc_statp, jrp->etype, op);
+        if (jrp->add_elem_statp) {
+            printf("  Additional Element Status:\n");
+            ae_ucp = jrp->add_elem_statp;
+            desc_len = ae_ucp[1] + 2;
+            additional_elem_helper("    ",  ae_ucp, desc_len, jrp->etype, op);
+        }
+        if (jrp->thresh_inp) {
+            printf("  Threshold In:\n");
+            t_ucp = jrp->thresh_inp;
+            ses_threshold_helper("    ", t_ucp, jrp->etype, op);
+        }
+    }
+    if (0 == got1) {
+        if (op->ind_given)
+            printf("      >>> no match on --index=%d,%d\n", op->ind_th,
+                   op->ind_indiv);
+        else if (op->desc_name)
+            printf("      >>> no match on --descriptor=%s\n", op->desc_name);
+        else if (op->dev_slot_num >= 0)
+            printf("      >>> no match on --dev-slot-name=%d\n",
+                   op->dev_slot_num);
+        else if (saddr_non_zero(op->sas_addr)) {
+            printf("      >>> no match on --sas-addr=0x");
+            for (j = 0; j < 8; ++j)
+                printf("%02x", op->sas_addr[j]);
+            printf("\n");
+        }
+    }
+    return res;
+}
+
+static uint64_t
+get_big_endian(const unsigned char * from, int start_bit, int num_bits)
+{
+    uint64_t res;
+    int sbit_o1 = start_bit + 1;
+
+    res = (*from++ & ((1 << sbit_o1) - 1));
+    num_bits -= sbit_o1;
+    while (num_bits > 0) {
+        res <<= 8;
+        res |= *from++;
+        num_bits -= 8;
+    }
+    if (num_bits < 0)
+        res >>= (-num_bits);
+    return res;
+}
+
+static void
+set_big_endian(uint64_t val, unsigned char * to, int start_bit, int num_bits)
+{
+    int sbit_o1 = start_bit + 1;
+    int mask, num, k, x;
+
+    mask = (8 != sbit_o1) ? ((1 << sbit_o1) - 1) : 0xff;
+    k = start_bit - ((num_bits - 1) % 8);
+    if (0 != k)
+        val <<= ((k > 0) ? k : (8 + k));
+    num = (num_bits + 15 - sbit_o1) / 8;
+    for (k = 0; k < num; ++k) {
+        if ((sbit_o1 - num_bits) > 0)
+            mask &= ~((1 << (sbit_o1 - num_bits)) - 1);
+        if (k < (num - 1))
+            x = (val >> ((num - k - 1) * 8)) & 0xff;
+        else
+            x = val & 0xff;
+        to[k] = (to[k] & ~mask) | (x & mask);
+        mask = 0xff;
+        num_bits -= sbit_o1;
+        sbit_o1 = 8;
+    }
+}
+
+/* Returns 1 if strings equal (same length, characters same or only differ
+ * by case), else returns 0. Assumes 7 bit ASCII (English alphabet). */
+static int
+strcase_eq(const char * s1p, const char * s2p)
+{
+    int c1, c2;
+
+    do {
+        c1 = *s1p++;
+        c2 = *s2p++;
+        if (c1 != c2) {
+            if (c2 >= 'a')
+                c2 = toupper(c2);
+            else if (c1 >= 'a')
+                c1 = toupper(c1);
+            else
+                return 0;
+            if (c1 != c2)
+                return 0;
+        }
+    } while (c1);
+    return 1;
+}
+
+static int
+is_acronym_in_status_ctl(const struct tuple_acronym_val * tavp)
+{
+    const struct acronym2tuple * ap;
+
+    for (ap = ecs_a2t_arr; ap->acron; ++ ap) {
+        if (strcase_eq(tavp->acron, ap->acron))
+            break;
+    }
+    return (ap->acron ? 1 : 0);
+}
+
+static int
+is_acronym_in_threshold(const struct tuple_acronym_val * tavp)
+{
+    const struct acronym2tuple * ap;
+
+    for (ap = th_a2t_arr; ap->acron; ++ ap) {
+        if (strcase_eq(tavp->acron, ap->acron))
+            break;
+    }
+    return (ap->acron ? 1 : 0);
+}
+
+static int
+is_acronym_in_additional(const struct tuple_acronym_val * tavp)
+{
+    const struct acronym2tuple * ap;
+
+    for (ap = ae_sas_a2t_arr; ap->acron; ++ ap) {
+        if (strcase_eq(tavp->acron, ap->acron))
+            break;
+    }
+    return (ap->acron ? 1 : 0);
+}
+
+/* DPC_ENC_STATUS  DPC_ENC_CONTROL
+ * Do clear/get/set (cgs) on Enclosure Control/Status page. Return 0 for ok
+ * -2 for acronym not found, else -1 . */
+static int
+cgs_enc_ctl_stat(int sg_fd, const struct join_row_t * jrp,
+                 const struct tuple_acronym_val * tavp,
+                 const struct opts_t * op)
+{
+    int ret, len, s_byte, s_bit, n_bits, k;
+    uint64_t ui;
+    const struct acronym2tuple * ap;
+
+    if (NULL == tavp->acron) {
+        s_byte = tavp->start_byte;
+        s_bit = tavp->start_bit;
+        n_bits = tavp->num_bits;
+    }
+    if (tavp->acron) {
+        for (ap = ecs_a2t_arr; ap->acron; ++ ap) {
+            if (((jrp->etype == ap->etype) || (-1 == ap->etype)) &&
+                strcase_eq(tavp->acron, ap->acron))
+                break;
+        }
+        if (ap->acron) {
+            s_byte = ap->start_byte;
+            s_bit = ap->start_bit;
+            n_bits = ap->num_bits;
+        } else {
+            if (-1 != ap->etype) {
+                for (ap = ecs_a2t_arr; ap->acron; ++ap) {
+                    if (0 == strcase_eq(tavp->acron, ap->acron)) {
+                        pr2serr(">>> Found %s acronym but not for element "
+                                "type %d\n", tavp->acron, jrp->etype);
+                        break;
+                    }
+                }
+            }
+            return -2;
+        }
+    }
+    if (op->verbose > 1)
+        pr2serr("  s_byte=%d, s_bit=%d, n_bits=%d\n", s_byte, s_bit, n_bits);
+    if (op->get_str) {
+        ui = get_big_endian(jrp->enc_statp + s_byte, s_bit, n_bits);
+        if (op->do_hex)
+            printf("0x%" PRIx64 "\n", ui);
+        else
+            printf("%" PRId64 "\n", (int64_t)ui);
+    } else {    /* --set or --clear */
+        if ((0 == op->mask_ign) && (jrp->etype < NUM_ETC)) {
+            if (op->verbose > 2)
+                pr2serr("Applying mask to element status [etc=%d] prior to "
+                        "modify then write\n", jrp->etype);
+            for (k = 0; k < 4; ++k)
+                jrp->enc_statp[k] &= ses3_element_cmask_arr[jrp->etype][k];
+        } else
+            jrp->enc_statp[0] &= 0x40;  /* keep PRDFAIL is set in byte 0 */
+        /* next we modify requested bit(s) */
+        set_big_endian((uint64_t)tavp->val,
+                       jrp->enc_statp + s_byte, s_bit, n_bits);
+        jrp->enc_statp[0] |= 0x80;  /* set SELECT bit */
+        if (op->byte1_given)
+            enc_stat_rsp[1] = op->byte1;
+        len = sg_get_unaligned_be16(enc_stat_rsp + 2) + 4;
+        ret = do_senddiag(sg_fd, 1, enc_stat_rsp, len, 1, op->verbose);
+        if (ret) {
+            pr2serr("couldn't send Enclosure Control page\n");
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/* DPC_THRESHOLD
+ * Do clear/get/set (cgs) on Threshold In/Out page. Return 0 for ok,
+ * -2 for acronym not found, else -1 . */
+static int
+cgs_threshold(int sg_fd, const struct join_row_t * jrp,
+              const struct tuple_acronym_val * tavp,
+              const struct opts_t * op)
+{
+    int ret, len, s_byte, s_bit, n_bits;
+    uint64_t ui;
+    const struct acronym2tuple * ap;
+
+    if (NULL == jrp->thresh_inp) {
+        pr2serr("No Threshold In/Out element available\n");
+        return -1;
+    }
+    if (NULL == tavp->acron) {
+        s_byte = tavp->start_byte;
+        s_bit = tavp->start_bit;
+        n_bits = tavp->num_bits;
+    }
+    if (tavp->acron) {
+        for (ap = th_a2t_arr; ap->acron; ++ap) {
+            if (((jrp->etype == ap->etype) || (-1 == ap->etype)) &&
+                strcase_eq(tavp->acron, ap->acron))
+                break;
+        }
+        if (ap->acron) {
+            s_byte = ap->start_byte;
+            s_bit = ap->start_bit;
+            n_bits = ap->num_bits;
+        } else
+            return -2;
+    }
+    if (op->get_str) {
+        ui = get_big_endian(jrp->thresh_inp + s_byte, s_bit, n_bits);
+        if (op->do_hex)
+            printf("0x%" PRIx64 "\n", ui);
+        else
+            printf("%" PRId64 "\n", (int64_t)ui);
+    } else {
+        set_big_endian((uint64_t)tavp->val,
+                       jrp->thresh_inp + s_byte, s_bit, n_bits);
+        if (op->byte1_given)
+            threshold_rsp[1] = op->byte1;
+        len = sg_get_unaligned_be16(threshold_rsp + 2) + 4;
+        ret = do_senddiag(sg_fd, 1, threshold_rsp, len, 1, op->verbose);
+        if (ret) {
+            pr2serr("couldn't send Threshold Out page\n");
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/* DPC_ADD_ELEM_STATUS
+ * Do get (cgs) on Additional element status page. Return 0 for ok,
+ * -2 for acronym not found, else -1 . */
+static int
+cgs_additional_el(const struct join_row_t * jrp,
+                  const struct tuple_acronym_val * tavp,
+                  const struct opts_t * op)
+{
+    int s_byte, s_bit, n_bits;
+    uint64_t ui;
+    const struct acronym2tuple * ap;
+
+    if (NULL == jrp->add_elem_statp) {
+        pr2serr("No additional element status element available\n");
+        return -1;
+    }
+    if (NULL == tavp->acron) {
+        s_byte = tavp->start_byte;
+        s_bit = tavp->start_bit;
+        n_bits = tavp->num_bits;
+    }
+    if (tavp->acron) {
+        for (ap = ae_sas_a2t_arr; ap->acron; ++ap) {
+            if (((jrp->etype == ap->etype) || (-1 == ap->etype)) &&
+                strcase_eq(tavp->acron, ap->acron))
+                break;
+        }
+        if (ap->acron) {
+            s_byte = ap->start_byte;
+            s_bit = ap->start_bit;
+            n_bits = ap->num_bits;
+        } else
+            return -2;
+    }
+    if (op->get_str) {
+        ui = get_big_endian(jrp->add_elem_statp + s_byte, s_bit, n_bits);
+        if (op->do_hex)
+            printf("0x%" PRIx64 "\n", ui);
+        else
+            printf("%" PRId64 "\n", (int64_t)ui);
+    } else {
+        pr2serr("--clear and --set not available for Additional Element "
+                "Status page\n");
+        return -1;
+    }
+    return 0;
+}
+
+/* Do --clear, --get or --set .
+ * Returns 0 for success, any other return value is an error. */
+static int
+ses_cgs(int sg_fd, const struct tuple_acronym_val * tavp,
+        struct opts_t * op)
+{
+    int ret, k, j, desc_len, dn_len, found;
+    const struct join_row_t * jrp;
+    const unsigned char * ed_ucp;
+    char b[64];
+
+    found = 0;
+    if (NULL == tavp->acron) {
+        if (! op->page_code_given)
+            op->page_code = DPC_ENC_CONTROL;
+        ++found;
+    } else if (is_acronym_in_status_ctl(tavp)) {
+        op->page_code = DPC_ENC_CONTROL;
+        ++found;
+    } else if (is_acronym_in_threshold(tavp)) {
+        op->page_code = DPC_THRESHOLD;
+        ++found;
+    } else if (is_acronym_in_additional(tavp)) {
+        op->page_code = DPC_ADD_ELEM_STATUS;
+        ++found;
+    }
+    if (! found) {
+        pr2serr("acroynm %s not found (try '-ee' option)\n", tavp->acron);
+        return -1;
+    }
+    ret = join_work(sg_fd, op, 0);
+    if (ret)
+        return ret;
+    dn_len = op->desc_name ? (int)strlen(op->desc_name) : 0;
+    for (k = 0, jrp = join_arr; ((k < MX_JOIN_ROWS) && jrp->enc_statp);
+         ++k, ++jrp) {
+        if (op->ind_given) {
+            if (op->ind_th != jrp->el_ind_th)
+                continue;
+            if (op->ind_indiv != jrp->el_ind_indiv)
+                continue;
+        } else if (op->desc_name) {
+            ed_ucp = jrp->elem_descp;
+            if (NULL == ed_ucp)
+                continue;
+            desc_len = sg_get_unaligned_be16(ed_ucp + 2);
+            /* some element descriptor strings have trailing NULLs and
+             * count them; adjust */
+            while (desc_len && ('\0' == ed_ucp[4 + desc_len - 1]))
+                --desc_len;
+            if (desc_len != dn_len)
+                continue;
+            if (0 != strncmp(op->desc_name, (const char *)(ed_ucp + 4),
+                             desc_len))
+                continue;
+        } else if (op->dev_slot_num >= 0) {
+            if (op->dev_slot_num != jrp->dev_slot_num)
+                continue;
+        } else if (saddr_non_zero(op->sas_addr)) {
+            for (j = 0; j < 8; ++j) {
+                if (op->sas_addr[j] != jrp->sas_addr[j])
+                    break;
+            }
+            if (j < 8)
+                continue;
+        }
+        if (DPC_ENC_CONTROL == op->page_code)
+            ret = cgs_enc_ctl_stat(sg_fd, jrp, tavp, op);
+        else if (DPC_THRESHOLD == op->page_code)
+            ret = cgs_threshold(sg_fd, jrp, tavp, op);
+        else if (DPC_ADD_ELEM_STATUS == op->page_code)
+            ret = cgs_additional_el(jrp, tavp, op);
+        else {
+            pr2serr("page %s not supported for cgs\n",
+                    find_element_tname(op->page_code, b, sizeof(b)));
+            ret = -1;
+        }
+        if (ret)
+            return ret;
+        break;
+    }
+    if ((NULL == jrp->enc_statp) || (k >= MX_JOIN_ROWS)) {
+        if (op->desc_name)
+            pr2serr("descriptor name: %s not found (check the 'ed' page "
+                    "[0x7])\n", op->desc_name);
+        else if (op->dev_slot_num >= 0)
+            pr2serr("device slot number: %d not found\n", op->dev_slot_num);
+        else if (saddr_non_zero(op->sas_addr))
+            pr2serr("SAS address not found\n");
+        else
+            pr2serr("index: %d,%d not found\n", op->ind_th, op->ind_indiv);
+        return -1;
+    }
+    return 0;
+}
+
+/* Called when '--nickname=SEN' given. First calls status page to fetch
+ * the generation code. Returns 0 for success, any other return value is
+ * an error. */
+static int
+ses_set_nickname(int sg_fd, struct opts_t * op)
+{
+    int res, len;
+    int resp_len = 0;
+    unsigned char b[64];
+    const int control_plen = 0x24;
+
+    memset(b, 0, sizeof(b));
+    /* Only after the generation code, offset 4 for 4 bytes */
+    res = do_rec_diag(sg_fd, DPC_SUBENC_NICKNAME, b, 8, op, &resp_len);
+    if (res) {
+        pr2serr("%s: Subenclosure nickname status page, res=%d\n", __func__,
+                res);
+        return -1;
+    }
+    if (resp_len < 8) {
+        pr2serr("%s: Subenclosure nickname status page, response length too "
+                "short: %d\n", __func__, resp_len);
+        return -1;
+    }
+    if (op->verbose) {
+        uint32_t gc;
+
+        gc = sg_get_unaligned_be32(b + 4);
+        pr2serr("%s: generation code from status page: %" PRIu32 "\n",
+                __func__, gc);
+    }
+    b[0] = (unsigned char)DPC_SUBENC_NICKNAME;  /* just in case */
+    b[1] = (unsigned char)op->seid;
+    sg_put_unaligned_be16((uint16_t)control_plen, b + 2);
+    len = strlen(op->nickname_str);
+    if (len > 32)
+        len = 32;
+    memcpy(b + 8, op->nickname_str, len);
+    return do_senddiag(sg_fd, 1, b, control_plen + 4, 1, op->verbose);
+}
+
+static void
+enumerate_diag_pages(void)
+{
+    const struct diag_page_code * pcdp;
+    const struct diag_page_abbrev * ap;
+    int got1;
+
+    printf("Diagnostic pages, followed by abbreviation(s) then page code:\n");
+    for (pcdp = dpc_arr; pcdp->desc; ++pcdp) {
+        printf("    %s  [", pcdp->desc);
+        for (ap = dp_abbrev, got1 = 0; ap->abbrev; ++ap) {
+            if (ap->page_code == pcdp->page_code) {
+                printf("%s%s", (got1 ? "," : ""), ap->abbrev);
+                ++got1;
+            }
+        }
+        printf("] [0x%x]\n", pcdp->page_code);
+    }
+}
+
+/* Output from --enumerate or --list option. Note that the output is
+ * different when the option is given twice. */
+static void
+enumerate_work(const struct opts_t * op)
+{
+    int num;
+    const struct element_type_t * etp;
+    const struct acronym2tuple * ap;
+    char b[64];
+    char a[160];
+    const char * cp;
+
+    if (op->dev_name)
+        printf(">>> DEVICE %s ignored when --%s option given.\n",
+               op->dev_name, (op->do_list ? "list" : "enumerate"));
+    num = op->enumerate + op->do_list;
+    if (num < 2) {
+        enumerate_diag_pages();
+        printf("\nSES element type names, followed by abbreviation and "
+               "element type code:\n");
+        for (etp = element_type_arr; etp->desc; ++etp)
+            printf("    %s  [%s] [0x%x]\n", etp->desc, etp->abbrev,
+                   etp->elem_type_code);
+    } else {
+        /* command line has multiple --enumerate and/or --list options */
+        printf("--clear, --get, --set acronyms for Enclosure Status/Control "
+               "['es' or 'ec'] page:\n");
+        for (ap = ecs_a2t_arr; ap->acron; ++ap) {
+            cp = (ap->etype < 0) ?
+                         "*" : find_element_tname(ap->etype, b, sizeof(b));
+            snprintf(a, sizeof(a), "  %s  [%s] [%d:%d:%d]", ap->acron,
+                     (cp ? cp : "??"), ap->start_byte, ap->start_bit,
+                     ap->num_bits);
+            if (ap->info)
+                printf("%-44s  %s\n", a, ap->info);
+            else
+                printf("%s\n", a);
+        }
+        printf("\n--clear, --get, --set acronyms for Threshold In/Out "
+               "['th'] page:\n");
+        for (ap = th_a2t_arr; ap->acron; ++ap) {
+            cp = (ap->etype < 0) ? "*" :
+                         find_element_tname(ap->etype, b, sizeof(b));
+            snprintf(a, sizeof(a), "  %s  [%s] [%d:%d:%d]", ap->acron,
+                     (cp ? cp : "??"), ap->start_byte, ap->start_bit,
+                     ap->num_bits);
+            if (ap->info)
+                printf("%-34s  %s\n", a, ap->info);
+            else
+                printf("%s\n", a);
+        }
+        printf("\n--get acronyms for Additional Element Status ['aes'] page "
+               "(SAS EIP=1):\n");
+        for (ap = ae_sas_a2t_arr; ap->acron; ++ap) {
+            cp = (ap->etype < 0) ? "*" :
+                        find_element_tname(ap->etype, b, sizeof(b));
+            snprintf(a, sizeof(a), "  %s  [%s] [%d:%d:%d]", ap->acron,
+                     (cp ? cp : "??"), ap->start_byte, ap->start_bit,
+                     ap->num_bits);
+            if (ap->info)
+                printf("%-34s  %s\n", a, ap->info);
+            else
+                printf("%s\n", a);
+        }
+    }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res;
+    char buff[128];
+    char b[80];
+    int pd_type = 0;
+    int have_cgs = 0;
+    int ret = 0;
+    struct sg_simple_inquiry_resp inq_resp;
+    const char * cp;
+    struct opts_t opts;
+    struct opts_t * op;
+    struct tuple_acronym_val tav;
+
+    op = &opts;
+    memset(op, 0, sizeof(*op));
+    res = cl_process(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_version) {
+        pr2serr("version: %s\n", version_str);
+        return 0;
+    }
+    if (op->do_help) {
+        usage(op->do_help);
+        return 0;
+    }
+    if (op->enumerate || op->do_list) {
+        enumerate_work(op);
+        return 0;
+    }
+    if (op->num_cgs) {
+        have_cgs = 1;
+        cp = op->clear_str ? op->clear_str :
+             (op->get_str ? op->get_str : op->set_str);
+        strncpy(buff, cp, sizeof(buff) - 1);
+        buff[sizeof(buff) - 1] = '\0';
+        if (parse_cgs_str(buff, &tav)) {
+            pr2serr("unable to decode STR argument to --clear, --get or "
+                    "--set\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->get_str && tav.val_str)
+            pr2serr("--get option ignoring =<val> at the end of STR "
+                    "argument\n");
+        if (! (op->ind_given || op->desc_name || (op->dev_slot_num >= 0) ||
+               saddr_non_zero(op->sas_addr))) {
+            pr2serr("with --clear, --get or --set option need either\n   "
+                    "--index, --descriptor, --dev-slot-num or --sas-addr\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (NULL == tav.val_str) {
+            if (op->clear_str)
+                tav.val = 0;
+            if (op->set_str)
+                tav.val = 1;
+        }
+        if (op->page_code_given && (DPC_ENC_STATUS != op->page_code) &&
+            (DPC_THRESHOLD != op->page_code) &&
+            (DPC_ADD_ELEM_STATUS != op->page_code)) {
+            pr2serr("--clear, --get or --set options only supported for the "
+                    "Enclosure\nControl/Status, Threshold In/Out and "
+                    "Additional Element Status pages\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+    if (op->verbose > 4)
+        pr2serr("Initial win32 SPT interface state: %s\n",
+                scsi_pt_win32_spt_state() ? "direct" : "indirect");
+    if (op->maxlen >= 16384)
+        scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
+#endif
+#endif
+    sg_fd = sg_cmds_open_device(op->dev_name, op->o_readonly, op->verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", op->dev_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (! (op->do_raw || have_cgs || (op->do_hex > 2))) {
+        if (sg_simple_inquiry(sg_fd, &inq_resp, 1, op->verbose)) {
+            pr2serr("%s doesn't respond to a SCSI INQUIRY\n", op->dev_name);
+            ret = SG_LIB_CAT_OTHER;
+            goto err_out;
+        } else {
+            printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor,
+                   inq_resp.product, inq_resp.revision);
+            pd_type = inq_resp.peripheral_type;
+            cp = sg_get_pdt_str(pd_type, sizeof(buff), buff);
+            if (0xd == pd_type) {
+                if (op->verbose)
+                    printf("    enclosure services device\n");
+            } else if (0x40 & inq_resp.byte_6)
+                printf("    %s device has EncServ bit set\n", cp);
+            else
+                printf("    %s device (not an enclosure)\n", cp);
+        }
+    }
+
+    if (op->nickname_str)
+        ret = ses_set_nickname(sg_fd, op);
+    else if (have_cgs)
+        ret = ses_cgs(sg_fd, &tav, op);
+    else if (op->do_join)
+        ret = join_work(sg_fd, op, 1);
+    else if (op->do_status)
+        ret = ses_process_status_page(sg_fd, op);
+    else { /* control page requested */
+        op->data_arr[0] = op->page_code;
+        op->data_arr[1] = op->byte1;
+        sg_put_unaligned_be16((uint16_t)op->arr_len, op->data_arr + 2);
+        switch (op->page_code) {
+        case DPC_ENC_CONTROL:  /* Enclosure Control diagnostic page [0x2] */
+            printf("Sending Enclosure Control [0x%x] page, with page "
+                   "length=%d bytes\n", op->page_code, op->arr_len);
+            ret = do_senddiag(sg_fd, 1, op->data_arr, op->arr_len + 4, 1,
+                              op->verbose);
+            if (ret) {
+                pr2serr("couldn't send Enclosure Control page\n");
+                goto err_out;
+            }
+            break;
+        case DPC_STRING:       /* String Out diagnostic page [0x4] */
+            printf("Sending String Out [0x%x] page, with page length=%d "
+                   "bytes\n", op->page_code, op->arr_len);
+            ret = do_senddiag(sg_fd, 1, op->data_arr, op->arr_len + 4, 1,
+                              op->verbose);
+            if (ret) {
+                pr2serr("couldn't send String Out page\n");
+                goto err_out;
+            }
+            break;
+        case DPC_THRESHOLD:       /* Threshold Out diagnostic page [0x5] */
+            printf("Sending Threshold Out [0x%x] page, with page length=%d "
+                   "bytes\n", op->page_code, op->arr_len);
+            ret = do_senddiag(sg_fd, 1, op->data_arr, op->arr_len + 4, 1,
+                              op->verbose);
+            if (ret) {
+                pr2serr("couldn't send Threshold Out page\n");
+                goto err_out;
+            }
+            break;
+        case DPC_ARRAY_CONTROL:   /* Array control diagnostic page [0x6] */
+            printf("Sending Array Control [0x%x] page, with page "
+                   "length=%d bytes\n", op->page_code, op->arr_len);
+            ret = do_senddiag(sg_fd, 1, op->data_arr, op->arr_len + 4, 1,
+                              op->verbose);
+            if (ret) {
+                pr2serr("couldn't send Array Control page\n");
+                goto err_out;
+            }
+            break;
+        case DPC_SUBENC_STRING: /* Subenclosure String Out page [0xc] */
+            printf("Sending Subenclosure String Out [0x%x] page, with page "
+                   "length=%d bytes\n", op->page_code, op->arr_len);
+            ret = do_senddiag(sg_fd, 1, op->data_arr, op->arr_len + 4, 1,
+                              op->verbose);
+            if (ret) {
+                pr2serr("couldn't send Subenclosure String Out page\n");
+                goto err_out;
+            }
+            break;
+        case DPC_DOWNLOAD_MICROCODE: /* Download Microcode Control [0xe] */
+            printf("Sending Download Microcode Control [0x%x] page, with "
+                   "page length=%d bytes\n", op->page_code, op->arr_len);
+            printf("  Perhaps it would be better to use the sg_ses_microcode "
+                   "utility\n");
+            ret = do_senddiag(sg_fd, 1, op->data_arr, op->arr_len + 4, 1,
+                              op->verbose);
+            if (ret) {
+                pr2serr("couldn't send Download Microcode Control page\n");
+                goto err_out;
+            }
+            break;
+        case DPC_SUBENC_NICKNAME: /* Subenclosure Nickname Control [0xf] */
+            printf("Sending Subenclosure Nickname Control [0x%x] page, with "
+                   "page length=%d bytes\n", op->page_code, op->arr_len);
+            ret = do_senddiag(sg_fd, 1, op->data_arr, op->arr_len + 4, 1,
+                              op->verbose);
+            if (ret) {
+                pr2serr("couldn't send Subenclosure Nickname Control page\n");
+                goto err_out;
+            }
+            break;
+        default:
+            pr2serr("Setting SES control page 0x%x not supported by this "
+                    "utility\n", op->page_code);
+            pr2serr("That can be done with the sg_senddiag utility with its "
+                    "'--raw=' option\n");
+            ret = SG_LIB_SYNTAX_ERROR;
+            break;
+        }
+    }
+
+err_out:
+    if (0 == op->do_status) {
+        sg_get_category_sense_str(ret, sizeof(b), b, op->verbose);
+        pr2serr("    %s\n", b);
+    }
+    if (ret && (0 == op->verbose))
+        pr2serr("Problem detected, try again with --verbose option for more "
+                "information\n");
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_ses_microcode.c b/sg3_utils/src/sg_ses_microcode.c
new file mode 100644
index 0000000..546d056
--- /dev/null
+++ b/sg3_utils/src/sg_ses_microcode.c
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2014-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+#include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
+#endif
+#endif
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/*
+ * This utility issues the SCSI SEND DIAGNOSTIC and RECEIVE DIAGNOSTIC
+ * RESULTS commands in order to send microcode to the given SES device.
+ */
+
+static const char * version_str = "1.03 20151219";    /* ses3r07 */
+
+#define ME "sg_ses_microcode: "
+#define MAX_XFER_LEN (128 * 1024 * 1024)
+#define DEF_XFER_LEN (8 * 1024 * 1024)
+#define DEF_DI_LEN (8 * 1024)
+#define EBUFF_SZ 256
+
+#define DPC_DOWNLOAD_MICROCODE 0xe
+
+struct opts_t {
+    int bpw;
+    int bpw_then_activate;
+    int mc_id;
+    int mc_len;
+    int mc_len_given;
+    int mc_mode;
+    int mc_non;
+    int mc_offset;
+    int mc_skip;
+    int mc_subenc;
+    int mc_tlen;
+    int verbose;
+};
+
+static struct option long_options[] = {
+    {"bpw", required_argument, 0, 'b'},
+    {"help", no_argument, 0, 'h'},
+    {"id", required_argument, 0, 'i'},
+    {"in", required_argument, 0, 'I'},
+    {"length", required_argument, 0, 'l'},
+    {"mode", required_argument, 0, 'm'},
+    {"non", no_argument, 0, 'N'},
+    {"offset", required_argument, 0, 'o'},
+    {"skip", required_argument, 0, 's'},
+    {"subenc", required_argument, 0, 'S'},
+    {"tlength", required_argument, 0, 't'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+            "sg_ses_microcode [--bpw=CS] [--help] [--id=ID] [--in=FILE]\n"
+            "                        [--length=LEN] [--mode=MO] "
+            "[--non]\n"
+            "                        [--offset=OFF] [--skip=SKIP] "
+            "[--subenc=SEID]\n"
+            "                        [--tlength=TLEN] [--verbose] "
+            "[--version]\n"
+            "                        DEVICE\n"
+            "  where:\n"
+            "    --bpw=CS|-b CS         CS is chunk size: bytes per send "
+            "diagnostic\n"
+            "                           command (def: 0 -> as many as "
+            "possible)\n"
+            "    --help|-h              print out usage message then exit\n"
+            "    --id=ID|-i ID          buffer identifier (0 (default) to "
+            "255)\n"
+            "    --in=FILE|-I FILE      read from FILE ('-I -' read "
+            "from stdin)\n"
+            "    --length=LEN|-l LEN    length in bytes to send; may be "
+            "deduced from\n"
+            "                           FILE\n"
+            "    --mode=MO|-m MO        download microcode mode, MO is "
+            "number or\n"
+            "                           acronym (def: 0 -> 'dmc_status')\n"
+            "    --non|-N               non-standard: bypass all receive "
+            "diagnostic\n"
+            "                           results commands except after check "
+            "condition\n"
+            "    --offset=OFF|-o OFF    buffer offset (unit: bytes, def: "
+            "0);\n"
+            "                           ignored if --bpw=CS given\n"
+            "    --skip=SKIP|-s SKIP    bytes in file FILE to skip before "
+            "reading\n"
+            "    --subenc=SEID|-S SEID     subenclosure identifier (def: 0 "
+            "(primary))\n"
+            "    --tlength=TLEN|-t TLEN    total length of firmware in "
+            "bytes\n"
+            "                              (def: 0). Only needed if "
+            "TLEN>LEN\n"
+            "    --verbose|-v           increase verbosity\n"
+            "    --version|-V           print version string and exit\n\n"
+            "Does one or more SCSI SEND DIAGNOSTIC followed by RECEIVE "
+            "DIAGNOSTIC\nRESULTS command sequences in order to download "
+            "microcode. Use '-m xxx'\nto list available modes. With only "
+            "DEVICE given, the Download Microcode\nStatus dpage is output.\n"
+          );
+}
+
+#define MODE_DNLD_STATUS        0
+#define MODE_DNLD_MC_OFFS       6
+#define MODE_DNLD_MC_OFFS_SAVE  7
+#define MODE_DNLD_MC_OFFS_DEFER 0x0E
+#define MODE_ACTIVATE_MC        0x0F
+
+struct mode_s {
+        const char *mode_string;
+        int   mode;
+        const char *comment;
+};
+
+static struct mode_s mode_arr[] = {
+    {"dmc_status", MODE_DNLD_STATUS, "report status of microcode "
+     "download"},
+    {"dmc_offs", MODE_DNLD_MC_OFFS, "download microcode with offsets "
+     "and activate"},
+    {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
+     "offsets, save and\n\t\t\t\tactivate"},
+    {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
+     "with offsets, save and\n\t\t\t\tdefer activation"},
+    {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
+    {NULL, 0, NULL},
+};
+
+
+static void
+print_modes(void)
+{
+    const struct mode_s * mp;
+
+    pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
+            "or symbolic:\n");
+    for (mp = mode_arr; mp->mode_string; ++mp) {
+        pr2serr(" %2d (0x%02x)  %-18s%s\n", mp->mode, mp->mode,
+                mp->mode_string, mp->comment);
+    }
+    pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred "
+            "microcode after a\nsuccessful multipart dmc_offs_defer mode "
+            "download.\n");
+}
+
+struct diag_page_code {
+    int page_code;
+    const char * desc;
+};
+
+/* An array of Download microcode status field values and descriptions */
+static struct diag_page_code mc_status_arr[] = {
+    {0x0, "No download microcode operation in progress"},
+    {0x1, "Download in progress, awaiting more"},
+    {0x2, "Download complete, updating storage"},
+    {0x3, "Updating storage with deferred microcode"},
+    {0x10, "Complete, no error, starting now"},
+    {0x11, "Complete, no error, start after hard reset or power cycle"},
+    {0x12, "Complete, no error, start after power cycle"},
+    {0x13, "Complete, no error, start after activate_mc, hard reset or "
+           "power cycle"},
+    {0x80, "Error, discarded, see additional status"},
+    {0x81, "Error, discarded, image error"},
+    {0x82, "Timeout, discarded"},
+    {0x83, "Internal error, need new microcode before reset"},
+    {0x84, "Internal error, need new microcode, reset safe"},
+    {0x85, "Unexpected activate_mc received"},
+    {0x1000, NULL},
+};
+
+static const char *
+get_mc_status_str(unsigned char status_val)
+{
+    const struct diag_page_code * mcsp;
+
+    for (mcsp = mc_status_arr; mcsp->desc; ++mcsp) {
+        if (status_val == mcsp->page_code)
+            return mcsp->desc;
+    }
+    return "";
+}
+
+/* DPC_DOWNLOAD_MICROCODE [0xe] */
+static void
+ses_download_code_sdg(const unsigned char * resp, int resp_len,
+                      uint32_t gen_code)
+{
+    int k, num_subs, num;
+    const unsigned char * ucp;
+    const char * cp;
+
+    printf("Download microcode status diagnostic page:\n");
+    if (resp_len < 8)
+        goto truncated;
+    num_subs = resp[1];  /* primary is additional one) */
+    num = (resp_len - 8) / 16;
+    if ((resp_len - 8) % 16)
+        pr2serr("Found %d Download microcode status descriptors, but there "
+                "is residual\n", num);
+    printf("  number of secondary subenclosures: %d\n", num_subs);
+    printf("  generation code: 0x%" PRIx32 "\n", gen_code);
+    ucp = resp + 8;
+    for (k = 0; k < num; ++k, ucp += 16) {
+        cp = (0 == ucp[1]) ? " [primary]" : "";
+        printf("   subenclosure identifier: %d%s\n", ucp[1], cp);
+        cp = get_mc_status_str(ucp[2]);
+        if (strlen(cp) > 0) {
+            printf("     download microcode status: %s [0x%x]\n", cp, ucp[2]);
+            printf("     download microcode additional status: 0x%x\n",
+                   ucp[3]);
+        } else
+            printf("     download microcode status: 0x%x [additional "
+                   "status: 0x%x]\n", ucp[2], ucp[3]);
+        printf("     download microcode maximum size: %" PRIu32 " bytes\n",
+               sg_get_unaligned_be32(ucp + 4));
+        printf("     download microcode expected buffer id: 0x%x\n", ucp[11]);
+        printf("     download microcode expected buffer id offset: %" PRIu32
+               "\n", sg_get_unaligned_be32(ucp + 12));
+    }
+    return;
+truncated:
+    pr2serr("    <<<download status: response too short>>>\n");
+    return;
+}
+
+struct dout_buff_t {
+    unsigned char * doutp;
+    int dout_len;
+};
+
+static int
+send_then_receive(int sg_fd, uint32_t gen_code, int off_off,
+                  const unsigned char * dmp, int dmp_len,
+                  struct dout_buff_t * wp, unsigned char * dip,
+                  int last, const struct opts_t * op)
+{
+    int do_len, rem, res, rsp_len, k, num, mc_status, verb;
+    int send_data = 0;
+    int ret = 0;
+    uint32_t rec_gen_code;
+    const unsigned char * ucp;
+    const char * cp;
+
+    verb = (op->verbose > 1) ? op->verbose - 1 : 0;
+    switch (op->mc_mode) {
+    case MODE_DNLD_MC_OFFS:
+    case MODE_DNLD_MC_OFFS_SAVE:
+    case MODE_DNLD_MC_OFFS_DEFER:
+        send_data = 1;
+        do_len = 24 + dmp_len;
+        rem = do_len % 4;
+        if (rem)
+            do_len += (4 - rem);
+        break;
+    case MODE_ACTIVATE_MC:
+        do_len = 24;
+        break;
+    default:
+        pr2serr("send_then_receive: unexpected mc_mode=0x%x\n", op->mc_mode);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (do_len > wp->dout_len) {
+        if (wp->doutp)
+            free(wp->doutp);
+        wp->doutp = (unsigned char *)malloc(do_len);
+        if (! wp->doutp) {
+            pr2serr("send_then_receive: unable to malloc %d bytes\n", do_len);
+            return SG_LIB_CAT_OTHER;
+        }
+        wp->dout_len = do_len;
+    }
+    memset(wp->doutp, 0, do_len);
+    wp->doutp[0] = DPC_DOWNLOAD_MICROCODE;
+    wp->doutp[1] = op->mc_subenc;
+    sg_put_unaligned_be16(do_len - 4, wp->doutp + 2);
+    sg_put_unaligned_be32(gen_code, wp->doutp + 4);
+    wp->doutp[8] = op->mc_mode;
+    wp->doutp[11] = op->mc_id;
+    if (send_data)
+        sg_put_unaligned_be32(op->mc_offset + off_off, wp->doutp + 12);
+    sg_put_unaligned_be32(op->mc_tlen, wp->doutp + 16);
+    sg_put_unaligned_be32(dmp_len, wp->doutp + 20);
+    if (send_data && (dmp_len > 0))
+        memcpy(wp->doutp + 24, dmp, dmp_len);
+    /* select long duration timeout (7200 seconds) */
+    res = sg_ll_send_diag(sg_fd, 0 /* sf_code */, 1 /* pf */, 0 /* sf */,
+                          0 /* devofl */, 0 /* unitofl */,
+                          1 /* long_duration */, wp->doutp, do_len,
+                          1 /* noisy */, verb);
+    if (op->mc_non) {
+        /* If non-standard, only call RDR after failed SD */
+        if (0 == res)
+            return 0;
+        /* If RDR error after SD error, prefer reporting SD error */
+        ret = res;
+    } else {
+        switch (op->mc_mode) {
+        case MODE_DNLD_MC_OFFS:
+        case MODE_DNLD_MC_OFFS_SAVE:
+            if (res)
+                return res;
+            else if (last)
+                return 0;   /* RDR after last may hit a device reset */
+            break;
+        case MODE_DNLD_MC_OFFS_DEFER:
+            if (res)
+                return res;
+            break;
+        case MODE_ACTIVATE_MC:
+            if (0 == res)
+                return 0;   /* RDR after ACTIVATE_MC may hit a device reset */
+            /* SD has failed, so do a RDR but return SD's error */
+            ret = res;
+            break;
+        default:
+            pr2serr("send_then_receive: mc_mode=0x%x\n", op->mc_mode);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    res = sg_ll_receive_diag(sg_fd, 1 /* pcv */, DPC_DOWNLOAD_MICROCODE, dip,
+                             DEF_DI_LEN, 1, verb);
+    if (res)
+        return ret ? ret : res;
+    rsp_len = sg_get_unaligned_be16(dip + 2) + 4;
+    if (rsp_len > DEF_DI_LEN) {
+        pr2serr("<<< warning response buffer too small [%d but need "
+                "%d]>>>\n", DEF_DI_LEN, rsp_len);
+        rsp_len = DEF_DI_LEN;
+    }
+    if (rsp_len < 8) {
+        pr2serr("Download microcode status dpage too short\n");
+        return ret ? ret : SG_LIB_CAT_OTHER;
+    }
+    rec_gen_code = sg_get_unaligned_be32(dip + 4);
+    if (rec_gen_code != gen_code)
+        pr2serr("gen_code changed from %" PRIu32 " to %" PRIu32
+                ", continuing but may fail\n", gen_code, rec_gen_code);
+    num = (rsp_len - 8) / 16;
+    if ((rsp_len - 8) % 16)
+        pr2serr("Found %d Download microcode status descriptors, but there "
+                "is residual\n", num);
+    ucp = dip + 8;
+    for (k = 0; k < num; ++k, ucp += 16) {
+        if ((unsigned int)op->mc_subenc == (unsigned int)ucp[1]) {
+            mc_status = ucp[2];
+            cp = get_mc_status_str(mc_status);
+            if ((mc_status >= 0x80) || op->verbose)
+                pr2serr("mc offset=%d: status: %s [0x%x, additional=0x%x]\n",
+                        off_off, cp, mc_status, ucp[3]);
+            if (op->verbose > 1)
+                pr2serr("  subenc_id=%d, expected_buffer_id=%d, "
+                        "expected_offset=0x%" PRIx32 "\n", ucp[1], ucp[11],
+                        sg_get_unaligned_be32(ucp + 12));
+            if (mc_status >= 0x80)
+                ret = ret ? ret : SG_LIB_CAT_OTHER;
+        }
+    }
+    return ret;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, len, k, n, got_stdin, is_reg, rsp_len, verb, last;
+    int infd = -1;
+    int do_help = 0;
+    const char * device_name = NULL;
+    const char * file_name = NULL;
+    unsigned char * dmp = NULL;
+    unsigned char * dip = NULL;
+    char * cp;
+    char ebuff[EBUFF_SZ];
+    struct stat a_stat;
+    struct dout_buff_t dout;
+    struct opts_t opts;
+    struct opts_t * op;
+    const struct mode_s * mp;
+    uint32_t gen_code = 0;
+    int ret = 0;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    memset(&dout, 0, sizeof(dout));
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "b:hi:I:l:m:No:s:S:t:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            op->bpw = sg_get_num(optarg);
+            if (op->bpw < 0) {
+                pr2serr("argument to '--bpw' should be in a positive "
+                        "number\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if ((cp = strchr(optarg, ','))) {
+                if (0 == strncmp("act", cp + 1, 3))
+                    ++op->bpw_then_activate;
+            }
+            break;
+        case 'h':
+        case '?':
+            ++do_help;
+            break;
+        case 'i':
+            op->mc_id = sg_get_num(optarg);
+            if ((op->mc_id < 0) || (op->mc_id > 255)) {
+                pr2serr("argument to '--id' should be in the range 0 to "
+                        "255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'I':
+            file_name = optarg;
+            break;
+        case 'l':
+            op->mc_len = sg_get_num(optarg);
+            if (op->mc_len < 0) {
+                pr2serr("bad argument to '--length'\n");
+                return SG_LIB_SYNTAX_ERROR;
+             }
+             op->mc_len_given = 1;
+             break;
+        case 'm':
+            if (isdigit(*optarg)) {
+                op->mc_mode = sg_get_num(optarg);
+                if ((op->mc_mode < 0) || (op->mc_mode > 255)) {
+                    pr2serr("argument to '--mode' should be in the range 0 "
+                            "to 255\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else {
+                len = strlen(optarg);
+                for (mp = mode_arr; mp->mode_string; ++mp) {
+                    if (0 == strncmp(mp->mode_string, optarg, len)) {
+                        op->mc_mode = mp->mode;
+                        break;
+                    }
+                }
+                if (! mp->mode_string) {
+                    print_modes();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+            break;
+        case 'N':
+            ++op->mc_non;
+            break;
+        case 'o':
+           op->mc_offset = sg_get_num(optarg);
+           if (op->mc_offset < 0) {
+                pr2serr("bad argument to '--offset'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if (0 != (op->mc_offset % 4)) {
+                pr2serr("'--offset' value needs to be a multiple of 4\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 's':
+           op->mc_skip = sg_get_num(optarg);
+           if (op->mc_skip < 0) {
+                pr2serr("bad argument to '--skip'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'S':
+           op->mc_subenc = sg_get_num(optarg);
+           if ((op->mc_subenc < 0) || (op->mc_subenc > 255)) {
+                pr2serr("expected argument to '--subenc' to be 0 to 255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 't':
+           op->mc_tlen = sg_get_num(optarg);
+           if (op->mc_tlen < 0) {
+                pr2serr("bad argument to '--tlength'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (do_help) {
+        if (do_help > 1) {
+            usage();
+            pr2serr("\n");
+            print_modes();
+        } else
+            usage();
+        return 0;
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if ((op->mc_len > 0) && (op->bpw > op->mc_len)) {
+        pr2serr("trim chunk size (CS) to be the same as LEN\n");
+        op->bpw = op->mc_len;
+    }
+
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+    if (op->verbose > 4)
+        pr2serr("Initial win32 SPT interface state: %s\n",
+                scsi_pt_win32_spt_state() ? "direct" : "indirect");
+    scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
+#endif
+#endif
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, op->verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (file_name && ((MODE_DNLD_STATUS == op->mc_mode) ||
+                      (MODE_ACTIVATE_MC == op->mc_mode)))
+        pr2serr("ignoring --in=FILE option\n");
+    else if (file_name) {
+        got_stdin = (0 == strcmp(file_name, "-")) ? 1 : 0;
+        if (got_stdin)
+            infd = STDIN_FILENO;
+        else {
+            if ((infd = open(file_name, O_RDONLY)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for reading", file_name);
+                perror(ebuff);
+                ret = SG_LIB_FILE_ERROR;
+                goto fini;
+            } else if (sg_set_binary_mode(infd) < 0)
+                perror("sg_set_binary_mode");
+        }
+        if ((0 == fstat(infd, &a_stat)) && S_ISREG(a_stat.st_mode)) {
+            is_reg = 1;
+            if (0 == op->mc_len) {
+                if (op->mc_skip >= a_stat.st_size) {
+                    pr2serr("skip exceeds file size of %d bytes\n",
+                            (int)a_stat.st_size);
+                    ret = SG_LIB_FILE_ERROR;
+                    goto fini;
+                }
+                op->mc_len = (int)(a_stat.st_size) - op->mc_skip;
+            }
+        } else {
+            is_reg = 0;
+            if (0 == op->mc_len)
+                op->mc_len = DEF_XFER_LEN;
+        }
+        if (op->mc_len > MAX_XFER_LEN) {
+            pr2serr("file size or requested length (%d) exceeds "
+                    "MAX_XFER_LEN of %d bytes\n", op->mc_len,
+                    MAX_XFER_LEN);
+            ret = SG_LIB_FILE_ERROR;
+            goto fini;
+        }
+        if (NULL == (dmp = (unsigned char *)malloc(op->mc_len))) {
+            pr2serr(ME "out of memory (to hold microcode)\n");
+            ret = SG_LIB_CAT_OTHER;
+            goto fini;
+        }
+        /* Don't remember why this is preset to 0xff, from write_buffer */
+        memset(dmp, 0xff, op->mc_len);
+        if (op->mc_skip > 0) {
+            if (! is_reg) {
+                if (got_stdin)
+                    pr2serr("Can't skip on stdin\n");
+                else
+                    pr2serr(ME "not a 'regular' file so can't apply skip\n");
+                ret = SG_LIB_FILE_ERROR;
+                goto fini;
+            }
+            if (lseek(infd, op->mc_skip, SEEK_SET) < 0) {
+                snprintf(ebuff,  EBUFF_SZ, ME "couldn't skip to "
+                         "required position on %s", file_name);
+                perror(ebuff);
+                ret = SG_LIB_FILE_ERROR;
+                goto fini;
+            }
+        }
+        res = read(infd, dmp, op->mc_len);
+        if (res < 0) {
+            snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
+                     file_name);
+            perror(ebuff);
+            ret = SG_LIB_FILE_ERROR;
+            goto fini;
+        }
+        if (res < op->mc_len) {
+            if (op->mc_len_given) {
+                pr2serr("tried to read %d bytes from %s, got %d bytes\n",
+                        op->mc_len, file_name, res);
+                pr2serr("pad with 0xff bytes and continue\n");
+            } else {
+                if (op->verbose) {
+                    pr2serr("tried to read %d bytes from %s, got %d "
+                            "bytes\n", op->mc_len, file_name, res);
+                    pr2serr("will send %d bytes", res);
+                    if ((op->bpw > 0) && (op->bpw < op->mc_len))
+                        pr2serr(", %d bytes per WRITE BUFFER command\n",
+                                op->bpw);
+                    else
+                        pr2serr("\n");
+                }
+                op->mc_len = res;
+            }
+        }
+        if (! got_stdin)
+            close(infd);
+        infd = -1;
+    } else if (! ((MODE_DNLD_STATUS == op->mc_mode) ||
+                  (MODE_ACTIVATE_MC == op->mc_mode))) {
+        pr2serr("need --in=FILE option with given mode\n");
+        ret = SG_LIB_SYNTAX_ERROR;
+        goto fini;
+    }
+    if (op->mc_tlen < op->mc_len)
+        op->mc_tlen = op->mc_len;
+    if (op->mc_non && (MODE_DNLD_STATUS == op->mc_mode)) {
+        pr2serr("Do nothing because '--non' given so fetching the Download "
+                "microcode status\ndpage might be dangerous\n");
+        goto fini;
+    }
+
+    if (NULL == (dip = (unsigned char *)malloc(DEF_DI_LEN))) {
+        pr2serr(ME "out of memory (data-in buffer)\n");
+        ret = SG_LIB_CAT_OTHER;
+        goto fini;
+    }
+    memset(dip, 0, DEF_DI_LEN);
+    verb = (op->verbose > 1) ? op->verbose - 1 : 0;
+    /* Fetch Download microcode status dpage for generation code ++ */
+    res = sg_ll_receive_diag(sg_fd, 1 /* pcv */, DPC_DOWNLOAD_MICROCODE, dip,
+                             DEF_DI_LEN, 1, verb);
+    if (0 == res) {
+        rsp_len = sg_get_unaligned_be16(dip + 2) + 4;
+        if (rsp_len > DEF_DI_LEN) {
+            pr2serr("<<< warning response buffer too small [%d but need "
+                    "%d]>>>\n", DEF_DI_LEN, rsp_len);
+            rsp_len = DEF_DI_LEN;
+        }
+        if (rsp_len < 8) {
+            pr2serr("Download microcode status dpage too short\n");
+            ret = SG_LIB_CAT_OTHER;
+            goto fini;
+        }
+    } else {
+        ret = res;
+        goto fini;
+    }
+    gen_code = sg_get_unaligned_be32(dip + 4);
+
+    if (MODE_DNLD_STATUS == op->mc_mode) {
+        ses_download_code_sdg(dip, rsp_len, gen_code);
+        goto fini;
+    } else if (MODE_ACTIVATE_MC == op->mc_mode) {
+        res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout, dip, 1,
+                                op);
+        ret = res;
+        goto fini;
+    }
+
+    res = 0;
+    if (op->bpw > 0) {
+        for (k = 0, last = 0; k < op->mc_len; k += n) {
+            n = op->mc_len - k;
+            if (n > op->bpw)
+                n = op->bpw;
+            else
+                last = 1;
+            if (op->verbose)
+                pr2serr("bpw loop: mode=0x%x, id=%d, off_off=%d, len=%d, "
+                        "last=%d\n", op->mc_mode, op->mc_id, k, n, last);
+            res = send_then_receive(sg_fd, gen_code, k, dmp + k, n, &dout,
+                                    dip, last, op);
+            if (res)
+                break;
+        }
+        if (op->bpw_then_activate && (0 == res)) {
+            op->mc_mode = MODE_ACTIVATE_MC;
+            if (op->verbose)
+                pr2serr("sending Activate deferred microcode [0xf]\n");
+            res = send_then_receive(sg_fd, gen_code, 0, NULL, 0, &dout,
+                                    dip, 1, op);
+        }
+    } else {
+        if (op->verbose)
+            pr2serr("single: mode=0x%x, id=%d, offset=%d, len=%d\n",
+                    op->mc_mode, op->mc_id, op->mc_offset, op->mc_len);
+        res = send_then_receive(sg_fd, gen_code, 0, dmp, op->mc_len, &dout,
+                                dip, 1, op);
+    }
+    if (res)
+        ret = res;
+
+fini:
+    if ((infd >= 0) && (! got_stdin))
+        close(infd);
+    if (dmp)
+        free(dmp);
+    if (dout.doutp)
+        free(dout.doutp);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    if (ret && (0 == op->verbose)) {
+        if (SG_LIB_CAT_INVALID_OP == ret)
+            pr2serr("%sRECEIVE DIAGNOSTIC RESULTS command not supported\n",
+                    ((MODE_DNLD_STATUS == op->mc_mode) ?
+                     "" : "SEND DIAGNOSTIC or "));
+        else if (ret > 0)
+            pr2serr("Failed, exit status %d\n", ret);
+        else if (ret < 0)
+            pr2serr("Some error occurred\n");
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_start.c b/sg3_utils/src/sg_start.c
new file mode 100644
index 0000000..49c92ab
--- /dev/null
+++ b/sg3_utils/src/sg_start.c
@@ -0,0 +1,563 @@
+/*
+ *  Copyright (C) 1999-2015 D. Gilbert
+ *  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.
+
+    Start/Stop parameter by Kurt Garloff <garloff at suse dot de>, 6/2000
+    Sync cache parameter by Kurt Garloff <garloff at suse dot de>, 1/2001
+    Guard block device answering sg's ioctls.
+                     <dgilbert at interlog dot com> 12/2002
+    Convert to SG_IO ioctl so can use sg or block devices in 2.6.* 3/2003
+
+    This utility was written for the Linux 2.4 kernel series. It now
+    builds for the Linux 2.6 and 3 kernel series and various other
+    Operating Systems.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "0.60 20151219";  /* sbc3r14; mmc6r01a */
+
+static struct option long_options[] = {
+        {"eject", 0, 0, 'e'},
+        {"fl", 1, 0, 'f'},
+        {"help", 0, 0, 'h'},
+        {"immed", 0, 0, 'i'},
+        {"load", 0, 0, 'l'},
+        {"loej", 0, 0, 'L'},
+        {"mod", 1, 0, 'm'},
+        {"noflush", 0, 0, 'n'},
+        {"new", 0, 0, 'N'},
+        {"old", 0, 0, 'O'},
+        {"pc", 1, 0, 'p'},
+        {"readonly", 0, 0, 'r'},
+        {"start", 0, 0, 's'},
+        {"stop", 0, 0, 'S'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_eject;
+    int do_fl;
+    int do_help;
+    int do_immed;
+    int do_load;
+    int do_loej;
+    int do_mod;
+    int do_noflush;
+    int do_readonly;
+    int do_pc;
+    int do_start;
+    int do_stop;
+    int do_verbose;
+    int do_version;
+    const char * device_name;
+    int opt_new;
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_start [--eject] [--fl=FL] [--help] "
+            "[--immed] [--load] [--loej]\n"
+            "                [--mod=PC_MOD] [--noflush] [--pc=PC] "
+            "[--readonly]\n"
+            "                [--start] [--stop] [--verbose] "
+            "[--version] DEVICE\n"
+            "  where:\n"
+            "    --eject|-e      stop unit then eject the medium\n"
+            "    --fl=FL|-f FL    format layer number (mmc5)\n"
+            "    --help|-h       print usage message then exit\n"
+            "    --immed|-i      device should return control after "
+            "receiving cdb,\n"
+            "                    default action is to wait until action "
+            "is complete\n"
+            "    --load|-l       load medium then start the unit\n"
+            "    --loej|-L       load or eject, corresponds to LOEJ bit "
+            "in cdb;\n"
+            "                    load when START bit also set, else "
+            "eject\n"
+            "    --mod=PC_MOD|-m PC_MOD    power condition modifier "
+            "(def: 0) (sbc)\n"
+            "    --noflush|-n    no flush prior to operation that limits "
+            "access (sbc)\n"
+            "    --pc=PC|-p PC    power condition: 0 (default) -> no "
+            "power condition,\n"
+            "                    1 -> active, 2 -> idle, 3 -> standby, "
+            "5 -> sleep (mmc)\n"
+            "    --readonly|-r    open DEVICE read-only (def: read-write)\n"
+            "                     recommended if DEVICE is ATA disk\n"
+            "    --start|-s      start unit, corresponds to START bit "
+            "in cdb,\n"
+            "                    default (START=1) if no other options "
+            "given\n"
+            "    --stop|-S       stop unit (e.g. spin down disk)\n"
+            "    --verbose|-v    increase verbosity\n"
+            "    --version|-V    print version string then exit\n\n"
+            "    Example: 'sg_start --stop /dev/sdb'    stops unit\n"
+            "             'sg_start --eject /dev/scd0'  stops unit and "
+            "ejects medium\n\n"
+            "Performs a SCSI START STOP UNIT command\n"
+            );
+}
+
+static void
+usage_old()
+{
+    pr2serr("Usage:  sg_start [0] [1] [--eject] [--fl=FL] "
+            "[-i] [--imm=0|1]\n"
+            "                 [--load] [--loej] [--mod=PC_MOD] "
+            "[--noflush] [--pc=PC]\n"
+            "                 [--readonly] [--start] [--stop] [-v] [-V]\n"
+            "                 DEVICE\n"
+            "  where:\n"
+            "    0          stop unit (e.g. spin down a disk or a "
+            "cd/dvd)\n"
+            "    1          start unit (e.g. spin up a disk or a "
+            "cd/dvd)\n"
+            "    --eject    stop then eject the medium\n"
+            "    --fl=FL    format layer number (mmc5)\n"
+            "    -i         return immediately (same as '--imm=1')\n"
+            "    --imm=0|1  0->await completion(def), 1->return "
+            "immediately\n"
+            "    --load     load then start the medium\n"
+            "    --loej     load the medium if '-start' option is "
+            "also given\n"
+            "               or stop unit and eject\n"
+            "    --mod=PC_MOD    power condition modifier "
+            "(def: 0) (sbc)\n"
+            "    --noflush    no flush prior to operation that limits "
+            "access (sbc)\n"
+            "    --pc=PC    power condition (in hex, default 0 -> no "
+            "power condition)\n"
+            "               1 -> active, 2 -> idle, 3 -> standby, "
+            "5 -> sleep (mmc)\n"
+            "    --readonly|-r    open DEVICE read-only (def: read-write)\n"
+            "                     recommended if DEVICE is ATA disk\n"
+            "    --start    start unit (same as '1'), default "
+            "action\n"
+            "    --stop     stop unit (same as '0')\n"
+            "    -v         verbose (print out SCSI commands)\n"
+            "    -V         print version string then exit\n\n"
+            "    Example: 'sg_start --stop /dev/sdb'    stops unit\n"
+            "             'sg_start --eject /dev/scd0'  stops unit and "
+            "ejects medium\n\n"
+            "Performs a SCSI START STOP UNIT command\n"
+            );
+}
+
+static int
+process_cl_new(struct opts_t * op, int argc, char * argv[])
+{
+    int c, n, err;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ef:hilLm:nNOp:rsSvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'e':
+            ++op->do_eject;
+            ++op->do_loej;
+            break;
+        case 'f':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 3)) {
+                pr2serr("bad argument to '--fl='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++op->do_loej;
+            ++op->do_start;
+            op->do_fl = n;
+            break;
+        case 'h':
+        case '?':
+            ++op->do_help;
+            break;
+        case 'i':
+            ++op->do_immed;
+            break;
+        case 'l':
+            ++op->do_load;
+            ++op->do_loej;
+            break;
+        case 'L':
+            ++op->do_loej;
+            break;
+        case 'm':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 15)) {
+                pr2serr("bad argument to '--mod='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->do_mod = n;
+            break;
+        case 'n':
+            ++op->do_noflush;
+            break;
+        case 'N':
+            break;      /* ignore */
+        case 'O':
+            op->opt_new = 0;
+            return 0;
+        case 'p':
+            n = sg_get_num(optarg);
+            if ((n < 0) || (n > 15)) {
+                pr2serr("bad argument to '--pc='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->do_pc = n;
+            break;
+        case 'r':
+            ++op->do_readonly;
+            break;
+        case 's':
+            ++op->do_start;
+            break;
+        case 'S':
+            ++op->do_stop;
+            break;
+        case 'v':
+            ++op->do_verbose;
+            break;
+        case 'V':
+            ++op->do_version;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (op->do_help)
+                break;
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    err = 0;
+    for (; optind < argc; ++optind) {
+        if (1 == strlen(argv[optind])) {
+            if (0 == strcmp("0", argv[optind])) {
+                ++op->do_stop;
+                continue;
+            } else if (0 == strcmp("1", argv[optind])) {
+                ++op->do_start;
+                continue;
+            }
+        }
+        if (NULL == op->device_name)
+            op->device_name = argv[optind];
+        else {
+            pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            ++err;
+        }
+    }
+    if (err) {
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    } else
+        return 0;
+}
+
+static int
+process_cl_old(struct opts_t * op, int argc, char * argv[])
+{
+    int k, jmp_out, plen, num;
+    int ambigu = 0;
+    int startstop = -1;
+    unsigned int u;
+    const char * cp;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0;
+                 --plen, ++cp) {
+                switch (*cp) {
+                case 'i':
+                    if ('\0' == *(cp + 1))
+                        op->do_immed = 1;
+                    else
+                        jmp_out = 1;
+                    break;
+                case 'r':
+                    ++op->do_readonly;
+                    break;
+                case 'v':
+                    ++op->do_verbose;
+                    break;
+                case 'V':
+                    ++op->do_version;
+                    break;
+                case 'h':
+                case '?':
+                    ++op->do_help;
+                    break;
+                case 'N':
+                    op->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case '-':
+                    ++cp;
+                    --plen;
+                    jmp_out = 1;
+                    break;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+
+            if (0 == strncmp(cp, "eject", 5)) {
+                op->do_loej = 1;
+                if (startstop == 1)
+                    ambigu = 1;
+                else
+                    startstop = 0;
+            } else if (0 == strncmp("fl=", cp, 3)) {
+                num = sscanf(cp + 3, "%x", &u);
+                if (1 != num) {
+                    pr2serr("Bad value after 'fl=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                startstop = 1;
+                op->do_loej = 1;
+                op->do_fl = u;
+            } else if (0 == strncmp("imm=", cp, 4)) {
+                num = sscanf(cp + 4, "%x", &u);
+                if ((1 != num) || (u > 1)) {
+                    pr2serr("Bad value after 'imm=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->do_immed = u;
+            } else if (0 == strncmp(cp, "load", 4)) {
+                op->do_loej = 1;
+                if (startstop == 0)
+                    ambigu = 1;
+                else
+                    startstop = 1;
+            } else if (0 == strncmp(cp, "loej", 4))
+                op->do_loej = 1;
+            else if (0 == strncmp("pc=", cp, 3)) {
+                num = sscanf(cp + 3, "%x", &u);
+                if ((1 != num) || (u > 15)) {
+                    pr2serr("Bad value after after 'pc=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->do_pc = u;
+            } else if (0 == strncmp("mod=", cp, 4)) {
+                num = sscanf(cp + 3, "%x", &u);
+                if (1 != num) {
+                    pr2serr("Bad value after 'mod=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                op->do_mod = u;
+            } else if (0 == strncmp(cp, "noflush", 7)) {
+                op->do_noflush = 1;
+            } else if (0 == strncmp(cp, "start", 5)) {
+                if (startstop == 0)
+                    ambigu = 1;
+                else
+                    startstop = 1;
+            } else if (0 == strncmp(cp, "stop", 4)) {
+                if (startstop == 1)
+                    ambigu = 1;
+                else
+                    startstop = 0;
+            } else if (0 == strncmp(cp, "old", 3))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage_old();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp("0", cp)) {
+            if (1 == startstop)
+                ambigu = 1;
+            else
+                startstop = 0;
+        } else if (0 == strcmp("1", cp)) {
+            if (0 == startstop)
+                ambigu = 1;
+            else
+                startstop = 1;
+        } else if (0 == op->device_name)
+                op->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not "
+                    "expecting: %s\n", op->device_name, cp);
+            usage_old();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (ambigu) {
+            pr2serr("please, only one of 0, 1, --eject, "
+                    "--load, --start or --stop\n");
+            usage_old();
+            return SG_LIB_SYNTAX_ERROR;
+        } else if (0 == startstop)
+            ++op->do_stop;
+        else if (1 == startstop)
+            ++op->do_start;
+    }
+    return 0;
+}
+
+static int
+process_cl(struct opts_t * op, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        op->opt_new = 0;
+        res = process_cl_old(op, argc, argv);
+        if ((0 == res) && op->opt_new)
+            res = process_cl_new(op, argc, argv);
+    } else {
+        op->opt_new = 1;
+        res = process_cl_new(op, argc, argv);
+        if ((0 == res) && (0 == op->opt_new))
+            res = process_cl_old(op, argc, argv);
+    }
+    return res;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int fd, res;
+    int ret = 0;
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->do_fl = -1;    /* only when >= 0 set FL bit */
+    res = process_cl(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        if (op->opt_new)
+            usage();
+        else
+            usage_old();
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+
+    if (op->do_start && op->do_stop) {
+        pr2serr("Ambiguous to give both '--start' and '--stop'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_load && op->do_eject) {
+        pr2serr("Ambiguous to give both '--load' and '--eject'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_load)
+       op->do_start = 1;
+    else if ((op->do_eject) || (op->do_stop))
+       op->do_start = 0;
+    else if (op->opt_new && op->do_loej && (0 == op->do_start))
+        op->do_start = 1;      /* --loej alone in new interface is load */
+    else if ((0 == op->do_loej) && (-1 == op->do_fl) && (0 == op->do_pc))
+       op->do_start = 1;
+    /* default action is to start when no other active options */
+
+    if (0 == op->device_name) {
+        pr2serr("No DEVICE argument given\n");
+        if (op->opt_new)
+            usage();
+        else
+            usage_old();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (op->do_fl >= 0) {
+        if (op->do_start == 0) {
+            pr2serr("Giving '--fl=FL' with '--stop' (or '--eject') is "
+                    "invalid\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (op->do_pc > 0) {
+            pr2serr("Giving '--fl=FL' with '--pc=PC' when PC is non-zero "
+                    "is invalid\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    fd = sg_cmds_open_device(op->device_name, op->do_readonly,
+                             op->do_verbose);
+    if (fd < 0) {
+        pr2serr("Error trying to open %s: %s\n", op->device_name,
+                safe_strerror(-fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    res = 0;
+    if (op->do_fl >= 0)
+        res = sg_ll_start_stop_unit(fd, op->do_immed, op->do_fl, 0 /* pc */,
+                                    1 /* fl */, 1 /* loej */,
+                                    1 /*start */, 1 /* noisy */,
+                                    op->do_verbose);
+    else if (op->do_pc > 0)
+        res = sg_ll_start_stop_unit(fd, op->do_immed, op->do_mod,
+                                    op->do_pc, op->do_noflush, 0, 0, 1,
+                                    op->do_verbose);
+    else
+        res = sg_ll_start_stop_unit(fd, op->do_immed, 0, 0, op->do_noflush,
+                                    op->do_loej, op->do_start, 1,
+                                    op->do_verbose);
+    ret = res;
+    if (res) {
+        if (op->do_verbose < 2) {
+            char b[80];
+
+            sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose);
+            pr2serr("%s\n", b);
+        }
+        pr2serr("START STOP UNIT command failed\n");
+    }
+    res = sg_cmds_close_device(fd);
+    if ((res < 0) && (0 == ret))
+        return SG_LIB_FILE_ERROR;
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_stpg.c b/sg3_utils/src/sg_stpg.c
new file mode 100644
index 0000000..d19b0ae
--- /dev/null
+++ b/sg3_utils/src/sg_stpg.c
@@ -0,0 +1,685 @@
+/*
+* Copyright (c) 2004-2015 Hannes Reinecke, Christophe Varoqui, 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI command SET TARGET PORT GROUPS
+ * to the given SCSI device.
+ */
+
+static const char * version_str = "1.10 20151219";
+
+#define TGT_GRP_BUFF_LEN 1024
+#define MX_ALLOC_LEN (0xc000 + 0x80)
+
+#define TPGS_STATE_OPTIMIZED 0x0
+#define TPGS_STATE_NONOPTIMIZED 0x1
+#define TPGS_STATE_STANDBY 0x2
+#define TPGS_STATE_UNAVAILABLE 0x3
+#define TPGS_STATE_OFFLINE 0xe          /* SPC-4 rev 9 */
+#define TPGS_STATE_TRANSITIONING 0xf
+
+/* See also table 306 - Target port group descriptor format in SPC-4 rev 36e */
+#ifndef __cplusplus
+
+static const unsigned char state_sup_mask[] = {
+        [TPGS_STATE_OPTIMIZED]     = 0x01,
+        [TPGS_STATE_NONOPTIMIZED]  = 0x02,
+        [TPGS_STATE_STANDBY]       = 0x04,
+        [TPGS_STATE_UNAVAILABLE]   = 0x08,
+        [TPGS_STATE_OFFLINE]       = 0x40,
+        [TPGS_STATE_TRANSITIONING] = 0x80,
+};
+
+#else
+
+// C++ does not support designated initializers
+static const unsigned char state_sup_mask[] = {
+    0x1, 0x2, 0x4, 0x8, 0x0, 0x0, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x80,
+};
+
+#endif  /* C or C++ ? */
+
+#define VPD_DEVICE_ID  0x83
+#define DEF_VPD_DEVICE_ID_LEN  252
+
+#define MAX_PORT_LIST_ARR_LEN 16
+
+struct tgtgrp {
+        int id;
+        int current;
+        int valid;
+};
+
+static struct option long_options[] = {
+        {"active", 0, 0, 'a'},
+        {"help", 0, 0, 'h'},
+        {"hex", 0, 0, 'H'},
+        {"offline", 0, 0, 'l'},
+        {"optimized", 0, 0, 'o'},
+        {"raw", 0, 0, 'r'},
+        {"standby", 0, 0, 's'},
+        {"state", required_argument, 0, 'S'},
+        {"tp", required_argument, 0, 't'},
+        {"unavailable", 0, 0, 'u'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_stpg   [--active] [--help] [--hex] [--offline] "
+            "[--optimized] [--raw]\n"
+            "                 [--standby] [--state=S,S...] [--tp=P,P...] "
+            "[--unavailable]\n"
+            "                 [--verbose] [--version] DEVICE\n"
+            "  where:\n"
+            "    --active|-a        set asymm. access state to "
+            "active/non-optimized\n"
+            "    --help|-h          print out usage message\n"
+            "    --hex|-H           print out report response in hex, then "
+            "exit\n"
+            "    --offline|-l       set asymm. access state to offline, takes "
+            "relative\n"
+            "                       target port id, rather than target port "
+            "group id\n"
+            "    --optimized|-o     set asymm. access state to "
+            "active/optimized\n"
+            "    --raw|-r           output report response in binary to "
+            "stdout, then exit\n"
+            "    --standby|-s       set asymm. access state to standby\n"
+            "    --state=S,S.. |-S S,S...     list of states (values or "
+            "acronyms)\n"
+            "    --tp=P,P.. |-t P,P...        list of target port group "
+            "identifiers,\n"
+            "                                 or relative target port "
+            "identifiers\n"
+            "    --unavailable|-u   set asymm. access state to unavailable\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "Performs a SCSI SET TARGET PORT GROUPS command\n");
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+static int
+decode_target_port(unsigned char * buff, int len, int *d_id, int *d_tpg)
+{
+    int c_set, assoc, desig_type, i_len;
+    int off, u;
+    const unsigned char * ucp;
+    const unsigned char * ip;
+
+    *d_id = -1;
+    *d_tpg = -1;
+    off = -1;
+    while ((u = sg_vpd_dev_id_iter(buff, len, &off, -1, -1, -1)) == 0) {
+        ucp = buff + off;
+        i_len = ucp[3];
+        if ((off + i_len + 4) > len) {
+            pr2serr("    VPD page error: designator length longer than\n     "
+                    "remaining response length=%d\n", (len - off));
+            return SG_LIB_CAT_MALFORMED;
+        }
+        ip = ucp + 4;
+        c_set = (ucp[0] & 0xf);
+        /* piv = ((ucp[1] & 0x80) ? 1 : 0); */
+        assoc = ((ucp[1] >> 4) & 0x3);
+        desig_type = (ucp[1] & 0xf);
+        switch (desig_type) {
+        case 4: /* Relative target port */
+            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+                pr2serr("      << expected binary code_set, target port "
+                        "association, length 4>>\n");
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            *d_id = sg_get_unaligned_be16(ip + 2);
+            break;
+        case 5: /* (primary) Target port group */
+            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+                pr2serr("      << expected binary code_set, target port "
+                        "association, length 4>>\n");
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            *d_tpg = sg_get_unaligned_be16(ip + 2);
+            break;
+        default:
+            break;
+        }
+    }
+    if (-1 == *d_id || -1 == *d_tpg) {
+        pr2serr("VPD page error: no target port group information\n");
+        return SG_LIB_CAT_MALFORMED;
+    }
+    return 0;
+}
+
+static void
+decode_tpgs_state(const int st)
+{
+    switch (st) {
+    case TPGS_STATE_OPTIMIZED:
+        printf(" (active/optimized)");
+        break;
+    case TPGS_STATE_NONOPTIMIZED:
+        printf(" (active/non optimized)");
+        break;
+    case TPGS_STATE_STANDBY:
+        printf(" (standby)");
+        break;
+    case TPGS_STATE_UNAVAILABLE:
+        printf(" (unavailable)");
+        break;
+    case TPGS_STATE_OFFLINE:
+        printf(" (offline)");
+        break;
+    case TPGS_STATE_TRANSITIONING:
+        printf(" (transitioning between states)");
+        break;
+    default:
+        printf(" (unknown: 0x%x)", st);
+        break;
+    }
+}
+
+static int
+transition_tpgs_states(struct tgtgrp *tgtState, int numgrp, int portgroup,
+                       int newstate)
+{
+     int i,oldstate;
+
+     for ( i = 0; i < numgrp; i++) {
+          if (tgtState[i].id == portgroup)
+               break;
+     }
+     if (i == numgrp) {
+          printf("Portgroup 0x%02x does not exist\n", portgroup);
+          return 1;
+     }
+
+     if (!( state_sup_mask[newstate] & tgtState[i].valid )) {
+          printf("Portgroup 0x%02x: Invalid state 0x%x\n",
+                 portgroup, newstate);
+          return 1;
+     }
+     oldstate = tgtState[i].current;
+     tgtState[i].current = newstate;
+     if (newstate == TPGS_STATE_OPTIMIZED) {
+          /* Switch with current optimized path */
+          for ( i = 0; i < numgrp; i++) {
+               if (tgtState[i].id == portgroup)
+                    continue;
+               if (tgtState[i].current == TPGS_STATE_OPTIMIZED)
+                    tgtState[i].current = oldstate;
+          }
+     } else if (oldstate == TPGS_STATE_OPTIMIZED) {
+          /* Enable next path group */
+          for ( i = 0; i < numgrp; i++) {
+               if (tgtState[i].id == portgroup)
+                    continue;
+               if (tgtState[i].current == TPGS_STATE_NONOPTIMIZED) {
+                    tgtState[i].current = TPGS_STATE_OPTIMIZED;
+                    break;
+               }
+          }
+     }
+     printf("New target port groups:\n");
+     for (i = 0; i < numgrp; i++) {
+            printf("  target port group id : 0x%x\n",
+                   tgtState[i].id);
+            printf("    target port group asymmetric access state : ");
+            printf("0x%02x\n", tgtState[i].current);
+     }
+     return 0;
+}
+
+static void
+encode_tpgs_states(unsigned char *buff, struct tgtgrp *tgtState, int numgrp)
+{
+     int i;
+     unsigned char *desc;
+
+     for (i = 0, desc = buff + 4; i < numgrp; desc += 4, i++) {
+          desc[0] = tgtState[i].current & 0x0f;
+          sg_put_unaligned_be16((uint16_t)tgtState[i].id, desc + 2);
+     }
+}
+
+/* Read numbers (up to 32 bits in size) from command line (comma separated */
+/* list). Assumed decimal unless prefixed by '0x', '0X' or contains */
+/* trailing 'h' or 'H' (which indicate hex). Returns 0 if ok, 1 if error. */
+static int
+build_port_arr(const char * inp, int * port_arr, int * port_arr_len,
+               int max_arr_len)
+{
+    int in_len, k;
+    const char * lcp;
+    int v;
+    char * cp;
+
+    if ((NULL == inp) || (NULL == port_arr) ||
+        (NULL == port_arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *port_arr_len = 0;
+    k = strspn(inp, "0123456789aAbBcCdDeEfFhHxX,");
+    if (in_len != k) {
+        pr2serr("build_port_arr: error at pos %d\n", k + 1);
+        return 1;
+    }
+    for (k = 0; k < max_arr_len; ++k) {
+        v = sg_get_num_nomult(lcp);
+        if (-1 != v) {
+            port_arr[k] = v;
+            cp = (char *)strchr(lcp, ',');
+            if (NULL == cp)
+                break;
+            lcp = cp + 1;
+        } else {
+            pr2serr("build_port_arr: error at pos %d\n",
+                    (int)(lcp - inp + 1));
+            return 1;
+        }
+    }
+    *port_arr_len = k + 1;
+    if (k == max_arr_len) {
+        pr2serr("build_port_arr: array length exceeded\n");
+        return 1;
+    }
+    return 0;
+}
+
+/* Read numbers (up to 32 bits in size) from command line (comma separated */
+/* list). Assumed decimal unless prefixed by '0x', '0X' or contains */
+/* trailing 'h' or 'H' (which indicate hex). Also accepts 'ao' for active */
+/* optimized [0], 'an' for active/non-optimized [1], 's' for standby [2], */
+/* 'u' for unavailable [3], 'o' for offline [14]. */
+/* Returns 0 if ok, 1 if error. */
+static int
+build_state_arr(const char * inp, int * state_arr, int * state_arr_len,
+                int max_arr_len)
+{
+    int in_len, k, v, try_num;
+    const char * lcp;
+    char * cp;
+
+    if ((NULL == inp) || (NULL == state_arr) ||
+        (NULL == state_arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *state_arr_len = 0;
+    k = strspn(inp, "0123456789aAbBcCdDeEfFhHnNoOsSuUxX,");
+    if (in_len != k) {
+        pr2serr("build_state_arr: error at pos %d\n", k + 1);
+        return 1;
+    }
+    for (k = 0; k < max_arr_len; ++k) {
+        try_num = 1;
+        if (isalpha(*lcp)) {
+            try_num = 0;
+            switch (toupper(*lcp)) {
+            case 'A':
+                if ('N' == toupper(*(lcp + 1)))
+                    state_arr[k] = 1;
+                else if ('O' == toupper(*(lcp + 1)))
+                    state_arr[k] = 0;
+                else
+                    try_num = 1;
+                break;
+            case 'O':
+                state_arr[k] = 14;
+                break;
+            case 'S':
+                state_arr[k] = 2;
+                break;
+            case 'U':
+                state_arr[k] = 3;
+                break;
+            default:
+                pr2serr("build_state_arr: expected 'ao', 'an', 'o', 's' or "
+                        "'u' at pos %d\n", (int)(lcp - inp + 1));
+                return 1;
+            }
+        }
+        if (try_num) {
+            v = sg_get_num_nomult(lcp);
+            if (((v >= 0) && (v <= 3)) || (14 ==v))
+                state_arr[k] = v;
+            else if (-1 == v) {
+                pr2serr("build_state_arr: error at pos %d\n",
+                        (int)(lcp - inp + 1));
+                return 1;
+            } else {
+                pr2serr("build_state_arr: expect 0,1,2,3 or 14\n");
+                return 1;
+            }
+        }
+        cp = (char *)strchr(lcp, ',');
+        if (NULL == cp)
+            break;
+        lcp = cp + 1;
+    }
+    *state_arr_len = k + 1;
+    if (k == max_arr_len) {
+        pr2serr("build_state_arr: array length exceeded\n");
+        return 1;
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, k, off, res, c, report_len, tgt_port_count;
+    unsigned char reportTgtGrpBuff[TGT_GRP_BUFF_LEN];
+    unsigned char setTgtGrpBuff[TGT_GRP_BUFF_LEN];
+    unsigned char rsp_buff[MX_ALLOC_LEN + 2];
+    unsigned char * ucp;
+    struct tgtgrp tgtGrpState[256], *tgtStatePtr;
+    int state = -1;
+    const char * state_arg = NULL;
+    const char * tp_arg = NULL;
+    int hex = 0;
+    int raw = 0;
+    int verbose = 0;
+    int port_arr[MAX_PORT_LIST_ARR_LEN];
+    int port_arr_len = 0;
+    int state_arr[MAX_PORT_LIST_ARR_LEN];
+    char b[80];
+    int state_arr_len = 0;
+    int portgroup = -1;
+    int relport = -1;
+    int numgrp = 0;
+    const char * device_name = NULL;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ahHloOrsS:t:uvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            state = TPGS_STATE_NONOPTIMIZED;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            hex = 1;
+            break;
+        case 'l':
+        case 'O':
+            state = TPGS_STATE_OFFLINE;
+            break;
+        case 'o':
+            state = TPGS_STATE_OPTIMIZED;
+            break;
+        case 'r':
+            raw = 1;
+            break;
+        case 's':
+            state = TPGS_STATE_STANDBY;
+            break;
+        case 'S':
+            state_arg = optarg;
+            break;
+        case 't':
+            tp_arg = optarg;
+            break;
+        case 'u':
+            state = TPGS_STATE_UNAVAILABLE;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("Version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (state_arg) {
+        if (build_state_arr(state_arg, state_arr, &state_arr_len,
+                            MAX_PORT_LIST_ARR_LEN)) {
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (tp_arg) {
+        if (build_port_arr(tp_arg, port_arr, &port_arr_len,
+                           MAX_PORT_LIST_ARR_LEN)) {
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if ((state >= 0) && (state_arr_len > 0)) {
+        pr2serr("either use individual state option or '--state=' but not "
+                "both\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((0 == state_arr_len) && (0 == port_arr_len) && (-1 == state))
+        state = 0;      /* default to active/optimized */
+    if ((1 == state_arr_len) && (0 == port_arr_len) && (-1 == state)) {
+        state = state_arr[0];
+        state_arr_len = 0;
+    }
+    if (state_arr_len > port_arr_len) {
+        pr2serr("'state=' list longer than expected\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((port_arr_len > 0) && (0 == state_arr_len)) {
+        if (-1 == state) {
+            pr2serr("target port list given but no state indicated\n");
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        state_arr[0] = state;
+        state_arr_len = 1;
+        state = -1;
+    }
+    if ((port_arr_len > 1) && (1 == state_arr_len)) {
+        for (k = 1; k < port_arr_len; ++k)
+            state_arr[k] = state_arr[0];
+        state_arr_len = port_arr_len;
+    }
+    if (port_arr_len != state_arr_len) {
+        pr2serr("'state=' and '--tp=' lists mismatched\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (0 == port_arr_len) {
+        res = sg_ll_inquiry(sg_fd, 0, 1, VPD_DEVICE_ID, rsp_buff,
+                            DEF_VPD_DEVICE_ID_LEN, 1, verbose);
+        if (0 == res) {
+            report_len = sg_get_unaligned_be16(rsp_buff + 2) + 4;
+            if (VPD_DEVICE_ID != rsp_buff[1]) {
+                pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
+                        "response\n");
+                if (verbose) {
+                    pr2serr("First 32 bytes of bad response\n");
+                    dStrHexErr((const char *)rsp_buff, 32, 0);
+                }
+                return SG_LIB_CAT_MALFORMED;
+            }
+            if (report_len > MX_ALLOC_LEN) {
+                pr2serr("response length too long: %d > %d\n", report_len,
+                        MX_ALLOC_LEN);
+                return SG_LIB_CAT_MALFORMED;
+            } else if (report_len > DEF_VPD_DEVICE_ID_LEN) {
+                if (sg_ll_inquiry(sg_fd, 0, 1, VPD_DEVICE_ID, rsp_buff,
+                                  report_len, 1, verbose))
+                    return SG_LIB_CAT_OTHER;
+            }
+            decode_target_port(rsp_buff + 4, report_len - 4, &relport,
+                               &portgroup);
+            printf("Device is at port Group 0x%02x, relative port 0x%02x\n",
+                   portgroup, relport);
+        }
+
+        memset(reportTgtGrpBuff, 0x0, sizeof(reportTgtGrpBuff));
+        /* trunc = 0; */
+
+        res = sg_ll_report_tgt_prt_grp2(sg_fd, reportTgtGrpBuff,
+                                        sizeof(reportTgtGrpBuff), 0, 1,
+                                        verbose);
+        ret = res;
+        if (0 == res) {
+            report_len = sg_get_unaligned_be32(reportTgtGrpBuff + 0) + 4;
+            if (report_len > (int)sizeof(reportTgtGrpBuff)) {
+                /* trunc = 1; */
+                pr2serr("  <<report too long for internal buffer, output "
+                        "truncated\n");
+                report_len = (int)sizeof(reportTgtGrpBuff);
+            }
+            if (raw) {
+                dStrRaw((const char *)reportTgtGrpBuff, report_len);
+                goto err_out;
+            }
+            if (verbose)
+                printf("Report list length = %d\n", report_len);
+            if (hex) {
+                if (verbose)
+                    printf("\nOutput response in hex:\n");
+                dStrHex((const char *)reportTgtGrpBuff, report_len, 1);
+                goto err_out;
+            }
+            memset(tgtGrpState, 0, sizeof(struct tgtgrp) * 256);
+            tgtStatePtr = tgtGrpState;
+            printf("Current target port groups:\n");
+            for (k = 4, ucp = reportTgtGrpBuff + 4, numgrp = 0; k < report_len;
+                 k += off, ucp += off, numgrp ++) {
+
+                printf("  target port group id : 0x%x , Pref=%d\n",
+                       sg_get_unaligned_be16(ucp + 2), !!(ucp[0] & 0x80));
+                printf("    target port group asymmetric access state : ");
+                printf("0x%02x", ucp[0] & 0x0f);
+                printf("\n");
+                tgtStatePtr->id = sg_get_unaligned_be16(ucp + 2);
+                tgtStatePtr->current = ucp[0] & 0x0f;
+                tgtStatePtr->valid = ucp[1];
+
+                tgt_port_count = ucp[7];
+
+                tgtStatePtr++;
+                off = 8 + tgt_port_count * 4;
+            }
+        } else {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("Report Target Port Groups: %s\n", b);
+            if (0 == verbose)
+                pr2serr("    try '-v' for more information\n");
+        }
+        if (0 != res)
+             goto err_out;
+
+        printf("Port group 0x%02x: Set asymmetric access state to", portgroup);
+        decode_tpgs_state(state);
+        printf("\n");
+
+        transition_tpgs_states(tgtGrpState, numgrp, portgroup, state);
+
+        memset(setTgtGrpBuff, 0x0, sizeof(setTgtGrpBuff));
+        /* trunc = 0; */
+
+        encode_tpgs_states(setTgtGrpBuff, tgtGrpState, numgrp);
+        report_len = numgrp * 4 + 4;
+    } else { /* port_arr_len > 0 */
+        memset(setTgtGrpBuff, 0x0, sizeof(setTgtGrpBuff));
+        for (k = 0, ucp = setTgtGrpBuff + 4; k < port_arr_len; ++k, ucp +=4) {
+            ucp[0] = state_arr[k] & 0xf;
+            sg_put_unaligned_be16((uint16_t)port_arr[k], ucp + 2);
+        }
+        report_len = port_arr_len * 4 + 4;
+    }
+
+    res = sg_ll_set_tgt_prt_grp(sg_fd, setTgtGrpBuff, report_len, 1, verbose);
+
+    if (0 == res)
+        goto err_out;
+    else {
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Set Target Port Groups: %s\n", b);
+        if (0 == verbose)
+            pr2serr("    try '-v' for more information\n");
+    }
+
+err_out:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_sync.c b/sg3_utils/src/sg_sync.c
new file mode 100644
index 0000000..0a9600f
--- /dev/null
+++ b/sg3_utils/src/sg_sync.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2004-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pt.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI command SYNCHRONIZE CACHE(10 or 16) to the
+ * given device. This command is defined for SCSI "direct access" devices
+ * (e.g. disks).
+ */
+
+static const char * version_str = "1.14 20151219";
+
+#define SYNCHRONIZE_CACHE16_CMD     0x91
+#define SYNCHRONIZE_CACHE16_CMDLEN  16
+#define SENSE_BUFF_LEN  64
+#define DEF_PT_TIMEOUT  60       /* 60 seconds */
+
+
+static struct option long_options[] = {
+        {"16", no_argument, 0, 'S'},
+        {"count", required_argument, 0, 'c'},
+        {"group", required_argument, 0, 'g'},
+        {"help", no_argument, 0, 'h'},
+        {"immed", no_argument, 0, 'i'},
+        {"lba", required_argument, 0, 'l'},
+        {"sync-nv", no_argument, 0, 's'},
+        {"timeout", required_argument, 0, 't'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void usage()
+{
+    pr2serr("Usage: sg_sync    [--16] [--count=COUNT] [--group=GN] [--help] "
+            "[--immed]\n"
+            "                  [--lba=LBA] [--sync-nv] [--timeout=SECS] "
+            "[--verbose]\n"
+            "                  [--version] DEVICE\n"
+            "  where:\n"
+            "    --16|-S             calls SYNCHRONIZE CACHE(16) (def: is "
+            "10 byte\n"
+            "                        variant)\n"
+            "    --count=COUNT|-c COUNT    number of blocks to sync (def: 0 "
+            "which\n"
+            "                              implies rest of device)\n"
+            "    --group=GN|-g GN    set group number field to GN (def: 0)\n"
+            "    --help|-h           print out usage message\n"
+            "    --immed|-i          command returns immediately when set "
+            "else wait\n"
+            "                        for 'sync' to complete\n"
+            "    --lba=LBA|-l LBA    logical block address to start sync "
+            "operation\n"
+            "                        from (def: 0)\n"
+            "    --sync-nv|-s        synchronize to non-volatile storage "
+            "(if distinct\n"
+            "                        from medium). Obsolete in sbc3r35d.\n"
+            "    --timeout=SECS|-t SECS    command timeout in seconds, only "
+            "active\n"
+            "                              if '--16' given (def: 60 seconds)\n"
+            "    --verbose|-v        increase verbosity\n"
+            "    --version|-V        print version string and exit\n\n"
+            "Performs a SCSI SYNCHRONIZE CACHE(10 or 16) command\n");
+}
+
+static int
+ll_sync_cache_16(int sg_fd, int sync_nv, int immed, int group,
+                 uint64_t lba, unsigned int num_lb, int to_secs,
+                 int noisy, int verbose)
+{
+    int res, ret, k, sense_cat;
+    unsigned char scCmdBlk[SYNCHRONIZE_CACHE16_CMDLEN] =
+                {SYNCHRONIZE_CACHE16_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (sync_nv)
+        scCmdBlk[1] |= 4;       /* obsolete in sbc3r35d */
+    if (immed)
+        scCmdBlk[1] |= 2;
+    sg_put_unaligned_be64(lba, scCmdBlk + 2);
+    scCmdBlk[14] = group & 0x1f;
+    sg_put_unaligned_be32((uint32_t)num_lb, scCmdBlk + 10);
+
+    if (verbose) {
+        pr2serr("    synchronize cache(16) cdb: ");
+        for (k = 0; k < SYNCHRONIZE_CACHE16_CMDLEN; ++k)
+            pr2serr("%02x ", scCmdBlk[k]);
+        pr2serr("\n");
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("synchronize cache(16): out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, scCmdBlk, sizeof(scCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    res = do_scsi_pt(ptvp, sg_fd, to_secs, verbose);
+    ret = sg_cmds_process_resp(ptvp, "synchronize cache(16)", res, 0,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, res, c;
+    int64_t count = 0;
+    unsigned int num_lb = 0;
+    int do_16 = 0;
+    int group = 0;
+    int64_t lba = 0;
+    int immed = 0;
+    int sync_nv = 0;
+    int to_secs = DEF_PT_TIMEOUT;
+    int verbose = 0;
+    const char * device_name = NULL;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "c:g:hil:sSt:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            count = sg_get_llnum(optarg);
+            if ((count < 0) || (count > UINT_MAX)) {
+                pr2serr("bad argument to '--count'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            num_lb = (unsigned int)count;
+            break;
+        case 'g':
+            group = sg_get_num(optarg);
+            if ((group < 0) || (group > 31)) {
+                pr2serr("bad argument to '--group'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'i':
+            immed = 1;
+            break;
+        case 'l':
+            lba = sg_get_llnum(optarg);
+            if (lba < 0) {
+                pr2serr("bad argument to '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 's':
+            sync_nv = 1;
+            break;
+        case 'S':
+            do_16 = 1;
+            break;
+        case 't':
+            to_secs = sg_get_num(optarg);
+            if (to_secs < 0) {
+                pr2serr("bad argument to '--timeout'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (do_16)
+        res = ll_sync_cache_16(sg_fd, sync_nv, immed, group, lba, num_lb,
+                               to_secs, 1, verbose);
+    else
+        res = sg_ll_sync_cache_10(sg_fd, sync_nv, immed, group,
+                                  (unsigned int)lba, num_lb, 1, verbose);
+    ret = res;
+    if (res) {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Synchronize cache failed: %s\n", b);
+    }
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_test_rwbuf.c b/sg3_utils/src/sg_test_rwbuf.c
new file mode 100644
index 0000000..13d98e3
--- /dev/null
+++ b/sg3_utils/src/sg_test_rwbuf.c
@@ -0,0 +1,520 @@
+/*
+ * (c) 2000 Kurt Garloff <garloff at suse dot de>
+ * heavily based on Douglas Gilbert's sg_rbuf program.
+ * (c) 1999-2015 Douglas Gilbert
+ *
+ * Program to test the SCSI host adapter by issueing
+ * write and read operations on a device's buffer
+ * and calculating checksums.
+ * NOTE: If you can not reserve the buffer of the device
+ * for this purpose (SG_GET_RESERVED_SIZE), you risk
+ * serious data corruption, if the device is accessed by
+ * somebody else in the meantime.
+ *
+ *  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.
+ *
+ * $Id: sg_test_rwbuf.c,v 1.1 2000/03/02 13:50:03 garloff Exp $
+ *
+ *   2003/11/11  switch sg3_utils version to use SG_IO ioctl [dpg]
+ *   2004/06/08  remove SG_GET_VERSION_NUM check [dpg]
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "1.09 20151220";
+
+#define BPI (signed)(sizeof(int))
+
+#define RB_MODE_DESC 3
+#define RWB_MODE_DATA 2
+#define RB_DESC_LEN 4
+
+/*  The microcode in a SCSI device is _not_ modified by doing a WRITE BUFFER
+ *  with mode set to "data" (0x2) as done by this utility. Therefore this
+ *  utility is safe in that respect. [Mode values 0x4, 0x5, 0x6 and 0x7 are
+ *  the dangerous ones :-)]
+ */
+
+#define ME "sg_test_rwbuf: "
+
+static int base = 0x12345678;
+static int buf_capacity = 0;
+static int buf_granul = 255;
+static unsigned char *cmpbuf = NULL;
+
+
+/* Options */
+static int size = -1;
+static char do_quick = 0;
+static int addwrite  = 0;
+static int addread   = 0;
+static int verbose   = 0;
+
+static struct option long_options[] = {
+        {"help", 0, 0, 'h'},
+        {"quick", 0, 0, 'q'},
+        {"addrd", 1, 0, 'r'},
+        {"size", 1, 0, 's'},
+        {"times", 1, 0, 't'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {"addwr", 1, 0, 'w'},
+        {0, 0, 0, 0},
+};
+
+int find_out_about_buffer (int sg_fd)
+{
+        unsigned char rbCmdBlk[] = {READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+        unsigned char rbBuff[RB_DESC_LEN];
+        unsigned char sense_buffer[32];
+        struct sg_io_hdr io_hdr;
+        int k, res;
+
+        rbCmdBlk[1] = RB_MODE_DESC;
+        rbCmdBlk[8] = RB_DESC_LEN;
+        memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof(rbCmdBlk);
+        io_hdr.mx_sb_len = sizeof(sense_buffer);
+        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        io_hdr.dxfer_len = RB_DESC_LEN;
+        io_hdr.dxferp = rbBuff;
+        io_hdr.cmdp = rbCmdBlk;
+        io_hdr.sbp = sense_buffer;
+        io_hdr.timeout = 60000;     /* 60000 millisecs == 60 seconds */
+
+        if (verbose) {
+                pr2serr("    read buffer [mode desc] cdb: ");
+                for (k = 0; k < (int)sizeof(rbCmdBlk); ++k)
+                        pr2serr("%02x ", rbCmdBlk[k]);
+                pr2serr("\n");
+        }
+        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+                perror(ME "SG_IO READ BUFFER descriptor error");
+                return -1;
+        }
+        /* now for the error processing */
+        res = sg_err_category3(&io_hdr);
+        switch (res) {
+        case SG_LIB_CAT_RECOVERED:
+                sg_chk_n_print3("READ BUFFER descriptor, continuing",
+                                &io_hdr, 1);
+                /* fall through */
+        case SG_LIB_CAT_CLEAN:
+                break;
+        default: /* won't bother decoding other categories */
+                sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr, 1);
+                return res;
+        }
+
+        buf_capacity = sg_get_unaligned_be24(rbBuff + 1);
+        buf_granul = (unsigned char)rbBuff[0];
+#if 0
+        printf("READ BUFFER reports: %02x %02x %02x %02x\n",
+               rbBuff[0], rbBuff[1], rbBuff[2], rbBuff[3]);
+#endif
+        if (verbose)
+                printf("READ BUFFER reports: buffer capacity=%d, offset "
+                       "boundary=%d\n", buf_capacity, buf_granul);
+        return 0;
+}
+
+int mymemcmp (unsigned char *bf1, unsigned char *bf2, int len)
+{
+        int df;
+        for (df = 0; df < len; df++)
+                if (bf1[df] != bf2[df]) return df;
+        return 0;
+}
+
+/* return 0 if good, else 2222 */
+int do_checksum (int *buf, int len, int quiet)
+{
+        int sum = base;
+        int i; int rln = len;
+        for (i = 0; i < len/BPI; i++)
+                sum += buf[i];
+        while (rln%BPI) sum += ((char*)buf)[--rln];
+        if (sum != 0x12345678) {
+                if (!quiet) printf ("sg_test_rwbuf: Checksum error (sz=%i):"
+                                    " %08x\n", len, sum);
+                if (cmpbuf && !quiet) {
+                        int diff = mymemcmp (cmpbuf, (unsigned char*)buf,
+                                             len);
+                        printf ("Differ at pos %i/%i:\n", diff, len);
+                        for (i = 0; i < 24 && i+diff < len; i++)
+                                printf (" %02x", cmpbuf[i+diff]);
+                        printf ("\n");
+                        for (i = 0; i < 24 && i+diff < len; i++)
+                                printf (" %02x",
+                                        ((unsigned char*)buf)[i+diff]);
+                        printf ("\n");
+                }
+                return 2222;
+        }
+        else {
+                if (verbose > 1)
+                        printf("Checksum value: 0x%x\n", sum);
+                return 0;
+        }
+}
+
+void do_fill_buffer (int *buf, int len)
+{
+        int sum;
+        int i; int rln = len;
+        srand (time (0));
+    retry:
+        if (len >= BPI)
+                base = 0x12345678 + rand ();
+        else
+                base = 0x12345678 + (char) rand ();
+        sum = base;
+        for (i = 0; i < len/BPI - 1; i++)
+        {
+                /* we rely on rand() giving full range of int */
+                buf[i] = rand ();
+                sum += buf[i];
+        }
+        while (rln%BPI)
+        {
+                ((char*)buf)[--rln] = rand ();
+                sum += ((char*)buf)[rln];
+        }
+        if (len >= BPI) buf[len/BPI - 1] = 0x12345678 - sum;
+        else ((char*)buf)[0] = 0x12345678 + ((char*)buf)[0] - sum;
+        if (do_checksum (buf, len, 1)) {
+                if (len < BPI) goto retry;
+                printf ("sg_test_rwbuf: Memory corruption?\n");
+                exit (1);
+        }
+        if (cmpbuf) memcpy (cmpbuf, (char*)buf, len);
+}
+
+
+int read_buffer (int sg_fd, unsigned size)
+{
+        int res, k;
+        unsigned char rbCmdBlk[] = {READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+        int bufSize = size + addread;
+        unsigned char * rbBuff = (unsigned char *)malloc(bufSize);
+        unsigned char sense_buffer[32];
+        struct sg_io_hdr io_hdr;
+
+        if (NULL == rbBuff)
+                return -1;
+        rbCmdBlk[1] = RWB_MODE_DATA;
+        sg_put_unaligned_be24((uint32_t)bufSize, rbCmdBlk + 6);
+        memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof(rbCmdBlk);
+        io_hdr.mx_sb_len = sizeof(sense_buffer);
+        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+        io_hdr.dxfer_len = bufSize;
+        io_hdr.dxferp = rbBuff;
+        io_hdr.cmdp = rbCmdBlk;
+        io_hdr.sbp = sense_buffer;
+        io_hdr.pack_id = 2;
+        io_hdr.timeout = 60000;     /* 60000 millisecs == 60 seconds */
+        if (verbose) {
+                pr2serr("    read buffer [mode data] cdb: ");
+                for (k = 0; k < (int)sizeof(rbCmdBlk); ++k)
+                        pr2serr("%02x ", rbCmdBlk[k]);
+                pr2serr("\n");
+        }
+
+        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+                perror(ME "SG_IO READ BUFFER data error");
+                free(rbBuff);
+                return -1;
+        }
+        /* now for the error processing */
+        res = sg_err_category3(&io_hdr);
+        switch (res) {
+        case SG_LIB_CAT_RECOVERED:
+            sg_chk_n_print3("READ BUFFER data, continuing", &io_hdr, 1);
+            /* fall through */
+        case SG_LIB_CAT_CLEAN:
+                break;
+        default: /* won't bother decoding other categories */
+                sg_chk_n_print3("READ BUFFER data error", &io_hdr, 1);
+                free(rbBuff);
+                return res;
+        }
+
+        res = do_checksum ((int*)rbBuff, size, 0);
+        free(rbBuff);
+        return res;
+}
+
+int write_buffer (int sg_fd, unsigned size)
+{
+        unsigned char wbCmdBlk[] = {WRITE_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+        int bufSize = size + addwrite;
+        unsigned char * wbBuff = (unsigned char *)malloc(bufSize);
+        unsigned char sense_buffer[32];
+        struct sg_io_hdr io_hdr;
+        int k, res;
+
+        if (NULL == wbBuff)
+                return -1;
+        memset(wbBuff, 0, bufSize);
+        do_fill_buffer ((int*)wbBuff, size);
+        wbCmdBlk[1] = RWB_MODE_DATA;
+        sg_put_unaligned_be24((uint32_t)bufSize, wbCmdBlk + 6);
+        memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+        io_hdr.interface_id = 'S';
+        io_hdr.cmd_len = sizeof(wbCmdBlk);
+        io_hdr.mx_sb_len = sizeof(sense_buffer);
+        io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+        io_hdr.dxfer_len = bufSize;
+        io_hdr.dxferp = wbBuff;
+        io_hdr.cmdp = wbCmdBlk;
+        io_hdr.sbp = sense_buffer;
+        io_hdr.pack_id = 1;
+        io_hdr.timeout = 60000;     /* 60000 millisecs == 60 seconds */
+        if (verbose) {
+                pr2serr("    write buffer [mode data] cdb: ");
+                for (k = 0; k < (int)sizeof(wbCmdBlk); ++k)
+                        pr2serr("%02x ", wbCmdBlk[k]);
+                pr2serr("\n");
+        }
+
+        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+                perror(ME "SG_IO WRITE BUFFER data error");
+                free(wbBuff);
+                return -1;
+        }
+        /* now for the error processing */
+        res = sg_err_category3(&io_hdr);
+        switch (res) {
+        case SG_LIB_CAT_RECOVERED:
+            sg_chk_n_print3("WRITE BUFFER data, continuing", &io_hdr, 1);
+            /* fall through */
+        case SG_LIB_CAT_CLEAN:
+                break;
+        default: /* won't bother decoding other categories */
+                sg_chk_n_print3("WRITE BUFFER data error", &io_hdr, 1);
+                free(wbBuff);
+                return res;
+        }
+        free(wbBuff);
+        return res;
+}
+
+void usage ()
+{
+        printf ("Usage: sg_test_rwbuf [--addrd=AR] [--addwr=AW] [--help] "
+                "[--quick]\n");
+        printf ("                     --size=SZ [--times=NUM] [--verbose] "
+                "[--version]\n"
+                "                     DEVICE\n"
+                " or\n"
+                "       sg_test_rwbuf DEVICE SZ [AW] [AR]\n");
+        printf ("  where:\n"
+                "    --addrd=AR       extra bytes to fetch during READ "
+                "BUFFER\n"
+                "    --addwr=AW       extra bytes to send to WRITE BUFFER\n"
+                "    --help           output this usage message then exit\n"
+                "    --quick          output read buffer size then exit\n"
+                "    --size=SZ        size of buffer (in bytes) to write "
+                "then read back\n"
+                "    --times=NUM      number of times to run test "
+                "(default 1)\n"
+                "    --verbose        increase verbosity of output\n"
+                "    --version        output version then exit\n");
+        printf ("\nWARNING: If you access the device at the same time, e.g. "
+                "because it's a\n");
+        printf (" mounted hard disk, the device's buffer may be used by the "
+                "device itself\n");
+        printf (" for other data at the same time, and overwriting it may or "
+                "may not\n");
+        printf (" cause data corruption!\n");
+        printf ("(c) Douglas Gilbert, Kurt Garloff, 2000-2007, GNU GPL\n");
+}
+
+
+int main (int argc, char * argv[])
+{
+        int sg_fd, res;
+        const char * device_name = NULL;
+        int times = 1;
+        int ret = 0;
+        int k = 0;
+
+        while (1) {
+                int option_index = 0;
+                int c;
+
+                c = getopt_long(argc, argv, "hqr:s:t:w:vV",
+                                long_options, &option_index);
+                if (c == -1)
+                        break;
+
+                switch (c) {
+                case 'h':
+                        usage();
+                        return 0;
+                case 'q':
+                        do_quick = 1;
+                        break;
+                case 'r':
+                        addread = sg_get_num(optarg);
+                        if (-1 == addread) {
+                                pr2serr("bad argument to '--addrd'\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 's':
+                        size = sg_get_num(optarg);
+                        if (-1 == size) {
+                                pr2serr("bad argument to '--size'\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 't':
+                        times = sg_get_num(optarg);
+                        if (-1 == times) {
+                                pr2serr("bad argument to '--times'\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                case 'v':
+                        verbose++;
+                        break;
+                case 'V':
+                        pr2serr(ME "version: %s\n", version_str);
+                        return 0;
+                case 'w':
+                        addwrite = sg_get_num(optarg);
+                        if (-1 == addwrite) {
+                                pr2serr("bad argument to '--addwr'\n");
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        break;
+                default:
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                }
+        }
+        if (optind < argc) {
+                if (NULL == device_name) {
+                        device_name = argv[optind];
+                        ++optind;
+                }
+        }
+        if (optind < argc) {
+                if (-1 == size) {
+                        size = sg_get_num(argv[optind]);
+                        if (-1 == size) {
+                                pr2serr("bad <sz>\n");
+                                usage();
+                                return SG_LIB_SYNTAX_ERROR;
+                        }
+                        if (++optind < argc) {
+                                addwrite = sg_get_num(argv[optind]);
+                                if (-1 == addwrite) {
+                                        pr2serr("bad [addwr]\n");
+                                        usage();
+                                        return SG_LIB_SYNTAX_ERROR;
+                                }
+                                if (++optind < argc) {
+                                        addread = sg_get_num(argv[optind]);
+                                        if (-1 == addread) {
+                                                pr2serr("bad [addrd]\n");
+                                                usage();
+                                                return SG_LIB_SYNTAX_ERROR;
+                                        }
+                                }
+                        }
+
+                }
+                if (optind < argc) {
+                        for (; optind < argc; ++optind)
+                                pr2serr("Unexpected extra argument" ": %s\n",
+                                        argv[optind]);
+                        usage();
+                        return SG_LIB_SYNTAX_ERROR;
+                }
+        }
+        if (NULL == device_name) {
+                pr2serr("no device name given\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+        if ((size <= 0) && (! do_quick)) {
+                pr2serr("must give '--size' or '--quick' options or <sz> "
+                        "argument\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+        }
+
+        sg_fd = open(device_name, O_RDWR | O_NONBLOCK);
+        if (sg_fd < 0) {
+                perror("sg_test_rwbuf: open error");
+                return SG_LIB_FILE_ERROR;
+        }
+        ret = find_out_about_buffer(sg_fd);
+        if (ret)
+                goto err_out;
+        if (do_quick) {
+                printf ("READ BUFFER read descriptor reports a buffer "
+                        "of %d bytes [%d KiB]\n", buf_capacity,
+                        buf_capacity / 1024);
+                goto err_out;
+        }
+        if (size > buf_capacity) {
+                pr2serr (ME "sz=%i > buf_capacity=%i\n", size, buf_capacity);
+                ret = SG_LIB_CAT_OTHER;
+                goto err_out;
+        }
+
+        cmpbuf = (unsigned char *)malloc(size);
+        for (k = 0; k < times; ++k) {
+                ret = write_buffer (sg_fd, size);
+                if (ret) {
+                        goto err_out;
+                }
+                ret = read_buffer (sg_fd, size);
+                if (ret) {
+                        if (2222 == ret)
+                                ret = SG_LIB_CAT_MALFORMED;
+                        goto err_out;
+                }
+        }
+
+err_out:
+        if (cmpbuf)
+                free(cmpbuf);
+        res = close(sg_fd);
+        if (res < 0) {
+                perror(ME "close error");
+                if (0 == ret)
+                        ret = SG_LIB_FILE_ERROR;
+        }
+        if ((0 == ret) && (! do_quick))
+                printf ("Success\n");
+        else if (times > 1)
+                printf ("Failed after %d successful cycles\n", k);
+        return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_timestamp.c b/sg3_utils/src/sg_timestamp.c
new file mode 100644
index 0000000..72c141f
--- /dev/null
+++ b/sg3_utils/src/sg_timestamp.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2015-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues a SCSI REPORT TIMESTAMP and SET TIMESTAMP commands
+ * to the given SCSI device. Based on spc5r07.pdf .
+ */
+
+static const char * version_str = "1.02 20160126";
+
+#define REP_TIMESTAMP_CMDLEN 12
+#define SET_TIMESTAMP_CMDLEN 12
+#define REP_TIMESTAMP_SA 0xf
+#define SET_TIMESTAMP_SA 0xf
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT  60      /* 60 seconds */
+
+uint8_t d_buff[256];
+
+/* example Report timestamp parameter data */
+/* uint8_t test[12] = {0, 0xa, 2, 0, 0x1, 0x51, 0x5b, 0xe2, 0xc1, 0x30,
+ *                     0, 0}; */
+
+
+static struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"milliseconds", required_argument, 0, 'm'},
+        {"origin", no_argument, 0, 'o'},
+        {"raw", no_argument, 0, 'r'},
+        {"readonly", no_argument, 0, 'R'},
+        {"seconds", required_argument, 0, 's'},
+        {"srep", no_argument, 0, 'S'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+/* Indexed by 'timestamp origin' field value */
+static const char * ts_origin_arr[] = {
+    "initialized to zero at power on or by hard reset",
+    "reserved [0x1]",
+    "initialized by SET TIMESTAMP command",
+    "initialized by other method",
+    "reserved [0x4]",
+    "reserved [0x5]",
+    "reserved [0x6]",
+    "reserved [0x7]",
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+            "sg_timestamp  [--help] [--milliseconds=MS] [--origin] [--raw]\n"
+            "                     [--readonly] [--seconds=SEC] [--srep] "
+            "[--verbose]\n"
+            "                     [--version] DEVICE\n");
+    pr2serr("  where:\n"
+            "    --help|-h          print out usage message\n"
+            "    --milliseconds=MS|-m MS    set timestamp to MS "
+            "milliseconds since\n"
+            "                               1970-01-01 00:00:00 UTC\n"
+            "    --origin|-o        show Report timestamp origin "
+            "(def: don't)\n"
+            "    --raw|-r           output Report timestamp response to "
+            "stdout in\n"
+            "                       binary\n"
+            "    --readonly|-R      open DEVICE read only (def: "
+            "read/write)\n"
+            "    --seconds=SEC|-s SEC    set timestamp to SEC "
+            "seconds since\n"
+            "                            1970-01-01 00:00:00 UTC\n"
+            "    --srep|-S          output Report timestamp in seconds "
+            "(def:\n"
+            "                       milliseconds)\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "Performs a SCSI REPORT TIMESTAMP or SET TIMESTAMP command. "
+            "The timestamp\nis SET if either the --milliseconds=MS or "
+            "--seconds=SEC option is given,\notherwise the existing "
+            "timestamp is reported. The DEVICE stores the\ntimestamp as "
+            "the number of milliseconds since power up (or reset) or\n"
+            "since 1970-01-01 00:00:00 UTC which also happens to be the "
+            "time 'epoch'\nof Unix machines. The 'date +%%s' command in "
+            "Unix returns the number of\nseconds since the epoch. To "
+            "convert a reported timestamp (in seconds since\nthe epoch) "
+            "to a more readable form use "
+            "'date --date='@<secs_since_epoch>' .\n");
+}
+
+/* Invokes a SCSI REPORT TIMESTAMP command.  Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_rep_timestamp(int sg_fd, void * resp, int mx_resp_len, int * residp,
+                    int noisy, int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char rtCmdBlk[REP_TIMESTAMP_CMDLEN] =
+          {SG_MAINTENANCE_IN, REP_TIMESTAMP_SA, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be32((uint32_t)mx_resp_len, rtCmdBlk + 6);
+    if (verbose) {
+        pr2serr("    Report timestamp cdb: ");
+        for (k = 0; k < REP_TIMESTAMP_CMDLEN; ++k)
+            pr2serr("%02x ", rtCmdBlk[k]);
+        pr2serr("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("%s: out of memory\n", __func__);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, rtCmdBlk, sizeof(rtCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "report timestamp", res, mx_resp_len,
+                               sense_b, noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    if (residp)
+        *residp = get_scsi_pt_resid(ptvp);
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+
+/* Invokes the SET TIMESTAMP command.  Return of 0 -> success, various
+ * SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_set_timestamp(int sg_fd, void * paramp, int param_len, int noisy,
+                    int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char stCmdBlk[SET_TIMESTAMP_CMDLEN] =
+          {SG_MAINTENANCE_OUT, SET_TIMESTAMP_SA, 0, 0,  0, 0, 0, 0,
+           0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    sg_put_unaligned_be32(param_len, stCmdBlk + 6);
+    if (verbose) {
+        pr2serr("    Set timestamp cdb: ");
+        for (k = 0; k < SET_TIMESTAMP_CMDLEN; ++k)
+            pr2serr("%02x ", stCmdBlk[k]);
+        pr2serr("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2serr("    set timestamp parameter list:\n");
+            dStrHexErr((const char *)paramp, param_len, -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("%s: out of memory\n", __func__);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, stCmdBlk, sizeof(stCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "set timestamp", res, 0, sense_b, noisy,
+                               verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c;
+    int do_origin = 0;
+    int do_set = 0;
+    int do_srep = 0;
+    int do_raw = 0;
+    int readonly = 0;
+    bool secs_given = false;
+    int verbose = 0;
+    uint64_t secs = 0;
+    uint64_t msecs = 0;
+    int64_t ll;
+    const char * device_name = NULL;
+    const char * cmd_name;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hm:orRs:SvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'm':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--milliseconds=MS'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            msecs = (uint64_t)ll;
+            ++do_set;
+            break;
+        case 'o':
+            ++do_origin;
+            break;
+        case 'r':
+            ++do_raw;
+            break;
+        case 'R':
+            ++readonly;
+            break;
+        case 's':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--seconds=SEC'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            secs = (uint64_t)ll;
+            ++do_set;
+            secs_given = true;
+            break;
+        case 'S':
+            ++do_srep;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (do_set > 1) {
+        pr2serr("either --milliseconds=MS or --seconds=SEC may be given, "
+                "not both\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    memset(d_buff, 0, 12);
+    if (do_set) {
+        cmd_name = "Set timestamp";
+        sg_put_unaligned_be48(secs_given ? (secs * 1000) : msecs, d_buff + 4);
+        res = sg_ll_set_timestamp(sg_fd, d_buff, 12, 1, verbose);
+    } else {
+        cmd_name = "Report timestamp";
+        res = sg_ll_rep_timestamp(sg_fd, d_buff, 12, NULL, 1, verbose);
+        if (0 == res) {
+            if (do_raw)
+                dStrRaw((const char *)d_buff, 12);
+            else {
+                int len = sg_get_unaligned_be16(d_buff + 0);
+                if (len < 8)
+                    pr2serr("timestamp parameter data length too short, "
+                            "expect >= 10, got %d\n", len + 2);
+                else {
+                    if (do_origin)
+                        printf("Device clock %s\n",
+                               ts_origin_arr[0x7 & d_buff[2]]);
+                    msecs = sg_get_unaligned_be48(d_buff + 4);
+                    printf("%" PRIu64 "\n", do_srep ? (msecs / 1000) : msecs);
+                }
+            }
+        }
+    }
+    ret = res;
+    if (res) {
+        if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr("%s command not supported\n", cmd_name);
+        else {
+            char b[80];
+
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("%s command: %s\n", cmd_name, b);
+        }
+    }
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_turs.c b/sg3_utils/src/sg_turs.c
new file mode 100644
index 0000000..1d6cc42
--- /dev/null
+++ b/sg3_utils/src/sg_turs.c
@@ -0,0 +1,383 @@
+/* This program sends a user specified number of TEST UNIT READY commands
+ * to the given sg device. Since TUR is a simple command involing no
+ * data transfer (and no REQUEST SENSE command iff the unit is ready)
+ * then this can be used for timing per SCSI command overheads.
+ *
+ * Copyright (C) 2000-2015 D. Gilbert
+ * 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.
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef SG_LIB_MINGW
+#include <sys/time.h>
+#endif
+
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "3.31 20151220";
+
+#if defined(MSC_VER) || defined(__MINGW32__)
+#define HAVE_MS_SLEEP
+#endif
+
+#ifdef HAVE_MS_SLEEP
+#include <windows.h>
+#define sleep_for(seconds)    Sleep( (seconds) * 1000)
+#else
+#define sleep_for(seconds)    sleep(seconds)
+#endif
+
+static struct option long_options[] = {
+        {"help", 0, 0, 'h'},
+        {"new", 0, 0, 'N'},
+        {"number", 1, 0, 'n'},
+        {"old", 0, 0, 'O'},
+        {"progress", 0, 0, 'p'},
+        {"time", 0, 0, 't'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int do_help;
+    int do_number;
+    int do_progress;
+    int do_time;
+    int do_verbose;
+    int do_version;
+    const char * device_name;
+    int opt_new;
+};
+
+
+static void usage()
+{
+    printf("Usage: sg_turs [--help] [--number=NUM] [--progress] [--time] "
+           "[--verbose]\n"
+           "               [--version] DEVICE\n"
+           "  where:\n"
+           "    --help|-h        print usage message then exit\n"
+           "    --number=NUM|-n NUM    number of test_unit_ready commands "
+           "(def: 1)\n"
+           "    --progress|-p    outputs progress indication (percentage) "
+           "if available\n"
+           "    --time|-t        outputs total duration and commands per "
+           "second\n"
+           "    --verbose|-v     increase verbosity\n"
+           "    --version|-V     print version string then exit\n\n"
+           "Performs a SCSI TEST UNIT READY command (or many of them)\n");
+}
+
+static void usage_old()
+{
+    printf("Usage: sg_turs [-n=NUM] [-p] [-t] [-v] [-V] "
+           "DEVICE\n"
+           "  where:\n"
+           "    -n=NUM    number of test_unit_ready commands "
+           "(def: 1)\n"
+           "    -p        outputs progress indication (percentage) "
+           "if available\n"
+           "    -t        outputs total duration and commands per "
+           "second\n"
+           "    -v        increase verbosity\n"
+           "    -V        print version string then exit\n\n"
+           "Performs a SCSI TEST UNIT READY command (or many of them)\n");
+}
+
+static void usage_for(const struct opts_t * op)
+{
+    if (op->opt_new)
+        usage();
+    else
+        usage_old();
+}
+
+static int process_cl_new(struct opts_t * op, int argc, char * argv[])
+{
+    int c, n;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "hn:NOptvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            ++op->do_help;
+            break;
+        case 'n':
+            n = sg_get_num(optarg);
+            if (n < 0) {
+                pr2serr("bad argument to '--number='\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->do_number = n;
+            break;
+        case 'N':
+            break;      /* ignore */
+        case 'O':
+            op->opt_new = 0;
+            return 0;
+        case 'p':
+            ++op->do_progress;
+            break;
+        case 't':
+            ++op->do_time;
+            break;
+        case 'v':
+            ++op->do_verbose;
+            break;
+        case 'V':
+            ++op->do_version;
+            break;
+        default:
+            pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+            if (op->do_help)
+                break;
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int process_cl_old(struct opts_t * op, int argc, char * argv[])
+{
+    int k, jmp_out, plen;
+    const char * cp;
+
+    for (k = 1; k < argc; ++k) {
+        cp = argv[k];
+        plen = strlen(cp);
+        if (plen <= 0)
+            continue;
+        if ('-' == *cp) {
+            for (--plen, ++cp, jmp_out = 0; plen > 0; --plen, ++cp) {
+                switch (*cp) {
+                case 'N':
+                    op->opt_new = 1;
+                    return 0;
+                case 'O':
+                    break;
+                case 'p':
+                    ++op->do_progress;
+                    break;
+                case 't':
+                    ++op->do_time;
+                    break;
+                case 'v':
+                    ++op->do_verbose;
+                    break;
+                case 'V':
+                    ++op->do_verbose;
+                    break;
+                case '?':
+                    usage_old();
+                    return 0;
+                default:
+                    jmp_out = 1;
+                    break;
+                }
+                if (jmp_out)
+                    break;
+            }
+            if (plen <= 0)
+                continue;
+            if (0 == strncmp("n=", cp, 2)) {
+                op->do_number = sg_get_num(cp + 2);
+                if (op->do_number <= 0) {
+                    printf("Couldn't decode number after 'n=' option\n");
+                    usage_old();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else if (0 == strncmp("-old", cp, 4))
+                ;
+            else if (jmp_out) {
+                pr2serr("Unrecognized option: %s\n", cp);
+                usage_old();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == op->device_name)
+            op->device_name = cp;
+        else {
+            pr2serr("too many arguments, got: %s, not expecting: %s\n",
+                    op->device_name, cp);
+            usage_old();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    return 0;
+}
+
+static int process_cl(struct opts_t * op, int argc, char * argv[])
+{
+    int res;
+    char * cp;
+
+    cp = getenv("SG3_UTILS_OLD_OPTS");
+    if (cp) {
+        op->opt_new = 0;
+        res = process_cl_old(op, argc, argv);
+        if ((0 == res) && op->opt_new)
+            res = process_cl_new(op, argc, argv);
+    } else {
+        op->opt_new = 1;
+        res = process_cl_new(op, argc, argv);
+        if ((0 == res) && (0 == op->opt_new))
+            res = process_cl_old(op, argc, argv);
+    }
+    return res;
+}
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, k, res, progress, pr, rem;
+    int num_errs = 0;
+    int reported = 0;
+    int ret = 0;
+#ifndef SG_LIB_MINGW
+    struct timeval start_tm, end_tm;
+#endif
+    struct opts_t opts;
+    struct opts_t * op;
+    char b[80];
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->do_number = 1;
+    res = process_cl(op, argc, argv);
+    if (res)
+        return SG_LIB_SYNTAX_ERROR;
+    if (op->do_help) {
+        usage_for(op);
+        return 0;
+    }
+    if (op->do_version) {
+        pr2serr("Version string: %s\n", version_str);
+        return 0;
+    }
+
+    if (NULL == op->device_name) {
+        pr2serr("No DEVICE argument given\n");
+        usage_for(op);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if ((sg_fd = sg_cmds_open_device(op->device_name, 1 /* ro */,
+                                     op->do_verbose)) < 0) {
+        pr2serr("sg_turs: error opening file: %s: %s\n", op->device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (op->do_progress) {
+        for (k = 0; k < op->do_number; ++k) {
+            if (k > 0)
+                sleep_for(30);
+            progress = -1;
+            res = sg_ll_test_unit_ready_progress(sg_fd, k, &progress,
+                     ((1 == op->do_number) ? 1 : 0), op->do_verbose);
+            if (progress < 0) {
+                ret = res;
+                break;
+            } else {
+                pr = (progress * 100) / 65536;
+                rem = ((progress * 100) % 65536) / 656;
+                printf("Progress indication: %d.%02d%% done\n", pr, rem);
+            }
+        }
+        if (op->do_number > 1)
+            printf("Completed %d Test Unit Ready commands\n",
+                   ((k < op->do_number) ? k + 1 : k));
+    } else {
+#ifndef SG_LIB_MINGW
+        if (op->do_time) {
+            start_tm.tv_sec = 0;
+            start_tm.tv_usec = 0;
+            gettimeofday(&start_tm, NULL);
+        }
+#endif
+        for (k = 0; k < op->do_number; ++k) {
+            /* Might get Unit Attention on first invocation */
+            res = sg_ll_test_unit_ready(sg_fd, k, (0 == k), op->do_verbose);
+            if (res) {
+                ++num_errs;
+                ret = res;
+                if (1 == op->do_number) {
+                    if (SG_LIB_CAT_NOT_READY == res)
+                        printf("device not ready\n");
+                    else {
+                        sg_get_category_sense_str(res, sizeof(b), b,
+                                                  op->do_verbose);
+                        printf("%s\n", b);
+                    }
+                    reported = 1;
+                    break;
+                }
+            }
+        }
+#ifndef SG_LIB_MINGW
+        if ((op->do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
+            struct timeval res_tm;
+            double a, b;
+
+            gettimeofday(&end_tm, NULL);
+            res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+            res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+            if (res_tm.tv_usec < 0) {
+                --res_tm.tv_sec;
+                res_tm.tv_usec += 1000000;
+            }
+            a = res_tm.tv_sec;
+            a += (0.000001 * res_tm.tv_usec);
+            b = (double)op->do_number;
+            printf("time to perform commands was %d.%06d secs",
+                   (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+            if (a > 0.00001)
+                printf("; %.2f operations/sec\n", b / a);
+            else
+                printf("\n");
+        }
+#endif
+
+        if (((op->do_number > 1) || (num_errs > 0)) && (! reported))
+            printf("Completed %d Test Unit Ready commands with %d errors\n",
+                   op->do_number, num_errs);
+    }
+    sg_cmds_close_device(sg_fd);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_unmap.c b/sg3_utils/src/sg_unmap.c
new file mode 100644
index 0000000..024499d
--- /dev/null
+++ b/sg3_utils/src/sg_unmap.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2009-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <limits.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ * This utility invokes the UNMAP SCSI command to unmap one or more
+ * logical blocks.
+ */
+
+static const char * version_str = "1.10 20160201";
+
+
+#define DEF_TIMEOUT_SECS 60
+#define MAX_NUM_ADDR 128
+
+#ifndef UINT32_MAX
+#define UINT32_MAX ((uint32_t)-1)
+#endif
+
+
+static struct option long_options[] = {
+        {"anchor", no_argument, 0, 'a'},
+        {"grpnum", required_argument, 0, 'g'},
+        {"help", no_argument, 0, 'h'},
+        {"in", required_argument, 0, 'I'},
+        {"lba", required_argument, 0, 'l'},
+        {"num", required_argument, 0, 'n'},
+        {"timeout", required_argument, 0, 't'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+          "sg_unmap [--anchor] [--grpnum=GN] [--help] [--in=FILE]\n"
+          "                [--lba=LBA,LBA...] [--num=NUM,NUM...] "
+          "[--timeout=TO]\n"
+          "                [--verbose] [--version] DEVICE\n"
+          "  where:\n"
+          "    --anchor|-a          set anchor field in cdb\n"
+          "    --grpnum=GN|-g GN    GN is group number field (def: 0)\n"
+          "    --help|-h            print out usage message\n"
+          "    --in=FILE|-I FILE    read LBA, NUM pairs from FILE (if "
+          "FILE is '-'\n"
+          "                         then stdin is read)\n"
+          "    --lba=LBA,LBA...|-l LBA,LBA...    LBA is the logical block "
+          "address\n"
+          "                                      to start NUM unmaps\n"
+          "    --num=NUM,NUM...|-n NUM,NUM...    NUM is number of logical "
+          "blocks to\n"
+          "                                      unmap starting at "
+          "corresponding LBA\n"
+          "    --timeout=TO|-t TO    command timeout (unit: seconds) "
+          "(def: 60)\n"
+          "    --verbose|-v         increase verbosity\n"
+          "    --version|-V         print version string and exit\n\n"
+          "Perform a SCSI UNMAP command. LBA, NUM and the values in FILE "
+          "are assumed\n"
+          "to be decimal. Use '0x' prefix or 'h' suffix for hex values.\n"
+          "Example to unmap LBA 0x12345:\n"
+          "    sg_unmap --lba=0x12345 --num=1 /dev/sdb\n"
+          );
+}
+
+#if 0
+/* Trying to decode multipliers as sg_get_llnum() [in sg_libs] does would
+ * only confuse things here, so use this local trimmed version */
+static int64_t
+get_llnum(const char * buf)
+{
+    int res, len;
+    int64_t num;
+    uint64_t unum;
+
+    if ((NULL == buf) || ('\0' == buf[0]))
+        return -1LL;
+    len = strspn(buf, "0123456789aAbBcCdDeEfFhHxX");
+    if (0 == len)
+        return -1LL;
+    if (('0' == buf[0]) && (('x' == buf[1]) || ('X' == buf[1]))) {
+        res = sscanf(buf + 2, "%" SCNx64 "", &unum);
+        num = unum;
+    } else if ('H' == toupper(buf[len - 1])) {
+        res = sscanf(buf, "%" SCNx64 "", &unum);
+        num = unum;
+    } else
+        res = sscanf(buf, "%" SCNd64 "", &num);
+    if (1 == res)
+        return num;
+    else
+        return -1LL;
+}
+#endif
+
+/* Read numbers (up to 64 bits in size) from command line (comma (or
+ * (single) space) separated list). Assumed decimal unless prefixed
+ * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
+ * Returns 0 if ok, or 1 if error. */
+static int
+build_lba_arr(const char * inp, uint64_t * lba_arr, int * lba_arr_len,
+              int max_arr_len)
+{
+    int in_len, k;
+    const char * lcp;
+    int64_t ll;
+    char * cp;
+    char * c2p;
+
+    if ((NULL == inp) || (NULL == lba_arr) ||
+        (NULL == lba_arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *lba_arr_len = 0;
+    if ('-' == inp[0]) {        /* read from stdin */
+        pr2serr("'--lba' cannot be read from stdin\n");
+        return 1;
+    } else {        /* list of numbers (default decimal) on command line */
+        k = strspn(inp, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP, ");
+        if (in_len != k) {
+            pr2serr("build_lba_arr: error at pos %d\n", k + 1);
+            return 1;
+        }
+        for (k = 0; k < max_arr_len; ++k) {
+            ll = sg_get_llnum(lcp);
+            if (-1 != ll) {
+                lba_arr[k] = (uint64_t)ll;
+                cp = (char *)strchr(lcp, ',');
+                c2p = (char *)strchr(lcp, ' ');
+                if (NULL == cp)
+                    cp = c2p;
+                if (NULL == cp)
+                    break;
+                if (c2p && (c2p < cp))
+                    cp = c2p;
+                lcp = cp + 1;
+            } else {
+                pr2serr("build_lba_arr: error at pos %d\n",
+                        (int)(lcp - inp + 1));
+                return 1;
+            }
+        }
+        *lba_arr_len = k + 1;
+        if (k == max_arr_len) {
+            pr2serr("build_lba_arr: array length exceeded\n");
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/* Read numbers (up to 32 bits in size) from command line (comma (or
+ * (single) space) separated list). Assumed decimal unless prefixed
+ * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
+ * Returns 0 if ok, or 1 if error. */
+static int
+build_num_arr(const char * inp, uint32_t * num_arr,
+              int * num_arr_len, int max_arr_len)
+{
+    int in_len, k;
+    const char * lcp;
+    int64_t ll;
+    char * cp;
+    char * c2p;
+
+    if ((NULL == inp) || (NULL == num_arr) ||
+        (NULL == num_arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *num_arr_len = 0;
+    if ('-' == inp[0]) {        /* read from stdin */
+        pr2serr("'--len' cannot be read from stdin\n");
+        return 1;
+    } else {        /* list of numbers (default decimal) on command line */
+        k = strspn(inp, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP, ");
+        if (in_len != k) {
+            pr2serr("build_num_arr: error at pos %d\n", k + 1);
+            return 1;
+        }
+        for (k = 0; k < max_arr_len; ++k) {
+            ll = sg_get_llnum(lcp);
+            if (-1 != ll) {
+                if (ll > UINT32_MAX) {
+                    pr2serr("build_num_arr: number exceeds 32 bits at pos "
+                            "%d\n", (int)(lcp - inp + 1));
+                    return 1;
+                }
+                num_arr[k] = (uint32_t)ll;
+                cp = (char *)strchr(lcp, ',');
+                c2p = (char *)strchr(lcp, ' ');
+                if (NULL == cp)
+                    cp = c2p;
+                if (NULL == cp)
+                    break;
+                if (c2p && (c2p < cp))
+                    cp = c2p;
+                lcp = cp + 1;
+            } else {
+                pr2serr("build_num_arr: error at pos %d\n",
+                        (int)(lcp - inp + 1));
+                return 1;
+            }
+        }
+        *num_arr_len = k + 1;
+        if (k == max_arr_len) {
+            pr2serr("build_num_arr: array length exceeded\n");
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+/* Read numbers from filename (or stdin) line by line (comma (or
+ * (single) space) separated list). Assumed decimal unless prefixed
+ * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
+ * Returns 0 if ok, or 1 if error. */
+static int
+build_joint_arr(const char * file_name, uint64_t * lba_arr, uint32_t * num_arr,
+              int * arr_len, int max_arr_len)
+{
+    char line[1024];
+    int off = 0;
+    int in_len, k, j, m, ind, bit0;
+    bool have_stdin;
+    char * lcp;
+    FILE * fp;
+    int64_t ll;
+
+    have_stdin = ((1 == strlen(file_name)) && ('-' == file_name[0]));
+    if (have_stdin)
+        fp = stdin;
+    else {
+        fp = fopen(file_name, "r");
+        if (NULL == fp) {
+            pr2serr("%s: unable to open %s\n", __func__, file_name);
+            return 1;
+        }
+    }
+
+    for (j = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        // could improve with carry_over logic if sizeof(line) too small
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+            }
+        }
+        if (in_len < 1)
+            continue;
+        lcp = line;
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP ,\t");
+        if ((k < in_len) && ('#' != lcp[k])) {
+            pr2serr("%s: syntax error at line %d, pos %d\n", __func__, j + 1,
+                    m + k + 1);
+            goto bad_exit;
+        }
+        for (k = 0; k < 1024; ++k) {
+            ll = sg_get_llnum(lcp);
+            if (-1 != ll) {
+                ind = ((off + k) >> 1);
+                bit0 = 0x1 & (off + k);
+                if (ind >= max_arr_len) {
+                    pr2serr("%s: array length exceeded\n", __func__);
+                    goto bad_exit;
+                }
+                if (bit0) {
+                    if (ll > UINT32_MAX) {
+                        pr2serr("%s: number exceeds 32 bits in line %d, at "
+                                "pos %d\n", __func__, j + 1,
+                                (int)(lcp - line + 1));
+                        goto bad_exit;
+                    }
+                    num_arr[ind] = (uint32_t)ll;
+                } else
+                   lba_arr[ind] = (uint64_t)ll;
+                lcp = strpbrk(lcp, " ,\t");
+                if (NULL == lcp)
+                    break;
+                lcp += strspn(lcp, " ,\t");
+                if ('\0' == *lcp)
+                    break;
+            } else {
+                if ('#' == *lcp) {
+                    --k;
+                    break;
+                }
+                pr2serr("%s: error on line %d, at pos %d\n", __func__, j + 1,
+                        (int)(lcp - line + 1));
+                goto bad_exit;
+            }
+        }
+        off += (k + 1);
+    }
+    if (0x1 & off) {
+        pr2serr("%s: expect LBA,NUM pairs but decoded odd number\n  from "
+                "%s\n", __func__, have_stdin ? "stdin" : file_name);
+        goto bad_exit;
+    }
+    *arr_len = off >> 1;
+    if (fp && (stdin != fp))
+        fclose(fp);
+    return 0;
+
+bad_exit:
+    if (fp && (stdin != fp))
+        fclose(fp);
+    return 1;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, num, k, j;
+    int grpnum = 0;
+    const char * lba_op = NULL;
+    const char * num_op = NULL;
+    const char * in_op = NULL;
+    int addr_arr_len = 0;
+    int num_arr_len = 0;
+    int anchor = 0;
+    int timeout = DEF_TIMEOUT_SECS;
+    int verbose = 0;
+    const char * device_name = NULL;
+    uint64_t addr_arr[MAX_NUM_ADDR];
+    uint32_t num_arr[MAX_NUM_ADDR];
+    unsigned char param_arr[8 + (MAX_NUM_ADDR * 16)];
+    int param_len = 4;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ag:hIHl:n:t:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            ++anchor;
+            break;
+        case 'g':
+            num = sscanf(optarg, "%d", &res);
+            if ((1 == num) && (res >= 0) && (res <= 31))
+                grpnum = res;
+            else {
+                pr2serr("value for '--grpnum=' must be 0 to 31\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'I':
+            in_op = optarg;
+            break;
+        case 'l':
+            lba_op = optarg;
+            break;
+        case 'n':
+            num_op = optarg;
+            break;
+        case 't':
+            timeout = sg_get_num(optarg);
+            if (timeout < 0)  {
+                pr2serr("bad argument to '--timeout'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else if (0 == timeout)
+                timeout = DEF_TIMEOUT_SECS;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (in_op && (lba_op || num_op)) {
+        pr2serr("expect '--in=' by itself, or both '--lba=' and '--num='\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    } else if (in_op || (lba_op && num_op))
+        ;
+    else {
+        if (lba_op)
+            pr2serr("since '--lba=' is given, also need '--num='\n");
+        else
+            pr2serr("expect either both '--lba=' and '--num=', or "
+                    "'--in=' by itself\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    memset(addr_arr, 0, sizeof(addr_arr));
+    memset(num_arr, 0, sizeof(num_arr));
+    addr_arr_len = 0;
+    if (lba_op && num_op) {
+        if (0 != build_lba_arr(lba_op, addr_arr, &addr_arr_len,
+                               MAX_NUM_ADDR)) {
+            pr2serr("bad argument to '--lba'\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (0 != build_num_arr(num_op, num_arr, &num_arr_len,
+                               MAX_NUM_ADDR)) {
+            pr2serr("bad argument to '--num'\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if ((addr_arr_len != num_arr_len) || (num_arr_len <= 0)) {
+            pr2serr("need same number of arguments to '--lba=' "
+                    "and '--num=' options\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (in_op) {
+        if (0 != build_joint_arr(in_op, addr_arr, num_arr, &addr_arr_len,
+                                 MAX_NUM_ADDR)) {
+            pr2serr("bad argument to '--in'\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (addr_arr_len <= 0) {
+            pr2serr("no addresses found in '--in=' argument, file: %s\n",
+                    in_op);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    param_len = 8 + (16 * addr_arr_len);
+    memset(param_arr, 0, param_len);
+    k = 8;
+    for (j = 0; j < addr_arr_len; ++j) {
+        sg_put_unaligned_be64(addr_arr[j], param_arr + k);
+        k += 8;
+        sg_put_unaligned_be32(num_arr[j], param_arr + k);
+        k += 4 + 4;
+    }
+    k = 0;
+    num = param_len - 2;
+    sg_put_unaligned_be16((uint16_t)num, param_arr + k);
+    k += 2;
+    num = param_len - 8;
+    sg_put_unaligned_be16((uint16_t)num, param_arr + k);
+
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    res = sg_ll_unmap_v2(sg_fd, anchor, grpnum, timeout, param_arr, param_len,
+                         1, verbose);
+    ret = res;
+    if (SG_LIB_CAT_NOT_READY == res) {
+        pr2serr("UNMAP failed, device not ready\n");
+        goto err_out;
+    } else if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+        pr2serr("UNMAP, unit attention\n");
+        goto err_out;
+    } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
+        pr2serr("UNMAP, aborted command\n");
+        goto err_out;
+    } else if (SG_LIB_CAT_INVALID_OP == res) {
+        pr2serr("UNMAP not supported\n");
+        goto err_out;
+    } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
+        pr2serr("bad field in UNMAP cdb\n");
+        goto err_out;
+    } else if (0 != res) {
+        pr2serr("UNMAP failed (use '-v' to get more information)\n");
+        goto err_out;
+    }
+
+err_out:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_verify.c b/sg3_utils/src/sg_verify.c
new file mode 100644
index 0000000..47c3d8c
--- /dev/null
+++ b/sg3_utils/src/sg_verify.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2004-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pr2serr.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ * This program issues the SCSI VERIFY(10) or VERIFY(16) command to the given
+ * SCSI block device.
+ *
+ * N.B. This utility should, but doesn't, check the logical block size with
+ * the SCSI READ CAPACITY command. It is up to the user to make sure that
+ * the count of blocks requested and the number of bytes transferred (when
+ * BYTCHK>0) are "in sync". That caclculation is somewhat complicated by
+ * the possibility of protection data (DIF).
+ */
+
+static const char * version_str = "1.21 20140516";    /* sbc4r01 */
+
+#define ME "sg_verify: "
+
+#define EBUFF_SZ 256
+
+
+static struct option long_options[] = {
+        {"16", no_argument, 0, 'S'},
+        {"bpc", required_argument, 0, 'b'},
+        {"bytchk", required_argument, 0, 'B'},
+        {"count", required_argument, 0, 'c'},
+        {"dpo", no_argument, 0, 'd'},
+        {"ebytchk", required_argument, 0, 'E'},
+        {"group", required_argument, 0, 'g'},
+        {"help", no_argument, 0, 'h'},
+        {"in", required_argument, 0, 'i'},
+        {"lba", required_argument, 0, 'l'},
+        {"nbo", required_argument, 0, 'n'},
+        {"quiet", no_argument, 0, 'q'},
+        {"readonly", no_argument, 0, 'r'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {"vrprotect", required_argument, 0, 'P'},
+        {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_verify [--16] [--bpc=BPC] [--count=COUNT] [--dpo] "
+            "[--ebytchk=BCH]\n"
+            "                 [--group=GN] [--help] [--in=IF] "
+            "[--lba=LBA] [--ndo=NDO]\n"
+            "                 [--quiet] [--readonly] [--verbose] "
+            "[--version]\n"
+            "                 [--vrprotect=VRP] DEVICE\n"
+            "  where:\n"
+            "    --16|-S             use VERIFY(16) (def: use "
+            "VERIFY(10) )\n"
+            "    --bpc=BPC|-b BPC    max blocks per verify command "
+            "(def: 128)\n"
+            "    --count=COUNT|-c COUNT    count of blocks to verify "
+            "(def: 1).\n"
+            "                              If BCH=3 then COUNT must "
+            "be 1 .\n"
+            "    --dpo|-d            disable page out (cache retention "
+            "priority)\n"
+            "    --ebytchk=BCH|-E BCH    sets BYTCHK value, either 1, 2 "
+            "or 3 (def: 0).\n"
+            "                            BCH overrides BYTCHK=1 set by "
+            "'--ndo='. If\n"
+            "                            BCH is 3 then NDO must be the LBA "
+            "size\n"
+            "                            (plus protection size if DIF "
+            "active)\n"
+            "    --group=GN|-g GN    set group number field to GN (def: 0)\n"
+            "    --help|-h           print out usage message\n"
+            "    --in=IF|-i IF       input from file called IF (def: "
+            "stdin)\n"
+            "                        only active if --bytchk=N given\n"
+            "    --lba=LBA|-l LBA    logical block address to start "
+            "verify (def: 0)\n"
+            "    --ndo=NDO|-n NDO    NDO is number of bytes placed in "
+            "data-out buffer.\n"
+            "                        These are fetched from IF (or "
+            "stdin) and used\n"
+            "                        to verify the device data against. "
+            "Forces\n"
+            "                        --bpc=COUNT. Sets BYTCHK (byte check) "
+            "to 1\n"
+            "    --quiet|-q          suppress miscompare report to stderr, "
+            "still\n"
+            "                        causes an exit status of 14\n"
+            "    --readonly|-r       open DEVICE read-only (def: open it "
+            "read-write)\n"
+            "    --verbose|-v        increase verbosity\n"
+            "    --version|-V        print version string and exit\n"
+            "    --vrprotect=VRP|-P VRP    set vrprotect field to VRP "
+            "(def: 0)\n"
+            "Performs one or more SCSI VERIFY(10) or SCSI VERIFY(16) "
+            "commands. sbc3r34\nmade the BYTCHK field two bits wide "
+            "(it was a single bit).\n");
+}
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, num, nread, infd;
+    int64_t ll;
+    int dpo = 0;
+    int bytchk = 0;
+    int ndo = 0;
+    char *ref_data = NULL;
+    int vrprotect = 0;
+    int64_t count = 1;
+    int64_t orig_count;
+    int bpc = 128;
+    int bpc_given = 0;
+    int got_stdin = 0;
+    int group = 0;
+    uint64_t lba = 0;
+    uint64_t orig_lba;
+    int quiet = 0;
+    int readonly = 0;
+    int verbose = 0;
+    int verify16 = 0;
+    const char * device_name = NULL;
+    const char * file_name = NULL;
+    const char * vc;
+    int ret = 0;
+    unsigned int info = 0;
+    uint64_t info64 = 0;
+    char ebuff[EBUFF_SZ];
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "b:B:c:dE:g:hi:l:n:P:qrSvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            bpc = sg_get_num(optarg);
+            if (bpc < 1) {
+                pr2serr("bad argument to '--bpc'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++bpc_given;
+            break;
+        case 'c':
+            count = sg_get_llnum(optarg);
+            if (count < 0) {
+                pr2serr("bad argument to '--count'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'd':
+            dpo = 1;
+            break;
+        case 'E':
+            bytchk = sg_get_num(optarg);
+            if ((bytchk < 1) || (bytchk > 3)) {
+                pr2serr("bad argument to '--ebytchk'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'g':
+            group = sg_get_num(optarg);
+            if ((group < 0) || (group > 31)) {
+                pr2serr("bad argument to '--group'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'i':
+            file_name = optarg;
+            break;
+        case 'l':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            lba = (uint64_t)ll;
+            break;
+        case 'n':
+        case 'B':       /* undocumented, old --bytchk=NDO option */
+            ndo = sg_get_num(optarg);
+            if (ndo < 1) {
+                pr2serr("bad argument to '--ndo'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'P':
+            vrprotect = sg_get_num(optarg);
+            if (-1 == vrprotect) {
+                pr2serr("bad argument to '--vrprotect'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if ((vrprotect < 0) || (vrprotect > 7)) {
+                pr2serr("'--vrprotect' requires a value from 0 to 7 "
+                        "(inclusive)\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'q':
+            ++quiet;
+            break;
+        case 'r':
+            ++readonly;
+            break;
+        case 'S':
+            ++verify16;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (ndo > 0) {
+        if (0 == bytchk)
+            bytchk = 1;
+        if (bpc_given && (bpc != count))
+            pr2serr("'bpc' argument ignored, using --bpc=%" PRIu64 "\n",
+                    count);
+        if (count > 0x7fffffffLL) {
+            pr2serr("count exceed 31 bits, way too large\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if ((3 == bytchk) && (1 != count)) {
+            pr2serr("count must be 1 when bytchk=3\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        bpc = (int)count;
+    } else if (bytchk > 0) {
+        pr2serr("when the 'ebytchk=BCH' option is given, then '--bytchk=NDO' "
+                "must also be given\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if ((bpc > 0xffff) && (0 == verify16)) {
+        pr2serr("'%s' exceeds 65535, so use VERIFY(16)\n",
+                (ndo > 0) ? "count" : "bpc");
+        ++verify16;
+    }
+    if (((lba + count - 1) > 0xffffffffLLU) && (0 == verify16)) {
+        pr2serr("'lba' exceed 32 bits, so use VERIFY(16)\n");
+        ++verify16;
+    }
+    if ((group > 0) && (0 == verify16))
+        pr2serr("group number ignored with VERIFY(10) command, use the --16 "
+                "option\n");
+
+    orig_count = count;
+    orig_lba = lba;
+
+    if (ndo > 0) {
+        ref_data = (char *)malloc(ndo);
+        if (NULL == ref_data) {
+            pr2serr("failed to allocate %d byte buffer\n", ndo);
+            return SG_LIB_FILE_ERROR;
+        }
+        if ((NULL == file_name) || (0 == strcmp(file_name, "-"))) {
+            ++got_stdin;
+            infd = STDIN_FILENO;
+            if (sg_set_binary_mode(STDIN_FILENO) < 0)
+                perror("sg_set_binary_mode");
+        } else {
+            if ((infd = open(file_name, O_RDONLY)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for reading", file_name);
+                perror(ebuff);
+                ret = SG_LIB_FILE_ERROR;
+                goto err_out;
+            } else if (sg_set_binary_mode(infd) < 0)
+                perror("sg_set_binary_mode");
+        }
+        if (verbose && got_stdin)
+                pr2serr("about to wait on STDIN\n");
+        for (nread = 0; nread < ndo; nread += res) {
+            res = read(infd, ref_data + nread, ndo - nread);
+            if (res <= 0) {
+                pr2serr("reading from %s failed at file offset=%d\n",
+                        (got_stdin ? "stdin" : file_name), nread);
+                ret = SG_LIB_FILE_ERROR;
+                goto err_out;
+            }
+        }
+        if (! got_stdin)
+            close(infd);
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        ret = SG_LIB_SYNTAX_ERROR;
+        goto err_out;
+    }
+    sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        ret = SG_LIB_FILE_ERROR;
+        goto err_out;
+    }
+
+    vc = verify16 ? "VERIFY(16)" : "VERIFY(10)";
+    for (; count > 0; count -= bpc, lba += bpc) {
+        num = (count > bpc) ? bpc : count;
+        if (verify16)
+            res = sg_ll_verify16(sg_fd, vrprotect, dpo, bytchk,
+                                 lba, num, group, ref_data,
+                                 ndo, &info64, !quiet , verbose);
+        else
+            res = sg_ll_verify10(sg_fd, vrprotect, dpo, bytchk,
+                                 (unsigned int)lba, num, ref_data,
+                                 ndo, &info, !quiet, verbose);
+        if (0 != res) {
+            char b[80];
+
+            ret = res;
+            switch (res) {
+            case SG_LIB_CAT_ILLEGAL_REQ:
+                pr2serr("bad field in %s cdb, near lba=0x%" PRIx64 "\n", vc,
+                        lba);
+                break;
+            case SG_LIB_CAT_MEDIUM_HARD:
+                pr2serr("%s medium or hardware error near lba=0x%" PRIx64 "\n",
+                        vc, lba);
+                break;
+            case SG_LIB_CAT_MEDIUM_HARD_WITH_INFO:
+                if (verify16)
+                    pr2serr("%s medium or hardware error, reported lba=0x%"
+                            PRIx64 "\n", vc, info64);
+                else
+                    pr2serr("%s medium or hardware error, reported lba=0x%x\n",
+                            vc, info);
+                break;
+            case SG_LIB_CAT_MISCOMPARE:
+                if ((0 == quiet) || verbose)
+                    pr2serr("%s reported MISCOMPARE\n", vc);
+                break;
+            default:
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                pr2serr("%s: %s\n", vc, b);
+                pr2serr("    failed near lba=%" PRIu64 " [0x%" PRIx64 "]\n",
+                        lba, lba);
+                break;
+            }
+            break;
+        }
+    }
+
+    if (verbose && (0 == ret) && (orig_count > 1))
+        pr2serr("Verified %" PRId64 " [0x%" PRIx64 "] blocks from lba %" PRIu64
+                " [0x%" PRIx64 "]\n    without error\n", orig_count,
+                (uint64_t)orig_count, orig_lba, orig_lba);
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            ret = SG_LIB_FILE_ERROR;
+    }
+
+ err_out:
+    if (ref_data)
+        free(ref_data);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_vpd.c b/sg3_utils/src/sg_vpd.c
new file mode 100644
index 0000000..831b0aa
--- /dev/null
+++ b/sg3_utils/src/sg_vpd.c
@@ -0,0 +1,4108 @@
+/*
+ * Copyright (c) 2006-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_pt.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* This utility program was originally written for the Linux OS SCSI subsystem.
+
+   This program fetches Vital Product Data (VPD) pages from the given
+   device and outputs it as directed. VPD pages are obtained via a
+   SCSI INQUIRY command. Most of the data in this program is obtained
+   from the SCSI SPC-4 document at http://www.t10.org .
+
+*/
+
+static const char * version_str = "1.14 20160217";  /* spc5r08 + sbc4r10 */
+
+
+/* These structures are duplicates of those of the same name in
+ * sg_vpd_vendor.c . Take care that both are the same. */
+struct opts_t {
+    int do_all;
+    int do_enum;
+    int do_hex;
+    int num_vpd;
+    int do_ident;
+    int do_long;
+    int maxlen;
+    int do_quiet;
+    int do_raw;
+    int vend_prod_num;
+    int verbose;
+    const char * device_name;
+    const char * page_str;
+    const char * inhex_fn;
+    const char * vend_prod;
+};
+
+struct svpd_values_name_t {
+    int value;       /* VPD page number */
+    int subvalue;    /* to differentiate if value+pdt are not unique */
+    int pdt;         /* peripheral device type id, -1 is the default */
+                     /* (all or not applicable) value */
+    const char * acron;
+    const char * name;
+};
+
+
+void svpd_enumerate_vendor(int vend_prod_num);
+int svpd_count_vendor_vpds(int num_vpd, int vend_prod_num);
+int svpd_decode_vendor(int sg_fd, struct opts_t * op, int off);
+const struct svpd_values_name_t * svpd_find_vendor_by_acron(const char * ap);
+int svpd_find_vp_num_by_acron(const char * vp_ap);
+const struct svpd_values_name_t * svpd_find_vendor_by_num(int page_num,
+                                                          int vend_prod_num);
+int vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page,
+                            int mxlen, int vb, int * rlenp);
+
+
+/* standard VPD pages, in ascending page number order */
+#define VPD_SUPPORTED_VPDS 0x0
+#define VPD_UNIT_SERIAL_NUM 0x80
+#define VPD_IMP_OP_DEF 0x81     /* obsolete in SPC-2 */
+#define VPD_ASCII_OP_DEF 0x82   /* obsolete in SPC-2 */
+#define VPD_DEVICE_ID 0x83
+#define VPD_SOFTW_INF_ID 0x84
+#define VPD_MAN_NET_ADDR 0x85
+#define VPD_EXT_INQ 0x86
+#define VPD_MODE_PG_POLICY 0x87
+#define VPD_SCSI_PORTS 0x88
+#define VPD_ATA_INFO 0x89
+#define VPD_POWER_CONDITION 0x8a
+#define VPD_DEVICE_CONSTITUENTS 0x8b
+#define VPD_CFA_PROFILE_INFO 0x8c
+#define VPD_POWER_CONSUMPTION  0x8d
+#define VPD_3PARTY_COPY 0x8f    /* 3PC, XCOPY, SPC-4, SBC-3 */
+#define VPD_PROTO_LU 0x90
+#define VPD_PROTO_PORT 0x91
+#define VPD_BLOCK_LIMITS 0xb0   /* SBC-3 */
+#define VPD_SA_DEV_CAP 0xb0     /* SSC-3 */
+#define VPD_OSD_INFO 0xb0       /* OSD */
+#define VPD_BLOCK_DEV_CHARS 0xb1 /* SBC-3 */
+#define VPD_MAN_ASS_SN 0xb1     /* SSC-3, ADC-2 */
+#define VPD_SECURITY_TOKEN 0xb1 /* OSD */
+#define VPD_TA_SUPPORTED 0xb2   /* SSC-3 */
+#define VPD_LB_PROVISIONING 0xb2   /* SBC-3 */
+#define VPD_REFERRALS 0xb3   /* SBC-3 */
+#define VPD_AUTOMATION_DEV_SN 0xb3   /* SSC-3 */
+#define VPD_SUP_BLOCK_LENS 0xb4 /* SBC-4 */
+#define VPD_DTDE_ADDRESS 0xb4   /* SSC-4 */
+#define VPD_BLOCK_DEV_C_EXTENS 0xb5 /* SBC-4 */
+#define VPD_LB_PROTECTION 0xb5  /* SSC-5 */
+#define VPD_ZBC_DEV_CHARS 0xb6  /* ZBC */
+#define VPD_BLOCK_LIMITS_EXT 0xb7   /* SBC-4 */
+#define VPD_NO_RATHER_STD_INQ -2      /* request for standard inquiry */
+
+/* Device identification VPD page associations */
+#define VPD_ASSOC_LU 0
+#define VPD_ASSOC_TPORT 1
+#define VPD_ASSOC_TDEVICE 2
+
+/* values for selection one or more associations (2**vpd_assoc),
+   except _AS_IS */
+#define VPD_DI_SEL_LU 1
+#define VPD_DI_SEL_TPORT 2
+#define VPD_DI_SEL_TARGET 4
+#define VPD_DI_SEL_AS_IS 32
+
+#define DEF_ALLOC_LEN 252
+#define MX_ALLOC_LEN (0xc000 + 0x80)
+#define VPD_ATA_INFO_LEN  572
+
+#define SENSE_BUFF_LEN  64       /* Arbitrary, could be larger */
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+#define DEF_PT_TIMEOUT  60       /* 60 seconds */
+
+
+unsigned char rsp_buff[MX_ALLOC_LEN + 2];
+
+static int decode_dev_ids(const char * print_if_found, unsigned char * buff,
+                          int len, int m_assoc, int m_desig_type,
+                          int m_code_set, const struct opts_t * op);
+static void decode_transport_id(const char * leadin, unsigned char * ucp,
+                                int len);
+
+static struct option long_options[] = {
+        {"all", no_argument, 0, 'a'},
+        {"enumerate", no_argument, 0, 'e'},
+        {"help", no_argument, 0, 'h'},
+        {"hex", no_argument, 0, 'H'},
+        {"ident", no_argument, 0, 'i'},
+        {"inhex", required_argument, 0, 'I'},
+        {"long", no_argument, 0, 'l'},
+        {"maxlen", required_argument, 0, 'm'},
+        {"page", required_argument, 0, 'p'},
+        {"quiet", no_argument, 0, 'q'},
+        {"raw", no_argument, 0, 'r'},
+        {"vendor", required_argument, 0, 'M'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+
+/* arranged in alphabetical order by acronym */
+static struct svpd_values_name_t standard_vpd_pg[] = {
+    {VPD_ATA_INFO, 0, -1, "ai", "ATA information (SAT)"},
+    {VPD_ASCII_OP_DEF, 0, -1, "aod",
+     "ASCII implemented operating definition (obsolete)"},
+    {VPD_AUTOMATION_DEV_SN, 0, 1, "adsn", "Automation device serial "
+     "number (SSC)"},
+    {VPD_BLOCK_LIMITS, 0, 0, "bl", "Block limits (SBC)"},
+    {VPD_BLOCK_LIMITS_EXT, 0, 0, "ble", "Block limits extension (SBC)"},
+    {VPD_BLOCK_DEV_CHARS, 0, 0, "bdc", "Block device characteristics "
+     "(SBC)"},
+    {VPD_BLOCK_DEV_C_EXTENS, 0, 0, "bdce", "Block device characteristics "
+     "extension (SBC)"},
+    {VPD_CFA_PROFILE_INFO, 0, 0, "cfa", "CFA profile information"},
+    {VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"},
+    {VPD_DEVICE_ID, 0, -1, "di", "Device identification"},
+    {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' "
+     "but designators ordered as found"},
+    {VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, "di_lu", "Device identification, "
+     "lu only"},
+    {VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, "di_port", "Device "
+     "identification, target port only"},
+    {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, "di_target", "Device "
+     "identification, target device only"},
+    {VPD_DTDE_ADDRESS, 0, 1, "dtde",
+     "Data transfer device element address (SSC)"},
+    {VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"},
+    {VPD_IMP_OP_DEF, 0, -1, "iod",
+     "Implemented operating definition (obsolete)"},
+    {VPD_LB_PROTECTION, 0, 0, "lbpro", "Logical block protection (SSC)"},
+    {VPD_LB_PROVISIONING, 0, 0, "lbpv", "Logical block provisioning (SBC)"},
+    {VPD_MAN_ASS_SN, 0, 1, "mas", "Manufacturer assigned serial number (SSC)"},
+    {VPD_MAN_ASS_SN, 0, 0x12, "masa",
+     "Manufacturer assigned serial number (ADC)"},
+    {VPD_MAN_NET_ADDR, 0, -1, "mna", "Management network addresses"},
+    {VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"},
+    {VPD_OSD_INFO, 0, 0x11, "oi", "OSD information"},
+    {VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"},
+    {VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"},
+    {VPD_PROTO_LU, 0, -1, "pslu", "Protocol-specific logical unit "
+     "information"},
+    {VPD_PROTO_PORT, 0, -1, "pspo", "Protocol-specific port information"},
+    {VPD_REFERRALS, 0, 0, "ref", "Referrals (SBC)"},
+    {VPD_SA_DEV_CAP, 0, 1, "sad",
+     "Sequential access device capabilities (SSC)"},
+    {VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"},
+    {VPD_NO_RATHER_STD_INQ, 0, -1, "sinq", "Standard inquiry response"},
+    {VPD_UNIT_SERIAL_NUM, 0, -1, "sn", "Unit serial number"},
+    {VPD_SCSI_PORTS, 0, -1, "sp", "SCSI ports"},
+    {VPD_SECURITY_TOKEN, 0, 0x11, "st", "Security token (OSD)"},
+    {VPD_SUP_BLOCK_LENS, 0, 0, "sbl", "Supported block lengths and "
+     "protection types (SBC)"},
+    {VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"},
+    {VPD_TA_SUPPORTED, 0, 1, "tas", "TapeAlert supported flags (SSC)"},
+    {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"},
+    {VPD_ZBC_DEV_CHARS, 0, -1, "zbdc", "Zoned block device characteristics"},
+        /* Use pdt of -1 since this page both for pdt=0 and pdt=0x14 */
+    {0, 0, 0, NULL, NULL},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_vpd  [--all] [--enumerate] [--help] [--hex] "
+            "[--ident]\n"
+            "               [--inhex=FN] [--long] [--maxlen=LEN] "
+            "[--page=PG] [--quiet]\n"
+            "               [--raw] [--vendor=VP] [--verbose] [--version] "
+            "DEVICE\n");
+    pr2serr("  where:\n"
+            "    --all|-a        output all pages listed in the supported "
+            "pages VPD\n"
+            "                    page\n"
+            "    --enumerate|-e    enumerate known VPD pages names (ignore "
+            "DEVICE),\n"
+            "                      can be used with --page=num to search\n"
+            "    --help|-h       output this usage message then exit\n"
+            "    --hex|-H        output page in ASCII hexadecimal\n"
+            "    --ident|-i      output device identification VPD page, "
+            "twice for\n"
+            "                    short logical unit designator (equiv: "
+            "'-qp di_lu')\n"
+            "    --inhex=FN|-I FN    read ASCII hex from file FN instead of "
+            "DEVICE;\n"
+            "                        if used with --raw then read binary "
+            "from FN\n"
+            "    --long|-l       perform extra decoding\n"
+            "    --maxlen=LEN|-m LEN    max response length (allocation "
+            "length in cdb)\n"
+            "                           (def: 0 -> 252 bytes)\n"
+            "    --page=PG|-p PG    fetch VPD page where PG is an "
+            "acronym, or a decimal\n"
+            "                       number unless hex indicator "
+            "is given (e.g. '0x83')\n"
+            "    --quiet|-q      suppress some output when decoding\n"
+            "    --raw|-r        output page in binary; if --inhex=FN is "
+            "also\n"
+            "                    given, FN is in binary (else FN is in "
+            "hex)\n"
+            "    --vendor=VP | -M VP    vendor/product abbreviation [or "
+            "number]\n"
+            "    --verbose|-v    increase verbosity\n"
+            "    --version|-V    print version string and exit\n\n"
+            "Fetch Vital Product Data (VPD) page using SCSI INQUIRY or "
+            "decodes VPD\npage response held in file FN. To list available "
+            "pages use '-e'. Also\n'-p -1' yields the standard INQUIRY "
+            "response.\n");
+}
+
+/* Read ASCII hex bytes or binary from fname (a file named '-' taken as
+ * stdin). If reading ASCII hex then there should be either one entry per
+ * line or a comma, space or tab separated list of bytes. If no_space is
+ * set then a string of ACSII hex digits is expected, 2 per byte. Everything
+ * from and including a '#' on a line is ignored. Returns 0 if ok, or 1 if
+ * error. */
+static int
+f2hex_arr(const char * fname, int as_binary, int no_space,
+          unsigned char * mp_arr, int * mp_arr_len, int max_arr_len)
+{
+    int fn_len, in_len, k, j, m, split_line, fd;
+    bool has_stdin;
+    unsigned int h;
+    const char * lcp;
+    FILE * fp;
+    char line[512];
+    char carry_over[4];
+    int off = 0;
+    struct stat a_stat;
+
+    if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len))
+        return 1;
+    fn_len = strlen(fname);
+    if (0 == fn_len)
+        return 1;
+    has_stdin = ((1 == fn_len) && ('-' == fname[0]));   /* read from stdin */
+    if (as_binary) {
+        if (has_stdin)
+            fd = STDIN_FILENO;
+        else {
+            fd = open(fname, O_RDONLY);
+            if (fd < 0) {
+                pr2serr("unable to open binary file %s: %s\n", fname,
+                         safe_strerror(errno));
+                return 1;
+            }
+        }
+        k = read(fd, mp_arr, max_arr_len);
+        if (k <= 0) {
+            if (0 == k)
+                pr2serr("read 0 bytes from binary file %s\n", fname);
+            else
+                pr2serr("read from binary file %s: %s\n", fname,
+                        safe_strerror(errno));
+            if (! has_stdin)
+                close(fd);
+            return 1;
+        }
+        if ((0 == fstat(fd, &a_stat)) && S_ISFIFO(a_stat.st_mode)) {
+            /* pipe; keep reading till error or 0 read */
+            while (k < max_arr_len) {
+                m = read(fd, mp_arr + k, max_arr_len - k);
+                if (0 == m)
+                   break;
+                if (m < 0) {
+                    pr2serr("read from binary pipe %s: %s\n", fname,
+                            safe_strerror(errno));
+                    if (! has_stdin)
+                        close(fd);
+                    return 1;
+                }
+                k += m;
+            }
+        }
+        *mp_arr_len = k;
+        if (! has_stdin)
+            close(fd);
+        return 0;
+    } else {    /* So read the file as ASCII hex */
+        if (has_stdin)
+            fp = stdin;
+        else {
+            fp = fopen(fname, "r");
+            if (NULL == fp) {
+                pr2serr("Unable to open %s for reading\n", fname);
+                return 1;
+            }
+        }
+     }
+
+    carry_over[0] = 0;
+    for (j = 0; j < 512; ++j) {
+        if (NULL == fgets(line, sizeof(line), fp))
+            break;
+        in_len = strlen(line);
+        if (in_len > 0) {
+            if ('\n' == line[in_len - 1]) {
+                --in_len;
+                line[in_len] = '\0';
+                split_line = 0;
+            } else
+                split_line = 1;
+        }
+        if (in_len < 1) {
+            carry_over[0] = 0;
+            continue;
+        }
+        if (carry_over[0]) {
+            if (isxdigit(line[0])) {
+                carry_over[1] = line[0];
+                carry_over[2] = '\0';
+                if (1 == sscanf(carry_over, "%4x", &h))
+                    mp_arr[off - 1] = h;       /* back up and overwrite */
+                else {
+                    pr2serr("%s: carry_over error ['%s'] around line %d\n",
+                            __func__, carry_over, j + 1);
+                    goto bad;
+                }
+                lcp = line + 1;
+                --in_len;
+            } else
+                lcp = line;
+            carry_over[0] = 0;
+        } else
+            lcp = line;
+
+        m = strspn(lcp, " \t");
+        if (m == in_len)
+            continue;
+        lcp += m;
+        in_len -= m;
+        if ('#' == *lcp)
+            continue;
+        k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+        if ((k < in_len) && ('#' != lcp[k]) && ('\r' != lcp[k])) {
+            pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
+                    j + 1, m + k + 1);
+            goto bad;
+        }
+        if (no_space) {
+            for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
+                 ++k, lcp += 2) {
+                if (1 != sscanf(lcp, "%2x", &h)) {
+                    pr2serr("%s: bad hex number in line %d, pos %d\n",
+                            __func__, j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+                if ((off + k) >= max_arr_len) {
+                    pr2serr("%s: array length exceeded\n", __func__);
+                    goto bad;
+                }
+                mp_arr[off + k] = h;
+            }
+            if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
+                carry_over[0] = *lcp;
+            off += k;
+        } else {
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%10x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("%s: hex number larger than 0xff in line "
+                                "%d, pos %d\n", __func__, j + 1,
+                                (int)(lcp - line + 1));
+                        goto bad;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("%s: array length exceeded\n", __func__);
+                        goto bad;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if (('#' == *lcp) || ('\r' == *lcp)) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("%s: error in line %d, at pos %d\n", __func__,
+                            j + 1, (int)(lcp - line + 1));
+                    goto bad;
+                }
+            }
+            off += (k + 1);
+        }
+    }
+    *mp_arr_len = off;
+    if (stdin != fp)
+        fclose(fp);
+    return 0;
+bad:
+    if (stdin != fp)
+        fclose(fp);
+    return 1;
+}
+
+/* Local version of sg_ll_inquiry() [found in libsgutils] that additionally
+ * passes back resid. Same return values as sg_ll_inquiry() (0 is good). */
+static int
+pt_inquiry(int sg_fd, int evpd, int pg_op, void * resp, int mx_resp_len,
+           int * residp, int noisy, int verbose)
+{
+    int res, ret, k, sense_cat, resid;
+    unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    unsigned char * up;
+    struct sg_pt_base * ptvp;
+
+    if (evpd)
+        inqCmdBlk[1] |= 1;
+    inqCmdBlk[2] = (unsigned char)pg_op;
+    /* 16 bit allocation length (was 8) is a recent SPC-3 addition */
+    sg_put_unaligned_be16((uint16_t)mx_resp_len, inqCmdBlk + 3);
+    if (verbose) {
+        pr2serr("    inquiry cdb: ");
+        for (k = 0; k < INQUIRY_CMDLEN; ++k)
+            pr2serr("%02x ", inqCmdBlk[k]);
+        pr2serr("\n");
+    }
+    if (resp && (mx_resp_len > 0)) {
+        up = (unsigned char *)resp;
+        up[0] = 0x7f;   /* defensive prefill */
+        if (mx_resp_len > 4)
+            up[4] = 0;
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("%s: out of memory\n", __func__);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, inqCmdBlk, sizeof(inqCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "inquiry", res, mx_resp_len, sense_b,
+                               noisy, verbose, &sense_cat);
+    resid = get_scsi_pt_resid(ptvp);
+    if (residp)
+        *residp = resid;
+    destruct_scsi_pt_obj(ptvp);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else if (ret < 4) {
+        if (verbose)
+            pr2serr("%s: got too few bytes (%d)\n", __func__, ret);
+        ret = SG_LIB_CAT_MALFORMED;
+    } else
+        ret = 0;
+
+    if (resid > 0) {
+        if (resid > mx_resp_len) {
+            pr2serr("INQUIRY resid (%d) should never exceed requested "
+                    "len=%d\n", resid, mx_resp_len);
+            return ret ? ret : SG_LIB_CAT_MALFORMED;
+        }
+        /* zero unfilled section of response buffer */
+        memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid);
+    }
+    return ret;
+}
+
+/* mxlen is command line --maxlen=LEN option (def: 0) or -1 for a VPD page
+ * with a short length (1 byte). Returns 0 for success. */
+int     /* global: use by sg_vpd_vendor.c */
+vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page,
+                        int mxlen, int vb, int * rlenp)
+{
+    int res, resid, rlen, len, n;
+
+    if (sg_fd < 0) {
+        len = sg_get_unaligned_be16(rp + 2) + 4;
+        if (vb && (len > mxlen))
+            pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN "
+                    "file (%d)\n",  len , mxlen);
+        if (rlenp)
+            *rlenp = (len < mxlen) ? len : mxlen;
+        return 0;
+    }
+    if (mxlen > MX_ALLOC_LEN) {
+        pr2serr("--maxlen=LEN too long: %d > %d\n", mxlen, MX_ALLOC_LEN);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    n = (mxlen > 0) ? mxlen : DEF_ALLOC_LEN;
+    res = pt_inquiry(sg_fd, 1, page, rp, n, &resid, 1, vb);
+    if (res)
+        return res;
+    rlen = n - resid;
+    if (rlen < 4) {
+        pr2serr("VPD response too short (len=%d)\n", rlen);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    if (page != rp[1]) {
+        pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
+                "response\n");
+        n = (rlen < 32) ? rlen : 32;
+        if (vb) {
+            pr2serr("First %d bytes of bad response\n", n);
+            dStrHexErr((const char *)rp, n, 0);
+        }
+        return SG_LIB_CAT_MALFORMED;
+    } else if ((0x80 == page) && (0x2 == rp[2]) && (0x2 == rp[3])) {
+        /* could be a Unit Serial number VPD page with a very long
+         * length of 4+514 bytes; more likely standard response for
+         * SCSI-2, RMB=1 and a response_data_format of 0x2. */
+        pr2serr("invalid Unit Serial Number VPD response; probably a "
+                "STANDARD INQUIRY response\n");
+        return SG_LIB_CAT_MALFORMED;
+    }
+    if (mxlen < 0)
+        len = rp[3] + 4;
+    else
+        len = sg_get_unaligned_be16(rp + 2) + 4;
+    if (len <= rlen) {
+        if (rlenp)
+            *rlenp = len;
+        return 0;
+    } else if (mxlen) {
+        if (rlenp)
+            *rlenp = rlen;
+        return 0;
+    }
+    if (len > MX_ALLOC_LEN) {
+        pr2serr("response length too long: %d > %d\n", len, MX_ALLOC_LEN);
+        return SG_LIB_CAT_MALFORMED;
+    } else {
+        res = pt_inquiry(sg_fd, 1, page, rp, len, &resid, 1, vb);
+        if (res)
+            return res;
+        rlen = len - resid;
+        /* assume it is well behaved: hence page and len still same */
+        if (rlenp)
+            *rlenp = rlen;
+        return 0;
+    }
+}
+
+static const struct svpd_values_name_t *
+sdp_get_vpd_detail(int page_num, int subvalue, int pdt)
+{
+    const struct svpd_values_name_t * vnp;
+    int sv, ty;
+
+    sv = (subvalue < 0) ? 1 : 0;
+    ty = (pdt < 0) ? 1 : 0;
+    for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+        if ((page_num == vnp->value) &&
+            (sv || (subvalue == vnp->subvalue)) &&
+            (ty || (pdt == vnp->pdt)))
+            return vnp;
+    }
+    if (! ty)
+        return sdp_get_vpd_detail(page_num, subvalue, -1);
+    if (! sv)
+        return sdp_get_vpd_detail(page_num, -1, -1);
+    return NULL;
+}
+
+static const struct svpd_values_name_t *
+sdp_find_vpd_by_acron(const char * ap)
+{
+    const struct svpd_values_name_t * vnp;
+
+    for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+        if (0 == strcmp(vnp->acron, ap))
+            return vnp;
+    }
+    return NULL;
+}
+
+static void
+enumerate_vpds(int standard, int vendor)
+{
+    const struct svpd_values_name_t * vnp;
+
+    if (standard) {
+        for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+            if (vnp->name) {
+                if (vnp->value < 0)
+                    printf("  %-10s -1        %s\n", vnp->acron, vnp->name);
+                else
+                    printf("  %-10s 0x%02x      %s\n", vnp->acron, vnp->value,
+                       vnp->name);
+            }
+        }
+    }
+    if (vendor)
+        svpd_enumerate_vendor(-2);
+}
+
+static int
+count_standard_vpds(int num_vpd)
+{
+    const struct svpd_values_name_t * vnp;
+    int matches;
+
+    for (vnp = standard_vpd_pg, matches = 0; vnp->acron; ++vnp) {
+        if ((num_vpd == vnp->value) && vnp->name) {
+            if (0 == matches)
+                printf("Matching standard VPD pages:\n");
+            ++matches;
+            if (vnp->value < 0)
+                printf("  %-10s -1        %s\n", vnp->acron, vnp->name);
+            else
+                printf("  %-10s 0x%02x      %s\n", vnp->acron, vnp->value,
+                   vnp->name);
+        }
+    }
+    return matches;
+}
+
+static void
+dStrRaw(const char * str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+/* Assume index is less than 16 */
+const char * sg_ansi_version_arr[] =
+{
+    "no conformance claimed",
+    "SCSI-1",           /* obsolete, ANSI X3.131-1986 */
+    "SCSI-2",           /* obsolete, ANSI X3.131-1994 */
+    "SPC",              /* withdrawn */
+    "SPC-2",
+    "SPC-3",
+    "SPC-4",
+    "SPC-5",
+    "ecma=1, [8h]",
+    "ecma=1, [9h]",
+    "ecma=1, [Ah]",
+    "ecma=1, [Bh]",
+    "reserved [Ch]",
+    "reserved [Dh]",
+    "reserved [Eh]",
+    "reserved [Fh]",
+};
+
+static void
+decode_std_inq(unsigned char * b, int len, int verbose)
+{
+    int pqual, n;
+
+    if (len < 4)
+        return;
+    pqual = (b[0] & 0xe0) >> 5;
+    if (0 == pqual)
+        printf("standard INQUIRY:\n");
+    else if (1 == pqual)
+        printf("standard INQUIRY: [qualifier indicates no connected "
+               "LU]\n");
+    else if (3 == pqual)
+        printf("standard INQUIRY: [qualifier indicates not capable "
+               "of supporting LU]\n");
+    else
+        printf("standard INQUIRY: [reserved or vendor specific "
+                       "qualifier [%d]]\n", pqual);
+    printf("  PQual=%d  Device_type=%d  RMB=%d  LU_CONG=%d  version=0x%02x ",
+           pqual, b[0] & 0x1f, !!(b[1] & 0x80), !!(b[1] & 0x40),
+           (unsigned int)b[2]);
+    printf(" [%s]\n", sg_ansi_version_arr[b[2] & 0xf]);
+    printf("  [AERC=%d]  [TrmTsk=%d]  NormACA=%d  HiSUP=%d "
+           " Resp_data_format=%d\n",
+           !!(b[3] & 0x80), !!(b[3] & 0x40), !!(b[3] & 0x20),
+           !!(b[3] & 0x10), b[3] & 0x0f);
+    if (len < 5)
+        return;
+    n = b[4] + 5;
+    if (verbose)
+        pr2serr(">> requested %d bytes, %d bytes available\n", len, n);
+    printf("  SCCS=%d  ACC=%d  TPGS=%d  3PC=%d  Protect=%d ",
+           !!(b[5] & 0x80), !!(b[5] & 0x40), ((b[5] & 0x30) >> 4),
+           !!(b[5] & 0x08), !!(b[5] & 0x01));
+    printf(" [BQue=%d]\n  EncServ=%d  ", !!(b[6] & 0x80), !!(b[6] & 0x40));
+    if (b[6] & 0x10)
+        printf("MultiP=1 (VS=%d)  ", !!(b[6] & 0x20));
+    else
+        printf("MultiP=0  ");
+    printf("[MChngr=%d]  [ACKREQQ=%d]  Addr16=%d\n  [RelAdr=%d]  ",
+           !!(b[6] & 0x08), !!(b[6] & 0x04), !!(b[6] & 0x01),
+           !!(b[7] & 0x80));
+    printf("WBus16=%d  Sync=%d  [Linked=%d]  [TranDis=%d]  ",
+           !!(b[7] & 0x20), !!(b[7] & 0x10), !!(b[7] & 0x08),
+           !!(b[7] & 0x04));
+    printf("CmdQue=%d\n", !!(b[7] & 0x02));
+    if (len < 36)
+        return;
+    printf("  Vendor_identification: %.8s\n", b + 8);
+    printf("  Product_identification: %.16s\n", b + 16);
+    printf("  Product_revision_level: %.4s\n", b + 32);
+}
+
+static void
+decode_id_vpd(unsigned char * buff, int len, int subvalue,
+              const struct opts_t * op)
+{
+    int m_a, m_d, m_cs;
+
+    if (len < 4) {
+        pr2serr("Device identification VPD page length too short=%d\n", len);
+        return;
+    }
+    m_a = -1;
+    m_d = -1;
+    m_cs = -1;
+    if (0 == subvalue) {
+        decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), buff + 4,
+                       len - 4, VPD_ASSOC_LU, m_d, m_cs, op);
+        decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), buff + 4,
+                       len - 4, VPD_ASSOC_TPORT, m_d, m_cs, op);
+        decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), buff + 4,
+                       len - 4, VPD_ASSOC_TDEVICE, m_d, m_cs, op);
+    } else if (VPD_DI_SEL_AS_IS == subvalue)
+        decode_dev_ids(NULL, buff + 4, len - 4, m_a, m_d, m_cs, op);
+    else {
+        if (VPD_DI_SEL_LU & subvalue)
+            decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), buff + 4,
+                           len - 4, VPD_ASSOC_LU, m_d, m_cs, op);
+        if (VPD_DI_SEL_TPORT & subvalue)
+            decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), buff + 4,
+                           len - 4, VPD_ASSOC_TPORT, m_d, m_cs, op);
+        if (VPD_DI_SEL_TARGET & subvalue)
+            decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE),
+                           buff + 4, len - 4, VPD_ASSOC_TDEVICE, m_d, m_cs,
+                           op);
+    }
+}
+
+static const char * network_service_type_arr[] =
+{
+    "unspecified",
+    "storage configuration service",
+    "diagnostics",
+    "status",
+    "logging",
+    "code download",
+    "copy service",
+    "administrative configuration service",
+    "reserved[0x8]", "reserved[0x9]",
+    "reserved[0xa]", "reserved[0xb]", "reserved[0xc]", "reserved[0xd]",
+    "reserved[0xe]", "reserved[0xf]", "reserved[0x10]", "reserved[0x11]",
+    "reserved[0x12]", "reserved[0x13]", "reserved[0x14]", "reserved[0x15]",
+    "reserved[0x16]", "reserved[0x17]", "reserved[0x18]", "reserved[0x19]",
+    "reserved[0x1a]", "reserved[0x1b]", "reserved[0x1c]", "reserved[0x1d]",
+    "reserved[0x1e]", "reserved[0x1f]",
+};
+
+/* VPD_MAN_NET_ADDR */
+static void
+decode_net_man_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, bump, na_len;
+    unsigned char * ucp;
+
+    if ((1 == do_hex) || (do_hex > 2)) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("Management network addresses VPD page length too short=%d\n",
+                len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        printf("  %s, Service type: %s\n",
+               sg_get_desig_assoc_str((ucp[0] >> 5) & 0x3),
+               network_service_type_arr[ucp[0] & 0x1f]);
+        na_len = sg_get_unaligned_be16(ucp + 2);
+        bump = 4 + na_len;
+        if ((k + bump) > len) {
+            pr2serr("Management network addresses VPD page, short "
+                    "descriptor length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (na_len > 0) {
+            if (do_hex > 1) {
+                printf("    Network address:\n");
+                dStrHex((const char *)(ucp + 4), na_len, 0);
+            } else
+                printf("    %s\n", ucp + 4);
+        }
+    }
+}
+
+static const char * mode_page_policy_arr[] =
+{
+    "shared",
+    "per target port",
+    "per initiator port",
+    "per I_T nexus",
+};
+
+/* VPD_MODE_PG_POLICY */
+static void
+decode_mode_policy_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, bump;
+    unsigned char * ucp;
+
+    if ((1 == do_hex) || (do_hex > 2)) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("Mode page policy VPD page length too short=%d\n", len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        bump = 4;
+        if ((k + bump) > len) {
+            pr2serr("Mode page policy VPD page, short "
+                    "descriptor length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (do_hex > 1)
+            dStrHex((const char *)ucp, 4, 1);
+        else {
+            printf("  Policy page code: 0x%x", (ucp[0] & 0x3f));
+            if (ucp[1])
+                printf(",  subpage code: 0x%x\n", ucp[1]);
+            else
+                printf("\n");
+            printf("    MLUS=%d,  Policy: %s\n", !!(ucp[2] & 0x80),
+                   mode_page_policy_arr[ucp[2] & 0x3]);
+        }
+    }
+}
+
+/* VPD_SCSI_PORTS */
+static void
+decode_scsi_ports_vpd(unsigned char * buff, int len, const struct opts_t * op)
+{
+    int k, bump, rel_port, ip_tid_len, tpd_len;
+    unsigned char * ucp;
+
+    if ((1 == op->do_hex) || (op->do_hex > 2)) {
+        dStrHex((const char *)buff, len, (1 == op->do_hex) ? 1 : -1);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("SCSI Ports VPD page length too short=%d\n", len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        rel_port = sg_get_unaligned_be16(ucp + 2);
+        printf("  Relative port=%d\n", rel_port);
+        ip_tid_len = sg_get_unaligned_be16(ucp + 6);
+        bump = 8 + ip_tid_len;
+        if ((k + bump) > len) {
+            pr2serr("SCSI Ports VPD page, short descriptor "
+                    "length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (ip_tid_len > 0) {
+            if (op->do_hex > 1) {
+                printf("    Initiator port transport id:\n");
+                dStrHex((const char *)(ucp + 8), ip_tid_len, 1);
+            } else
+                decode_transport_id("    ", ucp + 8, ip_tid_len);
+        }
+        tpd_len = sg_get_unaligned_be16(ucp + bump + 2);
+        if ((k + bump + tpd_len + 4) > len) {
+            pr2serr("SCSI Ports VPD page, short descriptor(tgt) "
+                    "length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (tpd_len > 0) {
+            if (op->do_hex > 1) {
+                printf("    Target port descriptor(s):\n");
+                dStrHex((const char *)(ucp + bump + 4), tpd_len, 1);
+            } else {
+                if ((0 == op->do_quiet) || (ip_tid_len > 0))
+                    printf("    Target port descriptor(s):\n");
+                decode_dev_ids("SCSI Ports", ucp + bump + 4, tpd_len,
+                               VPD_ASSOC_TPORT, -1, -1, op);
+            }
+        }
+        bump += tpd_len + 4;
+    }
+}
+
+/* Prints outs an abridged set of device identification designators
+   selected by association, designator type and/or code set. */
+static int
+decode_dev_ids_quiet(unsigned char * buff, int len, int m_assoc,
+                     int m_desig_type, int m_code_set)
+{
+    int m, p_id, c_set, piv, desig_type, i_len, naa, off, u;
+    int assoc, is_sas, rtp;
+    const unsigned char * ucp;
+    const unsigned char * ip;
+    unsigned char sas_tport_addr[8];
+
+    rtp = 0;
+    memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
+    off = -1;
+    if (buff[2] != 0) {
+        if (m_assoc != VPD_ASSOC_LU)
+            return 0;
+        ip = buff;
+        p_id = 0;
+        c_set = 1;
+        assoc = VPD_ASSOC_LU;
+        piv = 0;
+        is_sas = 0;
+        desig_type = 3;
+        i_len = 16;
+        off = 16;
+        goto skip_1st_iter;
+    }
+    while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type,
+                                   m_code_set)) == 0) {
+        ucp = buff + off;
+        i_len = ucp[3];
+        if ((off + i_len + 4) > len) {
+            pr2serr("    VPD page error: designator length longer than\n"
+                    "     remaining response length=%d\n", (len - off));
+            return SG_LIB_CAT_MALFORMED;
+        }
+        ip = ucp + 4;
+        p_id = ((ucp[0] >> 4) & 0xf);
+        c_set = (ucp[0] & 0xf);
+        piv = ((ucp[1] & 0x80) ? 1 : 0);
+        is_sas = (piv && (6 == p_id)) ? 1 : 0;
+        assoc = ((ucp[1] >> 4) & 0x3);
+        desig_type = (ucp[1] & 0xf);
+skip_1st_iter:
+        switch (desig_type) {
+        case 0: /* vendor specific */
+            break;
+        case 1: /* T10 vendor identification */
+            break;
+        case 2: /* EUI-64 based */
+            if ((8 != i_len) && (12 != i_len) && (16 != i_len))
+                pr2serr("      << expect 8, 12 and 16 byte "
+                        "EUI, got %d>>\n", i_len);
+            printf("  0x");
+            for (m = 0; m < i_len; ++m)
+                printf("%02x", (unsigned int)ip[m]);
+            printf("\n");
+            break;
+        case 3: /* NAA */
+            naa = (ip[0] >> 4) & 0xff;
+            if (1 != c_set) {
+                pr2serr("      << expected binary code_set (1), got %d for "
+                        "NAA=%d>>\n", c_set, naa);
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            switch (naa) {
+            case 2:             /* NAA IEEE extended */
+                if (8 != i_len) {
+                    pr2serr("      << unexpected NAA 2 identifier "
+                            "length: 0x%x>>\n", i_len);
+                    dStrHexErr((const char *)ip, i_len, 0);
+                    break;
+                }
+                printf("  0x");
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+                break;
+            case 3:             /* Locally assigned */
+            case 5:             /* IEEE Registered */
+                if (8 != i_len) {
+                    pr2serr("      << unexpected NAA 3 or 5 "
+                            "identifier length: 0x%x>>\n", i_len);
+                    dStrHexErr((const char *)ip, i_len, 0);
+                    break;
+                }
+                if ((0 == is_sas) || (1 != assoc)) {
+                    printf("  0x");
+                    for (m = 0; m < 8; ++m)
+                        printf("%02x", (unsigned int)ip[m]);
+                    printf("\n");
+                } else if (rtp) {
+                    printf("  0x");
+                    for (m = 0; m < 8; ++m)
+                        printf("%02x", (unsigned int)ip[m]);
+                    printf(",0x%x\n", rtp);
+                    rtp = 0;
+                } else {
+                    if (sas_tport_addr[0]) {
+                        printf("  0x");
+                        for (m = 0; m < 8; ++m)
+                            printf("%02x", (unsigned int)sas_tport_addr[m]);
+                        printf("\n");
+                    }
+                    memcpy(sas_tport_addr, ip, sizeof(sas_tport_addr));
+                }
+                break;
+            case 6:             /* NAA IEEE registered extended */
+                if (16 != i_len) {
+                    pr2serr("      << unexpected NAA 6 identifier length: "
+                            "0x%x>>\n", i_len);
+                    dStrHexErr((const char *)ip, i_len, 0);
+                    break;
+                }
+                printf("  0x");
+                for (m = 0; m < 16; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+                break;
+            default:
+                pr2serr("      << bad NAA nibble, expected 2, 3, 5 or 6, got "
+                        "%d>>\n", naa);
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            break;
+        case 4: /* Relative target port */
+            if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len))
+                break;
+            rtp = sg_get_unaligned_be16(ip + 2);
+            if (sas_tport_addr[0]) {
+                printf("  0x");
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)sas_tport_addr[m]);
+                printf(",0x%x\n", rtp);
+                memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
+                rtp = 0;
+            }
+            break;
+        case 5: /* (primary) Target port group */
+            break;
+        case 6: /* Logical unit group */
+            break;
+        case 7: /* MD5 logical unit identifier */
+            break;
+        case 8: /* SCSI name string */
+            if (3 != c_set) {
+                pr2serr("      << expected UTF-8 code_set>>\n");
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            if (! (strncmp((const char *)ip, "eui.", 4) ||
+                   strncmp((const char *)ip, "EUI.", 4) ||
+                   strncmp((const char *)ip, "naa.", 4) ||
+                   strncmp((const char *)ip, "NAA.", 4) ||
+                   strncmp((const char *)ip, "iqn.", 4))) {
+                pr2serr("      << expected name string prefix>>\n");
+                dStrHexErr((const char *)ip, i_len, -1);
+                break;
+            }
+            /* does %s print out UTF-8 ok??
+             * Seems to depend on the locale. Looks ok here with my
+             * locale setting: en_AU.UTF-8
+             */
+            printf("  %s\n", (const char *)ip);
+            break;
+        case 9: /* Protocol specific port identifier */
+            break;
+        case 0xa: /* UUID identifier */
+            if ((1 != c_set) || (18 != i_len) || (1 != ((ip[0] >> 4) & 0xf)))
+                break;
+            for (m = 0; m < 16; ++m) {
+                if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
+                    printf("-");
+                printf("%02x", (unsigned int)ip[2 + m]);
+            }
+            printf("\n");
+            break;
+        default: /* reserved */
+            break;
+        }
+    }
+    if (sas_tport_addr[0]) {
+        printf("  0x");
+        for (m = 0; m < 8; ++m)
+            printf("%02x", (unsigned int)sas_tport_addr[m]);
+        printf("\n");
+    }
+    if (-2 == u) {
+        pr2serr("VPD page error: short designator around offset %d\n", off);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    return 0;
+}
+
+static void
+decode_designation_descriptor(const unsigned char * ip, int i_len,
+                              int p_id, int c_set, int piv, int assoc,
+                              int desig_type, int print_assoc,
+                              const struct opts_t * op)
+{
+    int m, ci_off, c_id, d_id, naa;
+    int vsi, k;
+    uint64_t vsei;
+    uint64_t id_ext;
+    char b[64];
+
+    if (print_assoc)
+        printf("  %s:\n", sg_get_desig_assoc_str(assoc & 3));
+    printf("    designator type: %s,  code set: %s\n",
+           sg_get_desig_type_str(desig_type & 0xf),
+           sg_get_desig_code_set_str(c_set & 0xf));
+    if (piv && ((1 == assoc) || (2 == assoc)))
+        printf("     transport: %s\n",
+               sg_get_trans_proto_str(p_id, sizeof(b), b));
+/* printf("    associated with the %s\n", sg_get_desig_assoc_str(assoc)); */
+    switch (desig_type) {
+    case 0: /* vendor specific */
+        k = 0;
+        if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
+            for (k = 0; (k < i_len) && isprint(ip[k]); ++k)
+                ;
+            if (k >= i_len)
+                k = 1;
+        }
+        if (k)
+            printf("      vendor specific: %.*s\n", i_len, ip);
+        else {
+            pr2serr("      vendor specific:\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+        }
+        break;
+    case 1: /* T10 vendor identification */
+        printf("      vendor id: %.8s\n", ip);
+        if (i_len > 8) {
+            if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
+                printf("      vendor specific: %.*s\n", i_len - 8, ip + 8);
+            } else {
+                printf("      vendor specific: 0x");
+                for (m = 8; m < i_len; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            }
+        }
+        break;
+    case 2: /* EUI-64 based */
+        if (! op->do_long) {
+            if ((8 != i_len) && (12 != i_len) && (16 != i_len)) {
+                pr2serr("      << expect 8, 12 and 16 byte EUI, got %d>>\n",
+                        i_len);
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            printf("      0x");
+            for (m = 0; m < i_len; ++m)
+                printf("%02x", (unsigned int)ip[m]);
+            printf("\n");
+            break;
+        }
+        printf("      EUI-64 based %d byte identifier\n", i_len);
+        if (1 != c_set) {
+            pr2serr("      << expected binary code_set (1)>>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        ci_off = 0;
+        if (16 == i_len) {
+            ci_off = 8;
+            id_ext = sg_get_unaligned_be64(ip);
+            printf("      Identifier extension: 0x%" PRIx64 "\n", id_ext);
+        } else if ((8 != i_len) && (12 != i_len)) {
+            pr2serr("      << can only decode 8, 12 and 16 byte ids>>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        c_id = sg_get_unaligned_be24(ip + ci_off);
+        printf("      IEEE Company_id: 0x%x\n", c_id);
+        vsei = ((uint64_t)sg_get_unaligned_be32(ip + ci_off + 3) << 8) +
+               ip[ci_off + 3 + 4];      /* 5 byte integer */
+        printf("      Vendor Specific Extension Identifier: 0x%" PRIx64
+               "\n", vsei);
+        if (12 == i_len) {
+            d_id = sg_get_unaligned_be32(ip + 8);
+            printf("      Directory ID: 0x%x\n", d_id);
+        }
+        break;
+    case 3: /* NAA <n> */
+        if (1 != c_set) {
+            pr2serr("      << unexpected code set %d for NAA>>\n", c_set);
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        naa = (ip[0] >> 4) & 0xff;
+        switch (naa) {
+        case 2:         /* NAA 2: IEEE Extended */
+            if (8 != i_len) {
+                pr2serr("      << unexpected NAA 2 identifier length: "
+                        "0x%x>>\n", i_len);
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            d_id = sg_get_unaligned_be16(ip) & 0xfff;
+            c_id = sg_get_unaligned_be24(ip + 2);
+            vsi = sg_get_unaligned_be24(ip + 5);
+            if (op->do_long) {
+                printf("      NAA 2, vendor specific identifier A: "
+                       "0x%x\n", d_id);
+                printf("      IEEE Company_id: 0x%x\n", c_id);
+                printf("      vendor specific identifier B: 0x%x\n", vsi);
+                printf("      [0x");
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("]\n");
+            }
+            printf("      0x");
+            for (m = 0; m < 8; ++m)
+                printf("%02x", (unsigned int)ip[m]);
+            printf("\n");
+            break;
+        case 3:         /* NAA 3: Locally assigned */
+            if (8 != i_len) {
+                pr2serr("      << unexpected NAA 3 identifier length: "
+                        "0x%x>>\n", i_len);
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            if (op->do_long)
+                printf("      NAA 3, Locally assigned value:\n");
+            printf("      0x");
+            for (m = 0; m < 8; ++m)
+                printf("%02x", (unsigned int)ip[m]);
+            printf("\n");
+            break;
+        case 5:         /* NAA 5: IEEE Registered */
+            if (8 != i_len) {
+                pr2serr("      << unexpected NAA 5 identifier length: "
+                        "0x%x>>\n", i_len);
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            if (op->do_long) {
+                c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
+                        (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
+                vsei = ip[3] & 0xf;
+                for (m = 1; m < 5; ++m) {
+                    vsei <<= 8;
+                    vsei |= ip[3 + m];
+                }
+                printf("      NAA 5, IEEE Company_id: 0x%x\n", c_id);
+                printf("      Vendor Specific Identifier: 0x%" PRIx64
+                       "\n", vsei);
+                printf("      [0x");
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("]\n");
+            } else {
+                printf("      0x");
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            }
+            break;
+        case 6:         /* NAA 6: IEEE Registered extended */
+            if (16 != i_len) {
+                pr2serr("      << unexpected NAA 6 identifier length: "
+                        "0x%x>>\n", i_len);
+                dStrHexErr((const char *)ip, i_len, 0);
+                break;
+            }
+            c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
+                    (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
+            vsei = ip[3] & 0xf;
+            for (m = 1; m < 5; ++m) {
+                vsei <<= 8;
+                vsei |= ip[3 + m];
+            }
+            if (op->do_long) {
+                printf("      NAA 6, IEEE Company_id: 0x%x\n", c_id);
+                printf("      Vendor Specific Identifier: 0x%" PRIx64
+                       "\n", vsei);
+                vsei = sg_get_unaligned_be64(ip + 8);
+                printf("      Vendor Specific Identifier Extension: "
+                       "0x%" PRIx64 "\n", vsei);
+                printf("      [0x");
+                for (m = 0; m < 16; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("]\n");
+            } else {
+                printf("      0x");
+                for (m = 0; m < 16; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            }
+            break;
+        default:
+            pr2serr("      << unexpected NAA [0x%x]>>\n", naa);
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        break;
+    case 4: /* Relative target port */
+        if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+            pr2serr("      << expected binary code_set, target port "
+                    "association, length 4>>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        d_id = sg_get_unaligned_be16(ip + 2);
+        printf("      Relative target port: 0x%x\n", d_id);
+        break;
+    case 5: /* (primary) Target port group */
+        if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+            pr2serr("      << expected binary code_set, target port "
+                    "association, length 4>>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        d_id = sg_get_unaligned_be16(ip + 2);
+        printf("      Target port group: 0x%x\n", d_id);
+        break;
+    case 6: /* Logical unit group */
+        if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
+            pr2serr("      << expected binary code_set, logical unit "
+                    "association, length 4>>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        d_id = sg_get_unaligned_be16(ip + 2);
+        printf("      Logical unit group: 0x%x\n", d_id);
+        break;
+    case 7: /* MD5 logical unit identifier */
+        if ((1 != c_set) || (0 != assoc)) {
+            pr2serr("      << expected binary code_set, logical unit "
+                    "association>>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        printf("      MD5 logical unit identifier:\n");
+        dStrHex((const char *)ip, i_len, 0);
+        break;
+    case 8: /* SCSI name string */
+        if (3 != c_set) {
+            pr2serr("      << expected UTF-8 code_set>>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        if (! (strncmp((const char *)ip, "eui.", 4) ||
+               strncmp((const char *)ip, "EUI.", 4) ||
+               strncmp((const char *)ip, "naa.", 4) ||
+               strncmp((const char *)ip, "NAA.", 4) ||
+               strncmp((const char *)ip, "iqn.", 4))) {
+            pr2serr("      << expected name string prefix>>\n");
+            dStrHexErr((const char *)ip, i_len, -1);
+            break;
+        }
+        printf("      SCSI name string:\n");
+        /* does %s print out UTF-8 ok??
+         * Seems to depend on the locale. Looks ok here with my
+         * locale setting: en_AU.UTF-8
+         */
+        printf("      %s\n", (const char *)ip);
+        break;
+    case 9: /* Protocol specific port identifier */
+        /* added in spc4r36, PIV must be set, proto_id indicates */
+        /* whether UAS (USB) or SOP (PCIe) or ... */
+        if (! piv)
+            printf("      >>>> Protocol specific port identifier "
+                   "expects protocol\n"
+                   "           identifier to be valid and it is not\n");
+        if (TPROTO_UAS == p_id) {
+            printf("      USB device address: 0x%x\n", 0x7f & ip[0]);
+            printf("      USB interface number: 0x%x\n", ip[2]);
+        } else if (TPROTO_SOP == p_id) {
+            printf("      PCIe routing ID, bus number: 0x%x\n", ip[0]);
+            printf("          function number: 0x%x\n", ip[1]);
+            printf("          [or device number: 0x%x, function number: "
+                   "0x%x]\n", (0x1f & (ip[1] >> 3)), 0x7 & ip[1]);
+        } else
+            pr2serr("      >>>> unexpected protocol indentifier: %s\n"
+                    "           with Protocol specific port "
+                    "identifier\n",
+                    sg_get_trans_proto_str(p_id, sizeof(b), b));
+        break;
+    case 0xa: /* UUID identifier */
+        if (1 != c_set) {
+            pr2serr("      << expected binary code_set >>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        if ((1 != ((ip[0] >> 4) & 0xf)) || (18 != i_len)) {
+            pr2serr("      << expected locally assigned UUID, 16 bytes long "
+                    ">>\n");
+            dStrHexErr((const char *)ip, i_len, 0);
+            break;
+        }
+        printf("      Locally assigned UUID: ");
+        for (m = 0; m < 16; ++m) {
+            if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
+                printf("-");    /* RFC 4122 format */
+            printf("%02x", (unsigned int)ip[2 + m]);
+        }
+        printf("\n");
+        if (op->do_long) {
+            printf("      [0x");
+            for (m = 0; m < 16; ++m)
+                printf("%02x", (unsigned int)ip[2 + m]);
+            printf("]\n");
+        }
+        break;
+    default: /* reserved */
+        pr2serr("      reserved designator=0x%x\n", desig_type);
+        dStrHexErr((const char *)ip, i_len, 0);
+        break;
+    }
+}
+
+/* Prints outs device identification designators selected by association,
+   designator type and/or code set. */
+static int
+decode_dev_ids(const char * print_if_found, unsigned char * buff, int len,
+               int m_assoc, int m_desig_type, int m_code_set,
+               const struct opts_t * op)
+{
+    int assoc, i_len, c_set, piv, p_id, desig_type;
+    int printed, off, u;
+    const unsigned char * ucp;
+
+    if (op->do_quiet)
+        return decode_dev_ids_quiet(buff, len, m_assoc, m_desig_type,
+                                    m_code_set);
+    if ( buff[2] != 0 ) {
+        if (m_assoc == VPD_ASSOC_LU)
+            decode_designation_descriptor(buff, 16, 0, 1, 0, m_assoc, 3, 0,
+                                          op);
+        return 0;
+    }
+    off = -1;
+    printed = 0;
+    while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, m_desig_type,
+                                   m_code_set)) == 0) {
+        ucp = buff + off;
+        i_len = ucp[3];
+        if ((off + i_len + 4) > len) {
+            pr2serr("    VPD page error: designator length longer than\n"
+                    "     remaining response length=%d\n", (len - off));
+            return SG_LIB_CAT_MALFORMED;
+        }
+        assoc = ((ucp[1] >> 4) & 0x3);
+        if (print_if_found && (0 == printed)) {
+            printed = 1;
+            printf("  %s:\n", print_if_found);
+        }
+        if (NULL == print_if_found)
+            printf("  %s:\n", sg_get_desig_assoc_str(assoc));
+        p_id = ((ucp[0] >> 4) & 0xf);
+        c_set = (ucp[0] & 0xf);
+        piv = ((ucp[1] & 0x80) ? 1 : 0);
+        desig_type = (ucp[1] & 0xf);
+        decode_designation_descriptor(ucp + 4, i_len, p_id, c_set, piv, assoc,
+                                      desig_type, 0, op);
+    }
+    if (-2 == u) {
+        pr2serr("VPD page error: short designator around offset %d\n", off);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    return 0;
+}
+
+/* Transport IDs are initiator port identifiers, typically other than the
+   initiator port issuing a SCSI command. */
+static void
+decode_transport_id(const char * leadin, unsigned char * ucp, int len)
+{
+    int format_code, proto_id, num, k;
+    uint64_t ull;
+    int bump;
+
+    for (k = 0, bump= 24; k < len; k += bump, ucp += bump) {
+        if ((len < 24) || (0 != (len % 4)))
+            printf("%sTransport Id short or not multiple of 4 "
+                   "[length=%d]:\n", leadin, len);
+        else
+            printf("%sTransport Id of initiator:\n", leadin);
+        format_code = ((ucp[0] >> 6) & 0x3);
+        proto_id = (ucp[0] & 0xf);
+        switch (proto_id) {
+        case TPROTO_FCP: /* Fibre channel */
+            printf("%s  FCP-2 World Wide Name:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 8, -1);
+            bump = 24;
+            break;
+        case TPROTO_SPI:        /* Scsi Parallel Interface */
+            printf("%s  Parallel SCSI initiator SCSI address: 0x%x\n",
+                   leadin, sg_get_unaligned_be16(ucp + 2));
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            printf("%s  relative port number (of corresponding target): "
+                   "0x%x\n", leadin, sg_get_unaligned_be16(ucp + 6));
+            bump = 24;
+            break;
+        case TPROTO_SSA:
+            printf("%s  SSA (transport id not defined):\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
+            bump = 24;
+            break;
+        case TPROTO_1394: /* IEEE 1394 */
+            printf("%s  IEEE 1394 EUI-64 name:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 8, -1);
+            bump = 24;
+            break;
+        case TPROTO_SRP:
+            printf("%s  RDMA initiator port identifier:\n", leadin);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            dStrHex((const char *)&ucp[8], 16, -1);
+            bump = 24;
+            break;
+        case TPROTO_ISCSI:
+            printf("%s  iSCSI ", leadin);
+            num = sg_get_unaligned_be16(ucp + 2);
+            if (0 == format_code)
+                printf("name: %.*s\n", num, &ucp[4]);
+            else if (1 == format_code)
+                printf("world wide unique port id: %.*s\n", num, &ucp[4]);
+            else {
+                pr2serr("  [Unexpected format code: %d]\n", format_code);
+                dStrHexErr((const char *)ucp, num + 4, 0);
+            }
+            bump = (((num + 4) < 24) ? 24 : num + 4);
+            break;
+        case TPROTO_SAS:
+            ull = sg_get_unaligned_be64(ucp + 4);
+            printf("%s  SAS address: 0x%" PRIx64 "\n", leadin, ull);
+            if (0 != format_code)
+                printf("%s  [Unexpected format code: %d]\n", leadin,
+                       format_code);
+            bump = 24;
+            break;
+        case TPROTO_ADT:
+            printf("%s  ADT:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
+            bump = 24;
+            break;
+        case TPROTO_ATA: /* ATA/ATAPI */
+            printf("%s  ATAPI:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
+            bump = 24;
+            break;
+        case TPROTO_UAS:
+            printf("%s  UAS:\n", leadin);
+            printf("%s  format code: %d\n", leadin, format_code);
+            dStrHex((const char *)ucp, ((len > 24) ? 24 : len), 0);
+            bump = 24;
+            break;
+        case TPROTO_SOP:
+            printf("%s  SOP ", leadin);
+            num = sg_get_unaligned_be16(ucp + 2);
+            if (0 == format_code)
+                printf("Routing ID: 0x%x\n", num);
+            else {
+                pr2serr("  [Unexpected format code: %d]\n", format_code);
+                dStrHexErr((const char *)ucp, 24, 0);
+            }
+            bump = 24;
+            break;
+        case TPROTO_NONE:
+            pr2serr("%s  No specified protocol\n", leadin);
+            /* dStrHexErr((const char *)ucp, ((len > 24) ? 24 : len), 0); */
+            bump = 24;
+            break;
+        default:
+            pr2serr("%s  unknown protocol id=0x%x  format_code=%d\n", leadin,
+                    proto_id, format_code);
+            dStrHexErr((const char *)ucp, ((len > 24) ? 24 : len), 0);
+            bump = 24;
+            break;
+        }
+    }
+}
+
+/* VPD_EXT_INQ    Extended Inquiry VPD */
+static void
+decode_x_inq_vpd(unsigned char * b, int len, int do_hex, int do_long,
+                 int protect)
+{
+    int n;
+
+    if (len < 7) {
+        pr2serr("Extended INQUIRY data VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex) {
+        dStrHex((const char *)b, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    if (do_long) {
+        n = (b[4] >> 6) & 0x3;
+        printf("  ACTIVATE_MICROCODE=%d", n);
+        if (1 == n)
+            printf(" [before final WRITE BUFFER]\n");
+        else if (2 == n)
+            printf(" [after power on or hard reset]\n");
+        else
+            printf("\n");
+        n = (b[4] >> 3) & 0x7;
+        printf("  SPT=%d", n);
+        if (protect) {
+            switch (n)
+            {
+            case 0:
+                printf(" [protection type 1 supported]\n");
+                break;
+            case 1:
+                printf(" [protection types 1 and 2 supported]\n");
+                break;
+            case 2:
+                printf(" [protection type 2 supported]\n");
+                break;
+            case 3:
+                printf(" [protection types 1 and 3 supported]\n");
+                break;
+            case 4:
+                printf(" [protection type 3 supported]\n");
+                break;
+            case 5:
+                printf(" [protection types 2 and 3 supported]\n");
+                break;
+            case 6:
+                printf(" [see Supported block lengths and protection types "
+                       "VPD page]\n");
+                break;
+            case 7:
+                printf(" [protection types 1, 2 and 3 supported]\n");
+                break;
+            default:
+                printf("\n");
+                break;
+            }
+        } else
+            printf("\n");
+        printf("  GRD_CHK=%d\n", !!(b[4] & 0x4));
+        printf("  APP_CHK=%d\n", !!(b[4] & 0x2));
+        printf("  REF_CHK=%d\n", !!(b[4] & 0x1));
+        printf("  UASK_SUP=%d\n", !!(b[5] & 0x20));
+        printf("  GROUP_SUP=%d\n", !!(b[5] & 0x10));
+        printf("  PRIOR_SUP=%d\n", !!(b[5] & 0x8));
+        printf("  HEADSUP=%d\n", !!(b[5] & 0x4));
+        printf("  ORDSUP=%d\n", !!(b[5] & 0x2));
+        printf("  SIMPSUP=%d\n", !!(b[5] & 0x1));
+        printf("  WU_SUP=%d\n", !!(b[6] & 0x8));
+        printf("  CRD_SUP=%d\n", !!(b[6] & 0x4));
+        printf("  NV_SUP=%d\n", !!(b[6] & 0x2));
+        printf("  V_SUP=%d\n", !!(b[6] & 0x1));
+        printf("  NO_PI_CHK=%d\n", !!(b[7] & 0x10));    /* spc5r02 */
+        printf("  P_I_I_SUP=%d\n", !!(b[7] & 0x10));
+        printf("  LUICLR=%d\n", !!(b[7] & 0x1));
+        printf("  R_SUP=%d\n", !!(b[8] & 0x10));
+        printf("  HSSRELEF=%d\n", !!(b[8] & 0x2));      /* spc5r02 */
+        printf("  CBCS=%d\n", !!(b[8] & 0x1));  /* obsolete in spc5r01 */
+        printf("  Multi I_T nexus microcode download=%d\n", b[9] & 0xf);
+        printf("  Extended self-test completion minutes=%d\n",
+               sg_get_unaligned_be16(b + 10));
+        printf("  POA_SUP=%d\n", !!(b[12] & 0x80));     /* spc4r32 */
+        printf("  HRA_SUP=%d\n", !!(b[12] & 0x40));     /* spc4r32 */
+        printf("  VSA_SUP=%d\n", !!(b[12] & 0x20));     /* spc4r32 */
+        printf("  Maximum supported sense data length=%d\n",
+               b[13]); /* spc4r34 */
+        return;
+    }
+    printf("  ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d "
+           "REF_CHK=%d\n", ((b[4] >> 6) & 0x3), ((b[4] >> 3) & 0x7),
+           !!(b[4] & 0x4), !!(b[4] & 0x2), !!(b[4] & 0x1));
+    printf("  UASK_SUP=%d GROUP_SUP=%d PRIOR_SUP=%d HEADSUP=%d ORDSUP=%d "
+           "SIMPSUP=%d\n", !!(b[5] & 0x20), !!(b[5] & 0x10), !!(b[5] & 0x8),
+           !!(b[5] & 0x4), !!(b[5] & 0x2), !!(b[5] & 0x1));
+    /* CRD_SUP made obsolete in spc5r04 */
+    printf("  WU_SUP=%d [CRD_SUP=%d] NV_SUP=%d V_SUP=%d\n",
+           !!(b[6] & 0x8), !!(b[6] & 0x4), !!(b[6] & 0x2), !!(b[6] & 0x1));
+    /* CBCS, capability-based command security, obsolete in spc5r01 */
+    printf("  P_I_I_SUP=%d LUICLR=%d R_SUP=%d CBCS=%d\n",
+           !!(b[7] & 0x10), !!(b[7] & 0x1), !!(b[8] & 0x10), !!(b[8] & 0x1));
+    printf("  Multi I_T nexus microcode download=%d\n", b[9] & 0xf);
+    printf("  Extended self-test completion minutes=%d\n",
+           sg_get_unaligned_be16(b + 10));    /* spc4r27 */
+    printf("  POA_SUP=%d HRA_SUP=%d VSA_SUP=%d\n",      /* spc4r32 */
+           !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20));
+    printf("  Maximum supported sense data length=%d\n", b[13]); /* spc4r34 */
+}
+
+/* VPD_SOFTW_INF_ID */
+static void
+decode_softw_inf_id(unsigned char * buff, int len, int do_hex)
+{
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    len -= 4;
+    buff += 4;
+    for ( ; len > 5; len -= 6, buff += 6) {
+        printf("    IEEE Company_id: 0x%06x, vendor specific extension "
+               "id: 0x%06x\n", sg_get_unaligned_be24(buff),
+               sg_get_unaligned_be24(buff + 3));
+    }
+}
+
+/* VPD_ATA_INFO */
+static void
+decode_ata_info_vpd(unsigned char * buff, int len, int do_long, int do_hex)
+{
+    char b[80];
+    int num, is_be;
+    const char * cp;
+    const char * ata_transp;
+
+    if (len < 36) {
+        pr2serr("ATA information VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex && (2 != do_hex)) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    memcpy(b, buff + 8, 8);
+    b[8] = '\0';
+    printf("  SAT Vendor identification: %s\n", b);
+    memcpy(b, buff + 16, 16);
+    b[16] = '\0';
+    printf("  SAT Product identification: %s\n", b);
+    memcpy(b, buff + 32, 4);
+    b[4] = '\0';
+    printf("  SAT Product revision level: %s\n", b);
+    if (len < 56)
+        return;
+    ata_transp = (0x34 == buff[36]) ? "SATA" : "PATA";
+    if (do_long) {
+        printf("  Device signature [%s] (in hex):\n", ata_transp);
+        dStrHex((const char *)buff + 36, 20, 0);
+    } else
+        printf("  Device signature indicates %s transport\n", ata_transp);
+    if (len < 60)
+        return;
+    is_be = sg_is_big_endian();
+    if ((0xec == buff[56]) || (0xa1 == buff[56])) {
+        cp = (0xa1 == buff[56]) ? "PACKET " : "";
+        printf("  ATA command IDENTIFY %sDEVICE response summary:\n", cp);
+        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20,
+                               is_be, b);
+        b[num] = '\0';
+        printf("    model: %s\n", b);
+        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10,
+                               is_be, b);
+        b[num] = '\0';
+        printf("    serial number: %s\n", b);
+        num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4,
+                               is_be, b);
+        b[num] = '\0';
+        printf("    firmware revision: %s\n", b);
+        if (do_long)
+            printf("  ATA command IDENTIFY %sDEVICE response in hex:\n", cp);
+    } else if (do_long)
+        printf("  ATA command 0x%x got following response:\n",
+               (unsigned int)buff[56]);
+    if (len < 572)
+        return;
+    if (2 == do_hex)
+        dStrHex((const char *)(buff + 60), 512, 0);
+    else if (do_long)
+        dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be);
+}
+
+
+/* VPD_POWER_CONDITION 0x8a */
+static void
+decode_power_condition(unsigned char * buff, int len, int do_hex)
+{
+    if (len < 18) {
+        pr2serr("Power condition VPD page length too short=%d\n", len);
+        return;
+    }
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    printf("  Standby_y=%d Standby_z=%d Idle_c=%d Idle_b=%d Idle_a=%d\n",
+           !!(buff[4] & 0x2), !!(buff[4] & 0x1),
+           !!(buff[5] & 0x4), !!(buff[5] & 0x2), !!(buff[5] & 0x1));
+    printf("  Stopped condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 6));
+    printf("  Standby_z condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 8));
+    printf("  Standby_y condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 10));
+    printf("  Idle_a condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 12));
+    printf("  Idle_b condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 14));
+    printf("  Idle_c condition recovery time (ms) %d\n",
+           sg_get_unaligned_be16(buff + 16));
+}
+
+/* VPD_DEVICE_CONSTITUENTS 0x8b */
+static void
+decode_dev_const_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, j, bump, cd_len;
+    unsigned char * ucp;
+    const char * dcp = "Device constituents VPD page";
+
+    if ((1 == do_hex) || (do_hex > 2)) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("%s length too short=%d\n", dcp, len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0, j = 0; k < len; k += bump, ucp += bump, ++j) {
+
+
+        printf("  Constituent descriptor %d:\n", j + 1);
+        if ((k + 36) > len) {
+            pr2serr("%s, short descriptor length=36, left=%d\n", dcp,
+                    (len - k));
+            return;
+        }
+        printf("    Constituent type: 0x%x\n",
+               sg_get_unaligned_be16(ucp + 0));
+        printf("    Constituent device type: 0x%x\n", ucp[2]);
+        printf("    Vendor_identification: %.8s\n", ucp + 4);
+        printf("    Product_identification: %.16s\n", ucp + 12);
+        printf("    Product_revision_level: %.4s\n", ucp + 28);
+        cd_len = sg_get_unaligned_be16(ucp + 34);
+        bump = 36 + cd_len;
+        if ((k + bump) > len) {
+            pr2serr("%s, short descriptor length=%d, left=%d\n", dcp, bump,
+                    (len - k));
+            return;
+        }
+        if (cd_len > 0) {
+            printf("   Constituent specific descriptor list (in hex):\n");
+            dStrHex((const char *)(ucp + 36), cd_len, 1);
+        }
+    }
+}
+
+static const char * power_unit_arr[] =
+{
+    "Gigawatts",
+    "Megawatts",
+    "Kilowatts",
+    "Watts",
+    "Milliwatts",
+    "Microwatts",
+    "Unit reserved",
+    "Unit reserved",
+};
+
+/* VPD_POWER_CONSUMPTION */
+static void
+decode_power_consumption_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, bump;
+    unsigned char * ucp;
+    unsigned int value;
+    const char * pcp = "Power consumption VPD page";
+
+    if ((1 == do_hex) || (do_hex > 2)) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 1 : -1);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("%s length too short=%d\n", pcp,len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        bump = 4;
+        if ((k + bump) > len) {
+            pr2serr("%s, short descriptor length=%d, left=%d\n", pcp, bump,
+                    (len - k));
+            return;
+        }
+        if (do_hex > 1)
+            dStrHex((const char *)ucp, 4, 1);
+        else {
+            value = sg_get_unaligned_be16(ucp + 2);
+            printf("  Power consumption identifier: 0x%x", ucp[0]);
+            if (value >= 1000 && (ucp[1] & 0x7) > 0)
+                printf("    Maximum power consumption: %d.%03d %s\n",
+                       value / 1000, value % 1000,
+                       power_unit_arr[(ucp[1] & 0x7) - 1]);
+            else
+                printf("    Maximum power consumption: %u %s\n",
+                       value, power_unit_arr[ucp[1] & 0x7]);
+        }
+    }
+}
+
+/* This is xcopy(LID4) related: "ROD" == Representation Of Data
+ * Used by VPD_3PARTY_COPY */
+static void
+decode_rod_descriptor(const unsigned char * buff, int len)
+{
+    const unsigned char * ucp = buff;
+    int k, bump;
+
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        bump = sg_get_unaligned_be16(ucp + 2) + 4;
+        switch (ucp[0]) {
+            case 0:
+                /* Block ROD device type specific descriptor */
+                printf("  Optimal block ROD length granularity: %d\n",
+                       sg_get_unaligned_be16(ucp + 6));
+                printf("  Maximum Bytes in block ROD: %" PRIu64 "\n",
+                       sg_get_unaligned_be64(ucp + 8));
+                printf("  Optimal Bytes in block ROD transfer: %" PRIu64 "\n",
+                       sg_get_unaligned_be64(ucp + 16));
+                printf("  Optimal Bytes to token per segment: %" PRIu64 "\n",
+                       sg_get_unaligned_be64(ucp + 24));
+                printf("  Optimal Bytes from token per segment:"
+                       " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 32));
+                break;
+            case 1:
+                /* Stream ROD device type specific descriptor */
+                printf("  Maximum Bytes in stream ROD: %" PRIu64 "\n",
+                       sg_get_unaligned_be64(ucp + 8));
+                printf("  Optimal Bytes in stream ROD transfer:"
+                       " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 16));
+                break;
+            case 3:
+                /* Copy manager ROD device type specific descriptor */
+                printf("  Maximum Bytes in processor ROD: %" PRIu64 "\n",
+                       sg_get_unaligned_be64(ucp + 8));
+                printf("  Optimal Bytes in processor ROD transfer:"
+                       " %" PRIu64 "\n", sg_get_unaligned_be64(ucp + 16));
+                break;
+            default:
+                printf("  Unhandled descriptor (format %d, device type %d)\n",
+                       ucp[0] >> 5, ucp[0] & 0x1F);
+                break;
+        }
+    }
+}
+
+struct tpc_desc_type {
+    unsigned char code;
+    const char * name;
+};
+
+static struct tpc_desc_type tpc_desc_arr[] = {
+    {0x0, "block -> stream"},
+    {0x1, "stream -> block"},
+    {0x2, "block -> block"},
+    {0x3, "stream -> stream"},
+    {0x4, "inline -> stream"},
+    {0x5, "embedded -> stream"},
+    {0x6, "stream -> discard"},
+    {0x7, "verify CSCD"},
+    {0x8, "block<o> -> stream"},
+    {0x9, "stream -> block<o>"},
+    {0xa, "block<o> -> block<o>"},
+    {0xb, "block -> stream & application_client"},
+    {0xc, "stream -> block & application_client"},
+    {0xd, "block -> block & application_client"},
+    {0xe, "stream -> stream&application_client"},
+    {0xf, "stream -> discard&application_client"},
+    {0x10, "filemark -> tape"},
+    {0x11, "space -> tape"},            /* obsolete: spc5r02 */
+    {0x12, "locate -> tape"},           /* obsolete: spc5r02 */
+    {0x13, "<i>tape -> <i>tape"},
+    {0x14, "register persistent reservation key"},
+    {0x15, "third party persistent reservation source I_T nexus"},
+    {0x16, "<i>block -> <i>block"},
+    {0x17, "positioning -> tape"},      /* this and next added spc5r02 */
+    {0x18, "<loi>tape -> <loi>tape"},   /* loi: logical object identifier */
+    {0xbe, "ROD <- block range(n)"},
+    {0xbf, "ROD <- block range(1)"},
+    {0xe0, "CSCD: FC N_Port_Name"},
+    {0xe1, "CSCD: FC N_Port_ID"},
+    {0xe2, "CSCD: FC N_Port_ID with N_Port_Name, checking"},
+    {0xe3, "CSCD: Parallel interface: I_T"},
+    {0xe4, "CSCD: Identification Descriptor"},
+    {0xe5, "CSCD: IPv4"},
+    {0xe6, "CSCD: Alias"},
+    {0xe7, "CSCD: RDMA"},
+    {0xe8, "CSCD: IEEE 1394 EUI-64"},
+    {0xe9, "CSCD: SAS SSP"},
+    {0xea, "CSCD: IPv6"},
+    {0xeb, "CSCD: IP copy service"},
+    {0xfe, "CSCD: ROD"},
+    {0xff, "CSCD: extension"},
+    {0x0, NULL},
+};
+
+static const char *
+get_tpc_desc_name(unsigned char code)
+{
+    const struct tpc_desc_type * dtp;
+
+    for (dtp = tpc_desc_arr; dtp->name; ++dtp) {
+        if (code == dtp->code)
+            return dtp->name;
+    }
+    return "";
+}
+
+struct tpc_rod_type {
+    uint32_t type;
+    const char * name;
+};
+
+static struct tpc_rod_type tpc_rod_arr[] = {
+    {0x0, "copy manager internal"},
+    {0x10000, "access upon reference"},
+    {0x800000, "point in time copy - default"},
+    {0x800001, "point in time copy - change vulnerable"},
+    {0x800002, "point in time copy - persistent"},
+    {0x80ffff, "point in time copy - any"},
+    {0xffff0001, "block device zero"},
+    {0x0, NULL},
+};
+
+static const char *
+get_tpc_rod_name(uint32_t rod_type)
+{
+    const struct tpc_rod_type * rtp;
+
+    for (rtp = tpc_rod_arr; rtp->name; ++rtp) {
+        if (rod_type == rtp->type)
+            return rtp->name;
+    }
+    return "";
+}
+
+struct cscd_desc_id_t {
+    uint16_t id;
+    const char * name;
+};
+
+static struct cscd_desc_id_t cscd_desc_id_arr[] = {
+    /* only values higher than 0x7ff are listed */
+    {0xc000, "copy src or dst null LU, pdt=0"},
+    {0xc001, "copy src or dst null LU, pdt=1"},
+    {0xf800, "copy src or dst in ROD token"},
+    {0xffff, "copy src or dst is copy manager LU"},
+    {0x0, NULL},
+};
+
+static const char *
+get_cscd_desc_id_name(uint16_t cscd_desc_id)
+{
+    const struct cscd_desc_id_t * cdip;
+
+    for (cdip = cscd_desc_id_arr; cdip->name; ++cdip) {
+        if (cscd_desc_id == cdip->id)
+            return cdip->name;
+    }
+    return "";
+}
+
+/* VPD_3PARTY_COPY [3PC, third party copy] */
+static void
+decode_3party_copy_vpd(unsigned char * buff, int len, int do_hex, int verbose)
+{
+    int j, k, m, bump, desc_type, desc_len, sa_len;
+    unsigned int u;
+    const unsigned char * ucp;
+    const char * cp;
+    uint64_t ull;
+    char b[80];
+
+    if (len < 4) {
+        pr2serr("Third-party Copy VPD page length too short=%d\n", len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        desc_type = sg_get_unaligned_be16(ucp);
+        desc_len = sg_get_unaligned_be16(ucp + 2);
+        if (verbose)
+            printf("Descriptor type=%d [0x%x] , len %d\n", desc_type,
+                   desc_type, desc_len);
+        bump = 4 + desc_len;
+        if ((k + bump) > len) {
+            pr2serr("Third-party Copy VPD page, short descriptor length=%d, "
+                    "left=%d\n", bump, (len - k));
+            return;
+        }
+        if (0 == desc_len)
+            continue;
+        if (2 == do_hex)
+            dStrHex((const char *)ucp + 4, desc_len, 1);
+        else if (do_hex > 2)
+            dStrHex((const char *)ucp, bump, 1);
+        else {
+            switch (desc_type) {
+            case 0x0000:    /* Required if POPULATE TOKEN (or friend) used */
+                printf(" Block Device ROD Token Limits:\n");
+                printf("  Maximum Range Descriptors: %d\n",
+                       sg_get_unaligned_be16(ucp + 10));
+                u = sg_get_unaligned_be32(ucp + 12);
+                printf("  Maximum Inactivity Timeout: %u seconds\n", u);
+                u = sg_get_unaligned_be32(ucp + 16);
+                printf("  Default Inactivity Timeout: %u seconds\n", u);
+                ull = sg_get_unaligned_be64(ucp + 20);
+                printf("  Maximum Token Transfer Size: %" PRIu64 "\n", ull);
+                ull = sg_get_unaligned_be64(ucp + 28);
+                printf("  Optimal Transfer Count: %" PRIu64 "\n", ull);
+                break;
+            case 0x0001:    /* Mandatory (SPC-4) */
+                printf(" Supported Commands:\n");
+                j = 0;
+                while (j < ucp[4]) {
+                    sa_len = ucp[6 + j];
+                    for (m = 0; m < sa_len; ++m) {
+                        sg_get_opcode_sa_name(ucp[5 + j], ucp[7 + j + m],
+                                              0, sizeof(b), b);
+                        printf("  %s\n", b);
+                    }
+                    j += sa_len + 2;
+                }
+                break;
+            case 0x0004:
+                printf(" Parameter Data:\n");
+                printf("  Maximum CSCD Descriptor Count: %d\n",
+                       sg_get_unaligned_be16(ucp + 8));
+                printf("  Maximum Segment Descriptor Count: %d\n",
+                       sg_get_unaligned_be16(ucp + 10));
+                u = sg_get_unaligned_be32(ucp + 12);
+                printf("  Maximum Descriptor List Length: %u\n", u);
+                u = sg_get_unaligned_be32(ucp + 16);
+                printf("  Maximum Inline Data Length: %u\n", u);
+                break;
+            case 0x0008:
+                printf(" Supported Descriptors:\n");
+                for (j = 0; j < ucp[4]; j++) {
+                    cp = get_tpc_desc_name(ucp[5 + j]);
+                    if (strlen(cp) > 0)
+                        printf("  %s [0x%x]\n", cp, ucp[5 + j]);
+                    else
+                        printf("  0x%x\n", ucp[5 + j]);
+                }
+                break;
+            case 0x000C:
+                printf(" Supported CSCD IDs (above 0x7ff):\n");
+                for (j = 0; j < sg_get_unaligned_be16(ucp + 4); j += 2) {
+                    u = sg_get_unaligned_be16(ucp + 6 + j);
+                    cp = get_cscd_desc_id_name(u);
+                    if (strlen(cp) > 0)
+                        printf("  %s [0x%04x]\n", cp, u);
+                    else
+                        printf("  0x%04x\n", u);
+                }
+                break;
+            case 0x0106:
+                printf(" ROD Token Features:\n");
+                printf("  Remote Tokens: %d\n", ucp[4] & 0x0f);
+                u = sg_get_unaligned_be32(ucp + 16);
+                printf("  Minimum Token Lifetime: %u seconds\n", u);
+                u = sg_get_unaligned_be32(ucp + 20);
+                printf("  Maximum Token Lifetime: %u seconds\n", u);
+                u = sg_get_unaligned_be32(ucp + 24);
+                printf("  Maximum Token inactivity timeout: %u\n", u);
+                decode_rod_descriptor(ucp + 48,
+                                      sg_get_unaligned_be16(ucp + 46));
+                break;
+            case 0x0108:
+                printf(" Supported ROD Token and ROD Types:\n");
+                for (j = 0; j < sg_get_unaligned_be16(ucp + 6); j+= 64) {
+                    u = sg_get_unaligned_be32(ucp + 8 + j);
+                    cp = get_tpc_rod_name(u);
+                    if (strlen(cp) > 0)
+                        printf("  ROD Type: %s [0x%x]\n", cp, u);
+                    else
+                        printf("  ROD Type: 0x%x\n", u);
+                    printf("    Internal: %s\n",
+                           (ucp[8 + j + 4] & 0x80) ? "yes" : "no");
+                    printf("    Token In: %s\n",
+                           (ucp[8 + j + 4] & 0x02) ? "yes" : "no");
+                    printf("    Token Out: %s\n",
+                           (ucp[8 + j + 4] & 0x01) ? "yes" : "no");
+                    printf("    Preference: %d\n",
+                           sg_get_unaligned_be16(ucp + 8 + j + 6));
+                }
+                break;
+            case 0x8001:    /* Mandatory (SPC-4) */
+                printf(" General Copy Operations:\n");
+                u = sg_get_unaligned_be32(ucp + 4);
+                printf("  Total Concurrent Copies: %u\n", u);
+                u = sg_get_unaligned_be32(ucp + 8);
+                printf("  Maximum Identified Concurrent Copies: %u\n", u);
+                u = sg_get_unaligned_be32(ucp + 12);
+                printf("  Maximum Segment Length: %u\n", u);
+                ull = (1 << ucp[16]); /* field is power of 2 */
+                printf("  Data Segment Granularity: %" PRIu64 "\n", ull);
+                ull = (1 << ucp[17]);
+                printf("  Inline Data Granularity: %" PRIu64 "\n", ull);
+                break;
+            case 0x9101:
+                printf(" Stream Copy Operations:\n");
+                u = sg_get_unaligned_be32(ucp + 4);
+                printf("  Maximum Stream Device Transfer Size: %u\n", u);
+                break;
+            case 0xC001:
+                printf(" Held Data:\n");
+                u = sg_get_unaligned_be32(ucp + 4);
+                printf("  Held Data Limit: %u\n", u);
+                ull = (1 << ucp[8]);
+                printf("  Held Data Granularity: %" PRIu64 "\n", ull);
+                break;
+            default:
+                pr2serr("Unexpected type=%d\n", desc_type);
+                dStrHexErr((const char *)ucp, bump, 1);
+                break;
+            }
+        }
+    }
+}
+
+/* VPD_PROTO_LU */
+static void
+decode_proto_lu_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, bump, rel_port, desc_len, proto;
+    unsigned char * ucp;
+
+    if (1 == do_hex) {
+        dStrHex((const char *)buff, len, 0);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("Protocol-specific logical unit information VPD page length "
+                "too short=%d\n", len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        rel_port = sg_get_unaligned_be16(ucp);
+        printf("  Relative port=%d\n", rel_port);
+        proto = ucp[2] & 0xf;
+        desc_len = sg_get_unaligned_be16(ucp + 6);
+        bump = 8 + desc_len;
+        if ((k + bump) > len) {
+            pr2serr("Protocol-specific logical unit information VPD page, "
+                    "short descriptor length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (0 == desc_len)
+            continue;
+        if (2 == do_hex)
+            dStrHex((const char *)ucp + 8, desc_len, 1);
+        else if (do_hex > 2)
+            dStrHex((const char *)ucp, bump, 1);
+        else {
+            switch (proto) {
+            case TPROTO_SAS:
+                printf("    Protocol identifier: SAS\n");
+                printf("    TLR control supported: %d\n", !!(ucp[8] & 0x1));
+                break;
+            default:
+                pr2serr("Unexpected proto=%d\n", proto);
+                dStrHexErr((const char *)ucp, bump, 1);
+                break;
+            }
+        }
+    }
+}
+
+/* VPD_PROTO_PORT */
+static void
+decode_proto_port_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, j, bump, rel_port, desc_len, proto;
+    unsigned char * ucp;
+    unsigned char * pidp;
+
+    if (1 == do_hex) {
+        dStrHex((const char *)buff, len, 0);
+        return;
+    }
+    if (len < 4) {
+        pr2serr("Protocol-specific port information VPD page length too "
+                "short=%d\n", len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        rel_port = sg_get_unaligned_be16(ucp);
+        printf("  Relative port=%d\n", rel_port);
+        proto = ucp[2] & 0xf;
+        desc_len = sg_get_unaligned_be16(ucp + 6);
+        bump = 8 + desc_len;
+        if ((k + bump) > len) {
+            pr2serr("Protocol-specific port VPD page, short descriptor "
+                    "length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+        if (0 == desc_len)
+            continue;
+        if (2 == do_hex)
+            dStrHex((const char *)ucp + 8, desc_len, 1);
+        else if (do_hex > 2)
+            dStrHex((const char *)ucp, bump, 1);
+        else {
+            switch (proto) {
+            case TPROTO_SAS:    /* page added in spl3r02 */
+                printf("    power disable supported (pwr_d_s)=%d\n",
+                       !!(ucp[3] & 0x1));       /* added spl3r03 */
+                pidp = ucp + 8;
+                for (j = 0; j < desc_len; j += 4, pidp += 4)
+                    printf("      phy id=%d, SSP persistent capable=%d\n",
+                           pidp[1], (0x1 & pidp[2]));
+                break;
+            default:
+                pr2serr("Unexpected proto=%d\n", proto);
+                dStrHexErr((const char *)ucp, bump, 1);
+                break;
+            }
+        }
+    }
+}
+
+/* VPD_BLOCK_LIMITS sbc */
+/* VPD_SA_DEV_CAP ssc */
+/* VPD_OSD_INFO osd */
+static void
+decode_b0_vpd(unsigned char * buff, int len, int do_hex, int pdt)
+{
+    unsigned int u;
+    unsigned char b[4];
+
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    switch (pdt) {
+    case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+        if (len < 16) {
+            pr2serr("Block limits VPD page length too short=%d\n", len);
+            return;
+        }
+        printf("  Write same non-zero (WSNZ): %d\n", !!(buff[4] & 0x1));
+        printf("  Maximum compare and write length: %u blocks\n",
+               buff[5]);
+        u = sg_get_unaligned_be16(buff + 6);
+        printf("  Optimal transfer length granularity: %u blocks\n", u);
+        u = sg_get_unaligned_be32(buff + 8);
+        printf("  Maximum transfer length: %u blocks\n", u);
+        u = sg_get_unaligned_be32(buff + 12);
+        printf("  Optimal transfer length: %u blocks\n", u);
+        if (len > 19) {     /* added in sbc3r09 */
+            u = sg_get_unaligned_be32(buff + 16);
+            printf("  Maximum prefetch length: %u blocks\n", u);
+            /* was 'Maximum prefetch transfer length' prior to sbc3r33 */
+        }
+        if (len > 27) {     /* added in sbc3r18 */
+            u = sg_get_unaligned_be32(buff + 20);
+            printf("  Maximum unmap LBA count: %u\n", u);
+            u = sg_get_unaligned_be32(buff + 24);
+            printf("  Maximum unmap block descriptor count: %u\n", u);
+        }
+        if (len > 35) {     /* added in sbc3r19 */
+            u = sg_get_unaligned_be32(buff + 28);
+            printf("  Optimal unmap granularity: %u\n", u);
+            printf("  Unmap granularity alignment valid: %u\n",
+                   !!(buff[32] & 0x80));
+            memcpy(b, buff + 32, 4);
+            b[0] &= 0x7f;       /* mask off top bit */
+            u = sg_get_unaligned_be32(b);
+            printf("  Unmap granularity alignment: %u\n", u);
+            /* added in sbc3r26 */
+            printf("  Maximum write same length: 0x%" PRIx64 " blocks\n",
+                   sg_get_unaligned_be64(buff + 36));
+        }
+        if (len > 44) {     /* added in sbc4r02 */
+            u = sg_get_unaligned_be32(buff + 44);
+            printf("  Maximum atomic transfer length: %u\n", u);
+            u = sg_get_unaligned_be32(buff + 48);
+            printf("  Atomic alignment: %u\n", u);
+            u = sg_get_unaligned_be32(buff + 52);
+            printf("  Atomic transfer length granularity: %u\n", u);
+        }
+        if (len > 56) {     /* added in sbc4r04 */
+            u = sg_get_unaligned_be32(buff + 56);
+            printf("  Maximum atomic transfer length with atomic boundary: "
+                   "%u\n", u);
+            u = sg_get_unaligned_be32(buff + 60);
+            printf("  Maximum atomic boundary size: %u\n", u);
+        }
+        break;
+    case PDT_TAPE: case PDT_MCHANGER:
+        printf("  WORM=%d\n", !!(buff[4] & 0x1));
+        break;
+    case PDT_OSD:
+    default:
+        pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+        dStrHexErr((const char *)buff, len, 0);
+        break;
+    }
+}
+
+static const char * product_type_arr[] =
+{
+    "Not specified",
+    "CFast",
+    "CompactFlash",
+    "MemoryStick",
+    "MultiMediaCard",
+    "Secure Digital Card (SD)",
+    "XQD",
+    "Universal Flash Storage Card (UFS)",
+};
+
+/* VPD_BLOCK_DEV_CHARS sbc */
+/* VPD_MAN_ASS_SN ssc */
+/* VPD_SECURITY_TOKEN osd */
+static void
+decode_b1_vpd(unsigned char * buff, int len, int do_hex, int pdt)
+{
+    unsigned int u, k;
+
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    switch (pdt) {
+    case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+        if (len < 64) {
+            pr2serr("Block device characteristics VPD page length too "
+                    "short=%d\n", len);
+            return;
+        }
+        u = sg_get_unaligned_be16(buff + 4);
+        if (0 == u)
+            printf("  Medium rotation rate is not reported\n");
+        else if (1 == u)
+            printf("  Non-rotating medium (e.g. solid state)\n");
+        else if ((u < 0x401) || (0xffff == u))
+            printf("  Reserved [0x%x]\n", u);
+        else
+            printf("  Nominal rotation rate: %u rpm\n", u);
+        u = buff[6];
+        k = sizeof(product_type_arr) / sizeof(product_type_arr[0]);
+        if (u < k)
+            printf("  Product type: %s\n", product_type_arr[u]);
+        else if (u < 0xf0)
+            printf("  Product type: Reserved [0x%x]\n", u);
+        else
+            printf("  Product type: Vendor specific [0x%x]\n", u);
+        printf("  WABEREQ=%d\n", (buff[7] >> 6) & 0x3);
+        printf("  WACEREQ=%d\n", (buff[7] >> 4) & 0x3);
+        u = buff[7] & 0xf;
+        printf("  Nominal form factor");
+        switch (u) {
+        case 0:
+            printf(" not reported\n");
+            break;
+        case 1:
+            printf(": 5.25 inch\n");
+            break;
+        case 2:
+            printf(": 3.5 inch\n");
+            break;
+        case 3:
+            printf(": 2.5 inch\n");
+            break;
+        case 4:
+            printf(": 1.8 inch\n");
+            break;
+        case 5:
+            printf(": less then 1.8 inch\n");
+            break;
+        default:
+            printf(": reserved\n");
+            break;
+        }
+        printf("  ZONED=%d\n", (buff[8] >> 4) & 0x3);   /* sbc4r04 */
+        printf("  BOCS=%d\n", !!(buff[8] & 0x4));       /* sbc4r07 */
+        printf("  FUAB=%d\n", !!(buff[8] & 0x2));
+        printf("  VBULS=%d\n", !!(buff[8] & 0x1));
+        break;
+    case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC:
+        printf("  Manufacturer-assigned serial number: %.*s\n",
+               len - 4, buff + 4);
+        break;
+    default:
+        pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+        dStrHexErr((const char *)buff, len, 0);
+        break;
+    }
+}
+
+/* VPD_LB_PROVISIONING */
+static int
+decode_block_lb_prov_vpd(unsigned char * b, int len, const struct opts_t * op)
+{
+    int dp;
+
+    if (len < 4) {
+        pr2serr("Logical block provisioning page too short=%d\n", len);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    printf("  Unmap command supported (LBPU): %d\n", !!(0x80 & b[5]));
+    printf("  Write same (16) with unmap bit supported (LBWS): %d\n",
+           !!(0x40 & b[5]));
+    printf("  Write same (10) with unmap bit supported (LBWS10): %d\n",
+           !!(0x20 & b[5]));
+    printf("  Logical block provisioning read zeros (LBPRZ): %d\n",
+           (0x7 & (b[5] >> 2)));
+    printf("  Anchored LBAs supported (ANC_SUP): %d\n", !!(0x2 & b[5]));
+    dp = !!(b[5] & 0x1);
+    printf("  Threshold exponent: %d\n", b[4]);
+    printf("  Descriptor present (DP): %d\n", dp);
+    printf("  Minimum percentage: %d\n", 0x1f & (b[6] >> 3));
+    printf("  Provisioning type: %d\n", b[6] & 0x7);
+    printf("  Threshold percentage: %d\n", b[7]);
+    if (dp) {
+        const unsigned char * ucp;
+        int i_len, p_id, c_set, piv, assoc, desig_type;
+
+        ucp = b + 8;
+        i_len = ucp[3];
+        if (0 == i_len) {
+            pr2serr("Logical block provisioning page provisioning group "
+                    "descriptor too short=%d\n", i_len);
+            return 0;
+        }
+        printf("  Provisioning group descriptor\n");
+        p_id = ((ucp[0] >> 4) & 0xf);
+        c_set = (ucp[0] & 0xf);
+        piv = ((ucp[1] & 0x80) ? 1 : 0);
+        assoc = ((ucp[1] >> 4) & 0x3);
+        desig_type = (ucp[1] & 0xf);
+        decode_designation_descriptor(ucp, i_len, p_id, c_set, piv, assoc,
+                                      desig_type, 1, op);
+    }
+    return 0;
+}
+
+/* VPD_SUP_BLOCK_LENS  0xb4 */
+static void
+decode_sup_block_lens_vpd(unsigned char * buff, int len)
+{
+    int k;
+    unsigned int u;
+    unsigned char * ucp;
+
+    if (len < 4) {
+        pr2serr("Supported block lengths and protection types VPD page "
+                "length too short=%d\n", len);
+        return;
+    }
+    len -= 4;
+    ucp = buff + 4;
+    for (k = 0; k < len; k += 8, ucp += 8) {
+        u = sg_get_unaligned_be32(ucp);
+        printf("  Logical block length: %u\n", u);
+        printf("    P_I_I_SUP: %d\n", !!(ucp[4] & 0x40));
+        printf("    NO_PI_CHK: %d\n", !!(ucp[4] & 0x8));  /* sbc4r05 */
+        printf("    GRD_CHK: %d\n", !!(ucp[4] & 0x4));
+        printf("    APP_CHK: %d\n", !!(ucp[4] & 0x2));
+        printf("    REF_CHK: %d\n", !!(ucp[4] & 0x1));
+        printf("    T3PS_SUP: %d\n", !!(ucp[5] & 0x8));
+        printf("    T2PS_SUP: %d\n", !!(ucp[5] & 0x4));
+        printf("    T1PS_SUP: %d\n", !!(ucp[5] & 0x2));
+        printf("    T0PS_SUP: %d\n", !!(ucp[5] & 0x1));
+    }
+}
+
+/* VPD_BLOCK_DEV_C_EXTENS  0xb5 */
+static void
+decode_block_dev_char_ext_vpd(unsigned char * b, int len)
+{
+    if (len < 16) {
+        pr2serr("Block device characteristics extension VPD page "
+                "length too short=%d\n", len);
+        return;
+    }
+    printf("  Utilization type: ");
+    switch (b[5]) {
+    case 1:
+        printf("Combined writes and reads");
+        break;
+    case 2:
+        printf("Writes only");
+        break;
+    case 3:
+        printf("Separate writes and reads");
+        break;
+    default:
+        printf("Reserved");
+        break;
+    }
+    printf(" [0x%x]\n", b[5]);
+    printf("  Utilization units: ");
+    switch (b[6]) {
+    case 2:
+        printf("megabytes");
+        break;
+    case 3:
+        printf("gigabytes");
+        break;
+    case 4:
+        printf("terabytes");
+        break;
+    case 5:
+        printf("petabytes");
+        break;
+    case 6:
+        printf("exabytes");
+        break;
+    default:
+        printf("Reserved");
+        break;
+    }
+    printf(" [0x%x]\n", b[6]);
+    printf("  Utilization interval: ");
+    switch (b[7]) {
+    case 0xa:
+        printf("per day");
+        break;
+    case 0xe:
+        printf("per year");
+        break;
+    default:
+        printf("Reserved");
+        break;
+    }
+    printf(" [0x%x]\n", b[7]);
+    printf("  Utilization B: %u\n", sg_get_unaligned_be32(b + 8));
+    printf("  Utilization A: %u\n", sg_get_unaligned_be32(b + 12));
+}
+
+/* VPD_LB_PROTECTION    (SSC)  [added in ssc5r02a] */
+static void
+decode_lb_protection_vpd(unsigned char * buff, int len, int do_hex)
+{
+    int k, bump;
+    unsigned char * ucp;
+
+    if ((1 == do_hex) || (do_hex > 2)) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    if (len < 8) {
+        pr2serr("Logical block protection VPD page length too short=%d\n",
+                len);
+        return;
+    }
+    len -= 8;
+    ucp = buff + 8;
+    for (k = 0; k < len; k += bump, ucp += bump) {
+        bump = 1 + ucp[0];
+        printf("  method: %d, info_len: %d, LBP_W_C=%d, LBP_R_C=%d, "
+               "RBDP_C=%d\n", ucp[1], 0x3f & ucp[2], !!(0x80 & ucp[3]),
+               !!(0x40 & ucp[3]), !!(0x20 & ucp[3]));
+        if ((k + bump) > len) {
+            pr2serr("Logical block protection VPD page, short "
+                    "descriptor length=%d, left=%d\n", bump, (len - k));
+            return;
+        }
+    }
+}
+
+/* VPD_TA_SUPPORTED */
+static int
+decode_tapealert_supported_vpd(unsigned char * b, int len)
+{
+    if (len < 12) {
+        pr2serr("TapeAlert supported flags length too short=%d\n", len);
+        return SG_LIB_CAT_MALFORMED;
+    }
+    printf("  Flag01h: %d  02h: %d  03h: %d  04h: %d  05h: %d  06h: %d  "
+           "07h: %d  08h: %d\n", !!(b[4] & 0x80), !!(b[4] & 0x40),
+           !!(b[4] & 0x20), !!(b[4] & 0x10), !!(b[4] & 0x8), !!(b[4] & 0x4),
+           !!(b[4] & 0x2), !!(b[4] & 0x1));
+    printf("  Flag09h: %d  0ah: %d  0bh: %d  0ch: %d  0dh: %d  0eh: %d  "
+           "0fh: %d  10h: %d\n", !!(b[5] & 0x80), !!(b[5] & 0x40),
+           !!(b[5] & 0x20), !!(b[5] & 0x10), !!(b[5] & 0x8), !!(b[5] & 0x4),
+           !!(b[5] & 0x2), !!(b[5] & 0x1));
+    printf("  Flag11h: %d  12h: %d  13h: %d  14h: %d  15h: %d  16h: %d  "
+           "17h: %d  18h: %d\n", !!(b[6] & 0x80), !!(b[6] & 0x40),
+           !!(b[6] & 0x20), !!(b[6] & 0x10), !!(b[6] & 0x8), !!(b[6] & 0x4),
+           !!(b[6] & 0x2), !!(b[6] & 0x1));
+    printf("  Flag19h: %d  1ah: %d  1bh: %d  1ch: %d  1dh: %d  1eh: %d  "
+           "1fh: %d  20h: %d\n", !!(b[7] & 0x80), !!(b[7] & 0x40),
+           !!(b[7] & 0x20), !!(b[7] & 0x10), !!(b[7] & 0x8), !!(b[7] & 0x4),
+           !!(b[7] & 0x2), !!(b[7] & 0x1));
+    printf("  Flag21h: %d  22h: %d  23h: %d  24h: %d  25h: %d  26h: %d  "
+           "27h: %d  28h: %d\n", !!(b[8] & 0x80), !!(b[8] & 0x40),
+           !!(b[8] & 0x20), !!(b[8] & 0x10), !!(b[8] & 0x8), !!(b[8] & 0x4),
+           !!(b[8] & 0x2), !!(b[8] & 0x1));
+    printf("  Flag29h: %d  2ah: %d  2bh: %d  2ch: %d  2dh: %d  2eh: %d  "
+           "2fh: %d  30h: %d\n", !!(b[9] & 0x80), !!(b[9] & 0x40),
+           !!(b[9] & 0x20), !!(b[9] & 0x10), !!(b[9] & 0x8), !!(b[9] & 0x4),
+           !!(b[9] & 0x2), !!(b[9] & 0x1));
+    printf("  Flag31h: %d  32h: %d  33h: %d  34h: %d  35h: %d  36h: %d  "
+           "37h: %d  38h: %d\n", !!(b[10] & 0x80), !!(b[10] & 0x40),
+           !!(b[10] & 0x20), !!(b[10] & 0x10), !!(b[10] & 0x8),
+           !!(b[10] & 0x4), !!(b[10] & 0x2), !!(b[10] & 0x1));
+    printf("  Flag39h: %d  3ah: %d  3bh: %d  3ch: %d  3dh: %d  3eh: %d  "
+           "3fh: %d  40h: %d\n", !!(b[11] & 0x80), !!(b[11] & 0x40),
+           !!(b[11] & 0x20), !!(b[11] & 0x10), !!(b[11] & 0x8),
+           !!(b[11] & 0x4), !!(b[11] & 0x2), !!(b[11] & 0x1));
+    return 0;
+}
+
+/* VPD_LB_PROVISIONING sbc */
+/* VPD_TA_SUPPORTED ssc */
+static void
+decode_b2_vpd(unsigned char * buff, int len, int pdt,
+              const struct opts_t * op)
+{
+    if (op->do_hex) {
+        dStrHex((const char *)buff, len, (1 == op->do_hex) ? 0 : -1);
+        return;
+    }
+    switch (pdt) {
+    case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+        decode_block_lb_prov_vpd(buff, len, op);
+        break;
+    case PDT_TAPE: case PDT_MCHANGER:
+        decode_tapealert_supported_vpd(buff, len);
+        break;
+    default:
+        pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+        dStrHexErr((const char *)buff, len, 0);
+        break;
+    }
+}
+
+/* VPD_REFERRALS sbc */
+/* VPD_AUTOMATION_DEV_SN ssc */
+static void
+decode_b3_vpd(unsigned char * b, int len, int do_hex, int pdt)
+{
+    char obuff[DEF_ALLOC_LEN];
+    unsigned int u;
+
+    if (do_hex) {
+        dStrHex((const char *)b, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    switch (pdt) {
+    case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+        if (len < 16) {
+            pr2serr("Referrals VPD page length too short=%d\n", len);
+            break;
+        }
+        u = sg_get_unaligned_be32(b + 8);
+        printf("  User data segment size: %u\n", u);
+        u = sg_get_unaligned_be32(b + 12);
+        printf("  User data segment multiplier: %u\n", u);
+        break;
+    case PDT_TAPE: case PDT_MCHANGER:
+        memset(obuff, 0, sizeof(obuff));
+        len -= 4;
+        if (len >= (int)sizeof(obuff))
+            len = sizeof(obuff) - 1;
+        memcpy(obuff, b + 4, len);
+        printf("  Automation device serial number: %s\n", obuff);
+        break;
+    default:
+        pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+        dStrHexErr((const char *)b, len, 0);
+        break;
+    }
+}
+
+/* VPD_SUP_BLOCK_LENS sbc */
+/* VPD_DTDE_ADDRESS ssc */
+static void
+decode_b4_vpd(unsigned char * b, int len, int do_hex, int pdt)
+{
+    int k;
+
+    if (do_hex) {
+        dStrHex((const char *)b, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    switch (pdt) {
+    case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+        decode_sup_block_lens_vpd(b, len);
+        break;
+    case PDT_TAPE: case PDT_MCHANGER:
+        printf("  Data transfer device element address: 0x");
+        for (k = 4; k < len; ++k)
+            printf("%02x", (unsigned int)b[k]);
+        printf("\n");
+        break;
+    default:
+        pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+        dStrHexErr((const char *)b, len, 0);
+        break;
+    }
+}
+
+/* VPD_BLOCK_DEV_C_EXTENS sbc */
+static void
+decode_b5_vpd(unsigned char * b, int len, int do_hex, int pdt)
+{
+    if (do_hex) {
+        dStrHex((const char *)b, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    switch (pdt) {
+    case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+        decode_block_dev_char_ext_vpd(b, len);
+        break;
+    case PDT_TAPE: case PDT_MCHANGER:
+        decode_lb_protection_vpd(b, len, do_hex);
+        break;
+    default:
+        pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+        dStrHexErr((const char *)b, len, 0);
+        break;
+    }
+}
+
+/* VPD_ZBC_DEV_CHARS  sbc or zbc */
+static void
+decode_zbdc_vpd(unsigned char * b, int len, int do_hex)
+{
+    uint32_t u;
+
+    if (do_hex) {
+        dStrHex((const char *)b, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    if (len < 64) {
+        pr2serr("Zoned block device characteristics VPD page length too "
+                "short=%d\n", len);
+        return;
+    }
+    printf("  URSWRZ type: %d\n", !!(b[4] & 0x1));
+    u = sg_get_unaligned_be32(b + 8);
+    printf("  Optimal number of open sequential write preferred zones: ");
+    if (0xffffffff == u)
+        printf("not reported\n");
+    else
+        printf("%" PRIu32 "\n", u);
+    u = sg_get_unaligned_be32(b + 12);
+    printf("  Optimal number of non-sequentially written sequential write "
+           "preferred zones: ");
+    if (0xffffffff == u)
+        printf("not reported\n");
+    else
+        printf("%" PRIu32 "\n", u);
+    u = sg_get_unaligned_be32(b + 16);
+    printf("  Maximum number of open sequential write required zones: ");
+    if (0xffffffff == u)
+        printf("no limit\n");
+    else
+        printf("%" PRIu32 "\n", u);
+}
+
+/* VPD_BLOCK_LIMITS_EXT sbc */
+static void
+decode_b7_vpd(unsigned char * buff, int len, int do_hex, int pdt)
+{
+    unsigned int u;
+
+    if (do_hex) {
+        dStrHex((const char *)buff, len, (1 == do_hex) ? 0 : -1);
+        return;
+    }
+    switch (pdt) {
+    case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+        if (len < 12) {
+            pr2serr("Block limits extension VPD page length too short=%d\n",
+                    len);
+            return;
+        }
+        u = sg_get_unaligned_be16(buff + 6);
+        printf("  Maximum number of streams: %u\n", u);
+        u = sg_get_unaligned_be16(buff + 8);
+        printf("  Optimal stream write size: %u logical blocks\n", u);
+        u = sg_get_unaligned_be32(buff + 10);
+        printf("  Stream granularity size: %u\n", u);
+        break;
+    default:
+        pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
+        dStrHexErr((const char *)buff, len, 0);
+        break;
+    }
+}
+
+/* Returns 0 if successful */
+static int
+svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue, int off)
+{
+    int len, res;
+    int alloc_len = op->maxlen;
+    unsigned char * rp;
+
+    rp = rsp_buff + off;
+    if ((! op->do_hex) && (! op->do_raw))
+        printf("Only hex output supported\n");
+    if ((!op->do_raw) && (op->do_hex < 2)) {
+        if (subvalue)
+            printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", op->num_vpd,
+                   subvalue);
+        else if (op->num_vpd >= 0)
+            printf("VPD page code=0x%.2x:\n", op->num_vpd);
+        else
+            printf("VPD page code=%d:\n", op->num_vpd);
+    }
+    if (sg_fd >= 0) {
+        if (0 == alloc_len)
+            alloc_len = DEF_ALLOC_LEN;
+    }
+
+    res = vpd_fetch_page_from_dev(sg_fd, rp, op->num_vpd, alloc_len,
+                                  op->verbose, &len);
+    if (0 == res) {
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else {
+            if (op->do_hex > 1)
+                dStrHex((const char *)rp, len, -1);
+            else if (VPD_ASCII_OP_DEF == op->num_vpd)
+                dStrHex((const char *)rp, len, 0);
+            else
+                dStrHex((const char *)rp, len, (op->do_long ? 0 : 1));
+        }
+        return 0;
+    } else {
+        if (op->num_vpd >= 0)
+            pr2serr("fetching VPD page code=0x%.2x: failed\n", op->num_vpd);
+        else
+            pr2serr("fetching VPD page code=%d: failed\n", op->num_vpd);
+        return res;
+    }
+}
+
+/* Returns 0 if successful, else see sg_ll_inquiry() */
+static int
+svpd_decode_t10(int sg_fd, struct opts_t * op, int subvalue, int off)
+{
+    int len, pdt, num, k, resid, alloc_len, pn, vb, allow_name, long_notquiet;
+    int res = 0;
+    char b[48];
+    const struct svpd_values_name_t * vnp;
+    char obuff[DEF_ALLOC_LEN];
+    unsigned char * rp;
+
+    pn = op->num_vpd;
+    vb = op->verbose;
+    long_notquiet = op->do_long && (! op->do_quiet);
+    if (op->do_raw || (op->do_quiet && (! op->do_long) && (! op->do_all)) ||
+        (op->do_hex >= 3))
+        allow_name = 0;
+    else
+        allow_name = 1;
+    rp = rsp_buff + off;
+    switch(pn) {
+    case VPD_NO_RATHER_STD_INQ:    /* -2 (want standard inquiry response) */
+        if (sg_fd >= 0) {
+            if (op->maxlen > 0)
+                alloc_len = op->maxlen;
+            else if (op->do_long)
+                alloc_len = DEF_ALLOC_LEN;
+            else
+                alloc_len = 36;
+            res = pt_inquiry(sg_fd, 0, 0, rp, alloc_len, &resid, 1, vb);
+        } else {
+            alloc_len = op->maxlen;
+            resid = 0;
+            res = 0;
+        }
+        if (0 == res) {
+            alloc_len -= resid;
+            if (op->do_raw)
+                dStrRaw((const char *)rp, alloc_len);
+            else if (op->do_hex) {
+                if (! op->do_quiet && (op->do_hex < 3))
+                    printf("Standard Inquiry reponse:\n");
+                dStrHex((const char *)rp, alloc_len,
+                        (1 == op->do_hex) ? 0 : -1);
+            } else
+                decode_std_inq(rp, alloc_len, vb);
+            return 0;
+        }
+        break;
+    case VPD_SUPPORTED_VPDS:    /* 0x0 */
+        if (allow_name)
+            printf("Supported VPD pages VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else if (op->do_hex)
+                dStrHex((const char *)rp, len, (1 == op->do_hex) ? 0 : -1);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                num = rp[3];
+                if (num > (len - 4))
+                    num = (len - 4);
+                for (k = 0; k < num; ++k) {
+                    pn = rp[4 + k];
+                    vnp = sdp_get_vpd_detail(pn, -1, pdt);
+                    if (vnp) {
+                        if (op->do_long)
+                            printf("  0x%02x  %s [%s]\n", pn, vnp->name,
+                                   vnp->acron);
+                        else
+                            printf("  %s [%s]\n", vnp->name, vnp->acron);
+                    } else if (op->vend_prod_num >= 0) {
+                        vnp = svpd_find_vendor_by_num(pn, op->vend_prod_num);
+                        if (vnp) {
+                            if (op->do_long)
+                                printf("  0x%02x  %s [%s]\n", pn, vnp->name,
+                                       vnp->acron);
+                            else
+                                printf("  %s [%s]\n", vnp->name, vnp->acron);
+                        } else
+                            printf("  0x%x\n", pn);
+                    } else
+                        printf("  0x%x\n", pn);
+                }
+            }
+            return 0;
+        }
+        break;
+    case VPD_UNIT_SERIAL_NUM:   /* 0x80 */
+        if (allow_name)
+            printf("Unit serial number VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else if (op->do_hex)
+                dStrHex((const char *)rp, len, (1 == op->do_hex) ? 0 : -1);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                memset(obuff, 0, sizeof(obuff));
+                len -= 4;
+                if (len >= (int)sizeof(obuff))
+                    len = sizeof(obuff) - 1;
+                memcpy(obuff, rp + 4, len);
+                printf("  Unit serial number: %s\n", obuff);
+            }
+            return 0;
+        }
+        break;
+    case VPD_DEVICE_ID:         /* 0x83 */
+        if (allow_name)
+            printf("Device Identification VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else if (op->do_hex)
+                dStrHex((const char *)rp, len, (1 == op->do_hex) ? 0 : -1);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_id_vpd(rp, len, subvalue, op);
+            }
+            return 0;
+        }
+        break;
+    case VPD_SOFTW_INF_ID:      /* 0x84 */
+        if (allow_name)
+            printf("Software interface identification VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_softw_inf_id(rp, len, op->do_hex);
+            }
+            return 0;
+        }
+        break;
+    case VPD_MAN_NET_ADDR:      /* 0x85 */
+        if (allow_name)
+            printf("Management network addresses VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else
+                decode_net_man_vpd(rp, len, op->do_hex);
+            return 0;
+        }
+        break;
+    case VPD_EXT_INQ:           /* 0x86 */
+        if (allow_name)
+            printf("extended INQUIRY data VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                int protect = 0;
+                struct sg_simple_inquiry_resp sir;
+
+                if ((sg_fd >= 0) && long_notquiet) {
+                    res = sg_simple_inquiry(sg_fd, &sir, 0, vb);
+                    if (res) {
+                        if (op->verbose)
+                            pr2serr("%s: sg_simple_inquiry() failed, "
+                                    "res=%d\n", __func__, res);
+                    } else
+                        protect = sir.byte_5 & 0x1;  /* SPC-3 and later */
+                }
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_x_inq_vpd(rp, len, op->do_hex, long_notquiet, protect);
+            }
+            return 0;
+        }
+        break;
+    case VPD_MODE_PG_POLICY:    /* 0x87 */
+        if (allow_name)
+            printf("Mode page policy VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_mode_policy_vpd(rp, len, op->do_hex);
+            }
+            return 0;
+        }
+        break;
+    case VPD_SCSI_PORTS:        /* 0x88 */
+        if (allow_name)
+            printf("SCSI Ports VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_scsi_ports_vpd(rp, len, op);
+            }
+            return 0;
+        }
+        break;
+    case VPD_ATA_INFO:          /* 0x89 */
+        if (allow_name)
+            printf("ATA information VPD page:\n");
+        alloc_len = op->maxlen ? op->maxlen : VPD_ATA_INFO_LEN;
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, alloc_len, vb, &len);
+        if (0 == res) {
+            if ((2 == op->do_raw) || (3 == op->do_hex)) {  /* for hdparm */
+                if (len < (60 + 512))
+                    pr2serr("ATA_INFO VPD page len (%d) less than expected "
+                            "572\n", len);
+                else
+                    dWordHex((const unsigned short *)(rp + 60), 256, -2,
+                             sg_is_big_endian());
+            }
+            else if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_ata_info_vpd(rp, len, long_notquiet, op->do_hex);
+            }
+            return 0;
+        }
+        break;
+    case VPD_POWER_CONDITION:          /* 0x8a */
+        if (allow_name)
+            printf("Power condition VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_power_condition(rp, len, op->do_hex);
+            }
+            return 0;
+        }
+        break;
+    case VPD_DEVICE_CONSTITUENTS:      /* 0x8b */
+        if (allow_name)
+            printf("Device constituents VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else
+                decode_dev_const_vpd(rp, len, op->do_hex);
+            return 0;
+        }
+        break;
+    case VPD_POWER_CONSUMPTION:    /* 0x8d */
+        if (allow_name)
+            printf("Power consumption VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_power_consumption_vpd(rp, len, op->do_hex);
+            }
+            return 0;
+        }
+        break;
+    case VPD_3PARTY_COPY:   /* 0x8f */
+        if (allow_name)
+            printf("Third party copy VPD page:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else if (1 == op->do_hex)
+                dStrHex((const char *)rp, len, 0);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_3party_copy_vpd(rp, len, op->do_hex, vb);
+            }
+            return 0;
+        }
+        break;
+    case VPD_PROTO_LU:          /* 0x90 */
+        if (allow_name)
+            printf("Protocol-specific logical unit information:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rsp_buff[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_proto_lu_vpd(rp, len, op->do_hex);
+            }
+            return 0;
+        }
+        break;
+    case VPD_PROTO_PORT:        /* 0x91 */
+        if (allow_name)
+            printf("Protocol-specific port information:\n");
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_proto_port_vpd(rp, len, op->do_hex);
+            }
+            return 0;
+        }
+        break;
+    case 0xb0:  /* depends on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (allow_name) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+                    printf("Block limits VPD page (SBC):\n");
+                    break;
+                case PDT_TAPE: case PDT_MCHANGER:
+                    printf("Sequential-access device capabilities VPD page "
+                           "(SSC):\n");
+                    break;
+                case PDT_OSD:
+                    printf("OSD information VPD page (OSD):\n");
+                    break;
+                default:
+                    printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b0_vpd(rp, len, op->do_hex, pdt);
+            }
+            return 0;
+        } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
+            printf("VPD page=0xb0\n");
+        break;
+    case 0xb1:  /* depends on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (allow_name) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+                    printf("Block device characteristics VPD page (SBC):\n");
+                    break;
+                case PDT_TAPE: case PDT_MCHANGER:
+                    printf("Manufactured-assigned serial number VPD page "
+                           "(SSC):\n");
+                    break;
+                case PDT_OSD:
+                    printf("Security token VPD page (OSD):\n");
+                    break;
+                case PDT_ADC:
+                    printf("Manufactured-assigned serial number VPD page "
+                           "(ADC):\n");
+                    break;
+                default:
+                    printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b1_vpd(rp, len, op->do_hex, pdt);
+            }
+            return 0;
+        } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
+            printf("VPD page=0xb1\n");
+        break;
+    case 0xb2:          /* VPD page depends on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (allow_name) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+                    printf("Logical block provisioning VPD page (SBC):\n");
+                    break;
+                case PDT_TAPE: case PDT_MCHANGER:
+                    printf("TapeAlert supported flags VPD page (SSC):\n");
+                    break;
+                default:
+                    printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b2_vpd(rp, len, pdt, op);
+            }
+            return 0;
+        } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
+            printf("VPD page=0xb2\n");
+        break;
+    case 0xb3:          /* VPD page depends on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (allow_name) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+                    printf("Referrals VPD page (SBC):\n");
+                    break;
+                case PDT_TAPE: case PDT_MCHANGER:
+                    printf("Automation device serial number VPD page "
+                           "(SSC):\n");
+                    break;
+                default:
+                    printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b3_vpd(rp, len, op->do_hex, pdt);
+            }
+            return 0;
+        } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
+            printf("VPD page=0xb3\n");
+        break;
+    case 0xb4:          /* VPD page depends on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (allow_name) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+                    printf("Supported block lengths and protection types "
+                           "VPD page (SBC):\n");
+                    break;
+                case PDT_TAPE: case PDT_MCHANGER:
+                    printf("Data transfer device element address (SSC):\n");
+                    break;
+                default:
+                    printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b4_vpd(rp, len, op->do_hex, pdt);
+            }
+            return 0;
+        } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
+            printf("VPD page=0xb4\n");
+        break;
+    case 0xb5:          /* VPD page depends on pdt */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (allow_name) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+                    printf("Block device characteristics extension VPD page "
+                           "(SBC):\n");
+                    break;
+                case PDT_TAPE: case PDT_MCHANGER:
+                    printf("Logical block protection VPD page (SSC):\n");
+                    break;
+                default:
+                    printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b5_vpd(rp, len, op->do_hex, pdt);
+            }
+            return 0;
+        } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
+            printf("VPD page=0xb5\n");
+        break;
+    case VPD_ZBC_DEV_CHARS:       /* 0xb6 for both pdt=0 and pdt=0x14 */
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (allow_name) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+                    printf("Zoned block device characteristics VPD page "
+                           "(SBC, ZBC):\n");
+                    break;
+                default:
+                    printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_zbdc_vpd(rp, len, op->do_hex);
+            }
+            return 0;
+        } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
+            printf("VPD page=0xb5\n");
+        break;
+    case 0xb7:
+        res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len);
+        if (0 == res) {
+            pdt = rp[0] & 0x1f;
+            if (allow_name) {
+                switch (pdt) {
+                case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+                    printf("Block limits extension VPD page (SBC):\n");
+                    break;
+                default:
+                    printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+                    break;
+                }
+            }
+            if (op->do_raw)
+                dStrRaw((const char *)rp, len);
+            else {
+                pdt = rp[0] & 0x1f;
+                if (vb || long_notquiet)
+                    printf("   [PQual=%d  Peripheral device type: %s]\n",
+                           (rp[0] & 0xe0) >> 5,
+                           sg_get_pdt_str(pdt, sizeof(b), b));
+                decode_b7_vpd(rp, len, op->do_hex, pdt);
+            }
+            return 0;
+        } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
+            printf("VPD page=0xb7\n");
+        break;
+    default:
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    return res;
+}
+
+static int
+svpd_decode_all(int sg_fd, struct opts_t * op)
+{
+    int k, res, rlen, n, pn;
+    int max_pn = 255;
+    int any_err = 0;
+    unsigned char vpd0_buff[512];
+    unsigned char * rp = vpd0_buff;
+
+    if (op->num_vpd > 0)
+        max_pn = op->num_vpd;
+    if (sg_fd >= 0) {
+        res = vpd_fetch_page_from_dev(sg_fd, rp, VPD_SUPPORTED_VPDS,
+                                      op->maxlen, op->verbose, &rlen);
+        if (res) {
+            if (SG_LIB_CAT_ABORTED_COMMAND == res)
+                pr2serr("%s: VPD page 0, aborted command\n", __func__);
+            else if (res) {
+                char b[80];
+
+                sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+                pr2serr("%s: fetching VPD page 0 failed: %s\n", __func__, b);
+            }
+            return res;
+        }
+        n = sg_get_unaligned_be16(rp + 2);
+        if (n > (rlen - 4)) {
+            if (op->verbose)
+                pr2serr("%s: rlen=%d > page0 size=%d\n", __func__, rlen,
+                        n + 4);
+            n = (rlen - 4);
+        }
+        for (k = 0; k < n; ++k) {
+            pn = rp[4 + k];
+            if (pn > max_pn)
+                continue;
+            op->num_vpd = pn;
+            if (op->do_long)
+                printf("[0x%x] ", pn);
+
+            res = svpd_decode_t10(sg_fd, op, 0, 0);
+            if (SG_LIB_SYNTAX_ERROR == res) {
+                res = svpd_decode_vendor(sg_fd, op, 0);
+                if (SG_LIB_SYNTAX_ERROR == res)
+                    res = svpd_unable_to_decode(sg_fd, op, 0, 0);
+            }
+            if (SG_LIB_CAT_ABORTED_COMMAND == res)
+                pr2serr("fetching VPD page failed, aborted command\n");
+            else if (res) {
+                char b[80];
+
+                sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+                pr2serr("fetching VPD page failed: %s\n", b);
+            }
+            if (res)
+                any_err = res;
+        }
+        res = any_err;
+    } else {    /* input is coming from --inhex=FN */
+        int bump, off;
+        int in_len = op->maxlen;
+        int prev_pn = -1;
+
+        rp = rsp_buff;
+        for (k = 0, off = 0; off < in_len; ++k, off += bump) {
+            rp = rsp_buff + off;
+            pn = rp[1];
+            bump = sg_get_unaligned_be16(rp + 2) + 4;
+            if ((off + bump) > in_len) {
+                pr2serr("%s: page 0x%x size (%d) exceeds buffer\n", __func__,
+                        pn, bump);
+                bump = in_len - off;
+            }
+            if (pn <= prev_pn) {
+                pr2serr("%s: prev_pn=0x%x, this pn=0x%x, not ascending so "
+                        "exit\n", __func__, prev_pn, pn);
+                break;
+            }
+            prev_pn = pn;
+            op->num_vpd = pn;
+            if (pn > max_pn) {
+                if (op->verbose > 2)
+                    pr2serr("%s: skipping as this pn=0x%x exceeds "
+                            "max_pn=0x%x\n", __func__, pn, max_pn);
+                continue;
+            }
+            if (op->do_long)
+                printf("[0x%x] ", pn);
+
+            res = svpd_decode_t10(-1, op, 0, off);
+            if (SG_LIB_SYNTAX_ERROR == res) {
+                res = svpd_decode_vendor(-1, op, off);
+                if (SG_LIB_SYNTAX_ERROR == res)
+                    res = svpd_unable_to_decode(-1, op, 0, off);
+            }
+        }
+    }
+    return 0;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, c, res, matches;
+    const struct svpd_values_name_t * vnp;
+    const char * cp;
+    int inhex_len = 0;
+    int ret = 0;
+    int subvalue = 0;
+    int page_pdt = -1;
+    struct opts_t opts;
+    struct opts_t * op;
+
+    op = &opts;
+    memset(&opts, 0, sizeof(opts));
+    op->vend_prod_num = -1;
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "aehHiI:lm:M:p:qrvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            ++op->do_all;
+            break;
+        case 'e':
+            ++op->do_enum;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'H':
+            ++op->do_hex;
+            break;
+        case 'i':
+            ++op->do_ident;
+            break;
+        case 'I':
+            if (op->inhex_fn) {
+                pr2serr("only one '--inhex=' option permitted\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                op->inhex_fn = optarg;
+            break;
+        case 'l':
+            ++op->do_long;
+            break;
+        case 'm':
+            op->maxlen = sg_get_num(optarg);
+            if ((op->maxlen < 0) || (op->maxlen > MX_ALLOC_LEN)) {
+                pr2serr("argument to '--maxlen' should be %d or less\n",
+                        MX_ALLOC_LEN);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'M':
+            if (op->vend_prod) {
+                pr2serr("only one '--vendor=' option permitted\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                op->vend_prod = optarg;
+            break;
+        case 'p':
+            if (op->page_str) {
+                pr2serr("only one '--page=' option permitted\n");
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                op->page_str = optarg;
+            break;
+        case 'q':
+            ++op->do_quiet;
+            break;
+        case 'r':
+            ++op->do_raw;
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == op->device_name) {
+            op->device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (op->do_enum) {
+        if (op->device_name)
+            pr2serr("Device name %s ignored when --enumerate given\n",
+                    op->device_name);
+        if (op->vend_prod) {
+            if (isdigit(op->vend_prod[0])) {
+                op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
+                if ((op->vend_prod_num < 0) || (op->vend_prod_num > 10)) {
+                    pr2serr("Bad vendor/product number after '--vendor=' "
+                            "option\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else {
+                op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
+                if (op->vend_prod_num < 0) {
+                    pr2serr("Bad vendor/product acronym after '--vendor=' "
+                            "option\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+            svpd_enumerate_vendor(op->vend_prod_num);
+            return 0;
+        }
+        if (op->page_str) {
+            if ((0 == strcmp("-1", op->page_str)) ||
+                (0 == strcmp("-2", op->page_str)))
+                op->num_vpd = VPD_NO_RATHER_STD_INQ;
+            else if (isdigit(op->page_str[0])) {
+                op->num_vpd = sg_get_num_nomult(op->page_str);
+                if ((op->num_vpd < 0) || (op->num_vpd > 255)) {
+                    pr2serr("Bad page code value after '-p' option\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else {
+                pr2serr("with --enumerate only search using VPD page "
+                        "numbers\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            matches = count_standard_vpds(op->num_vpd);
+            if (0 == matches)
+                matches = svpd_count_vendor_vpds(op->num_vpd,
+                                                 op->vend_prod_num);
+            if (0 == matches)
+                printf("No matches found for VPD page number 0x%x\n",
+                       op->num_vpd);
+        } else {        /* enumerate standard then vendor VPD pages */
+            printf("Standard VPD pages:\n");
+            enumerate_vpds(1, 1);
+        }
+        return 0;
+    }
+    if (op->page_str) {
+        if ((0 == strcmp("-1", op->page_str)) ||
+            (0 == strcmp("-2", op->page_str)))
+            op->num_vpd = VPD_NO_RATHER_STD_INQ;
+        else if (isalpha(op->page_str[0])) {
+            vnp = sdp_find_vpd_by_acron(op->page_str);
+            if (NULL == vnp) {
+                vnp = svpd_find_vendor_by_acron(op->page_str);
+                if (NULL == vnp) {
+                    pr2serr("abbreviation doesn't match a VPD page\n");
+                    printf("Available standard VPD pages:\n");
+                    enumerate_vpds(1, 1);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+            op->num_vpd = vnp->value;
+            subvalue = vnp->subvalue;
+            op->vend_prod_num = subvalue;
+            page_pdt = vnp->pdt;
+        } else {
+            cp = strchr(op->page_str, ',');
+            if (cp && op->vend_prod) {
+                pr2serr("the --page=pg,vp and the --vendor=vp forms overlap, "
+                        "choose one or the other\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->num_vpd = sg_get_num_nomult(op->page_str);
+            if ((op->num_vpd < 0) || (op->num_vpd > 255)) {
+                pr2serr("Bad page code value after '-p' option\n");
+                printf("Available standard VPD pages:\n");
+                enumerate_vpds(1, 1);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if (cp) {
+                if (isdigit(*(cp + 1)))
+                    op->vend_prod_num = sg_get_num_nomult(cp + 1);
+                else
+                    op->vend_prod_num = svpd_find_vp_num_by_acron(cp + 1);
+                if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
+                    pr2serr("Bad vendor/product acronym after comma in '-p' "
+                            "option\n");
+                    if (op->vend_prod_num < 0)
+                        svpd_enumerate_vendor(-1);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                subvalue = op->vend_prod_num;
+            } else if (op->vend_prod) {
+                if (isdigit(op->vend_prod[0]))
+                    op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
+                else
+                    op->vend_prod_num =
+                        svpd_find_vp_num_by_acron(op->vend_prod);
+                if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
+                    pr2serr("Bad vendor/product acronym after '--vendor=' "
+                            "option\n");
+                    svpd_enumerate_vendor(-1);
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                subvalue = op->vend_prod_num;
+            }
+        }
+    } else if (op->vend_prod) {
+        if (isdigit(op->vend_prod[0]))
+            op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
+        else
+            op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
+        if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
+            pr2serr("Bad vendor/product acronym after '--vendor=' "
+                    "option\n");
+            svpd_enumerate_vendor(-1);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        subvalue = op->vend_prod_num;
+    }
+    if (op->inhex_fn) {
+        if (op->device_name) {
+            pr2serr("Cannot have both a DEVICE and --inhex= option\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (f2hex_arr(op->inhex_fn, op->do_raw, 0, rsp_buff, &inhex_len,
+                      sizeof(rsp_buff)))
+            return SG_LIB_FILE_ERROR;
+        if (op->verbose > 2)
+            pr2serr("Read %d bytes of user supplied data\n", inhex_len);
+        if (op->verbose > 3)
+            dStrHexErr((const char *)rsp_buff, inhex_len, 0);
+        op->do_raw = 0;         /* don't want raw on output with --inhex= */
+        if ((NULL == op->page_str) && (0 == op->do_all)) {
+            /* may be able to deduce VPD page */
+            if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) {
+                if (op->verbose)
+                    pr2serr("Guessing from --inhex= this is a standard "
+                            "INQUIRY\n");
+                if (page_pdt < 0)
+                    page_pdt = 0x1f & rsp_buff[0];
+            } else if (rsp_buff[2] <= 2) {
+                if (op->verbose)
+                    pr2serr("Guessing from --inhex this is VPD page 0x%x\n",
+                            rsp_buff[1]);
+                op->num_vpd = rsp_buff[1];
+                if (page_pdt < 0)
+                    page_pdt = 0x1f & rsp_buff[0];
+            } else {
+                if (op->num_vpd > 0x80) {
+                    op->num_vpd = rsp_buff[1];
+                    if (page_pdt < 0)
+                        page_pdt = 0x1f & rsp_buff[0];
+                    if (op->verbose)
+                        pr2serr("Guessing from --inhex this is VPD page "
+                                "0x%x\n", rsp_buff[1]);
+                } else {
+                    op->num_vpd = VPD_NO_RATHER_STD_INQ;
+                    if (op->verbose)
+                        pr2serr("page number unclear from --inhex, hope "
+                                "it's a standard INQUIRY response\n");
+                }
+            }
+        }
+    } else if (NULL == op->device_name) {
+        pr2serr("No DEVICE argument given\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (op->do_raw && op->do_hex) {
+        pr2serr("Can't do hex and raw at the same time\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (op->do_ident) {
+        op->num_vpd = VPD_DEVICE_ID;
+        if (op->do_ident > 1) {
+            if (0 == op->do_long)
+                ++op->do_quiet;
+            subvalue = VPD_DI_SEL_LU;
+        }
+    }
+    if (op->do_raw) {
+        if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+            perror("sg_set_binary_mode");
+            return SG_LIB_FILE_ERROR;
+        }
+    }
+
+    if (op->inhex_fn) {
+        if ((0 == op->maxlen) || (inhex_len < op->maxlen))
+            op->maxlen = inhex_len;
+        if (op->do_all)
+            res = svpd_decode_all(-1, op);
+        else {
+            res = svpd_decode_t10(-1, op, subvalue, 0);
+            if (SG_LIB_SYNTAX_ERROR == res) {
+                res = svpd_decode_vendor(-1, op, 0);
+                if (SG_LIB_SYNTAX_ERROR == res)
+                    res = svpd_unable_to_decode(-1, op, subvalue, 0);
+            }
+        }
+        return res;
+    }
+
+    if ((sg_fd = sg_cmds_open_device(op->device_name, 1 /* ro */,
+                                     op->verbose)) < 0) {
+        pr2serr("error opening file: %s: %s\n", op->device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (op->do_all)
+        ret = svpd_decode_all(sg_fd, op);
+    else {
+        memset(rsp_buff, 0, sizeof(rsp_buff));
+
+        res = svpd_decode_t10(sg_fd, op, subvalue, 0);
+        if (SG_LIB_SYNTAX_ERROR == res) {
+            res = svpd_decode_vendor(sg_fd, op, 0);
+                if (SG_LIB_SYNTAX_ERROR == res)
+            res = svpd_unable_to_decode(sg_fd, op, subvalue, 0);
+        }
+        if (SG_LIB_CAT_ABORTED_COMMAND == res)
+            pr2serr("fetching VPD page failed, aborted command\n");
+        else if (res) {
+            char b[80];
+
+            sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+            pr2serr("fetching VPD page failed: %s\n", b);
+        }
+        ret = res;
+    }
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_vpd_vendor.c b/sg3_utils/src/sg_vpd_vendor.c
new file mode 100644
index 0000000..8485967
--- /dev/null
+++ b/sg3_utils/src/sg_vpd_vendor.c
@@ -0,0 +1,1411 @@
+/*
+ * Copyright (c) 2006-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifndef SG_LIB_MINGW
+#include <time.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* This is a companion file to sg_vpd.c . It contains logic to output and
+   decode vendor specific VPD pages
+
+   This program fetches Vital Product Data (VPD) pages from the given
+   device and outputs it as directed. VPD pages are obtained via a
+   SCSI INQUIRY command. Most of the data in this program is obtained
+   from the SCSI SPC-4 document at http://www.t10.org .
+
+   Acknowledgments:
+      - Lars Marowsky-Bree <lmb at suse dot de> contributed Unit Path Report
+        VPD page decoding for EMC CLARiiON devices [20041016]
+      - Hannes Reinecke <hare at suse dot de> contributed RDAC vendor
+        specific VPD pages [20060421]
+      - Jonathan McDowell <noodles at hp dot com> contributed HP/3PAR InServ
+        VPD page [0xc0] containing volume information [20110922]
+
+*/
+
+/* vendor/product identifiers */
+#define VPD_VP_SEAGATE 0
+#define VPD_VP_RDAC 1
+#define VPD_VP_EMC 2
+#define VPD_VP_DDS 3
+#define VPD_VP_HP3PAR 4
+#define VPD_VP_IBM_LTO 5
+#define VPD_VP_HP_LTO 6
+
+
+/* vendor VPD pages */
+#define VPD_V_HP3PAR 0xc0
+#define VPD_V_FIRM_SEA  0xc0
+#define VPD_V_UPR_EMC  0xc0
+#define VPD_V_HVER_RDAC  0xc0
+#define VPD_V_FVER_DDS 0xc0
+#define VPD_V_FVER_LTO 0xc0
+#define VPD_V_DCRL_LTO 0xc0
+#define VPD_V_DATC_SEA  0xc1
+#define VPD_V_FVER_RDAC  0xc1
+#define VPD_V_HVER_LTO 0xc1
+#define VPD_V_DSN_LTO 0xc1
+#define VPD_V_JUMP_SEA 0xc2
+#define VPD_V_SVER_RDAC 0xc2
+#define VPD_V_PCA_LTO 0xc2
+#define VPD_V_DEV_BEH_SEA 0xc3
+#define VPD_V_FEAT_RDAC 0xc3
+#define VPD_V_MECH_LTO 0xc3
+#define VPD_V_SUBS_RDAC 0xc4
+#define VPD_V_HEAD_LTO 0xc4
+#define VPD_V_ACI_LTO 0xc5
+#define VPD_V_DUCD_LTO 0xc7
+#define VPD_V_EDID_RDAC 0xc8
+#define VPD_V_MPDS_LTO 0xc8
+#define VPD_V_VAC_RDAC 0xc9
+#define VPD_V_RVSI_RDAC 0xca
+#define VPD_V_SAID_RDAC 0xd0
+
+
+#define DEF_ALLOC_LEN 252
+#define MX_ALLOC_LEN (0xc000 + 0x80)
+
+/* These structures are duplicates of those of the same name in
+ * sg_vpd.c . Take care that both are the same. */
+struct opts_t {
+    int do_all;
+    int do_enum;
+    int do_hex;
+    int num_vpd;
+    int do_ident;
+    int do_long;
+    int maxlen;
+    int do_quiet;
+    int do_raw;
+    int vend_prod_num;
+    int verbose;
+    const char * device_name;
+    const char * page_str;
+    const char * inhex_fn;
+    const char * vend_prod;
+};
+
+struct svpd_values_name_t {
+    int value;       /* VPD page number */
+    int subvalue;    /* to differentiate if value+pdt are not unique */
+    int pdt;         /* peripheral device type id, -1 is the default */
+                     /* (all or not applicable) value */
+    const char * acron;
+    const char * name;
+};
+
+int vpd_fetch_page_from_dev(int sg_fd, unsigned char * rp, int page,
+                            int mxlen, int vb, int * rlenp);
+
+/* sharing large global buffer, defined in sg_vpd.c */
+extern unsigned char rsp_buff[];
+
+/* end of section copied from sg_vpd.c . Maybe sg_vpd.h is needed */
+
+struct svpd_vp_name_t {
+    int vend_prod_num;       /* vendor/product identifier */
+    const char * acron;
+    const char * name;
+};
+
+
+/* Supported vendor specific VPD pages */
+/* Arrange in alphabetical order by acronym */
+static struct svpd_vp_name_t vp_arr[] = {
+    {VPD_VP_DDS, "dds", "DDS tape family from IBM"},
+    {VPD_VP_EMC, "emc", "EMC (company)"},
+    {VPD_VP_HP3PAR, "hp3par", "3PAR array (HP was Left Hand)"},
+    {VPD_VP_IBM_LTO, "ibm_lto", "IBM LTO tape/systems"},
+    {VPD_VP_HP_LTO, "hp_lto", "HP LTO tape/systems"},
+    {VPD_VP_RDAC, "rdac", "RDAC array (NetApp E-Series)"},
+    {VPD_VP_SEAGATE, "sea", "Seagate disk"},
+    {0, NULL, NULL},
+};
+
+/* Supported vendor specific VPD pages */
+/* 'subvalue' holds vendor/product number to disambiguate */
+/* Arrange in alphabetical order by acronym */
+static struct svpd_values_name_t vendor_vpd_pg[] = {
+    {VPD_V_ACI_LTO, VPD_VP_HP_LTO, 1, "aci", "ACI revision level (HP LTO)"},
+    {VPD_V_DATC_SEA, VPD_VP_SEAGATE, 0, "datc", "Date code (Seagate)"},
+    {VPD_V_DCRL_LTO, VPD_VP_IBM_LTO, 1, "dcrl" , "Drive component revision "
+     "levels (IBM LTO)"},
+    {VPD_V_FVER_DDS, VPD_VP_DDS, 1, "ddsver", "Firmware revision (DDS)"},
+    {VPD_V_DEV_BEH_SEA, VPD_VP_SEAGATE, 0, "devb", "Device behavior "
+     "(Seagate)"},
+    {VPD_V_DSN_LTO, VPD_VP_IBM_LTO, 1, "dsn" , "Drive serial numbers (IBM "
+     "LTO)"},
+    {VPD_V_DUCD_LTO, VPD_VP_IBM_LTO, 1, "ducd" , "Device unique "
+     "configuration data (IBM LTO)"},
+    {VPD_V_EDID_RDAC, VPD_VP_RDAC, 0, "edid", "Extended device "
+     "identification (RDAC)"},
+    {VPD_V_FEAT_RDAC, VPD_VP_RDAC, 0, "prm4", "Feature Parameters (RDAC)"},
+    {VPD_V_FIRM_SEA, VPD_VP_SEAGATE, 0, "firm", "Firmware numbers "
+     "(Seagate)"},
+    {VPD_V_FVER_LTO, VPD_VP_HP_LTO, 0, "frl" , "Firmware revision level "
+     "(HP LTO)"},
+    {VPD_V_FVER_RDAC, VPD_VP_RDAC, 0, "fwr4", "Firmware version (RDAC)"},
+    {VPD_V_HEAD_LTO, VPD_VP_HP_LTO, 1, "head", "Head Assy revision level "
+     "(HP LTO)"},
+    {VPD_V_HP3PAR, VPD_VP_HP3PAR, 0, "hp3par", "Volume information "
+     "(HP/3PAR)"},
+    {VPD_V_HVER_LTO, VPD_VP_HP_LTO, 1, "hrl", "Hardware revision level "
+     "(HP LTO)"},
+    {VPD_V_HVER_RDAC, VPD_VP_RDAC, 0, "hwr4", "Hardware version (RDAC)"},
+    {VPD_V_JUMP_SEA, VPD_VP_SEAGATE, 0, "jump", "Jump setting (Seagate)"},
+    {VPD_V_MECH_LTO, VPD_VP_HP_LTO, 1, "mech", "Mechanism revision level "
+     "(HP LTO)"},
+    {VPD_V_MPDS_LTO, VPD_VP_IBM_LTO, 1, "mpds" , "Mode parameter default "
+     "settings (IBM LTO)"},
+    {VPD_V_PCA_LTO, VPD_VP_HP_LTO, 1, "pca", "PCA revision level (HP LTO)"},
+    {VPD_V_RVSI_RDAC, VPD_VP_RDAC, 0, "rvsi", "Replicated volume source "
+     "identifier (RDAC)"},
+    {VPD_V_SAID_RDAC, VPD_VP_RDAC, 0, "said", "Storage array world wide "
+     "name (RDAC)"},
+    {VPD_V_SUBS_RDAC, VPD_VP_RDAC, 0, "subs", "Subsystem identifier (RDAC)"},
+    {VPD_V_SVER_RDAC, VPD_VP_RDAC, 0, "swr4", "Software version (RDAC)"},
+    {VPD_V_UPR_EMC, VPD_VP_EMC, 0, "upr", "Unit path report (EMC)"},
+    {VPD_V_VAC_RDAC, VPD_VP_RDAC, 0, "vac1", "Volume access control (RDAC)"},
+    {0, 0, 0, NULL, NULL},
+};
+
+
+static int
+is_like_pdt(int actual_pdt, const struct svpd_values_name_t * vnp)
+{
+    if (actual_pdt == vnp->pdt)
+        return 1;
+    if (PDT_DISK == vnp->pdt) {
+        switch (actual_pdt) {
+        case PDT_DISK:
+        case PDT_RBC:
+        case PDT_PROCESSOR:
+        case PDT_SAC:
+        case PDT_ZBC:
+            return 1;
+        default:
+            return 0;
+        }
+    } else if (PDT_TAPE == vnp->pdt) {
+        switch (actual_pdt) {
+        case PDT_TAPE:
+        case PDT_MCHANGER:
+        case PDT_ADC:
+            return 1;
+        default:
+            return 0;
+        }
+    } else
+        return 0;
+}
+
+static const struct svpd_values_name_t *
+svpd_get_v_detail(int page_num, int vend_prod_num, int pdt)
+{
+    const struct svpd_values_name_t * vnp;
+    int vp, ty;
+
+    vp = (vend_prod_num < 0) ? 1 : 0;
+    ty = (pdt < 0) ? 1 : 0;
+    for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
+        if ((page_num == vnp->value) &&
+            (vp || (vend_prod_num == vnp->subvalue)) &&
+            (ty || is_like_pdt(pdt, vnp)))
+            return vnp;
+    }
+#if 0
+    if (! ty)
+        return svpd_get_v_detail(page_num, vend_prod_num, -1);
+    if (! vp)
+        return svpd_get_v_detail(page_num, -1, pdt);
+#endif
+    return NULL;
+}
+
+const struct svpd_values_name_t *
+svpd_find_vendor_by_num(int page_num, int vend_prod_num)
+{
+    const struct svpd_values_name_t * vnp;
+
+    for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
+        if ((page_num == vnp->value) &&
+            ((vend_prod_num < 0) || (vend_prod_num == vnp->subvalue)))
+            return vnp;
+    }
+    return NULL;
+}
+
+
+int
+svpd_find_vp_num_by_acron(const char * vp_ap)
+{
+    size_t len;
+    const struct svpd_vp_name_t * vpp;
+
+    for (vpp = vp_arr; vpp->acron; ++vpp) {
+        len = strlen(vpp->acron);
+        if (0 == strncmp(vpp->acron, vp_ap, len))
+            return vpp->vend_prod_num;
+    }
+    return -1;
+}
+
+
+const struct svpd_values_name_t *
+svpd_find_vendor_by_acron(const char * ap)
+{
+    const struct svpd_values_name_t * vnp;
+
+    for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
+        if (0 == strcmp(vnp->acron, ap))
+            return vnp;
+    }
+    return NULL;
+}
+
+/* if vend_prod_num < -1 then list vendor_product ids + vendor pages, =-1
+ * list only vendor_product ids, else list pages for that vend_prod_num */
+void
+svpd_enumerate_vendor(int vend_prod_num)
+{
+    const struct svpd_vp_name_t * vpp;
+    const struct svpd_values_name_t * vnp;
+    int seen;
+
+    if (vend_prod_num < 0) {
+        for (seen = 0, vpp = vp_arr; vpp->acron; ++vpp) {
+            if (vpp->name) {
+                if (! seen) {
+                    printf("\nVendor/product identifiers:\n");
+                    seen = 1;
+                }
+                printf("  %-10s %d      %s\n", vpp->acron,
+                       vpp->vend_prod_num, vpp->name);
+            }
+        }
+    }
+    if (-1 == vend_prod_num)
+        return;
+    for (seen = 0, vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
+        if ((vend_prod_num >= 0) && (vend_prod_num != vnp->subvalue))
+            continue;
+        if (vnp->name) {
+            if (! seen) {
+                printf("\nVendor specific VPD pages:\n");
+                seen = 1;
+            }
+            printf("  %-10s 0x%02x,%d      %s\n", vnp->acron,
+                   vnp->value, vnp->subvalue, vnp->name);
+        }
+    }
+}
+
+int
+svpd_count_vendor_vpds(int num_vpd, int vend_prod_num)
+{
+    const struct svpd_values_name_t * vnp;
+    int matches;
+
+    for (vnp = vendor_vpd_pg, matches = 0; vnp->acron; ++vnp) {
+        if ((num_vpd == vnp->value) && vnp->name) {
+            if ((vend_prod_num < 0) || (vend_prod_num == vnp->subvalue)) {
+                if (0 == matches)
+                    printf("Matching vendor specific VPD pages:\n");
+                ++matches;
+                printf("  %-10s 0x%02x,%d      %s\n", vnp->acron,
+                       vnp->value, vnp->subvalue, vnp->name);
+            }
+        }
+    }
+    return matches;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+    int k;
+
+    for (k = 0 ; k < len; ++k)
+        printf("%c", str[k]);
+}
+
+static void
+decode_vpd_c0_hp3par(unsigned char * buff, int len)
+{
+    int rev;
+    long offset;
+
+    if (len < 24) {
+        pr2serr("HP/3PAR vendor specific VPD page length too short=%d\n",
+                len);
+        return;
+    }
+
+    rev = buff[4];
+    printf("  Page revision: %d\n", rev);
+
+    printf("  Volume type: %s\n", (buff[5] & 0x01) ? "tpvv" :
+            (buff[5] & 0x02) ? "snap" : "base");
+    printf("  Reclaim supported: %s\n", (buff[5] & 0x04) ? "yes" : "no");
+    printf("  ATS supported: %s\n", (buff[5] & 0x10) ? "yes" : "no");
+    printf("  XCopy supported: %s\n", (buff[5] & 0x20) ? "yes" : "no");
+
+    if (rev > 3) {
+        printf("  VV ID: %" PRIu64 "\n", sg_get_unaligned_be64(buff + 28));
+        offset = 44;
+        printf("  Volume name: %s\n", &buff[offset]);
+
+        printf("  Domain ID: %d\n", sg_get_unaligned_be32(buff + 36));
+
+        offset += sg_get_unaligned_be32(buff + offset - 4) + 4;
+        printf("  Domain Name: %s\n", &buff[offset]);
+
+        offset += sg_get_unaligned_be32(buff + offset - 4) + 4;
+        printf("  User CPG: %s\n", &buff[offset]);
+
+        offset += sg_get_unaligned_be32(buff + offset - 4) + 4;
+        printf("  Snap CPG: %s\n", &buff[offset]);
+
+        offset += sg_get_unaligned_be32(buff + offset - 4);
+
+        printf("  VV policies: %s,%s,%s,%s\n",
+                (buff[offset + 3] & 0x01) ? "stale_ss" : "no_stale_ss",
+                (buff[offset + 3] & 0x02) ? "one_host" : "no_one_host",
+                (buff[offset + 3] & 0x04) ? "tp_bzero" : "no_tp_bzero",
+                (buff[offset + 3] & 0x08) ? "zero_detect" : "no_zero_detect");
+
+    }
+
+    if (buff[5] & 0x04) {
+        printf("  Allocation unit: %d\n", sg_get_unaligned_be32(buff + 8));
+
+        printf("  Data pool size: %" PRIu64 "\n",
+               sg_get_unaligned_be64(buff + 12));
+
+        printf("  Space allocated: %" PRIu64 "\n",
+               sg_get_unaligned_be64(buff + 20));
+    }
+    return;
+}
+
+
+static void
+decode_firm_vpd_c0_sea(unsigned char * buff, int len)
+{
+    if (len < 28) {
+        pr2serr("Seagate firmware numbers VPD page length too short=%d\n",
+                len);
+        return;
+    }
+    if (28 == len) {
+        printf("  SCSI firmware release number: %.8s\n", buff + 4);
+        printf("  Servo ROM release number: %.8s\n", buff + 20);
+    } else {
+        printf("  SCSI firmware release number: %.8s\n", buff + 4);
+        printf("  Servo ROM release number: %.8s\n", buff + 12);
+        printf("  SAP block point numbers (major/minor): %.8s\n", buff + 20);
+        if (len < 36)
+            return;
+        printf("  Servo firmware release date: %.4s\n", buff + 28);
+        printf("  Servo ROM release date: %.4s\n", buff + 32);
+        if (len < 44)
+            return;
+        printf("  SAP firmware release number: %.8s\n", buff + 36);
+        if (len < 52)
+            return;
+        printf("  SAP firmware release date: %.4s\n", buff + 44);
+        printf("  SAP firmware release year: %.4s\n", buff + 48);
+        if (len < 60)
+            return;
+        printf("  SAP manufacturing key: %.4s\n", buff + 52);
+        printf("  Servo firmware product family and product family "
+               "member: %.4s\n", buff + 56);
+    }
+}
+
+static void
+decode_date_code_vpd_c1_sea(unsigned char * buff, int len)
+{
+    if (len < 20) {
+        pr2serr("Seagate Data code VPD page length too short=%d\n",
+                len);
+        return;
+    }
+    printf("  ETF log (mmddyyyy): %.8s\n", buff + 4);
+    printf("  Compile date code (mmddyyyy): %.8s\n", buff + 12);
+}
+
+static void
+decode_dev_beh_vpd_c3_sea(unsigned char * buff, int len)
+{
+    if (len < 25) {
+        pr2serr("Seagate Device behaviour VPD page length too short=%d\n",
+                len);
+        return;
+    }
+    printf("  Version number: %d\n", buff[4]);
+    printf("  Behaviour code: %d\n", buff[5]);
+    printf("  Behaviour code version number: %d\n", buff[6]);
+    printf("  ASCII family number: %.16s\n", buff + 7);
+    printf("  Number of interleaves: %d\n", buff[23]);
+    printf("  Default number of cache segments: %d\n", buff[24]);
+}
+
+static const char * lun_state_arr[] =
+{
+    "LUN not bound or LUN_Z report",
+    "LUN bound, but not owned by this SP",
+    "LUN bound and owned by this SP",
+};
+
+static const char * ip_mgmt_arr[] =
+{
+    "No IP access",
+    "Reserved (undefined)",
+    "via IPv4",
+    "via IPv6",
+};
+
+static const char * sp_arr[] =
+{
+    "SP A",
+    "SP B",
+};
+
+static const char * lun_op_arr[] =
+{
+    "Normal operations",
+    "I/O Operations being rejected, SP reboot or NDU in progress",
+};
+
+static const char * failover_mode_arr[] =
+{
+    "Legacy mode 0",
+    "Unknown mode (1)",
+    "Unknown mode (2)",
+    "Unknown mode (3)",
+    "Active/Passive (PNR) mode 1",
+    "Unknown mode (5)",
+    "Active/Active (ALUA) mode 4",
+    "Unknown mode (7)",
+    "Legacy mode 2",
+    "Unknown mode (9)",
+    "Unknown mode (10)",
+    "Unknown mode (11)",
+    "Unknown mode (12)",
+    "Unknown mode (13)",
+    "AIX Active/Passive (PAR) mode 3",
+    "Unknown mode (15)",
+};
+
+static void
+decode_upr_vpd_c0_emc(unsigned char * buff, int len)
+{
+    int k, ip_mgmt, vpp80, lun_z;
+
+    if (len < 3) {
+        pr2serr("EMC upr VPD page [0xc0]: length too short=%d\n", len);
+        return;
+    }
+    if (buff[9] != 0x00) {
+        pr2serr("Unsupported page revision %d, decoding not possible.\n",
+                buff[9]);
+        return;
+    }
+    printf("  LUN WWN: ");
+    for (k = 0; k < 16; ++k)
+        printf("%02x", buff[10 + k]);
+    printf("\n");
+    printf("  Array Serial Number: ");
+    dStrRaw((const char *)&buff[50], buff[49]);
+    printf("\n");
+
+    printf("  LUN State: ");
+    if (buff[4] > 0x02)
+           printf("Unknown (%x)\n", buff[4]);
+    else
+           printf("%s\n", lun_state_arr[buff[4]]);
+
+    printf("  This path connects to: ");
+    if (buff[8] > 0x01)
+           printf("Unknown SP (%x)", buff[8]);
+    else
+           printf("%s", sp_arr[buff[8]]);
+    printf(", Port Number: %u\n", buff[7]);
+
+    printf("  Default Owner: ");
+    if (buff[5] > 0x01)
+           printf("Unknown (%x)\n", buff[5]);
+    else
+           printf("%s\n", sp_arr[buff[5]]);
+
+    printf("  NO_ATF: %s, Access Logix: %s\n",
+                   buff[6] & 0x80 ? "set" : "not set",
+                   buff[6] & 0x40 ? "supported" : "not supported");
+
+    ip_mgmt = (buff[6] >> 4) & 0x3;
+
+    printf("  SP IP Management Mode: %s\n", ip_mgmt_arr[ip_mgmt]);
+    if (ip_mgmt == 2)
+        printf("  SP IPv4 address: %u.%u.%u.%u\n",
+               buff[44], buff[45], buff[46], buff[47]);
+    else {
+        printf("  SP IPv6 address: ");
+        for (k = 0; k < 16; ++k)
+            printf("%02x", buff[32 + k]);
+        printf("\n");
+    }
+
+    vpp80 = buff[30] & 0x08;
+    lun_z = buff[30] & 0x04;
+
+    printf("  System Type: %x, Failover mode: %s\n",
+           buff[27], failover_mode_arr[buff[28] & 0x0f]);
+
+    printf("  Inquiry VPP 0x80 returns: %s, Arraycommpath: %s\n",
+                   vpp80 ? "array serial#" : "LUN serial#",
+                   lun_z ? "Set to 1" : "Unknown");
+
+    printf("  Lun operations: %s\n",
+               buff[48] > 1 ? "undefined" : lun_op_arr[buff[48]]);
+
+    return;
+}
+
+static void
+decode_rdac_vpd_c0(unsigned char * buff, int len)
+{
+    int memsize;
+    char name[65];
+
+    if (len < 3) {
+        pr2serr("Hardware Version VPD page length too short=%d\n", len);
+        return;
+    }
+    if (buff[4] != 'h' && buff[5] != 'w' && buff[6] != 'r') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
+                buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    printf("  Number of channels: %x\n", buff[8]);
+    memsize = sg_get_unaligned_be16(buff + 10);
+    printf("  Processor Memory Size: %d\n", memsize);
+    memset(name, 0, 65);
+    memcpy(name, buff + 16, 64);
+    printf("  Board Name: %s\n", name);
+    memset(name, 0, 65);
+    memcpy(name, buff + 80, 16);
+    printf("  Board Part Number: %s\n", name);
+    memset(name, 0, 65);
+    memcpy(name, buff + 96, 12);
+    printf("  Schematic Number: %s\n", name);
+    memset(name, 0, 65);
+    memcpy(name, buff + 108, 4);
+    printf("  Schematic Revision Number: %s\n", name);
+    memset(name, 0, 65);
+    memcpy(name, buff + 112, 16);
+    printf("  Board Serial Number: %s\n", name);
+    memset(name, 0, 65);
+    memcpy(name, buff + 144, 8);
+    printf("  Date of Manufacture: %s\n", name);
+    memset(name, 0, 65);
+    memcpy(name, buff + 152, 2);
+    printf("  Board Revision: %s\n", name);
+    memset(name, 0, 65);
+    memcpy(name, buff + 154, 4);
+    printf("  Board Identifier: %s\n", name);
+
+    return;
+}
+
+static void
+decode_rdac_vpd_c1(unsigned char * buff, int len)
+{
+    int i, n, v, r, m, p, d, y, num_part;
+    char part[5];
+
+    if (len < 3) {
+        pr2serr("Firmware Version VPD page length too short=%d\n", len);
+        return;
+    }
+    if (buff[4] != 'f' && buff[5] != 'w' && buff[6] != 'r') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
+                buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    printf("  Firmware Version: %02x.%02x.%02x\n", buff[8], buff[9], buff[10]);
+    printf("  Firmware Date: %02d/%02d/%02d\n", buff[11], buff[12], buff[13]);
+
+    num_part = (len - 12) / 16;
+    n = 16;
+    printf("  Partitions: %d\n", num_part);
+    for (i = 0; i < num_part; i++) {
+        memset(part,0, 5);
+        memcpy(part, &buff[n], 4);
+        printf("    Name: %s\n", part);
+        n += 4;
+        v = buff[n++];
+        r = buff[n++];
+        m = buff[n++];
+        p = buff[n++];
+        printf("    Version: %d.%d.%d.%d\n", v, r, m, p);
+        m = buff[n++];
+        d = buff[n++];
+        y = buff[n++];
+        printf("    Date: %d/%d/%d\n", m, d, y);
+
+        n += 5;
+    }
+
+    return;
+}
+
+static void
+decode_rdac_vpd_c2(unsigned char * buff, int len)
+{
+    int i, n, v, r, m, p, d, y, num_part;
+    char part[5];
+
+    if (len < 3) {
+        pr2serr("Software Version VPD page length too short=%d\n", len);
+        return;
+    }
+    if (buff[4] != 's' && buff[5] != 'w' && buff[6] != 'r') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
+                buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    printf("  Software Version: %02x.%02x.%02x\n", buff[8], buff[9], buff[10]);
+    printf("  Software Date: %02d/%02d/%02d\n", buff[11], buff[12], buff[13]);
+    printf("  Features:");
+    if (buff[14] & 0x01)
+        printf(" Dual Active,");
+    if (buff[14] & 0x02)
+        printf(" Series 3,");
+    if (buff[14] & 0x04)
+        printf(" Multiple Sub-enclosures,");
+    if (buff[14] & 0x08)
+        printf(" DCE/DRM/DSS/DVE,");
+    if (buff[14] & 0x10)
+        printf(" Asymmetric Logical Unit Access,");
+    printf("\n");
+    printf("  Max. #of LUNS: %d\n", buff[15]);
+
+    num_part = (len - 12) / 16;
+    n = 16;
+    printf("  Partitions: %d\n", num_part);
+    for (i = 0; i < num_part; i++) {
+        memset(part,0, 5);
+        memcpy(part, &buff[n], 4);
+        printf("    Name: %s\n", part);
+        n += 4;
+        v = buff[n++];
+        r = buff[n++];
+        m = buff[n++];
+        p = buff[n++];
+        printf("    Version: %d.%d.%d.%d\n", v, r, m, p);
+        m = buff[n++];
+        d = buff[n++];
+        y = buff[n++];
+        printf("    Date: %d/%d/%d\n", m, d, y);
+
+        n += 5;
+    }
+
+    return;
+}
+
+static void
+decode_rdac_vpd_c3(unsigned char * buff, int len)
+{
+    if (len < 0x2c) {
+        pr2serr("Feature parameters VPD page length too short=%d\n", len);
+        return;
+    }
+    if (buff[4] != 'p' && buff[5] != 'r' && buff[6] != 'm') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
+                buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    printf("  Maximum number of drives per LUN: %d\n", buff[8]);
+    printf("  Maximum number of hot spare drives: %d\n", buff[9]);
+    printf("  UTM: %s\n", buff[11] & 0x80?"enabled":"disabled");
+    if ((buff[11] & 0x80))
+        printf("    UTM LUN: %02x\n", buff[11] & 0x7f);
+    printf("  Persistent Reservations Bus Reset Support: %s\n",
+           (buff[12] & 0x01) ? "enabled" : "disabled");
+    return;
+}
+
+static void
+decode_rdac_vpd_c4(unsigned char * buff, int len)
+{
+    char subsystem_id[17];
+    char subsystem_rev[5];
+    char slot_id[3];
+
+    if (len < 0x1c) {
+        pr2serr("Subsystem identifier VPD page length too short=%d\n", len);
+        return;
+    }
+    if (buff[4] != 's' && buff[5] != 'u' && buff[6] != 'b') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
+                buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    memset(subsystem_id, 0, 17);
+    memcpy(subsystem_id, &buff[8], 16);
+    memset(subsystem_rev, 0, 5);
+    memcpy(subsystem_rev, &buff[24], 4);
+    slot_id[0] = buff[28];
+    slot_id[1] = buff[29];
+    slot_id[2] = 0;
+
+    printf("  Subsystem ID: %s\n  Subsystem Revision: %s",
+           subsystem_id, subsystem_rev);
+    if (!strcmp(subsystem_rev, "10.0"))
+        printf(" (Board ID 4884)\n");
+    else if (!strcmp(subsystem_rev, "12.0"))
+        printf(" (Board ID 5884)\n");
+    else if (!strcmp(subsystem_rev, "13.0"))
+        printf(" (Board ID 2882)\n");
+    else if (!strcmp(subsystem_rev, "13.1"))
+        printf(" (Board ID 2880)\n");
+    else if (!strcmp(subsystem_rev, "14.0"))
+        printf(" (Board ID 2822)\n");
+    else if (!strcmp(subsystem_rev, "15.0"))
+        printf(" (Board ID 6091)\n");
+    else if (!strcmp(subsystem_rev, "16.0"))
+        printf(" (Board ID 3992)\n");
+    else if (!strcmp(subsystem_rev, "16.1"))
+        printf(" (Board ID 3991)\n");
+    else if (!strcmp(subsystem_rev, "17.0"))
+        printf(" (Board ID 1331)\n");
+    else if (!strcmp(subsystem_rev, "17.1"))
+        printf(" (Board ID 1332)\n");
+    else if (!strcmp(subsystem_rev, "17.3"))
+        printf(" (Board ID 1532)\n");
+    else if (!strcmp(subsystem_rev, "17.4"))
+        printf(" (Board ID 1932)\n");
+    else if (!strcmp(subsystem_rev, "42.0"))
+        printf(" (Board ID 26x0)\n");
+    else if (!strcmp(subsystem_rev, "43.0"))
+        printf(" (Board ID 498x)\n");
+    else if (!strcmp(subsystem_rev, "44.0"))
+        printf(" (Board ID 548x)\n");
+    else if (!strcmp(subsystem_rev, "45.0"))
+        printf(" (Board ID 5501)\n");
+    else if (!strcmp(subsystem_rev, "46.0"))
+        printf(" (Board ID 2701)\n");
+    else if (!strcmp(subsystem_rev, "47.0"))
+        printf(" (Board ID 5601)\n");
+    else
+        printf(" (Board ID unknown)\n");
+
+    printf("  Slot ID: %s\n", slot_id);
+
+    return;
+}
+
+static void
+convert_binary_to_ascii(unsigned char * src, unsigned char * dst,  int len)
+{
+    int i;
+
+    for (i = 0; i < len; i++) {
+        sprintf((char *)(dst+2*i), "%02x", *(src+i));
+    }
+}
+
+static void
+decode_rdac_vpd_c8(unsigned char * buff, int len)
+{
+    int i;
+#ifndef SG_LIB_MINGW
+    time_t tstamp;
+#endif
+    char *c;
+    char label[61];
+    int label_len;
+    char uuid[33];
+    int uuid_len;
+    unsigned char port_id[128];
+    int n;
+
+    if (len < 0xab) {
+        pr2serr("Extended Device Identification VPD page length too "
+                "short=%d\n", len);
+        return;
+    }
+    if (buff[4] != 'e' && buff[5] != 'd' && buff[6] != 'i') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
+                buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+
+    uuid_len = buff[11];
+
+    for (i = 0, c = uuid; i < uuid_len; i++) {
+        sprintf(c,"%02x",buff[12 + i]);
+        c += 2;
+    }
+
+    printf("  Volume Unique Identifier: %s\n", uuid);
+#ifndef SG_LIB_MINGW
+    tstamp = sg_get_unaligned_be32(buff + 24);
+    printf("    Creation Number: %d, Timestamp: %s",
+           sg_get_unaligned_be16(buff + 22), ctime(&tstamp));
+#else
+    printf("    Creation Number: %d, Timestamp value: %u",
+           sg_get_unaligned_be16(buff + 22),
+           sg_get_unaligned_be32(buff + 24));
+#endif
+    memset(label, 0, 61);
+    label_len = buff[28];
+    for(i = 0; i < (label_len - 1); ++i)
+        *(label + i) = buff[29 + (2 * i) + 1];
+    printf("  Volume User Label: %s\n", label);
+
+    uuid_len = buff[89];
+
+    for (i = 0, c = uuid; i < uuid_len; i++) {
+        sprintf(c,"%02x",buff[90 + i]);
+        c += 2;
+    }
+
+    printf("  Storage Array Unique Identifier: %s\n", uuid);
+    memset(label, 0, 61);
+    label_len = buff[106];
+    for(i = 0; i < (label_len - 1); ++i)
+        *(label + i) = buff[107 + (2 * i) + 1];
+    printf("  Storage Array User Label: %s\n", label);
+
+    for (i = 0, c = uuid; i < 8; i++) {
+        sprintf(c,"%02x",buff[167 + i]);
+        c += 2;
+    }
+
+    printf("  Logical Unit Number: %s\n", uuid);
+
+    /* Initiator transport ID */
+    if ( buff[10] & 0x01 ) {
+        memset(port_id, 0, 128);
+        printf("  Transport Protocol: ");
+        switch (buff[175] & 0x0F) {
+        case TPROTO_FCP: /* FC */
+            printf("FC\n");
+            convert_binary_to_ascii(&buff[183], port_id, 8);
+            n = 199;
+            break;
+        case TPROTO_SRP: /* SRP */
+            printf("SRP\n");
+            convert_binary_to_ascii(&buff[183], port_id, 8);
+            n = 199;
+            break;
+        case TPROTO_ISCSI: /* iSCSI */
+            printf("iSCSI\n");
+            n = sg_get_unaligned_be32(buff + 177);
+            memcpy(port_id, &buff[179], n);
+            n = 179 + n;
+            break;
+        case TPROTO_SAS: /* SAS */
+            printf("SAS\n");
+            convert_binary_to_ascii(&buff[179], port_id, 8);
+            n = 199;
+            break;
+        default:
+            return; /* Can't continue decoding, so return */
+        }
+
+        printf("  Initiator Port Identifier: %s\n", port_id);
+        if ( buff[10] & 0x02 ) {
+            memset(port_id, 0, 128);
+            memcpy(port_id, &buff[n], 8);
+            printf("  Supplemental Vendor ID: %s\n", port_id);
+        }
+    }
+
+    return;
+}
+
+static void
+decode_rdac_vpd_c9_rtpg_data(unsigned char aas, unsigned char vendor)
+{
+    printf("  Asymmetric Access State:");
+    switch(aas & 0x0F) {
+    case 0x0:
+        printf(" Active/Optimized");
+        break;
+    case 0x1:
+        printf(" Active/Non-Optimized");
+        break;
+    case 0x2:
+        printf(" Standby");
+        break;
+    case 0x3:
+        printf(" Unavailable");
+        break;
+    case 0xE:
+        printf(" Offline");
+        break;
+    case 0xF:
+        printf(" Transitioning");
+        break;
+    default:
+        printf(" (unknown)");
+        break;
+    }
+    printf("\n");
+
+    printf("  Vendor Specific Field:");
+    switch(vendor) {
+    case 0x01:
+        printf(" Operating normally");
+        break;
+    case 0x02:
+        printf(" Non-responsive to queries");
+        break;
+    case 0x03:
+        printf(" Controller being held in reset");
+        break;
+    case 0x04:
+        printf(" Performing controller firmware download (1st controller)");
+        break;
+    case 0x05:
+        printf(" Performing controller firmware download (2nd controller)");
+        break;
+    case 0x06:
+        printf(" Quiesced as a result of an administrative request");
+        break;
+    case 0x07:
+        printf(" Service mode as a result of an administrative request");
+        break;
+    case 0xFF:
+        printf(" Details are not available");
+        break;
+    default:
+        printf(" (unknown)");
+        break;
+    }
+    printf("\n");
+}
+
+static void
+decode_rdac_vpd_c9(unsigned char * buff, int len)
+{
+    if (len < 3) {
+        pr2serr("Volume Access Control VPD page length too short=%d\n", len);
+        return;
+    }
+    if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
+                buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    if (buff[7] != '1') {
+        pr2serr("Invalid page version '%c' (should be 1)\n", buff[7]);
+    }
+    if ( (buff[8] & 0xE0) == 0xE0 ) {
+        printf("  IOShipping (ALUA): Enabled\n");
+    } else {
+        printf("  AVT:");
+        if (buff[8] & 0x80) {
+            printf(" Enabled");
+            if (buff[8] & 0x40)
+                printf(" (Allow reads on sector 0)");
+            printf("\n");
+        } else {
+            printf(" Disabled\n");
+        }
+    }
+    printf("  Volume Access via: ");
+    if (buff[8] & 0x01)
+        printf("primary controller\n");
+    else
+        printf("alternate controller\n");
+
+    if (buff[8] & 0x08) {
+        printf("  Path priority: %d ", buff[15] & 0xf);
+        switch(buff[15] & 0xf) {
+        case 0x1:
+            printf("(preferred path)\n");
+            break;
+        case 0x2:
+            printf("(secondary path)\n");
+            break;
+        default:
+            printf("(unknown)\n");
+            break;
+        }
+
+        printf("  Preferred Path Auto Changeable:");
+        switch(buff[14] & 0x3C) {
+        case 0x14:
+            printf(" No (User Disabled and Host Type Restricted)\n");
+            break;
+        case 0x18:
+            printf(" No (User Disabled)\n");
+            break;
+        case 0x24:
+            printf(" No (Host Type Restricted)\n");
+            break;
+        case 0x28:
+            printf(" Yes\n");
+            break;
+        default:
+            printf(" (Unknown)\n");
+            break;
+        }
+
+        printf("  Implicit Failback:");
+        switch(buff[14] & 0x03) {
+        case 0x1:
+            printf(" Disabled\n");
+            break;
+        case 0x2:
+            printf(" Enabled\n");
+            break;
+        default:
+            printf(" (Unknown)\n");
+            break;
+        }
+    } else {
+        printf("  Path priority: %d ", buff[9] & 0xf);
+        switch(buff[9] & 0xf) {
+        case 0x1:
+            printf("(preferred path)\n");
+            break;
+        case 0x2:
+            printf("(secondary path)\n");
+            break;
+        default:
+            printf("(unknown)\n");
+            break;
+        }
+    }
+
+
+    if (buff[8] & 0x80) {
+        printf(" Target Port Group Data (This controller):\n");
+        decode_rdac_vpd_c9_rtpg_data(buff[10], buff[11]);
+
+        printf(" Target Port Group Data (Alternate controller):\n");
+        decode_rdac_vpd_c9_rtpg_data(buff[12], buff[13]);
+    }
+}
+
+static void
+decode_rdac_vpd_ca(unsigned char * buff, int len)
+{
+    int i;
+
+    if (len < 16) {
+        pr2serr("Replicated Volume Source Identifier VPD page length too "
+                "short=%d\n", len);
+        return;
+    }
+    if (buff[4] != 'r' && buff[5] != 'v' && buff[6] != 's') {
+        pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
+                buff[4], buff[5], buff[6], buff[7]);
+        return;
+    }
+    if (buff[8] & 0x01) {
+        printf("  Snapshot Volume\n");
+        printf("  Base Volume WWID: ");
+        for (i = 0; i < 16; i++)
+            printf("%02x", buff[10 + i]);
+        printf("\n");
+    } else if (buff[8] & 0x02) {
+        printf("  Copy Target Volume\n");
+        printf("  Source Volume WWID: ");
+        for (i = 0; i < 16; i++)
+            printf("%02x", buff[10 + i]);
+        printf("\n");
+    } else
+        printf(" Neither a snapshot nor a copy target volume\n");
+
+    return;
+}
+
+static void
+decode_rdac_vpd_d0(unsigned char * buff, int len)
+{
+    int i;
+
+    if (len < 20) {
+        pr2serr("Storage Array World Wide Name VPD page length too "
+                "short=%d\n", len);
+        return;
+    }
+    printf("  Storage Array WWN: ");
+    for (i = 0; i < 16; i++)
+        printf("%02x", buff[8 + i]);
+    printf("\n");
+
+    return;
+}
+
+
+static void
+decode_dds_vpd_c0(unsigned char * buff, int len)
+{
+    char firmware_rev[25];
+    char build_date[43];
+    char hw_conf[21];
+    char fw_conf[21];
+
+    if (len < 0xb3) {
+        pr2serr("Vendor-Unique Firmware revision page invalid length=%d\n",
+                len);
+        return;
+    }
+    memset(firmware_rev, 0x0, 25);
+    memcpy(firmware_rev, &buff[5], 24);
+
+    printf("  %s\n", firmware_rev);
+
+    memset(build_date, 0x0, 43);
+    memcpy(build_date, &buff[30], 42);
+
+    printf("  %s\n", build_date);
+
+    memset(hw_conf, 0x0, 21);
+    memcpy(hw_conf, &buff[73], 20);
+    printf("  %s\n", hw_conf);
+
+    memset(fw_conf, 0x0, 21);
+    memcpy(fw_conf, &buff[94], 20);
+    printf("  %s\n", fw_conf);
+    return;
+}
+
+static void
+decode_hp_lto_vpd_cx(unsigned char * buff, int len, int page)
+{
+    char str[32];
+    const char *comp = NULL;
+
+    if (len < 0x5c) {
+        pr2serr("Driver Component Revision Levels page invalid length=%d\n",
+                len);
+        return;
+    }
+    switch (page) {
+        case 0xc0:
+            comp = "Firmware";
+            break;
+        case 0xc1:
+            comp = "Hardware";
+            break;
+        case 0xc2:
+            comp = "PCA";
+            break;
+        case 0xc3:
+            comp = "Mechanism";
+            break;
+        case 0xc4:
+            comp = "Head Assy";
+            break;
+        case 0xc5:
+            comp = "ACI";
+            break;
+    }
+    if (!comp) {
+        pr2serr("Driver Component Revision Level invalid page=0x%02x\n",
+                page);
+        return;
+    }
+
+    memset(str, 0x0, 32);
+    memcpy(str, &buff[4], 26);
+    printf("  %s\n", str);
+
+    memset(str, 0x0, 32);
+    memcpy(str, &buff[30], 19);
+    printf("  %s\n", str);
+
+    memset(str, 0x0, 32);
+    memcpy(str, &buff[49], 24);
+    printf("  %s\n", str);
+
+    memset(str, 0x0, 32);
+    memcpy(str, &buff[73], 23);
+    printf("  %s\n", str);
+    return;
+}
+
+static void
+decode_ibm_lto_dcrl(unsigned char * buff, int len)
+{
+    if (len < 0x2b) {
+        pr2serr("Driver Component Revision Levels page (IBM LTO) invalid "
+                "length=%d\n", len);
+        return;
+    }
+    printf("  Code name: %.12s\n", buff + 4);
+    printf("  Time (hhmmss): %.7s\n", buff + 16);
+    printf("  Date (yyyymmdd): %.8s\n", buff + 23);
+    printf("  Platform: %.12s\n", buff + 31);
+}
+
+static void
+decode_ibm_lto_dsn(unsigned char * buff, int len)
+{
+    if (len < 0x1c) {
+        pr2serr("Driver Serial Numbers page (IBM LTO) invalid "
+                "length=%d\n", len);
+        return;
+    }
+    printf("  Manufacturing serial number: %.12s\n", buff + 4);
+    printf("  Reported serial number: %.12s\n", buff + 16);
+}
+
+/* Returns 0 if successful, see sg_ll_inquiry() plus SG_LIB_SYNTAX_ERROR for
+   unsupported page */
+int
+svpd_decode_vendor(int sg_fd, struct opts_t * op, int off)
+{
+    int len, res;
+    char name[64];
+    const struct svpd_values_name_t * vnp;
+    int alloc_len = op->maxlen;
+    unsigned char * rp;
+
+    rp = rsp_buff + off;
+    if (sg_fd >= 0) {
+        if (0 == alloc_len)
+            alloc_len = DEF_ALLOC_LEN;
+    }
+    res = vpd_fetch_page_from_dev(sg_fd, rp, op->num_vpd, alloc_len,
+                                  op->verbose, &len);
+    if (0 == res) {
+        vnp = svpd_get_v_detail(op->num_vpd, op->vend_prod_num, 0xf & rp[0]);
+        if (vnp && vnp->name)
+            strcpy(name, vnp->name);
+        else
+            snprintf(name, sizeof(name) - 1, "Vendor VPD page=0x%x",
+                     op->num_vpd);
+        if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 2))
+            printf("%s VPD Page:\n", name);
+        if (op->do_raw)
+            dStrRaw((const char *)rp, len);
+        else if (op->do_hex)
+            dStrHex((const char *)rp, len,
+                    ((1 == op->do_hex) ? 0 : -1));
+        else {
+            switch(op->num_vpd) {
+                case 0xc0:
+                    if (VPD_VP_SEAGATE == op->vend_prod_num)
+                        decode_firm_vpd_c0_sea(rp, len);
+                    else if (VPD_VP_EMC == op->vend_prod_num)
+                        decode_upr_vpd_c0_emc(rp, len);
+                    else if (VPD_VP_HP3PAR == op->vend_prod_num)
+                        decode_vpd_c0_hp3par(rp, len);
+                    else if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_c0(rp, len);
+                    else if (VPD_VP_DDS == op->vend_prod_num)
+                        decode_dds_vpd_c0(rp, len);
+                    else if (VPD_VP_IBM_LTO == op->vend_prod_num)
+                        decode_ibm_lto_dcrl(rp, len);
+                    else if (VPD_VP_HP_LTO == op->vend_prod_num)
+                        decode_hp_lto_vpd_cx(rp, len, op->num_vpd);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xc1:
+                    if (VPD_VP_SEAGATE == op->vend_prod_num)
+                        decode_date_code_vpd_c1_sea(rp, len);
+                    else if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_c1(rp, len);
+                    else if (VPD_VP_IBM_LTO == op->vend_prod_num)
+                        decode_ibm_lto_dsn(rp, len);
+                    else if (VPD_VP_HP_LTO == op->vend_prod_num)
+                        decode_hp_lto_vpd_cx(rp, len, op->num_vpd);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xc2:
+                    if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_c2(rp, len);
+                    else if (VPD_VP_HP_LTO == op->vend_prod_num)
+                        decode_hp_lto_vpd_cx(rp, len, op->num_vpd);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xc3:
+                    if (VPD_VP_SEAGATE == op->vend_prod_num)
+                        decode_dev_beh_vpd_c3_sea(rp, len);
+                    else if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_c3(rp, len);
+                    else if (VPD_VP_HP_LTO == op->vend_prod_num)
+                        decode_hp_lto_vpd_cx(rp, len, op->num_vpd);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xc4:
+                    if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_c4(rp, len);
+                    else if (VPD_VP_HP_LTO == op->vend_prod_num)
+                        decode_hp_lto_vpd_cx(rp, len, op->num_vpd);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xc5:
+                    if (VPD_VP_HP_LTO == op->vend_prod_num)
+                        decode_hp_lto_vpd_cx(rp, len, op->num_vpd);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xc8:
+                    if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_c8(rp, len);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xc9:
+                    if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_c9(rp, len);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xca:
+                    if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_ca(rp, len);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                case 0xd0:
+                    if (VPD_VP_RDAC == op->vend_prod_num)
+                        decode_rdac_vpd_d0(rp, len);
+                    else
+                        dStrHex((const char *)rp, len, 0);
+                    break;
+                default:
+                    return SG_LIB_SYNTAX_ERROR;
+            }
+            return 0;
+        }
+    } else
+        pr2serr("Vendor VPD page=0x%x  failed to fetch", op->num_vpd);
+    return res;
+}
diff --git a/sg3_utils/src/sg_wr_mode.c b/sg3_utils/src/sg_wr_mode.c
new file mode 100644
index 0000000..0476648
--- /dev/null
+++ b/sg3_utils/src/sg_wr_mode.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright (c) 2004-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ * This program writes the given mode page contents to the corresponding
+ * mode page on the given device.
+ */
+
+static const char * version_str = "1.16 20160208";
+
+#define ME "sg_wr_mode: "
+
+#define MX_ALLOC_LEN 2048
+#define SHORT_ALLOC_LEN 252
+
+#define EBUFF_SZ 256
+
+
+static struct option long_options[] = {
+        {"contents", 1, 0, 'c'},
+        {"dbd", 0, 0, 'd'},
+        {"force", 0, 0, 'f'},
+        {"help", 0, 0, 'h'},
+        {"len", 1, 0, 'l'},
+        {"mask", 1, 0, 'm'},
+        {"page", 1, 0, 'p'},
+        {"save", 0, 0, 's'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void usage()
+{
+    pr2serr("Usage: sg_wr_mode [--contents=H,H...] [--dbd] [--force] "
+            "[--help]\n"
+            "                  [--len=10|6] [--mask=M,M...] "
+            "[--page=PG_H[,SPG_H]]\n"
+            "                  [--save] [--verbose] [--version] DEVICE\n"
+            "  where:\n"
+            "    --contents=H,H... | -c H,H...    comma separated string "
+            "of hex numbers\n"
+            "                                     that is mode page contents "
+            "to write\n"
+            "    --contents=- | -c -   read stdin for mode page contents"
+            " to write\n"
+            "    --dbd | -d            disable block descriptors (DBD bit"
+            " in cdb)\n"
+            "    --force | -f          force the contents to be written\n"
+            "    --help | -h           print out usage message\n"
+            "    --len=10|6 | -l 10|6    use 10 byte (def) or 6 byte "
+            "variants of\n"
+            "                            SCSI MODE SENSE/SELECT commands\n"
+            "    --mask=M,M... | -m M,M...   comma separated "
+            "string of hex\n"
+            "                                numbers that mask contents"
+            " to write\n"
+            "    --page=PG_H | -p PG_H     page_code to be written (in hex)\n"
+            "    --page=PG_H,SPG_H | -p PG_H,SPG_H    page and subpage code "
+            "to be\n"
+            "                                         written (in hex)\n"
+            "    --save | -s           set 'save page' (SP) bit; default "
+            "don't so\n"
+            "                          only 'current' values changed\n"
+            "    --verbose | -v        increase verbosity\n"
+            "    --version | -V        print version string and exit\n\n"
+            "writes given mode page with SCSI MODE SELECT (10 or 6) "
+            "command\n");
+}
+
+
+/* Read hex numbers from command line or stdin. On the command line can
+ * either be comma or space separated list. Space separated list need to be
+ * quoted. For stdin (indicated by *inp=='-') there should be either
+ * one entry per line, a comma separated list or space separated list.
+ * Returns 0 if ok, or 1 if error. */
+static int build_mode_page(const char * inp, unsigned char * mp_arr,
+                           int * mp_arr_len, int max_arr_len)
+{
+    int in_len, k, j, m;
+    unsigned int h;
+    const char * lcp;
+    char * cp;
+    char * c2p;
+
+    if ((NULL == inp) || (NULL == mp_arr) ||
+        (NULL == mp_arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *mp_arr_len = 0;
+    if ('-' == inp[0]) {        /* read from stdin */
+        char line[512];
+        char carry_over[4];
+        int off = 0;
+        int split_line;
+
+        carry_over[0] = 0;
+        for (j = 0; j < 512; ++j) {
+            if (NULL == fgets(line, sizeof(line), stdin))
+                break;
+            in_len = strlen(line);
+            if (in_len > 0) {
+                if ('\n' == line[in_len - 1]) {
+                    --in_len;
+                    line[in_len] = '\0';
+                    split_line = 0;
+                } else
+                    split_line = 1;
+            }
+            if (in_len < 1) {
+                carry_over[0] = 0;
+                continue;
+            }
+            if (carry_over[0]) {
+                if (isxdigit(line[0])) {
+                    carry_over[1] = line[0];
+                    carry_over[2] = '\0';
+                    if (1 == sscanf(carry_over, "%x", &h))
+                        mp_arr[off - 1] = h;       /* back up and overwrite */
+                    else {
+                        pr2serr("build_mode_page: carry_over error ['%s'] "
+                                "around line %d\n", carry_over, j + 1);
+                        return 1;
+                    }
+                    lcp = line + 1;
+                    --in_len;
+                } else
+                    lcp = line;
+                carry_over[0] = 0;
+            } else
+                lcp = line;
+            m = strspn(lcp, " \t");
+            if (m == in_len)
+                continue;
+            lcp += m;
+            in_len -= m;
+            if ('#' == *lcp)
+                continue;
+            k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
+            if ((k < in_len) && ('#' != lcp[k])) {
+                pr2serr("build_mode_page: syntax error at "
+                        "line %d, pos %d\n", j + 1, m + k + 1);
+                return 1;
+            }
+            for (k = 0; k < 1024; ++k) {
+                if (1 == sscanf(lcp, "%x", &h)) {
+                    if (h > 0xff) {
+                        pr2serr("%s: hex number larger than 0xff in line %d, "
+                                "pos %d\n", __func__, j + 1,
+                                (int)(lcp - line + 1));
+                        return 1;
+                    }
+                    if (split_line && (1 == strlen(lcp))) {
+                        /* single trailing hex digit might be a split pair */
+                        carry_over[0] = *lcp;
+                    }
+                    if ((off + k) >= max_arr_len) {
+                        pr2serr("%s: array length exceeded\n", __func__);
+                        return 1;
+                    }
+                    mp_arr[off + k] = h;
+                    lcp = strpbrk(lcp, " ,\t");
+                    if (NULL == lcp)
+                        break;
+                    lcp += strspn(lcp, " ,\t");
+                    if ('\0' == *lcp)
+                        break;
+                } else {
+                    if ('#' == *lcp) {
+                        --k;
+                        break;
+                    }
+                    pr2serr("%s: error in line %d, at pos %d\n", __func__,
+                            j + 1, (int)(lcp - line + 1));
+                    return 1;
+                }
+            }
+            off += (k + 1);
+        }
+        *mp_arr_len = off;
+    } else {        /* hex string on command line */
+        k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
+        if (in_len != k) {
+            pr2serr("%s: error at pos %d\n", __func__, k + 1);
+            return 1;
+        }
+        for (k = 0; k < max_arr_len; ++k) {
+            if (1 == sscanf(lcp, "%x", &h)) {
+                if (h > 0xff) {
+                    pr2serr("%s: hex number larger than 0xff at pos %d\n",
+                            __func__, (int)(lcp - inp + 1));
+                    return 1;
+                }
+                mp_arr[k] = h;
+                cp = (char *)strchr(lcp, ',');
+                c2p = (char *)strchr(lcp, ' ');
+                if (NULL == cp)
+                    cp = c2p;
+                if (NULL == cp)
+                    break;
+                if (c2p && (c2p < cp))
+                    cp = c2p;
+                lcp = cp + 1;
+            } else {
+                pr2serr("%s: error at pos %d\n", __func__,
+                        (int)(lcp - inp + 1));
+                return 1;
+            }
+        }
+        *mp_arr_len = k + 1;
+        if (k == max_arr_len) {
+            pr2serr("%s: array length exceeded\n", __func__);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/* Read hex numbers from command line (comma separated list).
+ * Can also be (single) space separated list but needs to be quoted on the
+ * command line. Returns 0 if ok, or 1 if error. */
+static int build_mask(const char * inp, unsigned char * mask_arr,
+                      int * mask_arr_len, int max_arr_len)
+{
+    int in_len, k;
+    unsigned int h;
+    const char * lcp;
+    char * cp;
+    char * c2p;
+
+    if ((NULL == inp) || (NULL == mask_arr) ||
+        (NULL == mask_arr_len))
+        return 1;
+    lcp = inp;
+    in_len = strlen(inp);
+    if (0 == in_len)
+        *mask_arr_len = 0;
+    if ('-' == inp[0]) {        /* read from stdin */
+        pr2serr("'--mask' does not accept input from stdin\n");
+        return 1;
+    } else {        /* hex string on command line */
+        k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
+        if (in_len != k) {
+            pr2serr("%s: error at pos %d\n", __func__, k + 1);
+            return 1;
+        }
+        for (k = 0; k < max_arr_len; ++k) {
+            if (1 == sscanf(lcp, "%x", &h)) {
+                if (h > 0xff) {
+                    pr2serr("%s: hex number larger than 0xff at pos %d\n",
+                            __func__, (int)(lcp - inp + 1));
+                    return 1;
+                }
+                mask_arr[k] = h;
+                cp = (char *)strchr(lcp, ',');
+                c2p = (char *)strchr(lcp, ' ');
+                if (NULL == cp)
+                    cp = c2p;
+                if (NULL == cp)
+                    break;
+                if (c2p && (c2p < cp))
+                    cp = c2p;
+                lcp = cp + 1;
+            } else {
+                pr2serr("%s: error at pos %d\n", __func__,
+                        (int)(lcp - inp + 1));
+                return 1;
+            }
+        }
+        *mask_arr_len = k + 1;
+        if (k == max_arr_len) {
+            pr2serr("%s: array length exceeded\n", __func__);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+int main(int argc, char * argv[])
+{
+    int sg_fd, res, c, num, alloc_len, off, pdt;
+    int k, md_len, hdr_len, bd_len, mask_in_len;
+    unsigned u, uu;
+    int dbd = 0;
+    int got_contents = 0;
+    int force = 0;
+    int got_mask = 0;
+    int mode_6 = 0;
+    int pg_code = -1;
+    int sub_pg_code = 0;
+    int save = 0;
+    int verbose = 0;
+    int read_in_len = 0;
+    const char * device_name = NULL;
+    unsigned char read_in[MX_ALLOC_LEN];
+    unsigned char mask_in[MX_ALLOC_LEN];
+    unsigned char ref_md[MX_ALLOC_LEN];
+    char ebuff[EBUFF_SZ];
+    char b[80];
+    struct sg_simple_inquiry_resp inq_data;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "c:dfhl:m:p:svV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            memset(read_in, 0, sizeof(read_in));
+            if (0 != build_mode_page(optarg, read_in, &read_in_len,
+                                     sizeof(read_in))) {
+                pr2serr("bad argument to '--contents'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            got_contents = 1;
+            break;
+        case 'd':
+            dbd = 1;
+            break;
+        case 'f':
+            force = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'l':
+            num = sscanf(optarg, "%d", &res);
+            if ((1 == num) && ((6 == res) || (10 == res)))
+                mode_6 = (6 == res) ? 1 : 0;
+            else {
+                pr2serr("length (of cdb) must be 6 or 10\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'm':
+            memset(mask_in, 0xff, sizeof(mask_in));
+            if (0 != build_mask(optarg, mask_in, &mask_in_len,
+                                sizeof(mask_in))) {
+                pr2serr("bad argument to '--mask'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            got_mask = 1;
+            break;
+        case 'p':
+           if (NULL == strchr(optarg, ',')) {
+                num = sscanf(optarg, "%x", &u);
+                if ((1 != num) || (u > 62)) {
+                    pr2serr("Bad hex page code value after '--page' "
+                            "switch\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                pg_code = u;
+            } else if (2 == sscanf(optarg, "%x,%x", &u, &uu)) {
+                if (uu > 254) {
+                    pr2serr("Bad hex sub page code value after '--page' "
+                            "switch\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+                pg_code = u;
+                sub_pg_code = uu;
+            } else {
+                pr2serr("Bad hex page code, subpage code sequence after "
+                        "'--page' switch\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 's':
+            save = 1;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (pg_code < 0) {
+        pr2serr("need page code (see '--page=')\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (got_mask && force) {
+        pr2serr("cannot use both '--force' and '--mask'\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (0 == sg_simple_inquiry(sg_fd, &inq_data, 0, verbose))
+        pdt = inq_data.peripheral_type;
+    else
+        pdt = 0x1f;
+
+    /* do MODE SENSE to fetch current values */
+    memset(ref_md, 0, MX_ALLOC_LEN);
+    alloc_len = mode_6 ? SHORT_ALLOC_LEN : MX_ALLOC_LEN;
+    if (mode_6)
+        res = sg_ll_mode_sense6(sg_fd, dbd, 0 /*current */, pg_code,
+                                sub_pg_code, ref_md, alloc_len, 1, verbose);
+     else
+        res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, 0 /* current */,
+                                 pg_code, sub_pg_code, ref_md, alloc_len, 1,
+                                 verbose);
+    ret = res;
+    if (res) {
+        if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr("MODE SENSE (%d) not supported, try '--len=%d'\n",
+                    (mode_6 ? 6 : 10), (mode_6 ? 10 : 6));
+        else {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("MODE SENSE (%d): %s\n", (mode_6 ? 6 : 10), b);
+        }
+        goto err_out;
+    }
+    off = sg_mode_page_offset(ref_md, alloc_len, mode_6, ebuff, EBUFF_SZ);
+    if (off < 0) {
+        pr2serr("MODE SENSE (%d): %s\n", (mode_6 ? 6 : 10), ebuff);
+        goto err_out;
+    }
+    if (mode_6) {
+        hdr_len = 4;
+        md_len = ref_md[0] + 1;
+        bd_len = ref_md[3];
+    } else {
+        hdr_len = 8;
+        md_len = sg_get_unaligned_be16(ref_md + 0) + 2;
+        bd_len = sg_get_unaligned_be16(ref_md + 6);
+    }
+    if (got_contents) {
+        if (read_in_len < 2) {
+            pr2serr("contents length=%d too short\n", read_in_len);
+            goto err_out;
+        }
+        ref_md[0] = 0;  /* mode data length reserved for mode select */
+        if (! mode_6)
+            ref_md[1] = 0;    /* mode data length reserved for mode select */
+        if (0 == pdt)   /* for disks mask out DPOFUA bit */
+            ref_md[mode_6 ? 2 : 3] &= 0xef;
+        if (md_len > alloc_len) {
+            pr2serr("mode data length=%d exceeds allocation length=%d\n",
+                    md_len, alloc_len);
+            goto err_out;
+        }
+        if (got_mask) {
+            for (k = 0; k < (md_len - off); ++k) {
+                if ((0x0 == mask_in[k]) || (k > read_in_len))
+                   read_in[k] = ref_md[off + k];
+                else if (mask_in[k] < 0xff) {
+                   c = (ref_md[off + k] & (0xff & ~mask_in[k]));
+                   read_in[k] = (c | (read_in[k] & mask_in[k]));
+                }
+            }
+            read_in_len = md_len - off;
+        }
+        if (! force) {
+            if ((! (ref_md[off] & 0x80)) && save) {
+                pr2serr("PS bit in existing mode page indicates that it is "
+                        "not saveable\n    but '--save' option given\n");
+                goto err_out;
+            }
+            read_in[0] &= 0x7f; /* mask out PS bit, reserved in mode select */
+            if ((md_len - off) != read_in_len) {
+                pr2serr("contents length=%d but reference mode page "
+                        "length=%d\n", read_in_len, md_len - off);
+                goto err_out;
+            }
+            if (pg_code != (read_in[0] & 0x3f)) {
+                pr2serr("contents page_code=0x%x but reference "
+                        "page_code=0x%x\n", (read_in[0] & 0x3f), pg_code);
+                goto err_out;
+            }
+            if ((read_in[0] & 0x40) != (ref_md[off] & 0x40)) {
+                pr2serr("contents flags subpage but reference page does not "
+                        "(or vice versa)\n");
+                goto err_out;
+            }
+            if ((read_in[0] & 0x40) && (read_in[1] != sub_pg_code)) {
+                pr2serr("contents subpage_code=0x%x but reference "
+                        "sub_page_code=0x%x\n", read_in[1], sub_pg_code);
+                goto err_out;
+            }
+        } else
+            md_len = off + read_in_len; /* force length */
+
+        memcpy(ref_md + off, read_in, read_in_len);
+        if (mode_6)
+            res = sg_ll_mode_select6(sg_fd, 1 /* PF */, save, ref_md, md_len,
+                                     1, verbose);
+        else
+            res = sg_ll_mode_select10(sg_fd, 1 /* PF */, save, ref_md,
+                                      md_len, 1, verbose);
+        ret = res;
+        if (res) {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("MODE SELECT (%d): %s\n", (mode_6 ? 6 : 10), b);
+            goto err_out;
+        }
+    } else {
+        printf(">>> No contents given, so show current mode page data:\n");
+        printf("  header:\n");
+        dStrHex((const char *)ref_md, hdr_len, -1);
+        if (bd_len) {
+            printf("  block descriptor(s):\n");
+            dStrHex((const char *)(ref_md + hdr_len), bd_len, -1);
+        } else
+            printf("  << no block descriptors >>\n");
+        printf("  mode page:\n");
+        dStrHex((const char *)(ref_md + off), md_len - off, -1);
+    }
+err_out:
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_write_buffer.c b/sg3_utils/src/sg_write_buffer.c
new file mode 100644
index 0000000..0e2f1bc
--- /dev/null
+++ b/sg3_utils/src/sg_write_buffer.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2006-2015 Luben Tuikov and 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pt.h"      /* needed for scsi_pt_win32_direct() */
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/*
+ * This utility issues the SCSI WRITE BUFFER command to the given device.
+ */
+
+static const char * version_str = "1.21 20151219";    /* spc5r02 */
+
+#define ME "sg_write_buffer: "
+#define DEF_XFER_LEN (8 * 1024 * 1024)
+#define EBUFF_SZ 256
+
+#define WRITE_BUFFER_CMD 0x3b
+#define WRITE_BUFFER_CMDLEN 10
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT 300      /* 300 seconds, 5 minutes */
+
+static struct option long_options[] = {
+        {"bpw", required_argument, 0, 'b'},
+        {"help", no_argument, 0, 'h'},
+        {"id", required_argument, 0, 'i'},
+        {"in", required_argument, 0, 'I'},
+        {"length", required_argument, 0, 'l'},
+        {"mode", required_argument, 0, 'm'},
+        {"offset", required_argument, 0, 'o'},
+        {"raw", no_argument, 0, 'r'},
+        {"skip", required_argument, 0, 's'},
+        {"specific", required_argument, 0, 'S'},
+        {"timeout", required_argument, 0, 't' },
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+            "sg_write_buffer [--bpw=CS] [--help] [--id=ID] [--in=FILE]\n"
+            "                       [--length=LEN] [--mode=MO] "
+            "[--offset=OFF] [--raw]\n"
+            "                       [--skip=SKIP] [--specific=MS] "
+            "[--timeout=TO]\n"
+            "                       [--verbose] [--version] DEVICE\n"
+            "  where:\n"
+            "    --bpw=CS|-b CS         CS is chunk size: bytes per write "
+            "buffer\n"
+            "                           command (def: 0 -> as many as "
+            "possible)\n"
+            "    --help|-h              print out usage message then exit\n"
+            "    --id=ID|-i ID          buffer identifier (0 (default) to "
+            "255)\n"
+            "    --in=FILE|-I FILE      read from FILE ('-I -' read "
+            "from stdin)\n"
+            "    --length=LEN|-l LEN    length in bytes to write; may be "
+            "deduced from\n"
+            "                           FILE\n"
+            "    --mode=MO|-m MO        write buffer mode, MO is number or "
+            "acronym\n"
+            "                           (def: 0 -> 'combined header and "
+            "data' (obs))\n"
+            "    --offset=OFF|-o OFF    buffer offset (unit: bytes, def: 0)\n"
+            "    --raw|-r               read from stdin (same as '-I -')\n"
+            "    --skip=SKIP|-s SKIP    bytes in file FILE to skip before "
+            "reading\n"
+            "    --specific=MS|-S MS    mode specific value; 3 bit field "
+            "(0 to 7)\n"
+            "    --timeout=TO|-t TO     command timeout in seconds (def: "
+            "300)\n"
+            "    --verbose|-v           increase verbosity\n"
+            "    --version|-V           print version string and exit\n\n"
+            "Performs one or more SCSI WRITE BUFFER commands. Use '-m xxx' "
+            "to list\navailable modes. A chunk size of 4 KB ('--bpw=4k') "
+            "seems to work well.\nExample: sg_write_buffer -b 4k -I xxx.lod "
+            "-m 7 /dev/sg3\n"
+          );
+
+}
+
+#define MODE_HEADER_DATA        0
+#define MODE_VENDOR             1
+#define MODE_DATA               2
+#define MODE_DNLD_MC            4
+#define MODE_DNLD_MC_SAVE       5
+#define MODE_DNLD_MC_OFFS       6
+#define MODE_DNLD_MC_OFFS_SAVE  7
+#define MODE_ECHO_BUFFER        0x0A
+#define MODE_DNLD_MC_EV_OFFS_DEFER 0x0D
+#define MODE_DNLD_MC_OFFS_DEFER 0x0E
+#define MODE_ACTIVATE_MC        0x0F
+#define MODE_EN_EX_ECHO         0x1A
+#define MODE_DIS_EX             0x1B
+#define MODE_DNLD_ERR_HISTORY   0x1C
+
+
+struct mode_s {
+        const char *mode_string;
+        int   mode;
+        const char *comment;
+};
+
+static struct mode_s mode_arr[] = {
+        {"hd",         MODE_HEADER_DATA, "combined header and data "
+                "(obsolete)"},
+        {"vendor",     MODE_VENDOR,    "vendor specific"},
+        {"data",       MODE_DATA,      "data"},
+        {"dmc",        MODE_DNLD_MC,   "download microcode and activate"},
+        {"dmc_save",   MODE_DNLD_MC_SAVE, "download microcode, save and "
+                "activate"},
+        {"dmc_offs",   MODE_DNLD_MC_OFFS, "download microcode with offsets "
+                "and activate"},
+        {"dmc_offs_save", MODE_DNLD_MC_OFFS_SAVE, "download microcode with "
+                "offsets, save and\n\t\t\t\tactivate"},
+        {"echo",       MODE_ECHO_BUFFER, "write data to echo buffer"},
+        {"dmc_offs_ev_defer", MODE_DNLD_MC_EV_OFFS_DEFER, "download "
+                "microcode with offsets, select\n\t\t\t\tactivation event, "
+                "save and defer activation"},
+        {"dmc_offs_defer", MODE_DNLD_MC_OFFS_DEFER, "download microcode "
+                "with offsets, save and\n\t\t\t\tdefer activation"},
+        {"activate_mc", MODE_ACTIVATE_MC, "activate deferred microcode"},
+        {"en_ex",      MODE_EN_EX_ECHO, "enable expander communications "
+                "protocol and\n\t\t\t\techo buffer (obsolete)"},
+        {"dis_ex",     MODE_DIS_EX, "disable expander communications "
+                "protocol\n\t\t\t\t(obsolete)"},
+        {"deh",        MODE_DNLD_ERR_HISTORY, "download application client "
+                "error history "},
+        {NULL, 0, NULL},
+};
+
+static void
+print_modes(void)
+{
+    const struct mode_s * mp;
+
+    pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
+            "or symbolic:\n");
+    for (mp = mode_arr; mp->mode_string; ++mp) {
+        pr2serr(" %2d (0x%02x)  %-18s%s\n", mp->mode, mp->mode,
+                mp->mode_string, mp->comment);
+    }
+    pr2serr("\nAdditionally '--bpw=<val>,act' does a activate deferred "
+            "microcode after\nsuccessful dmc_offs_defer and "
+            "dmc_offs_ev_defer mode downloads.\n");
+}
+
+/* <<<< This function may be moved to the library in the future >>> */
+/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 ->
+ * success, SG_LIB_CAT_INVALID_OP -> invalid opcode,
+ * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION,
+ * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND,
+ * -1 -> other failure */
+static int
+sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id,
+                      uint32_t buffer_offset, void * paramp,
+                      uint32_t param_len, int to_secs, int noisy, int verbose)
+{
+    int k, res, ret, sense_cat;
+    uint8_t wbufCmdBlk[WRITE_BUFFER_CMDLEN] =
+        {WRITE_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    uint8_t sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    if (buffer_offset > 0xffffff) {
+        pr2serr("%s: buffer_offset value too large for 24 bits\n", __func__);
+        return -1;
+    }
+    if (param_len > 0xffffff) {
+        pr2serr("%s: param_len value too large for 24 bits\n", __func__);
+        return -1;
+    }
+    wbufCmdBlk[1] = (uint8_t)(mode & 0x1f);
+    wbufCmdBlk[1] |= (uint8_t)((m_specific & 0x7) << 5);
+    wbufCmdBlk[2] = (uint8_t)(buffer_id & 0xff);
+    sg_put_unaligned_be24(buffer_offset, wbufCmdBlk + 3);
+    sg_put_unaligned_be24(param_len, wbufCmdBlk + 6);
+    if (verbose) {
+        pr2serr("    Write buffer cmd: ");
+        for (k = 0; k < WRITE_BUFFER_CMDLEN; ++k)
+            pr2serr("%02x ", wbufCmdBlk[k]);
+        pr2serr("\n");
+        if ((verbose > 1) && paramp && param_len) {
+            pr2serr("    Write buffer parameter list%s:\n",
+                    ((param_len > 256) ? " (first 256 bytes)" : ""));
+            dStrHexErr((const char *)paramp,
+                       ((param_len > 256) ? 256 : param_len), -1);
+        }
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("%s: out of memory\n", __func__);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, wbufCmdBlk, sizeof(wbufCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len);
+    res = do_scsi_pt(ptvp, sg_fd, to_secs, verbose);
+    ret = sg_cmds_process_resp(ptvp, "Write buffer", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, infd, res, c, len, k, n, got_stdin;
+    int bpw = 0;
+    int bpw_then_activate = 0;
+    int do_help = 0;
+    int wb_id = 0;
+    int wb_len = 0;
+    int wb_len_given = 0;
+    int wb_mode = 0;
+    int wb_offset = 0;
+    int wb_skip = 0;
+    int wb_timeout = DEF_PT_TIMEOUT;
+    int wb_mspec = 0;
+    int verbose = 0;
+    const char * device_name = NULL;
+    const char * file_name = NULL;
+    unsigned char * dop = NULL;
+    char * cp;
+    const struct mode_s * mp;
+    char ebuff[EBUFF_SZ];
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "b:hi:I:l:m:o:rs:S:t:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            bpw = sg_get_num(optarg);
+            if (bpw < 0) {
+                pr2serr("argument to '--bpw' should be in a positive "
+                        "number\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            if ((cp = strchr(optarg, ','))) {
+                if (0 == strncmp("act", cp + 1, 3))
+                    ++bpw_then_activate;
+            }
+            break;
+        case 'h':
+        case '?':
+            ++do_help;
+            break;
+        case 'i':
+            wb_id = sg_get_num(optarg);
+            if ((wb_id < 0) || (wb_id > 255)) {
+                pr2serr("argument to '--id' should be in the range 0 to "
+                        "255\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'I':
+            file_name = optarg;
+            break;
+        case 'l':
+            wb_len = sg_get_num(optarg);
+            if (wb_len < 0) {
+                pr2serr("bad argument to '--length'\n");
+                return SG_LIB_SYNTAX_ERROR;
+             }
+             wb_len_given = 1;
+             break;
+        case 'm':
+            if (isdigit(*optarg)) {
+                wb_mode = sg_get_num(optarg);
+                if ((wb_mode < 0) || (wb_mode > 31)) {
+                    pr2serr("argument to '--mode' should be in the range 0 "
+                            "to 31\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            } else {
+                len = strlen(optarg);
+                for (mp = mode_arr; mp->mode_string; ++mp) {
+                    if (0 == strncmp(mp->mode_string, optarg, len)) {
+                        wb_mode = mp->mode;
+                        break;
+                    }
+                }
+                if (! mp->mode_string) {
+                    print_modes();
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }
+            break;
+        case 'o':
+           wb_offset = sg_get_num(optarg);
+           if (wb_offset < 0) {
+                pr2serr("bad argument to '--offset'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'r':
+            file_name = "-";
+            break;
+        case 's':
+           wb_skip = sg_get_num(optarg);
+           if (wb_skip < 0) {
+                pr2serr("bad argument to '--skip'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'S':
+            wb_mspec = sg_get_num(optarg);
+            if ((wb_mspec < 0) || (wb_mspec > 7)) {
+                pr2serr("expected argument to '--specific' to be 0 to 7\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 't':
+            wb_timeout = sg_get_num(optarg);
+            if (wb_timeout < 0) {
+                pr2serr("Invalid argument to '--timeout'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (do_help) {
+        if (do_help > 1) {
+            usage();
+            pr2serr("\n");
+            print_modes();
+        } else
+            usage();
+        return 0;
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if ((wb_len > 0) && (bpw > wb_len)) {
+        pr2serr("trim chunk size (CS) to be the same as LEN\n");
+        bpw = wb_len;
+    }
+
+#ifdef SG_LIB_WIN32
+#ifdef SG_LIB_WIN32_DIRECT
+    if (verbose > 4)
+        pr2serr("Initial win32 SPT interface state: %s\n",
+                scsi_pt_win32_spt_state() ? "direct" : "indirect");
+    scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
+#endif
+#endif
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+    if (file_name || (wb_len > 0)) {
+        if (0 == wb_len)
+            wb_len = DEF_XFER_LEN;
+        if (NULL == (dop = (unsigned char *)malloc(wb_len))) {
+            pr2serr(ME "out of memory\n");
+            ret = SG_LIB_SYNTAX_ERROR;
+            goto err_out;
+        }
+        memset(dop, 0xff, wb_len);
+        if (file_name) {
+            got_stdin = (0 == strcmp(file_name, "-")) ? 1 : 0;
+            if (got_stdin) {
+                if (wb_skip > 0) {
+                    pr2serr("Can't skip on stdin\n");
+                    ret = SG_LIB_FILE_ERROR;
+                    goto err_out;
+                }
+                infd = STDIN_FILENO;
+            } else {
+                if ((infd = open(file_name, O_RDONLY)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                             ME "could not open %s for reading", file_name);
+                    perror(ebuff);
+                    ret = SG_LIB_FILE_ERROR;
+                    goto err_out;
+                } else if (sg_set_binary_mode(infd) < 0)
+                    perror("sg_set_binary_mode");
+                if (wb_skip > 0) {
+                    if (lseek(infd, wb_skip, SEEK_SET) < 0) {
+                        snprintf(ebuff,  EBUFF_SZ, ME "couldn't skip to "
+                                 "required position on %s", file_name);
+                        perror(ebuff);
+                        close(infd);
+                        ret = SG_LIB_FILE_ERROR;
+                        goto err_out;
+                    }
+                }
+            }
+            res = read(infd, dop, wb_len);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
+                         file_name);
+                perror(ebuff);
+                if (! got_stdin)
+                    close(infd);
+                ret = SG_LIB_FILE_ERROR;
+                goto err_out;
+            }
+            if (res < wb_len) {
+                if (wb_len_given) {
+                    pr2serr("tried to read %d bytes from %s, got %d bytes\n",
+                            wb_len, file_name, res);
+                    pr2serr("pad with 0xff bytes and continue\n");
+                } else {
+                    if (verbose) {
+                        pr2serr("tried to read %d bytes from %s, got %d "
+                                "bytes\n", wb_len, file_name, res);
+                        pr2serr("will write %d bytes", res);
+                        if ((bpw > 0) && (bpw < wb_len))
+                            pr2serr(", %d bytes per WRITE BUFFER command\n",
+                                    bpw);
+                        else
+                            pr2serr("\n");
+                    }
+                    wb_len = res;
+                }
+            }
+            if (! got_stdin)
+                close(infd);
+        }
+    }
+
+    res = 0;
+    if (bpw > 0) {
+        for (k = 0; k < wb_len; k += n) {
+            n = wb_len - k;
+            if (n > bpw)
+                n = bpw;
+            if (verbose)
+                pr2serr("sending write buffer, mode=0x%x, mspec=%d, id=%d, "
+                        " offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
+                        wb_offset + k, n);
+            res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
+                                        wb_offset + k, dop + k, n,
+                                        wb_timeout, 1, verbose);
+            if (res)
+                break;
+        }
+        if (bpw_then_activate) {
+            if (verbose)
+                pr2serr("sending Activate deferred microcode [0xf]\n");
+            res = sg_ll_write_buffer_v2(sg_fd, MODE_ACTIVATE_MC, 0, 0, 0,
+                                        NULL, 0, wb_timeout, 1, verbose);
+        }
+    } else {
+        if (verbose)
+            pr2serr("sending single write buffer, mode=0x%x, mpsec=%d, "
+                    "id=%d, offset=%d, len=%d\n", wb_mode, wb_mspec, wb_id,
+                    wb_offset, wb_len);
+        res = sg_ll_write_buffer_v2(sg_fd, wb_mode, wb_mspec, wb_id,
+                                    wb_offset, dop, wb_len, wb_timeout, 1,
+                                    verbose);
+    }
+    if (0 != res) {
+        char b[80];
+
+        ret = res;
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("Write buffer failed: %s\n", b);
+    }
+
+err_out:
+    if (dop)
+        free(dop);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_write_long.c b/sg3_utils/src/sg_write_long.c
new file mode 100644
index 0000000..f5a3229
--- /dev/null
+++ b/sg3_utils/src/sg_write_long.c
@@ -0,0 +1,287 @@
+/* A utility program for the Linux OS SCSI subsystem.
+ *  Copyright (C) 2004-2016 D. Gilbert
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ * This program issues the SCSI command WRITE LONG to a given SCSI device.
+ * It sends the command with the logical block address passed as the lba
+ * argument, and the transfer length set to the xfer_len argument. the
+ * buffer to be writen to the device filled with 0xff, this buffer includes
+ * the sector data and the ECC bytes.
+ *
+ * This code was contributed by Saeed Bishara
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.12 20160121";
+
+
+#define MAX_XFER_LEN 10000
+
+/* #define SG_DEBUG */
+
+#define ME "sg_write_long: "
+
+#define EBUFF_SZ 256
+
+static struct option long_options[] = {
+        {"16", 0, 0, 'S'},
+        {"cor_dis", 0, 0, 'c'},
+        {"help", 0, 0, 'h'},
+        {"in", 1, 0, 'i'},
+        {"lba", 1, 0, 'l'},
+        {"pblock", 0, 0, 'p'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {"wr_uncor", 0, 0, 'w'},
+        {"xfer_len", 1, 0, 'x'},
+        {0, 0, 0, 0},
+};
+
+
+
+static void
+usage()
+{
+  pr2serr("Usage: sg_write_long [--16] [--cor_dis] [--help] [--in=IF] "
+          "[--lba=LBA]\n"
+          "                     [--pblock] [--verbose] [--version] "
+          "[--wr_uncor]\n"
+          "                     [--xfer_len=BTL] DEVICE\n"
+          "  where:\n"
+          "    --16|-S              do WRITE LONG(16) (default: 10)\n"
+          "    --cor_dis|-c         set correction disabled bit\n"
+          "    --help|-h            print out usage message\n"
+          "    --in=IF|-i IF        input from file called IF (default: "
+          "use\n"
+          "                         0xff bytes as fill)\n"
+          "    --lba=LBA|-l LBA     logical block address "
+          "(default: 0)\n"
+          "    --pblock|-p          physical block (default: logical "
+          "block)\n"
+          "    --verbose|-v         increase verbosity\n"
+          "    --version|-V         print version string then exit\n"
+          "    --wr_uncor|-w        set an uncorrectable error (no "
+          "data transferred)\n"
+          "    --xfer_len=BTL|-x BTL    byte transfer length (< 10000) "
+          "(default:\n"
+          "                             520 bytes)\n\n"
+          "Performs a SCSI WRITE LONG (10 or 16) command. Writes a single "
+          "block\nincluding associated ECC data. That data may be obtained "
+          "from the\nSCSI READ LONG command. See the sg_read_long utility.\n"
+          );
+}
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, infd, offset;
+    unsigned char * writeLongBuff = NULL;
+    void * rawp = NULL;
+    int xfer_len = 520;
+    int cor_dis = 0;
+    int pblock = 0;
+    int wr_uncor = 0;
+    int do_16 = 0;
+    uint64_t llba = 0;
+    int verbose = 0;
+    int64_t ll;
+    int got_stdin;
+    const char * device_name = NULL;
+    char file_name[256];
+    char b[80];
+    char ebuff[EBUFF_SZ];
+    const char * ten_or;
+    int ret = 1;
+
+    memset(file_name, 0, sizeof file_name);
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "chi:l:pSvVwx:", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'c':
+            cor_dis = 1;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'i':
+            strncpy(file_name, optarg, sizeof(file_name));
+            break;
+        case 'l':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            llba = (uint64_t)ll;
+            break;
+        case 'p':
+            pblock = 1;
+            break;
+        case 'S':
+            do_16 = 1;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        case 'w':
+            wr_uncor = 1;
+            break;
+        case 'x':
+            xfer_len = sg_get_num(optarg);
+            if (-1 == xfer_len) {
+                pr2serr("bad argument to '--xfer_len'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (wr_uncor)
+        xfer_len = 0;
+    else if (xfer_len >= MAX_XFER_LEN) {
+        pr2serr("xfer_len (%d) is out of range ( < %d)\n", xfer_len,
+                MAX_XFER_LEN);
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (wr_uncor) {
+        if ('\0' != file_name[0])
+            pr2serr(">>> warning: when '--wr_uncor' given '-in=' is "
+                    "ignored\n");
+    } else {
+        if (NULL == (rawp = malloc(MAX_XFER_LEN))) {
+            pr2serr(ME "out of memory\n");
+            ret = SG_LIB_FILE_ERROR;
+            goto err_out;
+        }
+        writeLongBuff = (unsigned char *)rawp;
+        memset(rawp, 0xff, MAX_XFER_LEN);
+        if (file_name[0]) {
+            got_stdin = (0 == strcmp(file_name, "-")) ? 1 : 0;
+            if (got_stdin) {
+                infd = STDIN_FILENO;
+                if (sg_set_binary_mode(STDIN_FILENO) < 0)
+                    perror("sg_set_binary_mode");
+            } else {
+                if ((infd = open(file_name, O_RDONLY)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                             ME "could not open %s for reading", file_name);
+                    perror(ebuff);
+                    goto err_out;
+                } else if (sg_set_binary_mode(infd) < 0)
+                    perror("sg_set_binary_mode");
+            }
+            res = read(infd, writeLongBuff, xfer_len);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
+                         file_name);
+                perror(ebuff);
+                if (! got_stdin)
+                    close(infd);
+                goto err_out;
+            }
+            if (res < xfer_len) {
+                pr2serr("tried to read %d bytes from %s, got %d bytes\n",
+                        xfer_len, file_name, res);
+                pr2serr("pad with 0xff bytes and continue\n");
+            }
+            if (! got_stdin)
+                close(infd);
+        }
+    }
+    if (verbose)
+        pr2serr(ME "issue write long to device %s\n\t\txfer_len= %d (0x%x), "
+                "lba=%" PRIu64 " (0x%" PRIx64 ")\n    cor_dis=%d, "
+                "wr_uncor=%d, pblock=%d\n", device_name, xfer_len, xfer_len,
+                llba, llba, cor_dis, wr_uncor, pblock);
+
+    ten_or = do_16 ? "16" : "10";
+    if (do_16)
+        res = sg_ll_write_long16(sg_fd, cor_dis, wr_uncor, pblock, llba,
+                                 writeLongBuff, xfer_len, &offset, 1, verbose);
+    else
+        res = sg_ll_write_long10(sg_fd, cor_dis, wr_uncor, pblock,
+                                 (unsigned int)llba, writeLongBuff, xfer_len,
+                                 &offset, 1, verbose);
+    ret = res;
+    switch (res) {
+    case 0:
+        break;
+    case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:
+        pr2serr("<<< device indicates 'xfer_len' should be %d >>>\n",
+                xfer_len - offset);
+        break;
+    default:
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("  SCSI WRITE LONG (%s): %s\n", ten_or, b);
+        break;
+    }
+
+err_out:
+    if (rawp)
+        free(rawp);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_write_same.c b/sg3_utils/src/sg_write_same.c
new file mode 100644
index 0000000..486e1ad
--- /dev/null
+++ b/sg3_utils/src/sg_write_same.c
@@ -0,0 +1,609 @@
+/*
+ * Copyright (c) 2009-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.11 20151220";
+
+
+#define ME "sg_write_same: "
+
+#define WRITE_SAME10_OP 0x41
+#define WRITE_SAME16_OP 0x93
+#define VARIABLE_LEN_OP 0x7f
+#define WRITE_SAME32_SA 0xd
+#define WRITE_SAME32_ADD 0x18
+#define WRITE_SAME10_LEN 10
+#define WRITE_SAME16_LEN 16
+#define WRITE_SAME32_LEN 32
+#define RCAP10_RESP_LEN 8
+#define RCAP16_RESP_LEN 32
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_TIMEOUT_SECS 60
+#define DEF_WS_CDB_SIZE WRITE_SAME10_LEN
+#define DEF_WS_NUMBLOCKS 1
+#define MAX_XFER_LEN (64 * 1024)
+#define EBUFF_SZ 256
+
+static struct option long_options[] = {
+    {"10", no_argument, 0, 'R'},
+    {"16", no_argument, 0, 'S'},
+    {"32", no_argument, 0, 'T'},
+    {"anchor", no_argument, 0, 'a'},
+    {"grpnum", required_argument, 0, 'g'},
+    {"help", no_argument, 0, 'h'},
+    {"in", required_argument, 0, 'i'},
+    {"lba", required_argument, 0, 'l'},
+    {"lbdata", no_argument, 0, 'L'},
+    {"ndob", no_argument, 0, 'N'},
+    {"num", required_argument, 0, 'n'},
+    {"pbdata", no_argument, 0, 'P'},
+    {"timeout", required_argument, 0, 'r'},
+    {"unmap", no_argument, 0, 'U'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {"wrprotect", required_argument, 0, 'w'},
+    {"xferlen", required_argument, 0, 'x'},
+    {0, 0, 0, 0},
+};
+
+struct opts_t {
+    int anchor;
+    int grpnum;
+    char ifilename[256];
+    uint64_t lba;
+    int lbdata;
+    int ndob;
+    int numblocks;
+    int pbdata;
+    int timeout;
+    int unmap;
+    int verbose;
+    int wrprotect;
+    int xfer_len;
+    int pref_cdb_size;
+    int want_ws10;
+};
+
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_write_same [--10] [--16] [--32] [--anchor] "
+            "[--grpnum=GN] [--help]\n"
+            "                     [--in=IF] [--lba=LBA] [--lbdata] "
+            "[--ndob] [--num=NUM]\n"
+            "                     [--pbdata] [--timeout=TO] [--unmap] "
+            "[--verbose]\n"
+            "                     [--version] [--wrprotect=WRP] "
+            "[xferlen=LEN]\n"
+            "                     DEVICE\n"
+            "  where:\n"
+            "    --10|-R              do WRITE SAME(10) (even if '--unmap' "
+            "is given)\n"
+            "    --16|-S              do WRITE SAME(16) (def: 10 unless "
+            "'--unmap' given,\n"
+            "                         LBA+NUM > 32 bits, or NUM > 65535; "
+            "then def 16)\n"
+            "    --32|-T              do WRITE SAME(32) (def: 10 or 16)\n"
+            "    --anchor|-a          set anchor field in cdb\n"
+            "    --grpnum=GN|-g GN    GN is group number field (def: 0)\n"
+            "    --help|-h            print out usage message\n"
+            "    --in=IF|-i IF        IF is file to fetch one block of data "
+            "from (use LEN\n"
+            "                         bytes or whole file). Block written to "
+            "DEVICE\n"
+            "    --lba=LBA|-l LBA     LBA is the logical block address to "
+            "start (def: 0)\n"
+            "    --lbdata|-L          set LBDATA bit (obsolete)\n"
+            "    --ndob|-N            set 'no data-out buffer' bit\n"
+            "    --num=NUM|-n NUM     NUM is number of logical blocks to "
+            "write (def: 1)\n"
+            "                         [Beware NUM==0 may mean rest of "
+            "device]\n"
+            "    --pbdata|-P          set PBDATA bit (obsolete)\n"
+            "    --timeout=TO|-t TO    command timeout (unit: seconds) (def: "
+            "60)\n"
+            "    --unmap|-U           set UNMAP bit\n"
+            "    --verbose|-v         increase verbosity\n"
+            "    --version|-V         print version string then exit\n"
+            "    --wrprotect=WPR|-w WPR    WPR is the WRPROTECT field value "
+            "(def: 0)\n"
+            "    --xferlen=LEN|-x LEN    LEN is number of bytes from IF to "
+            "send to\n"
+            "                            DEVICE (def: IF file length)\n\n"
+            "Performs a SCSI WRITE SAME (10, 16 or 32) command\n"
+            );
+}
+
+static int
+do_write_same(int sg_fd, const struct opts_t * op, const void * dataoutp,
+              int * act_cdb_lenp)
+{
+    int k, ret, res, sense_cat, cdb_len;
+    uint64_t llba;
+    unsigned char wsCmdBlk[WRITE_SAME32_LEN];
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    cdb_len = op->pref_cdb_size;
+    if (WRITE_SAME10_LEN == cdb_len) {
+        llba = op->lba + op->numblocks;
+        if ((op->numblocks > 0xffff) || (llba > ULONG_MAX) ||
+            op->ndob || (op->unmap && (0 == op->want_ws10))) {
+            cdb_len = WRITE_SAME16_LEN;
+            if (op->verbose) {
+                const char * cp = "use WRITE SAME(16) instead of 10 byte "
+                                  "cdb";
+
+                if (op->numblocks > 0xffff)
+                    pr2serr("%s since blocks exceed 65535\n", cp);
+                else if (llba > ULONG_MAX)
+                    pr2serr("%s since LBA may exceed 32 bits\n", cp);
+                else
+                    pr2serr("%s due to ndob or unmap settings\n", cp);
+            }
+        }
+    }
+    if (act_cdb_lenp)
+        *act_cdb_lenp = cdb_len;
+    memset(wsCmdBlk, 0, sizeof(wsCmdBlk));
+    switch (cdb_len) {
+    case WRITE_SAME10_LEN:
+        wsCmdBlk[0] = WRITE_SAME10_OP;
+        wsCmdBlk[1] = ((op->wrprotect & 0x7) << 5);
+        /* ANCHOR + UNMAP not allowed for WRITE_SAME10 in sbc3r24+r25 but
+         * a proposal has been made to allow it. Anticipate approval. */
+        if (op->anchor)
+            wsCmdBlk[1] |= 0x10;
+        if (op->unmap)
+            wsCmdBlk[1] |= 0x8;
+        if (op->pbdata)
+            wsCmdBlk[1] |= 0x4;
+        if (op->lbdata)
+            wsCmdBlk[1] |= 0x2;
+        sg_put_unaligned_be32((uint32_t)op->lba, wsCmdBlk + 2);
+        wsCmdBlk[6] = (op->grpnum & 0x1f);
+        sg_put_unaligned_be16((uint16_t)op->numblocks, wsCmdBlk + 7);
+        break;
+    case WRITE_SAME16_LEN:
+        wsCmdBlk[0] = WRITE_SAME16_OP;
+        wsCmdBlk[1] = ((op->wrprotect & 0x7) << 5);
+        if (op->anchor)
+            wsCmdBlk[1] |= 0x10;
+        if (op->unmap)
+            wsCmdBlk[1] |= 0x8;
+        if (op->pbdata)
+            wsCmdBlk[1] |= 0x4;
+        if (op->lbdata)
+            wsCmdBlk[1] |= 0x2;
+        if (op->ndob)
+            wsCmdBlk[1] |= 0x1;
+        sg_put_unaligned_be64(op->lba, wsCmdBlk + 2);
+        sg_put_unaligned_be32((uint32_t)op->numblocks, wsCmdBlk + 10);
+        wsCmdBlk[14] = (op->grpnum & 0x1f);
+        break;
+    case WRITE_SAME32_LEN:
+        /* Note: In Linux at this time the sg driver does not support
+         * cdb_s > 16 bytes long, but the bsg driver does. */
+        wsCmdBlk[0] = VARIABLE_LEN_OP;
+        wsCmdBlk[6] = (op->grpnum & 0x1f);
+        wsCmdBlk[7] = WRITE_SAME32_ADD;
+        sg_put_unaligned_be16((uint16_t)WRITE_SAME32_SA, wsCmdBlk + 8);
+        wsCmdBlk[10] = ((op->wrprotect & 0x7) << 5);
+        if (op->anchor)
+            wsCmdBlk[10] |= 0x10;
+        if (op->unmap)
+            wsCmdBlk[10] |= 0x8;
+        if (op->pbdata)
+            wsCmdBlk[10] |= 0x4;
+        if (op->lbdata)
+            wsCmdBlk[10] |= 0x2;
+        if (op->ndob)
+            wsCmdBlk[10] |= 0x1;
+        sg_put_unaligned_be64(op->lba, wsCmdBlk + 12);
+        sg_put_unaligned_be32((uint32_t)op->numblocks, wsCmdBlk + 28);
+        break;
+    default:
+        pr2serr("do_write_same: bad cdb length %d\n", cdb_len);
+        return -1;
+    }
+
+    if (op->verbose > 1) {
+        pr2serr("    Write same(%d) cmd: ", cdb_len);
+        for (k = 0; k < cdb_len; ++k)
+            pr2serr("%02x ", wsCmdBlk[k]);
+        pr2serr("\n    Data-out buffer length=%d\n",
+                op->xfer_len);
+    }
+    if ((op->verbose > 3) && (op->xfer_len > 0)) {
+        pr2serr("    Data-out buffer contents:\n");
+        dStrHexErr((const char *)dataoutp, op->xfer_len, 1);
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("Write same(%d): out of memory\n", cdb_len);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, wsCmdBlk, cdb_len);
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, (unsigned char *)dataoutp, op->xfer_len);
+    res = do_scsi_pt(ptvp, sg_fd, op->timeout, op->verbose);
+    ret = sg_cmds_process_resp(ptvp, "Write same", res, 0, sense_b,
+                               1 /*noisy */, op->verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_MEDIUM_HARD:
+            {
+                int valid, slen;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                if (valid)
+                    pr2serr("Medium or hardware error starting at lba=%"
+                            PRIu64 " [0x%" PRIx64 "]\n", ull, ull);
+            }
+            ret = sense_cat;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, infd, prot_en, act_cdb_len, vb;
+    int num_given = 0;
+    int lba_given = 0;
+    int if_given = 0;
+    int got_stdin = 0;
+    int64_t ll;
+    uint32_t block_size;
+    const char * device_name = NULL;
+    char ebuff[EBUFF_SZ];
+    char b[80];
+    unsigned char resp_buff[RCAP16_RESP_LEN];
+    unsigned char * wBuff = NULL;
+    int ret = -1;
+    struct opts_t opts;
+    struct opts_t * op;
+    struct stat a_stat;
+
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->numblocks = DEF_WS_NUMBLOCKS;
+    op->pref_cdb_size = DEF_WS_CDB_SIZE;
+    op->timeout = DEF_TIMEOUT_SECS;
+    vb = 0;
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ag:hi:l:Ln:NPRSt:TUvVw:x:",
+                        long_options, &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+            ++op->anchor;
+            break;
+        case 'g':
+            op->grpnum = sg_get_num(optarg);
+            if ((op->grpnum < 0) || (op->grpnum > 31))  {
+                pr2serr("bad argument to '--grpnum'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'i':
+            strncpy(op->ifilename, optarg, sizeof(op->ifilename));
+            if_given = 1;
+            break;
+        case 'l':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->lba = (uint64_t)ll;
+            lba_given = 1;
+            break;
+        case 'L':
+            ++op->lbdata;
+            break;
+        case 'n':
+            op->numblocks = sg_get_num(optarg);
+            if (op->numblocks < 0)  {
+                pr2serr("bad argument to '--num'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            num_given = 1;
+            break;
+        case 'N':
+            ++op->ndob;
+            break;
+        case 'P':
+            ++op->pbdata;
+            break;
+        case 'R':
+            ++op->want_ws10;
+            break;
+        case 'S':
+            if (DEF_WS_CDB_SIZE != op->pref_cdb_size) {
+                pr2serr("only one '--10', '--16' or '--32' please\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->pref_cdb_size = 16;
+            break;
+        case 't':
+            op->timeout = sg_get_num(optarg);
+            if (op->timeout < 0)  {
+                pr2serr("bad argument to '--timeout'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'T':
+            if (DEF_WS_CDB_SIZE != op->pref_cdb_size) {
+                pr2serr("only one '--10', '--16' or '--32' please\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            op->pref_cdb_size = 32;
+            break;
+        case 'U':
+            ++op->unmap;
+            break;
+        case 'v':
+            ++op->verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        case 'w':
+            op->wrprotect = sg_get_num(optarg);
+            if ((op->wrprotect < 0) || (op->wrprotect > 7))  {
+                pr2serr("bad argument to '--wrprotect'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'x':
+            op->xfer_len = sg_get_num(optarg);
+            if (op->xfer_len < 0) {
+                pr2serr("bad argument to '--xferlen'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (op->want_ws10 && (DEF_WS_CDB_SIZE != op->pref_cdb_size)) {
+        pr2serr("only one '--10', '--16' or '--32' please\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    vb = op->verbose;
+
+    if ((! if_given) && (! lba_given) && (! num_given)) {
+        pr2serr("As a precaution, one of '--in=', '--lba=' or '--num=' is "
+                "required\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    if (op->ndob) {
+        if (if_given) {
+            pr2serr("Can't have both --ndob and '--in='\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (0 != op->xfer_len) {
+            pr2serr("With --ndob only '--xferlen=0' (or not given) is "
+                    "acceptable\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    } else if (op->ifilename[0]) {
+        got_stdin = (0 == strcmp(op->ifilename, "-")) ? 1 : 0;
+        if (! got_stdin) {
+            memset(&a_stat, 0, sizeof(a_stat));
+            if (stat(op->ifilename, &a_stat) < 0) {
+                if (vb)
+                    pr2serr("unable to stat(%s): %s\n", op->ifilename,
+                            safe_strerror(errno));
+                return SG_LIB_FILE_ERROR;
+            }
+            if (op->xfer_len <= 0)
+                op->xfer_len = (int)a_stat.st_size;
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, vb);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (! op->ndob) {
+        prot_en = 0;
+        if (0 == op->xfer_len) {
+            res = sg_ll_readcap_16(sg_fd, 0 /* pmi */, 0 /* llba */, resp_buff,
+                                   RCAP16_RESP_LEN, 1, (vb ? (vb - 1): 0));
+            if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+                pr2serr("Read capacity(16) unit attention, try again\n");
+                res = sg_ll_readcap_16(sg_fd, 0, 0, resp_buff,
+                                       RCAP16_RESP_LEN, 1, (vb ? (vb - 1): 0));
+            }
+            if (0 == res) {
+                if (vb > 3)
+                    dStrHexErr((const char *)resp_buff, RCAP16_RESP_LEN, 1);
+                block_size = sg_get_unaligned_be32(resp_buff + 8);
+                prot_en = !!(resp_buff[12] & 0x1);
+                op->xfer_len = block_size;
+                if (prot_en && (op->wrprotect > 0))
+                    op->xfer_len += 8;
+            } else if ((SG_LIB_CAT_INVALID_OP == res) ||
+                       (SG_LIB_CAT_ILLEGAL_REQ == res)) {
+                if (vb)
+                    pr2serr("Read capacity(16) not supported, try Read "
+                            "capacity(10)\n");
+                res = sg_ll_readcap_10(sg_fd, 0 /* pmi */, 0 /* lba */,
+                                       resp_buff, RCAP10_RESP_LEN, 1,
+                                       (vb ? (vb - 1): 0));
+                if (0 == res) {
+                    if (vb > 3)
+                        dStrHexErr((const char *)resp_buff, RCAP10_RESP_LEN,
+                                   1);
+                    block_size = sg_get_unaligned_be32(resp_buff + 4);
+                    op->xfer_len = block_size;
+                } else {
+                    sg_get_category_sense_str(res, sizeof(b), b, vb);
+                    pr2serr("Read capacity(10): %s\n", b);
+                    pr2serr("Unable to calculate block size\n");
+                }
+            } else if (vb) {
+                sg_get_category_sense_str(res, sizeof(b), b, vb);
+                pr2serr("Read capacity(16): %s\n", b);
+                pr2serr("Unable to calculate block size\n");
+            }
+        }
+        if (op->xfer_len < 1) {
+            pr2serr("unable to deduce block size, please give '--xferlen=' "
+                    "argument\n");
+            ret = SG_LIB_SYNTAX_ERROR;
+            goto err_out;
+        }
+        if (op->xfer_len > MAX_XFER_LEN) {
+            pr2serr("'--xferlen=%d is out of range ( want <= %d)\n",
+                    op->xfer_len, MAX_XFER_LEN);
+            ret = SG_LIB_SYNTAX_ERROR;
+            goto err_out;
+        }
+        wBuff = (unsigned char*)calloc(op->xfer_len, 1);
+        if (NULL == wBuff) {
+            pr2serr("unable to allocate %d bytes of memory with calloc()\n",
+                    op->xfer_len);
+            ret = SG_LIB_SYNTAX_ERROR;
+            goto err_out;
+        }
+        if (op->ifilename[0]) {
+            if (got_stdin) {
+                infd = STDIN_FILENO;
+                if (sg_set_binary_mode(STDIN_FILENO) < 0)
+                    perror("sg_set_binary_mode");
+            } else {
+                if ((infd = open(op->ifilename, O_RDONLY)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
+                             "reading", op->ifilename);
+                    perror(ebuff);
+                    ret = SG_LIB_FILE_ERROR;
+                    goto err_out;
+                } else if (sg_set_binary_mode(infd) < 0)
+                    perror("sg_set_binary_mode");
+            }
+            res = read(infd, wBuff, op->xfer_len);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
+                         op->ifilename);
+                perror(ebuff);
+                if (! got_stdin)
+                    close(infd);
+                ret = SG_LIB_FILE_ERROR;
+                goto err_out;
+            }
+            if (res < op->xfer_len) {
+                pr2serr("tried to read %d bytes from %s, got %d bytes\n",
+                        op->xfer_len, op->ifilename, res);
+                pr2serr("  so pad with 0x0 bytes and continue\n");
+            }
+            if (! got_stdin)
+                close(infd);
+        } else {
+            if (vb)
+                pr2serr("Default data-out buffer set to %d zeros\n",
+                        op->xfer_len);
+            if (prot_en && (op->wrprotect > 0)) {
+               /* default for protection is 0xff, rest get 0x0 */
+                memset(wBuff + op->xfer_len - 8, 0xff, 8);
+                if (vb)
+                    pr2serr(" ... apart from last 8 bytes which are set to "
+                            "0xff\n");
+            }
+        }
+    }
+
+    ret = do_write_same(sg_fd, op, wBuff, &act_cdb_len);
+    if (ret) {
+        sg_get_category_sense_str(ret, sizeof(b), b, vb);
+        pr2serr("Write same(%d): %s\n", act_cdb_len, b);
+    }
+
+err_out:
+    if (wBuff)
+        free(wBuff);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_write_verify.c b/sg3_utils/src/sg_write_verify.c
new file mode 100644
index 0000000..e5412fb
--- /dev/null
+++ b/sg3_utils/src/sg_write_verify.c
@@ -0,0 +1,591 @@
+/*
+ * Copyright (c) 2014-2015 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.
+ *
+ * This program issues the SCSI command WRITE AND VERIFY to a given SCSI
+ * device. It sends the command with the logical block address passed as the
+ * LBA argument, for the given number of blocks. The number of bytes sent is
+ * supplied separately, either by the size of the given file (IF) or
+ * explicitly with ILEN.
+ *
+ * This code was contributed by Bruno Goncalves
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "1.08 20151220";
+
+
+#define ME "sg_write_verify: "
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+
+#define WRITE_VERIFY10_CMD      0x2e
+#define WRITE_VERIFY10_CMDLEN   10
+#define WRITE_VERIFY16_CMD      0x8e
+#define WRITE_VERIFY16_CMDLEN   16
+
+#define WRPROTECT_MASK  (0x7)
+#define WRPROTECT_SHIFT (5)
+
+#define DEF_TIMEOUT_SECS 60
+
+
+static struct option long_options[] = {
+    {"16", no_argument, 0, 'S'},
+    {"bytchk", required_argument, 0, 'b'},
+    {"dpo", no_argument, 0, 'd'},
+    {"group", required_argument, 0, 'g'},
+    {"help", no_argument, 0, 'h'},
+    {"ilen", required_argument, 0, 'I'},
+    {"in", required_argument, 0, 'i'},
+    {"lba", required_argument, 0, 'l'},
+    {"num", required_argument, 0, 'n'},
+    {"repeat", no_argument, 0, 'R'},
+    {"timeout", required_argument, 0, 't'},
+    {"verbose", no_argument, 0, 'v'},
+    {"version", no_argument, 0, 'V'},
+    {"wrprotect", required_argument, 0, 'w'},
+    {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: sg_write_verify [--16] [--bytchk=BC] [--dpo] [--group=GN] "
+            "[--help]\n"
+            "                       [--ilen=IL] [--in=IF] --lba=LBA "
+            "[--num=NUM]\n"
+            "                       [--repeat] [--timeout=TO] [--verbose] "
+            "[--version]\n"
+            "                       [--wrprotect=WPR] DEVICE\n"
+            "  where:\n"
+            "    --16|-S              do WRITE AND VERIFY(16) (default: 10)\n"
+            "    --bytchk=BC|-b BC    set BYTCHK field (default: 0)\n"
+            "    --dpo|-d             set DPO bit (default: 0)\n"
+            "    --group=GN|-g GN     GN is group number (default: 0)\n"
+            "    --help|-h            print out usage message\n"
+            "    --ilen=IL| -I IL     input (file) length in bytes, becomes "
+            "data-out\n"
+            "                         buffer length (def: deduced from IF "
+            "size)\n"
+            "    --in=IF|-i IF        IF is a file containing the data to "
+            "be written\n"
+            "    --lba=LBA|-l LBA     LBA of the first block to write "
+            "and verify;\n"
+            "                         no default, must be given\n"
+            "    --num=NUM|-n NUM     logical blocks to write and verify "
+            "(def: 1)\n"
+            "    --repeat|-R          while IF still has data to read, send "
+            "another\n"
+            "                         command, bumping LBA with up to NUM "
+            "blocks again\n"
+            "    --timeout=TO|-t TO   command timeout in seconds (def: 60)\n"
+            "    --verbose|-v         increase verbosity\n"
+            "    --version|-V         print version string then exit\n"
+            "    --wrprotect|-w WPR   WPR is the WRPROTECT field value "
+            "(def: 0)\n\n"
+            "Performs a SCSI WRITE AND VERIFY (10 or 16) command on DEVICE, "
+            "startings\nat LBA for NUM logical blocks. More commands "
+            "performed only if '--repeat'\noption given. Data to be written "
+            "is fetched from the IF file.\n"
+         );
+}
+
+/* Invokes a SCSI WRITE AND VERIFY according with CDB. Returns 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+run_scsi_transaction(int sg_fd, const unsigned char *cdbp, int cdb_len,
+                     unsigned char *dop, int do_len, int timeout, int verbose)
+{
+    int res, k, sense_cat, ret;
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    int noisy = 1;
+    struct sg_pt_base * ptvp;
+    char b[32];
+
+    snprintf(b, sizeof(b), "Write and verify(%d)", cdb_len);
+    if (verbose) {
+       pr2serr("    %s cmd: ", b);
+       for (k = 0; k < cdb_len; ++k)
+           pr2serr("%02x ", cdbp[k]);
+       pr2serr("\n");
+       if ((verbose > 2) && dop && do_len) {
+            pr2serr("    Data out buffer [%d bytes]:\n", do_len);
+            dStrHexErr((const char *)dop, do_len, -1);
+        }
+    }
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("%s: out of memory\n", b);
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, cdbp, cdb_len);
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    set_scsi_pt_data_out(ptvp, dop, do_len);
+    res = do_scsi_pt(ptvp, sg_fd, timeout, verbose);
+    ret = sg_cmds_process_resp(ptvp, b, res, 0, sense_b, noisy, verbose,
+                               &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        case SG_LIB_CAT_MEDIUM_HARD:    /* write or verify failed */
+            {
+                int valid, slen;
+                uint64_t ull = 0;
+
+                slen = get_scsi_pt_sense_len(ptvp);
+                valid = sg_get_sense_info_fld(sense_b, slen, &ull);
+                if (valid)
+                    pr2serr("Medium or hardware error starting at lba=%"
+                            PRIu64 " [0x%" PRIx64 "]\n", ull, ull);
+            }
+            ret = sense_cat;
+            break;
+        case SG_LIB_CAT_PROTECTION:     /* PI failure */
+        case SG_LIB_CAT_MISCOMPARE:     /* only in bytchk=1 case */
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+/* Invokes a SCSI WRITE AND VERIFY (10) command (SBC). Returns 0 -> success,
+* various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_write_verify10(int sg_fd, int wrprotect, int dpo, int bytchk,
+                     unsigned int lba, int num_lb, int group,
+                     unsigned char *dop, int do_len, int timeout, int verbose)
+{
+    int ret;
+    unsigned char wv_cdb[WRITE_VERIFY10_CMDLEN];
+
+    memset(wv_cdb, 0, WRITE_VERIFY10_CMDLEN);
+    wv_cdb[0] = WRITE_VERIFY10_CMD;
+    wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT);
+    if (dpo)
+        wv_cdb[1] |= 0x10;
+    if (bytchk)
+       wv_cdb[1] |= ((bytchk & 0x3) << 1);
+
+    sg_put_unaligned_be32((uint32_t)lba, wv_cdb + 2);
+    wv_cdb[6] = group & 0x1f;
+    sg_put_unaligned_be16((uint16_t)num_lb, wv_cdb + 7);
+    ret = run_scsi_transaction(sg_fd, wv_cdb, sizeof(wv_cdb), dop, do_len,
+                               timeout, verbose);
+    return ret;
+}
+
+/* Invokes a SCSI WRITE AND VERIFY (16) command (SBC). Returns 0 -> success,
+* various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_write_verify16(int sg_fd, int wrprotect, int dpo, int bytchk,
+                     uint64_t llba, int num_lb, int group, unsigned char *dop,
+                     int do_len, int timeout, int verbose)
+{
+    int ret;
+    unsigned char wv_cdb[WRITE_VERIFY16_CMDLEN];
+
+
+    memset(wv_cdb, 0, sizeof(wv_cdb));
+    wv_cdb[0] = WRITE_VERIFY16_CMD;
+    wv_cdb[1] = ((wrprotect & WRPROTECT_MASK) << WRPROTECT_SHIFT);
+    if (dpo)
+        wv_cdb[1] |= 0x10;
+    if (bytchk)
+        wv_cdb[1] |= ((bytchk & 0x3) << 1);
+
+    sg_put_unaligned_be64(llba, wv_cdb + 2);
+    sg_put_unaligned_be32((uint32_t)num_lb, wv_cdb + 10);
+    wv_cdb[14] = group & 0x1f;
+    ret = run_scsi_transaction(sg_fd, wv_cdb, sizeof(wv_cdb), dop, do_len,
+                               timeout, verbose);
+    return ret;
+}
+
+static int
+open_if(const char * fn, int got_stdin)
+{
+    int fd;
+
+    if (got_stdin)
+        fd = STDIN_FILENO;
+    else {
+        fd = open(fn, O_RDONLY);
+        if (fd < 0) {
+            pr2serr(ME "open error: %s: %s\n", fn, safe_strerror(errno));
+            return -SG_LIB_FILE_ERROR;
+        }
+    }
+    if (sg_set_binary_mode(fd) < 0) {
+        perror("sg_set_binary_mode");
+        return -SG_LIB_FILE_ERROR;
+    }
+    return fd;
+}
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c, n, first_time;
+    unsigned char * wvb = NULL;
+    void * wrkBuff = NULL;
+    int dpo = 0;
+    int bytchk = 0;
+    int group = 0;
+    int do_16 = 0;
+    int given_do_16 = 0;
+    uint64_t llba = 0;
+    int lba_given = 0;
+    uint32_t num_lb = 1;
+    uint32_t snum_lb = 1;
+    int repeat = 0;
+    int timeout = DEF_TIMEOUT_SECS;
+    int verbose = 0;
+    int64_t ll;
+    int wrprotect = 0;
+    const char * device_name = NULL;
+    const char * ifnp;
+    int has_filename = 0;
+    int ilen = -1;
+    int ifd = -1;
+    int ret = 1;
+    int b_p_lb = 512;
+    int tnum_lb_wr = 0;
+    char cmd_name[32];
+
+    ifnp = "";          /* keep MinGW quiet */
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "b:dg:hi:I:l:n:RSt:w:vV", long_options,
+                       &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'b':
+            /* Only bytchk=0 and =1 are meaningful for this command in
+             * sbc4r02 (not =2 nor =3) but that may change in the future. */
+            bytchk = sg_get_num(optarg);
+            if ((bytchk < 0) || (bytchk > 3))  {
+                pr2serr("argument to '--bytchk' expected to be 0 to 3\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'd':
+            dpo = 1;
+            break;
+        case 'g':
+            group = sg_get_num(optarg);
+            if ((group < 0) || (group > 31))  {
+                pr2serr("argument to '--group' expected to be 0 to 31\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'i':
+            ifnp = optarg;
+            has_filename = 1;
+            break;
+        case 'I':
+            ilen = sg_get_num(optarg);
+            if (-1 == ilen) {
+                pr2serr("bad argument to '--ilen'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'l':
+            if (lba_given) {
+                pr2serr("must have one and only one '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ll = sg_get_llnum(optarg);
+            if (ll < 0) {
+                pr2serr("bad argument to '--lba'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            llba = (uint64_t)ll;
+            ++lba_given;
+            break;
+        case 'n':
+            n = sg_get_num(optarg);
+            if (-1 == n) {
+                pr2serr("bad argument to '--num'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            num_lb = (uint32_t)n;
+            break;
+        case 'R':
+            ++repeat;
+            break;
+        case 'S':
+            do_16 = 1;
+            given_do_16 = 1;
+            break;
+        case 't':
+            timeout = sg_get_num(optarg);
+            if (timeout < 1) {
+                pr2serr("bad argument to '--timeout'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr(ME "version: %s\n", version_str);
+            return 0;
+        case 'w':
+            wrprotect = sg_get_num(optarg);
+            if ((wrprotect < 0) || (wrprotect > 7))  {
+                pr2serr("wrprotect (%d) is out of range ( < %d)\n", wrprotect,
+                        7);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+       }
+    }
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (! lba_given) {
+        pr2serr("need a --lba=LBA option\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (repeat) {
+        if (! has_filename) {
+            pr2serr("with '--repeat' need '--in=IF' option\n");
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (ilen < 1) {
+            pr2serr("with '--repeat' need '--ilen=ILEN' option\n");
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        } else {
+            b_p_lb = ilen / num_lb;
+            if (b_p_lb < 64) {
+                pr2serr("calculated %d bytes per logical block, too small\n",
+                        b_p_lb);
+                usage();
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        }
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose);
+    if (sg_fd < 0) {
+        pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if ((0 == do_16) && (llba > UINT_MAX))
+        do_16 = 1;
+    if ((0 == do_16) && (num_lb > 0xffff))
+        do_16 = 1;
+    snprintf(cmd_name, sizeof(cmd_name), "Write and verify(%d)",
+             (do_16 ? 16 : 10));
+    if (verbose && (0 == given_do_16) && do_16)
+        pr2serr("Switching to %s because LBA or NUM too large\n", cmd_name);
+    if (verbose) {
+        pr2serr("Issue %s to device %s\n\tilen=%d", cmd_name, device_name,
+                ilen);
+        if (ilen > 0)
+            pr2serr(" [0x%x]", ilen);
+        pr2serr(", lba=%" PRIu64 " [0x%" PRIx64 "]\n\twrprotect=%d, dpo=%d, "
+                "bytchk=%d, group=%d, repeat=%d\n", llba, llba, wrprotect,
+                dpo, bytchk, group, repeat);
+    }
+
+    first_time = 1;
+    do {
+        if (first_time) {
+            //If a file with data to write has been provided
+            if (has_filename) {
+                struct stat a_stat;
+
+                if ((1 == strlen(ifnp)) && ('-' == ifnp[0])) {
+                    ifd = STDIN_FILENO;
+                    ifnp = "<stdin>";
+                    if (verbose > 1)
+                        pr2serr("Reading input data from stdin\n");
+                } else {
+                    ifd = open_if(ifnp, 0);
+                    if (ifd < 0) {
+                        ret = -ifd;
+                        goto err_out;
+                    }
+                }
+                if (ilen < 1) {
+                    if (fstat(ifd, &a_stat) < 0) {
+                        pr2serr("Could not fstat(%s)\n", ifnp);
+                        goto err_out;
+                    }
+                    if (! S_ISREG(a_stat.st_mode)) {
+                        pr2serr("Cannot determine IF size, please give "
+                                "'--ilen='\n");
+                        goto err_out;
+                    }
+                    ilen = (int)a_stat.st_size;
+                    if (ilen < 1) {
+                        pr2serr("%s file size too small\n", ifnp);
+                        goto err_out;
+                    } else if (verbose)
+                        pr2serr("Using file size of %d bytes\n", ilen);
+                }
+                if (NULL == (wrkBuff = malloc(ilen))) {
+                    pr2serr(ME "out of memory\n");
+                    ret = SG_LIB_CAT_OTHER;
+                    goto err_out;
+                }
+                wvb = (unsigned char *)wrkBuff;
+                res = read(ifd, wvb, ilen);
+                if (res < 0) {
+                    pr2serr("Could not read from %s", ifnp);
+                    goto err_out;
+                }
+                if (res < ilen) {
+                    pr2serr("Read only %d bytes (expected %d) from %s\n", res,
+                            ilen, ifnp);
+                    if (repeat)
+                        pr2serr("Will scale subsequent pieces when repeat=1, "
+                                "but this is first\n");
+                    goto err_out;
+                }
+            } else {
+                if (ilen < 1) {
+                    if (verbose)
+                        pr2serr("Default write length to %d*%d=%d bytes\n",
+                                num_lb, 512, 512 * num_lb);
+                    ilen = 512 * num_lb;
+                }
+                if (NULL == (wrkBuff = malloc(ilen))) {
+                    pr2serr(ME "out of memory\n");
+                    ret = SG_LIB_CAT_OTHER;
+                    goto err_out;
+                }
+                wvb = (unsigned char *)wrkBuff;
+                /* Not sure about this: default contents to 0xff bytes */
+                memset(wrkBuff, 0xff, ilen);
+            }
+            first_time = 0;
+            snum_lb = num_lb;
+        } else {        /* repeat=1, first_time=0, must be reading file */
+            llba += snum_lb;
+            res = read(ifd, wvb, ilen);
+            if (res < 0) {
+                pr2serr("Could not read from %s", ifnp);
+                goto err_out;
+            } else {
+                if (verbose > 1)
+                pr2serr("Subsequent read from %s got %d bytes\n", ifnp, res);
+                if (0 == res)
+                    break;
+                if (res < ilen) {
+                    snum_lb = (uint32_t)(res / b_p_lb);
+                    n = res % b_p_lb;
+                    if (0 != n)
+                        pr2serr(">>> warning: ignoring last %d bytes of %s\n",
+                                n, ifnp);
+                    if (snum_lb < 1)
+                        break;
+                }
+            }
+        }
+        if (do_16)
+            res = sg_ll_write_verify16(sg_fd, wrprotect, dpo, bytchk, llba,
+                                       snum_lb, group, wvb, ilen, timeout,
+                                       verbose);
+        else
+            res = sg_ll_write_verify10(sg_fd, wrprotect, dpo, bytchk,
+                                       (unsigned int)llba, snum_lb, group,
+                                       wvb, ilen, timeout, verbose);
+        ret = res;
+        if (repeat && (0 == ret))
+            tnum_lb_wr += snum_lb;
+        if (ret || (snum_lb != num_lb))
+            break;
+    } while (repeat);
+
+err_out:
+    if (repeat)
+        pr2serr("%d [0x%x] logical blocks written, in total\n", tnum_lb_wr,
+                tnum_lb_wr);
+    if (wrkBuff)
+        free(wrkBuff);
+    if ((ifd >= 0) && (STDIN_FILENO != ifd))
+        close(ifd);
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    if (ret && (0 == verbose)) {
+        if (SG_LIB_CAT_INVALID_OP == ret)
+            pr2serr("%s command not supported\n", cmd_name);
+        else if (ret > 0)
+            pr2serr("%s, exit status %d\n", cmd_name, ret);
+        else if (ret < 0)
+            pr2serr("Some error occurred\n");
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sg_xcopy.c b/sg3_utils/src/sg_xcopy.c
new file mode 100644
index 0000000..a6a6ca1
--- /dev/null
+++ b/sg3_utils/src/sg_xcopy.c
@@ -0,0 +1,1842 @@
+/* A utility program for copying files. Similar to 'dd' but using
+ * the 'Extended Copy' command.
+ *
+ *  Copyright (c) 2011-2016 Hannes Reinecke, SUSE Labs
+ *
+ *  Largely taken from 'sg_dd', which has the
+ *
+ *  Copyright (C) 1999 - 2010 D. Gilbert and P. Allworth
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+
+   This program is a specialisation of the Unix "dd" command in which
+   either the input or the output file is a scsi generic device, raw
+   device, a block device or a normal file. The block size ('bs') is
+   assumed to be 512 if not given. This program complains if 'ibs' or
+   'obs' are given with a value that differs from 'bs' (or the default 512).
+   If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
+   not given or 'of=-' then stdout assumed.
+
+   A non-standard argument "bpt" (blocks per transfer) is added to control
+   the maximum number of blocks in each transfer. The default value is 128.
+   For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
+   in this case) is transferred to or from the sg device in a single SCSI
+   command.
+
+   This version is designed for the linux kernel 2.4, 2.6 and 3 series.
+*/
+
+#define _XOPEN_SOURCE 600
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <linux/major.h>
+#include <linux/fs.h>   /* <sys/mount.h> */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+static const char * version_str = "0.53 20160201";
+
+#define ME "sg_xcopy: "
+
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+#define EBUFF_SZ 512
+
+#define DEF_BLOCK_SIZE 512
+#define DEF_BLOCKS_PER_TRANSFER 128
+#define MAX_BLOCKS_PER_TRANSFER 65535
+
+#define DEF_MODE_RESP_LEN 252
+#define RW_ERR_RECOVERY_MP 1
+#define CACHING_MP 8
+#define CONTROL_MP 0xa
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define READ_CAP_REPLY_LEN 8
+#define RCAP16_REPLY_LEN 32
+
+#define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
+
+#ifndef RAW_MAJOR
+#define RAW_MAJOR 255   /*unlikey value */
+#endif
+
+#define SG_LIB_FLOCK_ERR 90
+
+/* In SPC-4 the cdb opcodes have more generic names */
+#define THIRD_PARTY_COPY_OUT_CMD 0x83
+#define THIRD_PARTY_COPY_IN_CMD 0x84
+
+/* Third party copy IN (opcode 0x84) and OUT (opcode 0x83) command service
+ * actions */
+#define SA_XCOPY_LID1           0x0     /* OUT, originate */
+#define SA_XCOPY_LID4           0x1     /* OUT, originate */
+#define SA_POP_TOK              0x10    /* OUT, originate */
+#define SA_WR_USING_TOK         0x11    /* OUT, originate */
+#define SA_COPY_ABORT           0x1C    /* OUT, abort */
+#define SA_COPY_STATUS_LID1     0x0     /* IN, retrieve */
+#define SA_COPY_DATA_LID1       0x1     /* IN, retrieve */
+#define SA_COPY_OP_PARAMS       0x3     /* IN, retrieve */
+#define SA_COPY_FAIL_DETAILS    0x4     /* IN, retrieve */
+#define SA_COPY_STATUS_LID4     0x5     /* IN, retrieve */
+#define SA_COPY_DATA_LID4       0x6     /* IN, retrieve */
+#define SA_ROD_TOK_INFO         0x7     /* IN, retrieve */
+#define SA_ALL_ROD_TOKS         0x8     /* IN, retrieve */
+
+#define DEF_3PC_OUT_TIMEOUT (10 * 60)   /* is 10 minutes enough? */
+#define DEF_GROUP_NUM 0x0
+
+#define VPD_DEVICE_ID 0x83
+#define VPD_3PARTY_COPY 0x8f
+
+#define FT_OTHER 1              /* filetype is probably normal */
+#define FT_SG 2                 /* filetype is sg or bsg char device */
+#define FT_RAW 4                /* filetype is raw char device */
+#define FT_DEV_NULL 8           /* either "/dev/null" or "." as filename */
+#define FT_ST 16                /* filetype is st char device (tape) */
+#define FT_BLOCK 32             /* filetype is block device */
+#define FT_FIFO 64              /* filetype is a fifo (name pipe) */
+#define FT_ERROR 128            /* couldn't "stat" file */
+
+#define TD_FC_WWPN 1
+#define TD_FC_PORT 2
+#define TD_FC_WWPN_AND_PORT 4
+#define TD_SPI 8
+#define TD_VPD 16
+#define TD_IPV4 32
+#define TD_ALIAS 64
+#define TD_RDMA 128
+#define TD_FW 256
+#define TD_SAS 512
+#define TD_IPV6 1024
+#define TD_IP_COPY_SERVICE 2048
+#define TD_ROD 4096
+
+#define XCOPY_TO_SRC "XCOPY_TO_SRC"
+#define XCOPY_TO_DST "XCOPY_TO_DST"
+#define DEF_XCOPY_SRC0_DST1 1
+
+#define DEV_NULL_MINOR_NUM 3
+
+#define MIN_RESERVED_SIZE 8192
+
+#define MAX_UNIT_ATTENTIONS 10
+#define MAX_ABORTED_CMDS 256
+
+static int64_t dd_count = -1;
+static int64_t in_full = 0;
+static int in_partial = 0;
+static int64_t out_full = 0;
+static int out_partial = 0;
+
+static int do_time = 0;
+static int verbose = 0;
+static int start_tm_valid = 0;
+static struct timeval start_tm;
+static int blk_sz = 0;
+static int priority = 1;
+static int list_id_usage = -1;
+
+static int xcopy_flag_cat = 0;
+static int xcopy_flag_dc = 0;
+
+struct xcopy_fp_t {
+    char fname[INOUTF_SZ];
+    dev_t devno;
+    int sg_type;
+    int sg_fd;
+    unsigned long min_bytes;
+    unsigned long max_bytes;
+    int64_t num_sect;
+    int sect_sz;
+    int append;
+    int excl;
+    int flock;
+    int pad;     /* Data descriptor PAD bit (residual data treatment) */
+    int pdt;     /* Peripheral device type */
+    int xcopy_given;
+};
+
+static struct xcopy_fp_t ixcf;
+static struct xcopy_fp_t oxcf;
+
+static const char * read_cap_str = "Read capacity";
+static const char * rec_copy_op_params_str = "Receive copy operating "
+                                             "parameters";
+
+static void calc_duration_throughput(int contin);
+
+
+static void
+install_handler(int sig_num, void (*sig_handler) (int sig))
+{
+    struct sigaction sigact;
+    sigaction (sig_num, NULL, &sigact);
+    if (sigact.sa_handler != SIG_IGN)
+    {
+        sigact.sa_handler = sig_handler;
+        sigemptyset (&sigact.sa_mask);
+        sigact.sa_flags = 0;
+        sigaction (sig_num, &sigact, NULL);
+    }
+}
+
+static void
+print_stats(const char * str)
+{
+    if (0 != dd_count)
+        pr2serr("  remaining block count=%" PRId64 "\n", dd_count);
+    pr2serr("%s%" PRId64 "+%d records in\n", str, in_full - in_partial,
+            in_partial);
+    pr2serr("%s%" PRId64 "+%d records out\n", str, out_full - out_partial,
+            out_partial);
+}
+
+static void
+interrupt_handler(int sig)
+{
+    struct sigaction sigact;
+
+    sigact.sa_handler = SIG_DFL;
+    sigemptyset(&sigact.sa_mask);
+    sigact.sa_flags = 0;
+    sigaction(sig, &sigact, NULL);
+    pr2serr("Interrupted by signal,");
+    if (do_time)
+        calc_duration_throughput(0);
+    print_stats("");
+    kill(getpid (), sig);
+}
+
+static void
+siginfo_handler(int sig)
+{
+    if (sig) { ; }      /* unused, dummy to suppress warning */
+    pr2serr("Progress report, continuing ...\n");
+    if (do_time)
+        calc_duration_throughput(1);
+    print_stats("  ");
+}
+
+static int bsg_major_checked = 0;
+static int bsg_major = 0;
+
+static void
+find_bsg_major(void)
+{
+    const char * proc_devices = "/proc/devices";
+    FILE *fp;
+    char a[128];
+    char b[128];
+    char * cp;
+    int n;
+
+    if (NULL == (fp = fopen(proc_devices, "r"))) {
+        if (verbose)
+            pr2serr("fopen %s failed: %s\n", proc_devices, strerror(errno));
+        return;
+    }
+    while ((cp = fgets(b, sizeof(b), fp))) {
+        if ((1 == sscanf(b, "%126s", a)) &&
+            (0 == memcmp(a, "Character", 9)))
+            break;
+    }
+    while (cp && (cp = fgets(b, sizeof(b), fp))) {
+        if (2 == sscanf(b, "%d %126s", &n, a)) {
+            if (0 == strcmp("bsg", a)) {
+                bsg_major = n;
+                break;
+            }
+        } else
+            break;
+    }
+    if (verbose > 5) {
+        if (cp)
+            pr2serr("found bsg_major=%d\n", bsg_major);
+        else
+            pr2serr("found no bsg char device in %s\n", proc_devices);
+    }
+    fclose(fp);
+}
+
+/* Returns a file descriptor on success (0 or greater), -1 for an open
+ * error, -2 for a standard INQUIRY problem. */
+static int
+open_sg(struct xcopy_fp_t * fp, int verbose)
+{
+    int devmajor, devminor, offset;
+    struct sg_simple_inquiry_resp sir;
+    char ebuff[EBUFF_SZ];
+    int len;
+
+    devmajor = major(fp->devno);
+    devminor = minor(fp->devno);
+
+    if (fp->sg_type & FT_SG) {
+        snprintf(ebuff, EBUFF_SZ, "%s", fp->fname);
+    } else if (fp->sg_type & FT_BLOCK || fp->sg_type & FT_OTHER) {
+        int fd;
+
+        snprintf(ebuff, EBUFF_SZ, "/sys/dev/block/%d:%d/partition",
+                 devmajor, devminor);
+        if ((fd = open(ebuff, O_RDONLY)) >= 0) {
+            ebuff[EBUFF_SZ - 1] = '\0';
+            len = read(fd, ebuff, EBUFF_SZ - 1);
+            if (len < 0) {
+                perror("read partition");
+            } else {
+                offset = strtoul(ebuff, NULL, 10);
+                devminor -= offset;
+            }
+            close(fd);
+        }
+        snprintf(ebuff, EBUFF_SZ, "/dev/block/%d:%d", devmajor, devminor);
+    } else {
+        snprintf(ebuff, EBUFF_SZ, "/dev/char/%d:%d", devmajor, devminor);
+    }
+    fp->sg_fd = sg_cmds_open_device(ebuff, 0, verbose);
+    if (fp->sg_fd < 0) {
+        snprintf(ebuff, EBUFF_SZ,
+                 ME "could not open %s device %d:%d for sg",
+                 fp->sg_type & FT_BLOCK ? "block" : "char",
+                 devmajor, devminor);
+        perror(ebuff);
+        return -1;
+    }
+    if (sg_simple_inquiry(fp->sg_fd, &sir, 0, verbose)) {
+        pr2serr("INQUIRY failed on %s\n", ebuff);
+        sg_cmds_close_device(fp->sg_fd);
+        fp->sg_fd = -1;
+        return -2;
+    }
+
+    fp->pdt = sir.peripheral_type;
+    if (verbose)
+        pr2serr("    %s: %.8s  %.16s  %.4s  [pdt=%d, 3pc=%d]\n", fp->fname,
+                sir.vendor, sir.product, sir.revision, fp->pdt,
+                !! (0x8 & sir.byte_5));
+
+    return fp->sg_fd;
+}
+
+static int
+dd_filetype(struct xcopy_fp_t * fp)
+{
+    struct stat st;
+    size_t len = strlen(fp->fname);
+
+    if ((1 == len) && ('.' == fp->fname[0]))
+        return FT_DEV_NULL;
+    if (stat(fp->fname, &st) < 0)
+        return FT_ERROR;
+    if (S_ISCHR(st.st_mode)) {
+        fp->devno = st.st_rdev;
+        /* major() and minor() defined in sys/sysmacros.h */
+        if ((MEM_MAJOR == major(st.st_rdev)) &&
+            (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
+            return FT_DEV_NULL;
+        if (RAW_MAJOR == major(st.st_rdev))
+            return FT_RAW;
+        if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
+            return FT_SG;
+        if (SCSI_TAPE_MAJOR == major(st.st_rdev))
+            return FT_ST;
+        if (! bsg_major_checked) {
+            bsg_major_checked = 1;
+            find_bsg_major();
+        }
+        if (bsg_major == (int)major(st.st_rdev))
+            return FT_SG;
+    } else if (S_ISBLK(st.st_mode)) {
+        fp->devno = st.st_rdev;
+        return FT_BLOCK;
+    } else if (S_ISFIFO(st.st_mode)) {
+        fp->devno = st.st_dev;
+        return FT_FIFO;
+    }
+    fp->devno = st.st_dev;
+    return FT_OTHER | FT_BLOCK;
+}
+
+
+static char *
+dd_filetype_str(int ft, char * buff)
+{
+    int off = 0;
+
+    if (FT_DEV_NULL & ft)
+        off += snprintf(buff + off, 32, "null device ");
+    if (FT_SG & ft)
+        off += snprintf(buff + off, 32, "SCSI generic (sg) device ");
+    if (FT_BLOCK & ft)
+        off += snprintf(buff + off, 32, "block device ");
+    if (FT_FIFO & ft)
+        off += snprintf(buff + off, 32, "fifo (named pipe) ");
+    if (FT_ST & ft)
+        off += snprintf(buff + off, 32, "SCSI tape device ");
+    if (FT_RAW & ft)
+        off += snprintf(buff + off, 32, "raw device ");
+    if (FT_OTHER & ft)
+        off += snprintf(buff + off, 32, "other (perhaps ordinary file) ");
+    if (FT_ERROR & ft)
+        off += snprintf(buff + off, 32, "unable to 'stat' file ");
+    return buff;
+}
+
+static int
+simplified_ft(const struct xcopy_fp_t * xfp)
+{
+    int ftype = xfp->sg_type;
+
+    switch (ftype) {
+    case FT_BLOCK:
+    case FT_ST:
+    case FT_OTHER:      /* typically regular file */
+    case FT_DEV_NULL:
+    case FT_FIFO:
+    case FT_ERROR:
+        return ftype;
+    default:
+        if (FT_SG & ftype) {
+            if ((0 == xfp->pdt) || (0xe == xfp->pdt)) /* D-A or RBC */
+            return FT_BLOCK;
+        else if (0x1 == xfp->pdt)
+            return FT_ST;
+        }
+        return FT_OTHER;
+    }
+}
+
+static int
+seg_desc_from_dd_type(int in_ft, int in_off, int out_ft, int out_off)
+{
+    int desc_type = -1;
+
+    switch (in_ft) {
+    case FT_BLOCK:
+        switch (out_ft) {
+        case FT_ST:
+            if (out_off)
+                break;
+
+            if (in_off)
+                desc_type = 0x8;
+            else
+                desc_type = 0;
+            break;
+        case FT_BLOCK:
+            if (in_off || out_off)
+                desc_type = 0xA;
+            else
+                desc_type = 2;
+            break;
+        default:
+            break;
+        }
+        break;
+    case FT_ST:
+        if (in_off)
+            break;
+
+        switch (out_ft) {
+        case FT_ST:
+            if (!out_off) {
+                desc_type = 3;
+                break;
+            }
+            break;
+        case FT_BLOCK:
+            if (out_off)
+                desc_type = 9;
+            else
+                desc_type = 3;
+            break;
+        case FT_DEV_NULL:
+            desc_type = 6;
+            break;
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+
+    return desc_type;
+}
+
+static void
+usage(int n_help)
+{
+    if (n_help < 2)
+        goto primary_help;
+    else
+        goto secondary_help;
+
+primary_help:
+    pr2serr("Usage: "
+            "sg_xcopy  [bpt=BPT] [bs=BS] [cat=0|1] [count=COUNT] [dc=0|1] "
+            "[ibs=BS]\n"
+            "                 [id_usage=hold|discard|disable] [if=IFILE] "
+            "[iflag=FLAGS]\n"
+            "                 [list_id=ID] [obs=BS] [of=OFILE] [oflag=FLAGS] "
+            "[prio=PRIO]\n"
+            "                 [seek=SEEK] [skip=SKIP] [time=0|1] "
+            "[verbose=VERB] [--help]\n"
+            "                 [--on_dst|--on_src] [--verbose] [--version]\n\n"
+            "  where:\n"
+            "    bpt         is blocks_per_transfer (default: 128)\n"
+            "    bs          block size (default is 512)\n");
+    pr2serr("    cat         xcopy segment descriptor CAT bit (default: "
+            "0)\n"
+            "    count       number of blocks to copy (def: device size)\n"
+            "    dc          xcopy segment descriptor DC bit (default: 0)\n"
+            "    ibs         input block size (if given must be same as "
+            "'bs=')\n"
+            "    id_usage    sets list_id_usage field to hold (0), "
+            "discard (2) or\n"
+            "                disable (3)\n"
+            "    if          file or device to read from (def: stdin)\n"
+            "    iflag       comma separated list of flags applying to "
+            "IFILE\n"
+            "    list_id     sets list_id field to ID (default: 1 or 0)\n"
+            "    obs         output block size (if given must be same as "
+            "'bs=')\n"
+            "    of          file or device to write to (def: stdout), "
+            "OFILE of '.'\n");
+    pr2serr("                treated as /dev/null\n"
+            "    oflag       comma separated list of flags applying to "
+            "OFILE\n"
+            "    prio        set xcopy priority field to PRIO (def: 1)\n"
+            "    seek        block position to start writing to OFILE\n"
+            "    skip        block position to start reading from IFILE\n"
+            "    time        0->no timing(def), 1->time plus calculate "
+            "throughput\n"
+            "    verbose     0->quiet(def), 1->some noise, 2->more noise, "
+            "etc\n"
+            "    --help      print out this usage message then exit\n"
+            "    --on_dst    send XCOPY command to OFILE\n"
+            "    --on_src    send XCOPY command to IFILE\n"
+            "    --verbose   same action as verbose=1\n"
+            "    --version   print version information then exit\n\n"
+            "Copy from IFILE to OFILE, similar to dd command; "
+            "but using the SCSI\nEXTENDED COPY (XCOPY(LID1)) command. For "
+            "list of flags, use '-hh'.\n");
+    return;
+
+secondary_help:
+    pr2serr("FLAGS:\n"
+            "  append (o)     ignored\n"
+            "  excl           open corresponding device with O_EXCL\n"
+            "  flock          call flock(LOCK_EX|LOCK_NB)\n"
+            "  null           does nothing, placeholder\n"
+            "  pad            set xcopy data descriptor PAD bit on\n"
+            "                 corresponding device\n"
+            "  xcopy          send XCOPY command to corresponding device\n"
+            "\n"
+            "ENVIRONMENT VARIABLES:\n"
+            "  XCOPY_TO_DST   send XCOPY command to OFILE (destination) "
+            "if no other\n"
+            "                 indication\n"
+            "  XCOPY_TO_SRC   send XCOPY command to IFILE (source)\n"
+           );
+}
+
+static int
+scsi_encode_seg_desc(unsigned char *seg_desc, int seg_desc_type,
+                     int64_t num_blk, uint64_t src_lba, uint64_t dst_lba)
+{
+    int seg_desc_len = 0;
+
+    seg_desc[0] = seg_desc_type;
+    seg_desc[1] = xcopy_flag_cat | (xcopy_flag_dc << 1);
+    if (seg_desc_type == 0x02) {
+        seg_desc_len = 0x18;
+        seg_desc[4] = 0;
+        seg_desc[5] = 0; /* Source target index */
+        seg_desc[7] = 1; /* Destination target index */
+        sg_put_unaligned_be16(num_blk, seg_desc + 10);
+        sg_put_unaligned_be64(src_lba, seg_desc + 12);
+        sg_put_unaligned_be64(dst_lba, seg_desc + 20);
+    }
+    sg_put_unaligned_be16(seg_desc_len, seg_desc + 2);
+    return seg_desc_len + 4;
+}
+
+static int
+scsi_extended_copy(int sg_fd, unsigned char list_id,
+                   unsigned char *src_desc, int src_desc_len,
+                   unsigned char *dst_desc, int dst_desc_len,
+                   int seg_desc_type, int64_t num_blk,
+                   uint64_t src_lba, uint64_t dst_lba)
+{
+    unsigned char xcopyBuff[256];
+    int desc_offset = 16;
+    int seg_desc_len;
+    int verb, res;
+    char b[80];
+
+    verb = (verbose > 1) ? (verbose - 2) : 0;
+    memset(xcopyBuff, 0, 256);
+    xcopyBuff[0] = list_id;
+    xcopyBuff[1] = (list_id_usage << 3) | priority;
+    xcopyBuff[2] = 0;
+    xcopyBuff[3] = src_desc_len + dst_desc_len; /* Two target descriptors */
+    memcpy(xcopyBuff + desc_offset, src_desc, src_desc_len);
+    desc_offset += src_desc_len;
+    memcpy(xcopyBuff + desc_offset, dst_desc, dst_desc_len);
+    desc_offset += dst_desc_len;
+    seg_desc_len = scsi_encode_seg_desc(xcopyBuff + desc_offset,
+                                        seg_desc_type, num_blk,
+                                        src_lba, dst_lba);
+    xcopyBuff[11] = seg_desc_len; /* One segment descriptor */
+    desc_offset += seg_desc_len;
+    /* set noisy so if a UA happens it will be printed to stderr */
+    res = sg_ll_3party_copy_out(sg_fd, SA_XCOPY_LID1, list_id,
+                                DEF_GROUP_NUM, DEF_3PC_OUT_TIMEOUT,
+                                xcopyBuff, desc_offset, 1, verb);
+    if (res) {
+        sg_get_category_sense_str(res, sizeof(b), b, verb);
+        pr2serr("Xcopy(LID1): %s\n", b);
+    }
+    return res;
+}
+
+/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
+static int
+scsi_read_capacity(struct xcopy_fp_t *xfp)
+{
+    int res;
+    unsigned int ui;
+    unsigned char rcBuff[RCAP16_REPLY_LEN];
+    int verb;
+    char b[80];
+
+    verb = (verbose ? verbose - 1: 0);
+    res = sg_ll_readcap_10(xfp->sg_fd, 0, 0, rcBuff,
+                           READ_CAP_REPLY_LEN, 1, verb);
+    if (0 != res) {
+        sg_get_category_sense_str(res, sizeof(b), b, verb);
+        pr2serr("Read capacity(10): %s\n", b);
+        return res;
+    }
+
+    if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
+        (0xff == rcBuff[3])) {
+        uint64_t ls;
+
+        res = sg_ll_readcap_16(xfp->sg_fd, 0, 0, rcBuff,
+                               RCAP16_REPLY_LEN, 1, verb);
+        if (0 != res) {
+            sg_get_category_sense_str(res, sizeof(b), b, verb);
+            pr2serr("Read capacity(16): %s\n", b);
+            return res;
+        }
+        ls = sg_get_unaligned_be64(rcBuff + 0);
+        xfp->num_sect = (int64_t)(ls + 1);
+        xfp->sect_sz = sg_get_unaligned_be32(rcBuff + 8);
+    } else {
+        ui = sg_get_unaligned_be32(rcBuff + 0);
+        /* take care not to sign extend values > 0x7fffffff */
+        xfp->num_sect = (int64_t)ui + 1;
+        xfp->sect_sz = sg_get_unaligned_be32(rcBuff + 4);
+    }
+    if (verbose)
+        pr2serr("    %s: number of blocks=%" PRId64 " [0x%" PRIx64 "], block "
+                "size=%d\n", xfp->fname, xfp->num_sect, xfp->num_sect,
+                xfp->sect_sz);
+    return 0;
+}
+
+static int
+scsi_operating_parameter(struct xcopy_fp_t *xfp, int is_target)
+{
+    int res, ftype, snlid;
+    unsigned char rcBuff[256];
+    uint32_t rcBuffLen = 256, len, n, td_list = 0;
+    uint32_t num, max_target_num, max_segment_num, max_segment_len;
+    uint32_t max_desc_len, max_inline_data, held_data_limit;
+    int verb, valid = 0;
+    char b[80];
+
+    verb = (verbose ? verbose - 1: 0);
+    ftype = xfp->sg_type;
+    if (FT_SG & ftype) {
+        if ((0 == xfp->pdt) || (0xe == xfp->pdt)) /* direct-access or RBC */
+            ftype |= FT_BLOCK;
+        else if (0x1 == xfp->pdt)
+            ftype |= FT_ST;
+    }
+    res = sg_ll_receive_copy_results(xfp->sg_fd, SA_COPY_OP_PARAMS, 0, rcBuff,
+                                     rcBuffLen, 1, verb);
+    if (0 != res) {
+        sg_get_category_sense_str(res, sizeof(b), b, verb);
+        pr2serr("Xcopy operating parameters: %s\n", b);
+        return -res;
+    }
+
+    len = sg_get_unaligned_be32(rcBuff + 0);
+    if (len > rcBuffLen) {
+        pr2serr("  <<report len %d > %d too long for internal buffer, output "
+                "truncated\n", len, rcBuffLen);
+    }
+    if (verbose > 2) {
+        pr2serr("\nOutput response in hex:\n");
+        dStrHexErr((const char *)rcBuff, len, 1);
+    }
+    snlid = rcBuff[4] & 0x1;
+    max_target_num = sg_get_unaligned_be16(rcBuff + 8);
+    max_segment_num = sg_get_unaligned_be16(rcBuff + 10);
+    max_desc_len = sg_get_unaligned_be32(rcBuff + 12);
+    max_segment_len = sg_get_unaligned_be32(rcBuff + 16);
+    xfp->max_bytes = max_segment_len ? max_segment_len : ULONG_MAX;
+    max_inline_data = sg_get_unaligned_be32(rcBuff + 20);
+    if (verbose) {
+        pr2serr(" >> %s response:\n", rec_copy_op_params_str);
+        pr2serr("    Support No List IDentifier (SNLID): %d\n", snlid);
+        pr2serr("    Maximum target descriptor count: %u\n",
+                (unsigned int)max_target_num);
+        pr2serr("    Maximum segment descriptor count: %u\n",
+                (unsigned int)max_segment_num);
+        pr2serr("    Maximum descriptor list length: %u\n",
+                (unsigned int)max_desc_len);
+        pr2serr("    Maximum segment length: %u\n",
+                (unsigned int)max_segment_len);
+        pr2serr("    Maximum inline data length: %u\n",
+                (unsigned int)max_inline_data);
+    }
+    held_data_limit = sg_get_unaligned_be32(rcBuff + 24);
+    if (list_id_usage < 0) {
+        if (!held_data_limit)
+            list_id_usage = 2;
+        else
+            list_id_usage = 0;
+    }
+    if (verbose) {
+        pr2serr("    Held data limit: %u (list_id_usage: %d)\n",
+                (unsigned int)held_data_limit, list_id_usage);
+        num = sg_get_unaligned_be32(rcBuff + 28);
+        pr2serr("    Maximum stream device transfer size: %u\n",
+                (unsigned int)num);
+        pr2serr("    Maximum concurrent copies: %u\n", rcBuff[36]);
+        if (rcBuff[37] > 30)
+            pr2serr("    Data segment granularity: 2**%u bytes\n",
+                    rcBuff[37]);
+        else
+            pr2serr("    Data segment granularity: %u bytes\n",
+                    1 << rcBuff[37]);
+        if (rcBuff[38] > 30)
+            pr2serr("    Inline data granularity: 2**%u bytes\n", rcBuff[38]);
+        else
+            pr2serr("    Inline data granularity: %u bytes\n",
+                    1 << rcBuff[38]);
+        if (rcBuff[39] > 30)
+            pr2serr("    Held data granularity: 2**%u bytes\n",
+                    1 << rcBuff[39]);
+        else
+            pr2serr("    Held data granularity: %u bytes\n", 1 << rcBuff[39]);
+
+        pr2serr("    Implemented descriptor list:\n");
+    }
+    xfp->min_bytes = 1 << rcBuff[37];
+
+    for (n = 0; n < rcBuff[43]; n++) {
+        switch(rcBuff[44 + n]) {
+        case 0x00: /* copy block to stream device */
+            if (!is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (is_target && (ftype & FT_ST))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy Block to Stream device\n");
+            break;
+        case 0x01: /* copy stream to block device */
+            if (!is_target && (ftype & FT_ST))
+                valid++;
+            if (is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy Stream to Block device\n");
+            break;
+        case 0x02: /* copy block to block device */
+            if (!is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy Block to Block device\n");
+            break;
+        case 0x03: /* copy stream to stream device */
+            if (!is_target && (ftype & FT_ST))
+                valid++;
+            if (is_target && (ftype & FT_ST))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy Stream to Stream device\n");
+            break;
+        case 0x04: /* copy inline data to stream device */
+            if (!is_target && (ftype & FT_OTHER))
+                valid++;
+            if (is_target && (ftype & FT_ST))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy inline data to Stream device\n");
+            break;
+        case 0x05: /* copy embedded data to stream device */
+            if (!is_target && (ftype & FT_OTHER))
+                valid++;
+            if (is_target && (ftype & FT_ST))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy embedded data to Stream device\n");
+            break;
+        case 0x06: /* Read from stream device and discard */
+            if (!is_target && (ftype & FT_ST))
+                valid++;
+            if (is_target && (ftype & FT_DEV_NULL))
+                valid++;
+            if (verbose)
+                pr2serr("        Read from stream device and discard\n");
+            break;
+        case 0x07: /* Verify block or stream device operation */
+            if (!is_target && (ftype & (FT_ST | FT_BLOCK)))
+                valid++;
+            if (is_target && (ftype & (FT_ST | FT_BLOCK)))
+                valid++;
+            if (verbose)
+                pr2serr("        Verify block or stream device operation\n");
+            break;
+        case 0x08: /* copy block device with offset to stream device */
+            if (!is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (is_target && (ftype & FT_ST))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy block device with offset to stream "
+                       "device\n");
+            break;
+        case 0x09: /* copy stream device to block device with offset */
+            if (!is_target && (ftype & FT_ST))
+                valid++;
+            if (is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy stream device to block device with "
+                       "offset\n");
+            break;
+        case 0x0a: /* copy block device with offset to block device with
+                    * offset */
+            if (!is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy block device with offset to block "
+                       "device with offset\n");
+            break;
+        case 0x0b: /* copy block device to stream device and hold data */
+            if (!is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (is_target && (ftype & FT_ST))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy block device to stream device and hold "
+                       "data\n");
+            break;
+        case 0x0c: /* copy stream device to block device and hold data */
+            if (!is_target && (ftype & FT_ST))
+                valid++;
+            if (is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy stream device to block device and hold "
+                       "data\n");
+            break;
+        case 0x0d: /* copy block device to block device and hold data */
+            if (!is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (is_target && (ftype & FT_BLOCK))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy block device to block device and hold "
+                       "data\n");
+            break;
+        case 0x0e: /* copy stream device to stream device and hold data */
+            if (!is_target && (ftype & FT_ST))
+                valid++;
+            if (is_target && (ftype & FT_ST))
+                valid++;
+            if (verbose)
+                pr2serr("        Copy block device to block device and hold "
+                       "data\n");
+            break;
+        case 0x0f: /* read from stream device and hold data */
+            if (!is_target && (ftype & FT_ST))
+                valid++;
+            if (is_target && (ftype & FT_DEV_NULL))
+                valid++;
+            if (verbose)
+                pr2serr("        Read from stream device and hold data\n");
+            break;
+        case 0xe0: /* FC N_Port_Name */
+            if (verbose)
+                pr2serr("        FC N_Port_Name target descriptor\n");
+            td_list |= TD_FC_WWPN;
+            break;
+        case 0xe1: /* FC Port_ID */
+            if (verbose)
+                pr2serr("        FC Port_ID target descriptor\n");
+            td_list |= TD_FC_PORT;
+            break;
+        case 0xe2: /* FC N_Port_ID with N_Port_Name checking */
+            if (verbose)
+                pr2serr("        FC N_Port_ID with N_Port_Name target "
+                       "descriptor\n");
+            td_list |= TD_FC_WWPN_AND_PORT;
+            break;
+        case 0xe3: /* Parallel Interface T_L  */
+            if (verbose)
+                pr2serr("        SPI T_L target descriptor\n");
+            td_list |= TD_SPI;
+            break;
+        case 0xe4: /* identification descriptor */
+            if (verbose)
+                pr2serr("        Identification target descriptor\n");
+            td_list |= TD_VPD;
+            break;
+        case 0xe5: /* IPv4  */
+            if (verbose)
+                pr2serr("        IPv4 target descriptor\n");
+            td_list |= TD_IPV4;
+            break;
+        case 0xe6: /* Alias */
+            if (verbose)
+                pr2serr("        Alias target descriptor\n");
+            td_list |= TD_ALIAS;
+            break;
+        case 0xe7: /* RDMA */
+            if (verbose)
+                pr2serr("        RDMA target descriptor\n");
+            td_list |= TD_RDMA;
+            break;
+        case 0xe8: /* FireWire */
+            if (verbose)
+                pr2serr("        IEEE 1394 target descriptor\n");
+            td_list |= TD_FW;
+            break;
+        case 0xe9: /* SAS */
+            if (verbose)
+                pr2serr("        SAS target descriptor\n");
+            td_list |= TD_SAS;
+            break;
+        case 0xea: /* IPv6 */
+            if (verbose)
+                pr2serr("        IPv6 target descriptor\n");
+            td_list |= TD_IPV6;
+            break;
+        case 0xeb: /* IP Copy Service */
+            if (verbose)
+                pr2serr("        IP Copy Service target descriptor\n");
+            td_list |= TD_IP_COPY_SERVICE;
+            break;
+        case 0xfe: /* ROD */
+            if (verbose)
+                pr2serr("        ROD target descriptor\n");
+            td_list |= TD_ROD;
+            break;
+        default:
+            pr2serr(">> Unhandled target descriptor 0x%02x\n",
+                    rcBuff[44 + n]);
+            break;
+        }
+    }
+    if (!valid) {
+        pr2serr(">> no matching target descriptor supported\n");
+        td_list = 0;
+    }
+    return td_list;
+}
+
+static void
+decode_designation_descriptor(const unsigned char * ucp, int i_len)
+{
+    char c[2048];
+
+    sg_get_designation_descriptor_str(NULL, ucp, i_len, 1, verbose,
+                                      sizeof(c), c);
+    pr2serr("%s", c);
+}
+
+static int
+desc_from_vpd_id(int sg_fd, unsigned char *desc, int desc_len,
+                 unsigned int block_size, int pad)
+{
+    int res, verb;
+    unsigned char rcBuff[256], *ucp, *best = NULL;
+    unsigned int len = 254;
+    int off = -1, u, i_len, best_len = 0, assoc, desig, f_desig = 0;
+    char b[80];
+
+    verb = (verbose ? verbose - 1: 0);
+    memset(rcBuff, 0xff, len);
+    res = sg_ll_inquiry(sg_fd, 0, 1, VPD_DEVICE_ID, rcBuff, 4, 1, verb);
+    if (0 != res) {
+        if (SG_LIB_CAT_ILLEGAL_REQ == res)
+            pr2serr("Device identification VPD page not found\n");
+        else {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("VPD inquiry (Device ID): %s\n", b);
+            pr2serr("   try again with '-vv'\n");
+        }
+        return res;
+    } else if (rcBuff[1] != VPD_DEVICE_ID) {
+        pr2serr("invalid VPD response\n");
+        return SG_LIB_CAT_MALFORMED;
+    }
+    len = sg_get_unaligned_be16(rcBuff + 2) + 4;
+    res = sg_ll_inquiry(sg_fd, 0, 1, VPD_DEVICE_ID, rcBuff, len, 1, verb);
+    if (0 != res) {
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("VPD inquiry (Device ID): %s\n", b);
+        return res;
+    } else if (rcBuff[1] != VPD_DEVICE_ID) {
+        pr2serr("invalid VPD response\n");
+        return SG_LIB_CAT_MALFORMED;
+    }
+    if (verbose > 2) {
+        pr2serr("Output response in hex:\n");
+        dStrHexErr((const char *)rcBuff, len, 1);
+    }
+
+    while ((u = sg_vpd_dev_id_iter(rcBuff + 4, len - 4, &off, 0, -1, -1)) ==
+           0) {
+        ucp = rcBuff + 4 + off;
+        i_len = ucp[3];
+        if (((unsigned int)off + i_len + 4) > len) {
+            pr2serr("    VPD page error: designator length %d longer "
+                    "than\n     remaining response length=%d\n", i_len,
+                    (len - off));
+            return SG_LIB_CAT_MALFORMED;
+        }
+        assoc = ((ucp[1] >> 4) & 0x3);
+        desig = (ucp[1] & 0xf);
+        if (verbose > 2)
+            pr2serr("    Desc %d: assoc %u desig %u len %d\n", off, assoc,
+                    desig, i_len);
+        /* Descriptor must be less than 16 bytes */
+        if (i_len > 16)
+            continue;
+        if (desig == 3) {
+            best = ucp;
+            best_len = i_len;
+            break;
+        }
+        if (desig == 2) {
+            if (!best || f_desig < 2) {
+                best = ucp;
+                best_len = i_len;
+                f_desig = 2;
+            }
+        } else if (desig == 1) {
+            if (!best || f_desig == 0) {
+                best = ucp;
+                best_len = i_len;
+                f_desig = desig;
+            }
+        } else if (desig == 0) {
+            if (!best) {
+                best = ucp;
+                best_len = i_len;
+                f_desig = desig;
+            }
+        }
+    }
+    if (best) {
+        if (verbose)
+            decode_designation_descriptor(best, best_len);
+        if (best_len + 4 < desc_len) {
+            memset(desc, 0, 32);
+            desc[0] = 0xe4;
+            memcpy(desc + 4, best, best_len + 4);
+            desc[4] &= 0x1f;
+            desc[28] = pad << 2;
+            sg_put_unaligned_be24((uint32_t)block_size, desc + 29);
+            if (verbose > 3) {
+                pr2serr("Descriptor in hex (bs %d):\n", block_size);
+                dStrHexErr((const char *)desc, 32, 1);
+            }
+            return 32;
+        }
+        return  best_len + 8;
+    }
+    return 0;
+}
+
+static void
+calc_duration_throughput(int contin)
+{
+    struct timeval end_tm, res_tm;
+    double a, b;
+    int64_t blks;
+
+    if (start_tm_valid && (start_tm.tv_sec || start_tm.tv_usec)) {
+        blks = (in_full > out_full) ? in_full : out_full;
+        gettimeofday(&end_tm, NULL);
+        res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+        res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+        if (res_tm.tv_usec < 0) {
+            --res_tm.tv_sec;
+            res_tm.tv_usec += 1000000;
+        }
+        a = res_tm.tv_sec;
+        a += (0.000001 * res_tm.tv_usec);
+        b = (double)blk_sz * blks;
+        pr2serr("time to transfer data%s: %d.%06d secs",
+                (contin ? " so far" : ""), (int)res_tm.tv_sec,
+                (int)res_tm.tv_usec);
+        if ((a > 0.00001) && (b > 511))
+            pr2serr(" at %.2f MB/sec\n", b / (a * 1000000.0));
+        else
+            pr2serr("\n");
+    }
+}
+
+/* Process arguments given to 'iflag=" or 'oflag=" options. Returns 0
+ * on success, 1 on error. */
+static int
+process_flags(const char * arg, struct xcopy_fp_t * fp)
+{
+    char buff[256];
+    char * cp;
+    char * np;
+
+    strncpy(buff, arg, sizeof(buff) - 1);
+    buff[sizeof(buff) - 1] = '\0';
+    if ('\0' == buff[0]) {
+        pr2serr("no flag found\n");
+        return 1;
+    }
+    cp = buff;
+    do {
+        np = strchr(cp, ',');
+        if (np)
+            *np++ = '\0';
+        if (0 == strcmp(cp, "append"))
+            fp->append = 1;
+        else if (0 == strcmp(cp, "excl"))
+            fp->excl = 1;
+        else if (0 == strcmp(cp, "flock"))
+            ++fp->flock;
+        else if (0 == strcmp(cp, "null"))
+            ;
+        else if (0 == strcmp(cp, "pad"))
+            fp->pad = 1;
+        else if (0 == strcmp(cp, "xcopy"))
+            ++fp->xcopy_given;   /* for ddpt compatibility */
+        else {
+            pr2serr("unrecognised flag: %s\n", cp);
+            return 1;
+        }
+        cp = np;
+    } while (cp);
+    return 0;
+}
+
+/* Returns open input file descriptor (>= 0) or a negative value
+ * (-SG_LIB_FILE_ERROR or -SG_LIB_CAT_OTHER) if error.
+ */
+static int
+open_if(struct xcopy_fp_t * ifp, int verbose)
+{
+    int infd = -1, flags, fl, res;
+    char ebuff[EBUFF_SZ];
+
+    ifp->sg_type = dd_filetype(ifp);
+
+    if (verbose)
+        pr2serr(" >> Input file type: %s, devno %d:%d\n",
+                dd_filetype_str(ifp->sg_type, ebuff),
+                major(ifp->devno), minor(ifp->devno));
+    if (FT_ERROR & ifp->sg_type) {
+        pr2serr(ME "unable access %s\n", ifp->fname);
+        return -SG_LIB_FILE_ERROR;
+    }
+    flags = O_NONBLOCK;
+    if (ifp->excl)
+        flags |= O_EXCL;
+    fl = O_RDWR;
+    if ((infd = open(ifp->fname, fl | flags)) < 0) {
+        fl = O_RDONLY;
+        if ((infd = open(ifp->fname, fl | flags)) < 0) {
+            snprintf(ebuff, EBUFF_SZ,
+                     ME "could not open %s for sg reading", ifp->fname);
+            perror(ebuff);
+            return -SG_LIB_FILE_ERROR;
+        }
+    }
+    if (verbose)
+        pr2serr("        open input(sg_io), flags=0x%x\n", fl | flags);
+
+    if (ifp->flock) {
+        res = flock(infd, LOCK_EX | LOCK_NB);
+        if (res < 0) {
+            close(infd);
+            snprintf(ebuff, EBUFF_SZ, ME "flock(LOCK_EX | LOCK_NB) on %s "
+                     "failed", ifp->fname);
+            perror(ebuff);
+            return -SG_LIB_FLOCK_ERR;
+        }
+    }
+    return infd;
+}
+
+/* Returns open output file descriptor (>= 0), -1 for don't
+ * bother opening (e.g. /dev/null), or a more negative value
+ * (-SG_LIB_FILE_ERROR or -SG_LIB_CAT_OTHER) if error.
+ */
+static int
+open_of(struct xcopy_fp_t * ofp, int verbose)
+{
+    int outfd, flags, res;
+    char ebuff[EBUFF_SZ];
+
+    ofp->sg_type = dd_filetype(ofp);
+    if (verbose)
+        pr2serr(" >> Output file type: %s, devno %d:%d\n",
+                dd_filetype_str(ofp->sg_type, ebuff),
+                major(ofp->devno), minor(ofp->devno));
+
+    if (!(FT_DEV_NULL & ofp->sg_type)) {
+        flags = O_RDWR | O_NONBLOCK;
+        if (ofp->excl)
+            flags |= O_EXCL;
+        if ((outfd = open(ofp->fname, flags)) < 0) {
+            snprintf(ebuff, EBUFF_SZ,
+                     ME "could not open %s for sg writing", ofp->fname);
+            perror(ebuff);
+            return -SG_LIB_FILE_ERROR;
+        }
+        if (verbose)
+            pr2serr("        open output(sg_io), flags=0x%x\n", flags);
+    } else
+        outfd = -1; /* don't bother opening */
+    if ((outfd >= 0) && ofp->flock) {
+        res = flock(outfd, LOCK_EX | LOCK_NB);
+        if (res < 0) {
+            close(outfd);
+            snprintf(ebuff, EBUFF_SZ, ME "flock(LOCK_EX | LOCK_NB) on %s "
+                     "failed", ofp->fname);
+            perror(ebuff);
+            return -SG_LIB_FLOCK_ERR;
+        }
+    }
+    return outfd;
+}
+
+static int
+num_chs_in_str(const char * s, int slen, int ch)
+{
+    int res = 0;
+
+    while (--slen >= 0) {
+        if (ch == s[slen])
+            ++res;
+    }
+    return res;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int64_t skip = 0;
+    int64_t seek = 0;
+    int ibs = 0;
+    int obs = 0;
+    int bpt = DEF_BLOCKS_PER_TRANSFER;
+    int bpt_given = 0;
+    char str[STR_SZ];
+    char * key;
+    char * buf;
+    int blocks = 0;
+    int num_help = 0;
+    int num_xcopy = 0;
+    int res, k, n, keylen;
+    int infd, outfd, xcopy_fd;
+    int ret = 0;
+    unsigned char list_id = 1;
+    int list_id_given = 0;
+    unsigned char src_desc[256];
+    unsigned char dst_desc[256];
+    int src_desc_len;
+    int dst_desc_len;
+    int seg_desc_type;
+    int on_src = 0;
+    int on_dst = 0;
+
+    ixcf.fname[0] = '\0';
+    oxcf.fname[0] = '\0';
+    ixcf.num_sect = -1;
+    oxcf.num_sect = -1;
+
+    if (argc < 2) {
+        pr2serr("Won't default both IFILE to stdin _and_ OFILE to stdout\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    for (k = 1; k < argc; k++) {
+        if (argv[k]) {
+            strncpy(str, argv[k], STR_SZ - 1);
+            str[STR_SZ - 1] = '\0';
+        } else
+            continue;
+        for (key = str, buf = key; *buf && *buf != '=';)
+            buf++;
+        if (*buf)
+            *buf++ = '\0';
+        keylen = (int)strlen(key);
+        if (0 == strncmp(key, "app", 3)) {
+            ixcf.append = sg_get_num(buf);
+            oxcf.append = ixcf.append;
+        } else if (0 == strcmp(key, "bpt")) {
+            bpt = sg_get_num(buf);
+            if (-1 == bpt) {
+                pr2serr(ME "bad argument to 'bpt='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            bpt_given = 1;
+        } else if (0 == strcmp(key, "bs")) {
+            blk_sz = sg_get_num(buf);
+            if (-1 == blk_sz) {
+                pr2serr(ME "bad argument to 'bs='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "list_id")) {
+            ret = sg_get_num(buf);
+            if (-1 == ret || ret > 0xff) {
+                pr2serr(ME "bad argument to 'list_id='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            list_id = (ret & 0xff);
+            list_id_given = 1;
+        } else if (0 == strcmp(key, "id_usage")) {
+            if (!strncmp(buf, "hold", 4))
+                list_id_usage = 0;
+            else if (!strncmp(buf, "discard", 7))
+                list_id_usage = 2;
+            else if (!strncmp(buf, "disable", 7))
+                list_id_usage = 3;
+            else {
+                pr2serr(ME "bad argument to 'id_usage='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "conv"))
+            pr2serr(ME ">>> ignoring all 'conv=' arguments\n");
+        else if (0 == strcmp(key, "count")) {
+            if (0 != strcmp("-1", buf)) {
+                dd_count = sg_get_llnum(buf);
+                if (-1LL == dd_count) {
+                    pr2serr(ME "bad argument to 'count='\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }   /* treat 'count=-1' as calculate count (same as not given) */
+        } else if (0 == strcmp(key, "prio")) {
+            priority = sg_get_num(buf);
+        } else if (0 == strcmp(key, "cat")) {
+            xcopy_flag_cat = sg_get_num(buf);
+            if (xcopy_flag_cat < 0 || xcopy_flag_cat > 1) {
+                pr2serr(ME "bad argument to 'cat='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "dc")) {
+            xcopy_flag_dc = sg_get_num(buf);
+            if (xcopy_flag_dc < 0 || xcopy_flag_dc > 1) {
+                pr2serr(ME "bad argument to 'dc='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "ibs")) {
+            ibs = sg_get_num(buf);
+        } else if (strcmp(key, "if") == 0) {
+            if ('\0' != ixcf.fname[0]) {
+                pr2serr("Second IFILE argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(ixcf.fname, buf, INOUTF_SZ - 1);
+        } else if (0 == strcmp(key, "iflag")) {
+            if (process_flags(buf, &ixcf)) {
+                pr2serr(ME "bad argument to 'iflag='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "obs")) {
+            obs = sg_get_num(buf);
+        } else if (strcmp(key, "of") == 0) {
+            if ('\0' != oxcf.fname[0]) {
+                pr2serr("Second OFILE argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(oxcf.fname, buf, INOUTF_SZ - 1);
+        } else if (0 == strcmp(key, "oflag")) {
+            if (process_flags(buf, &oxcf)) {
+                pr2serr(ME "bad argument to 'oflag='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "seek")) {
+            seek = sg_get_llnum(buf);
+            if (-1LL == seek) {
+                pr2serr(ME "bad argument to 'seek='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "skip")) {
+            skip = sg_get_llnum(buf);
+            if (-1LL == skip) {
+                pr2serr(ME "bad argument to 'skip='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key, "time"))
+            do_time = sg_get_num(buf);
+        else if (0 == strncmp(key, "verb", 4))
+            verbose = sg_get_num(buf);
+        /* look for long options that start with '--' */
+        else if (0 == strncmp(key, "--help", 6))
+            ++num_help;
+        else if (0 == strncmp(key, "--on_dst", 8))
+            ++on_dst;
+        else if (0 == strncmp(key, "--on_src", 8))
+            ++on_src;
+        else if (0 == strncmp(key, "--verb", 6))
+            verbose += 1;
+        else if (0 == strncmp(key, "--vers", 6)) {
+            pr2serr(ME "%s\n", version_str);
+            return 0;
+        }
+        else if (0 == strncmp(key, "--xcopy", 7))
+            ;   /* ignore; for compatibility with ddpt */
+        /* look for short options that start with a single '-', they can be
+         * concaternated (e.g. '-vvvV') */
+        else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
+            res = 0;
+            n = num_chs_in_str(key + 1, keylen - 1, 'h');
+            num_help += n;
+            res += n;
+            n = num_chs_in_str(key + 1, keylen - 1, 'v');
+            verbose += n;
+            res += n;
+            if (num_chs_in_str(key + 1, keylen - 1, 'V')) {
+                pr2serr("%s\n", version_str);
+                return -1;
+            }
+            n = num_chs_in_str(key + 1, keylen - 1, 'x');
+            /* accept and ignore; for compatibility with ddpt */
+            res += n;
+            if (res < (keylen - 1)) {
+                pr2serr(ME "Unrecognised short option in '%s', try "
+                        "'--help'\n", key);
+                if (0 == num_help)
+                    return -1;
+            }
+        } else {
+            pr2serr("Unrecognized option '%s'\n", key);
+            if (num_help)
+                usage(num_help);
+            else
+                pr2serr("For more information use '--help'\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (num_help) {
+        usage(num_help);
+        return 0;
+    }
+    if (on_src && on_dst) {
+        pr2serr("Syntax error - either specify --on_src OR "
+                "--on_dst\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((! on_src) && (! on_dst)) {
+        if ((!! ixcf.xcopy_given) == (!! oxcf.xcopy_given)) {
+            char * csp;
+            char * cdp;
+
+            csp = getenv(XCOPY_TO_SRC);
+            cdp = getenv(XCOPY_TO_DST);
+            if ((!! csp) == (!! cdp)) {
+#if DEF_XCOPY_SRC0_DST1 == 0
+                on_src = 1;
+#else
+                on_dst = 1;
+#endif
+            } else if (csp)
+                on_src = 1;
+            else
+                on_dst = 1;
+        } else if (ixcf.xcopy_given)
+            on_src = 1;
+        else
+            on_dst = 1;
+    }
+    if (verbose > 1)
+        pr2serr(" >>> Extended Copy(LID1) command will be sent to %s device "
+                "[%s]\n", (on_src ? "src" : "dst"),
+                (on_src ? ixcf.fname : oxcf.fname));
+
+    if ((ibs && blk_sz && (ibs != blk_sz)) ||
+        (obs && blk_sz && (obs != blk_sz))) {
+        pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (blk_sz && !ibs)
+        ibs = blk_sz;
+    if (blk_sz && !obs)
+        obs = blk_sz;
+
+    if ((skip < 0) || (seek < 0)) {
+        pr2serr("skip and seek cannot be negative\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((oxcf.append > 0) && (seek > 0)) {
+        pr2serr("Can't use both append and seek switches\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (bpt < 1) {
+        pr2serr("bpt must be greater than 0\n");
+        return SG_LIB_SYNTAX_ERROR;
+    } else if (bpt > MAX_BLOCKS_PER_TRANSFER) {
+        pr2serr("bpt must be less than or equal to %d\n",
+                MAX_BLOCKS_PER_TRANSFER);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (list_id_usage == 3) { /* list_id usage disabled */
+        if (!list_id_given)
+            list_id = 0;
+        if (list_id) {
+            pr2serr("list_id disabled by id_usage flag\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (verbose > 1)
+        pr2serr(" >>> " ME " if=%s skip=%" PRId64 " of=%s seek=%" PRId64
+                " count=%" PRId64 "\n", ixcf.fname, skip, oxcf.fname, seek,
+                dd_count);
+    install_handler(SIGINT, interrupt_handler);
+    install_handler(SIGQUIT, interrupt_handler);
+    install_handler(SIGPIPE, interrupt_handler);
+    install_handler(SIGUSR1, siginfo_handler);
+
+    infd = STDIN_FILENO;
+    outfd = STDOUT_FILENO;
+    ixcf.pdt = -1;
+    oxcf.pdt = -1;
+    if (ixcf.fname[0] && ('-' != ixcf.fname[0])) {
+        infd = open_if(&ixcf, verbose);
+        if (infd < 0)
+            return -infd;
+    } else {
+        pr2serr("stdin not acceptable for IFILE\n");
+        return SG_LIB_FILE_ERROR;
+    }
+
+    if (oxcf.fname[0] && ('-' != oxcf.fname[0])) {
+        outfd = open_of(&oxcf, verbose);
+        if (outfd < -1)
+            return -outfd;
+    } else {
+        pr2serr("stdout not acceptable for OFILE\n");
+        return SG_LIB_FILE_ERROR;
+    }
+
+    res = open_sg(&ixcf, verbose);
+    if (res < 0) {
+        if (-1 == res)
+            return SG_LIB_FILE_ERROR;
+        else
+            return SG_LIB_CAT_OTHER;
+    }
+    res = open_sg(&oxcf, verbose);
+    if (res < 0) {
+        if (-1 == res)
+            return SG_LIB_FILE_ERROR;
+        else
+            return SG_LIB_CAT_OTHER;
+    }
+
+    if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
+        pr2serr("Can't have both 'if' as stdin _and_ 'of' as stdout\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    res = scsi_read_capacity(&ixcf);
+    if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+        pr2serr("Unit attention (%s in), continuing\n", read_cap_str);
+        res = scsi_read_capacity(&ixcf);
+    } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
+        pr2serr("Aborted command (%s in), continuing\n", read_cap_str);
+        res = scsi_read_capacity(&ixcf);
+    }
+    if (0 != res) {
+        if (res == SG_LIB_CAT_INVALID_OP)
+            pr2serr("%s command not supported on %s\n", read_cap_str,
+                    ixcf.fname);
+        else if (res == SG_LIB_CAT_NOT_READY)
+            pr2serr("%s failed on %s - not ready\n", read_cap_str,
+                    ixcf.fname);
+        else
+            pr2serr("Unable to %s on %s\n", read_cap_str, ixcf.fname);
+        ixcf.num_sect = -1;
+    } else if (ibs && ixcf.sect_sz != ibs) {
+        pr2serr(">> warning: block size on %s confusion: "
+                "ibs=%d, device claims=%d\n", ixcf.fname, ibs, ixcf.sect_sz);
+    }
+    if (skip && ixcf.num_sect < skip) {
+        pr2serr("argument to 'skip=' exceeds device size (max %" PRId64 ")\n",
+                ixcf.num_sect);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    res = scsi_read_capacity(&oxcf);
+    if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+        pr2serr("Unit attention (%s out), continuing\n", read_cap_str);
+        res = scsi_read_capacity(&oxcf);
+    } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
+        pr2serr("Aborted command (%s out), continuing\n", read_cap_str);
+        res = scsi_read_capacity(&oxcf);
+    }
+    if (0 != res) {
+        if (res == SG_LIB_CAT_INVALID_OP)
+            pr2serr("%s command not supported on %s\n", read_cap_str,
+                    oxcf.fname);
+        else
+            pr2serr("Unable to %s on %s\n", read_cap_str, oxcf.fname);
+        oxcf.num_sect = -1;
+    } else if (obs && obs != oxcf.sect_sz) {
+        pr2serr(">> warning: block size on %s confusion: obs=%d, device "
+                "claims=%d\n", oxcf.fname, obs, oxcf.sect_sz);
+    }
+    if (seek && oxcf.num_sect < seek) {
+        pr2serr("argument to 'seek=' exceeds device size (max %" PRId64 ")\n",
+                oxcf.num_sect);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((dd_count < 0) || ((verbose > 0) && (0 == dd_count))) {
+        if (xcopy_flag_dc == 0) {
+            dd_count = ixcf.num_sect - skip;
+            if ((dd_count * ixcf.sect_sz) >
+                ((oxcf.num_sect - seek) * oxcf.sect_sz))
+                dd_count = (oxcf.num_sect - seek) * oxcf.sect_sz /
+                           ixcf.sect_sz;
+        } else {
+            dd_count = oxcf.num_sect - seek;
+            if ((dd_count * oxcf.sect_sz) >
+                ((ixcf.num_sect - skip) * ixcf.sect_sz))
+                dd_count = (ixcf.num_sect - skip) * ixcf.sect_sz /
+                           oxcf.sect_sz;
+        }
+    } else {
+        int64_t dd_bytes;
+
+        if (xcopy_flag_dc)
+            dd_bytes = dd_count * oxcf.sect_sz;
+        else
+            dd_bytes = dd_count * ixcf.sect_sz;
+
+        if (dd_bytes > ixcf.num_sect * ixcf.sect_sz) {
+            pr2serr("access beyond end of source device (max %" PRId64 ")\n",
+                    ixcf.num_sect);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+        if (dd_bytes > oxcf.num_sect * oxcf.sect_sz) {
+            pr2serr("access beyond end of target device (max %" PRId64 ")\n",
+                    oxcf.num_sect);
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    res = scsi_operating_parameter(&ixcf, 0);
+    if (res < 0) {
+        if (SG_LIB_CAT_UNIT_ATTENTION == -res) {
+            pr2serr("Unit attention (%s), continuing\n",
+                    rec_copy_op_params_str);
+            res = scsi_operating_parameter(&ixcf, 0);
+        } else {
+            if (-res == SG_LIB_CAT_INVALID_OP) {
+                pr2serr("%s command not supported on %s\n",
+                        rec_copy_op_params_str, ixcf.fname);
+                return EINVAL;
+            } else if (-res == SG_LIB_CAT_NOT_READY)
+                pr2serr("%s failed on %s - not ready\n",
+                        rec_copy_op_params_str, ixcf.fname);
+            else {
+                pr2serr("Unable to %s on %s\n", rec_copy_op_params_str,
+                        ixcf.fname);
+                return -res;
+            }
+        }
+    } else if (res == 0)
+        return SG_LIB_CAT_INVALID_OP;
+
+    if (res & TD_VPD) {
+        if (verbose)
+            pr2serr("  >> using VPD identification for source %s\n",
+                    ixcf.fname);
+        src_desc_len = desc_from_vpd_id(ixcf.sg_fd, src_desc,
+                                 sizeof(src_desc), ixcf.sect_sz, ixcf.pad);
+        if (src_desc_len > (int)sizeof(src_desc)) {
+            pr2serr("source descriptor too large (%d bytes)\n", res);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else {
+        return SG_LIB_CAT_INVALID_OP;
+    }
+
+    res = scsi_operating_parameter(&oxcf, 1);
+    if (res < 0) {
+        if (SG_LIB_CAT_UNIT_ATTENTION == -res) {
+            pr2serr("Unit attention (%s), continuing\n",
+                    rec_copy_op_params_str);
+            res = scsi_operating_parameter(&oxcf, 1);
+        } else {
+            if (-res == SG_LIB_CAT_INVALID_OP) {
+                pr2serr("%s command not supported on %s\n",
+                        rec_copy_op_params_str, oxcf.fname);
+                return EINVAL;
+            } else if (-res == SG_LIB_CAT_NOT_READY)
+                pr2serr("%s failed on %s - not ready\n",
+                        rec_copy_op_params_str, oxcf.fname);
+            else {
+                pr2serr("Unable to %s on %s\n", rec_copy_op_params_str,
+                        oxcf.fname);
+                return -res;
+            }
+        }
+    } else if (res == 0)
+        return SG_LIB_CAT_INVALID_OP;
+
+    if (res & TD_VPD) {
+        if (verbose)
+            pr2serr("  >> using VPD identification for destination %s\n",
+                    oxcf.fname);
+        dst_desc_len = desc_from_vpd_id(oxcf.sg_fd, dst_desc,
+                                 sizeof(dst_desc), oxcf.sect_sz, oxcf.pad);
+        if (dst_desc_len > (int)sizeof(dst_desc)) {
+            pr2serr("destination descriptor too large (%d bytes)\n", res);
+            return SG_LIB_CAT_MALFORMED;
+        }
+    } else {
+        return SG_LIB_CAT_INVALID_OP;
+    }
+
+    if (dd_count < 0) {
+        pr2serr("Couldn't calculate count, please give one\n");
+        return SG_LIB_CAT_OTHER;
+    }
+
+    if ((unsigned long)dd_count < ixcf.min_bytes / ixcf.sect_sz) {
+        pr2serr("not enough data to read (min %ld bytes)\n", oxcf.min_bytes);
+        return SG_LIB_CAT_OTHER;
+    }
+    if ((unsigned long)dd_count < oxcf.min_bytes / oxcf.sect_sz) {
+        pr2serr("not enough data to write (min %ld bytes)\n", oxcf.min_bytes);
+        return SG_LIB_CAT_OTHER;
+    }
+
+    if (bpt_given) {
+        if (xcopy_flag_dc) {
+            if ((unsigned long)bpt * oxcf.sect_sz > oxcf.max_bytes) {
+                pr2serr("bpt too large (max %ld blocks)\n",
+                        oxcf.max_bytes / oxcf.sect_sz);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else {
+            if ((unsigned long)bpt * ixcf.sect_sz > ixcf.max_bytes) {
+                pr2serr("bpt too large (max %ld blocks)\n",
+                        ixcf.max_bytes / ixcf.sect_sz);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        }
+    } else {
+        unsigned long r;
+
+        if (xcopy_flag_dc)
+            r = oxcf.max_bytes / (unsigned long)oxcf.sect_sz;
+        else
+            r = ixcf.max_bytes / (unsigned long)ixcf.sect_sz;
+        bpt = (r > MAX_BLOCKS_PER_TRANSFER) ? MAX_BLOCKS_PER_TRANSFER : r;
+    }
+
+    seg_desc_type = seg_desc_from_dd_type(simplified_ft(&ixcf), 0,
+                                          simplified_ft(&oxcf), 0);
+
+    if (do_time) {
+        start_tm.tv_sec = 0;
+        start_tm.tv_usec = 0;
+        gettimeofday(&start_tm, NULL);
+        start_tm_valid = 1;
+    }
+
+    if (verbose)
+        pr2serr("Start of loop, count=%" PRId64 ", bpt=%d, lba_in=%" PRId64
+                ", lba_out=%" PRId64 "\n", dd_count, bpt, skip, seek);
+
+    xcopy_fd = (on_src) ? infd : outfd;
+
+    while (dd_count > 0) {
+        if (dd_count > bpt)
+            blocks = bpt;
+        else
+            blocks = dd_count;
+        res = scsi_extended_copy(xcopy_fd, list_id, src_desc, src_desc_len,
+                                 dst_desc, dst_desc_len, seg_desc_type,
+                                 blocks, skip, seek);
+        if (res != 0)
+            break;
+        in_full += blocks;
+        skip += blocks;
+        seek += blocks;
+        dd_count -= blocks;
+        num_xcopy++;
+    }
+
+    if (do_time)
+        calc_duration_throughput(0);
+    if (res)
+        pr2serr("sg_xcopy: failed with error %d (%" PRId64 " blocks left)\n",
+                res, dd_count);
+    else
+        pr2serr("sg_xcopy: %" PRId64 " blocks, %d command%s\n", in_full,
+                num_xcopy, ((num_xcopy > 1) ? "s" : ""));
+
+    return res;
+}
diff --git a/sg3_utils/src/sg_zone.c b/sg3_utils/src/sg_zone.c
new file mode 100644
index 0000000..1a2647c
--- /dev/null
+++ b/sg3_utils/src/sg_zone.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2014-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues a SCSI CLOSE ZONE, FINISH ZONE or OPEN ZONE command
+ * to the given SCSI device. Based on zbc-r04c.pdf .
+ */
+
+static const char * version_str = "1.02 20151219";
+
+#define SG_ZONING_OUT_CMDLEN 16
+#define CLOSE_ZONE_SA 0x1
+#define FINISH_ZONE_SA 0x2
+#define OPEN_ZONE_SA 0x3
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT  60      /* 60 seconds */
+
+
+static struct option long_options[] = {
+        {"all", no_argument, 0, 'a'},
+        {"close", no_argument, 0, 'c'},
+        {"finish", no_argument, 0, 'f'},
+        {"help", no_argument, 0, 'h'},
+        {"open", no_argument, 0, 'o'},
+        {"reset-all", no_argument, 0, 'R'},
+        {"reset_all", no_argument, 0, 'R'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {"zone", required_argument, 0, 'z'},
+        {0, 0, 0, 0},
+};
+
+/* Indexed by service action */
+static const char * sa_name_arr[] = {
+    "no SA=0",
+    "Close zone",
+    "Finish zone",
+    "Open zone",
+};
+
+
+static void
+usage()
+{
+    pr2serr("Usage: "
+            "sg_zone  [--all] [--close] [--finish] [--help] [--open]\n"
+            "                [--verbose] [--version] [--zone=ID] DEVICE\n");
+    pr2serr("  where:\n"
+            "    --all|-a           sets the ALL flag in the cdb\n"
+            "    --close|-c         issue CLOSE ZONE command\n"
+            "    --finish|-f        issue FINISH ZONE command\n"
+            "    --help|-h          print out usage message\n"
+            "    --open|-o          issue OPEN ZONE command\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n"
+            "    --zone=ID|-z ID    ID is the starting LBA of the zone\n\n"
+            "Performs a SCSI OPEN ZONE, CLOSE ZONE or FINISH ZONE command. "
+            "ID is\ndecimal by default, for hex use a leading '0x' or a "
+            "trailing 'h'.\nEither --close, --finish, or --open option "
+            "needs to be given.\n");
+}
+
+/* Invokes the zone out command indicated by 'sa' (ZBC).  Return of 0
+ * -> success, various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_zone_out(int sg_fd, int sa, uint64_t zid, int all, int noisy,
+               int verbose)
+{
+    int k, ret, res, sense_cat;
+    unsigned char zoCmdBlk[SG_ZONING_OUT_CMDLEN] =
+          {SG_ZONING_OUT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0};
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_pt_base * ptvp;
+
+    zoCmdBlk[1] = 0x1f & sa;
+    sg_put_unaligned_be64(zid, zoCmdBlk + 2);
+    if (all)
+        zoCmdBlk[14] = 0x1;
+    if (verbose) {
+        pr2serr("    Reset write pointer cdb: ");
+        for (k = 0; k < SG_ZONING_OUT_CMDLEN; ++k)
+            pr2serr("%02x ", zoCmdBlk[k]);
+        pr2serr("\n");
+    }
+
+    ptvp = construct_scsi_pt_obj();
+    if (NULL == ptvp) {
+        pr2serr("Reset write pointer: out of memory\n");
+        return -1;
+    }
+    set_scsi_pt_cdb(ptvp, zoCmdBlk, sizeof(zoCmdBlk));
+    set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+    res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
+    ret = sg_cmds_process_resp(ptvp, "reset write pointer", res, 0, sense_b,
+                               noisy, verbose, &sense_cat);
+    if (-1 == ret)
+        ;
+    else if (-2 == ret) {
+        switch (sense_cat) {
+        case SG_LIB_CAT_RECOVERED:
+        case SG_LIB_CAT_NO_SENSE:
+            ret = 0;
+            break;
+        default:
+            ret = sense_cat;
+            break;
+        }
+    } else
+        ret = 0;
+    destruct_scsi_pt_obj(ptvp);
+    return ret;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int sg_fd, res, c;
+    int all = 0;
+    int close = 0;
+    int finish = 0;
+    int open = 0;
+    int verbose = 0;
+    int zid_given = 0;
+    int sa = 0;
+    uint64_t zid = 0;
+    int64_t ll;
+    const char * device_name = NULL;
+    const char * sa_name;
+    int ret = 0;
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "acfhoRvVz:", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'a':
+        case 'R':
+            ++all;
+            break;
+        case 'c':
+            ++close;
+            sa = CLOSE_ZONE_SA;
+            break;
+        case 'f':
+            ++finish;
+            sa = FINISH_ZONE_SA;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'o':
+            ++open;
+            sa = OPEN_ZONE_SA;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            pr2serr("version: %s\n", version_str);
+            return 0;
+        case 'z':
+            ll = sg_get_llnum(optarg);
+            if (-1 == ll) {
+                pr2serr("bad argument to '--zone=ID'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            zid = (uint64_t)ll;
+            ++zid_given;
+            break;
+        default:
+            pr2serr("unrecognised option code 0x%x ??\n", c);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (optind < argc) {
+        if (NULL == device_name) {
+            device_name = argv[optind];
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                pr2serr("Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+
+    if (1 != ((!! close) + (!! finish) + (!! open))) {
+        pr2serr("one from the --close, --finish and --open options must be "
+                "given\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    sa_name = sa_name_arr[sa];
+
+    if (NULL == device_name) {
+        pr2serr("missing device name!\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    sg_fd = sg_cmds_open_device(device_name, 0, verbose);
+    if (sg_fd < 0) {
+        pr2serr("open error: %s: %s\n", device_name,
+                safe_strerror(-sg_fd));
+        return SG_LIB_FILE_ERROR;
+    }
+
+    res = sg_ll_zone_out(sg_fd, sa, zid, all, 1, verbose);
+    ret = res;
+    if (res) {
+        if (SG_LIB_CAT_INVALID_OP == res)
+            pr2serr("%s command not supported\n", sa_name);
+        else {
+            char b[80];
+
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("%s command: %s\n", sa_name, b);
+        }
+    }
+
+    res = sg_cmds_close_device(sg_fd);
+    if (res < 0) {
+        pr2serr("close error: %s\n", safe_strerror(-res));
+        if (0 == ret)
+            return SG_LIB_FILE_ERROR;
+    }
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sginfo.c b/sg3_utils/src/sginfo.c
new file mode 100644
index 0000000..60edb66
--- /dev/null
+++ b/sg3_utils/src/sginfo.c
@@ -0,0 +1,3977 @@
+/* * This program reads various mode pages and bits of other
+ * information from a scsi device and interprets the raw data for you
+ * with a report written to stdout.  Usage:
+ *
+ * ./sginfo [options] /dev/sg2 [replace parameters]
+ *
+ * Options are:
+ * -6    do 6 byte mode sense + select (deafult: 10 byte)
+ * -a    display all mode pages reported by the device: equivalent to '-t 63'.
+ * -A    display all mode pages and subpages reported by the device: equivalent
+ *       to '-t 63,255'.
+ * -c    access Cache control page.
+ * -C    access Control Page.
+ * -d    display defect lists (default format: index).
+ * -D    access disconnect-reconnect page.
+ * -e    access Read-Write error recovery page.
+ * -E    access Control Extension page.
+ * -f    access Format Device Page.
+ * -Farg defect list format (-Flogical, -flba64, -Fphysical, -Findex, -Fhead)
+ * -g    access rigid disk geometry page.
+ * -G    display only "grown" defect list (default format: index)
+ * -i    display information from Inquiry command.
+ * -I    access Informational Exceptions page.
+ * -l    list known scsi devices on the system [deprecated]
+ * -n    access notch parameters page.
+ * -N    Negate (stop) storing to saved page (active with -R)
+ * -P    access Power Condition Page.
+ * -r    list known raw scsi devices on the system
+ * -s    display serial number (from INQUIRY VPD page)
+ * -t <n[,spn]> access page number <n> [and subpage <spn>], try to decode
+ * -u <n[,spn]> access page number <n> [and subpage <spn>], output in hex
+ * -v    show this program's version number
+ * -V    access Verify Error Recovery Page.
+ * -T    trace commands (for debugging, double for more debug)
+ * -z    do a single fetch for mode pages (rather than double fetch)
+ *
+ * Only one of the following three options can be specified.
+ * None of these three implies the current values are returned.
+ * -m    Display modifiable fields instead of current values
+ * -M    Display manufacturer defaults instead of current values
+ * -S    Display saved defaults instead of current values
+ *
+ * -X    Display output values in a list.
+ * -R    Replace parameters - best used with -X
+ *
+ * Eric Youngdale - 11/1/93.  Version 1.0.
+ *
+ * Version 1.1: Ability to change parameters on cache page, support for
+ *  X front end.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Michael Weller (eowmob at exp-math dot uni-essen dot de)
+ *      11/23/94 massive extensions from 1.4a
+ *      08/23/97 fix problems with defect lists
+ *
+ * Douglas Gilbert (dgilbert at interlog dot com)
+ *      990628   port to sg .... (version 1.81)
+ *               up 4KB limit on defect list to 32KB
+ *               'sginfo -l' also shows sg devices and mapping to other
+ *                    scsi devices
+ *               'sginfo' commands can take either an sd, sr (scd), st
+ *                    or an sg device (all non-sg devices converted to a
+ *                    sg device)
+ *
+ *      001208   Add Kurt Garloff's "-uno" flag for displaying info
+ *               from a page number. <garloff at suse dot de> [version 1.90]
+ *
+ * Kurt Garloff <garloff at suse dot de>
+ *    20000715  allow displaying and modification of vendor specific pages
+ *                      (unformatted - @ hexdatafield)
+ *              accept vendor lengths for those pages
+ *              enabled page saving
+ *              cleaned parameter parsing a bit (it's still a terrible mess!)
+ *              Use sr (instead of scd) and sg%d (instead of sga,b,...) in -l
+ *                      and support much more devs in -l (incl. nosst)
+ *              Fix segfault in defect list (len=0xffff) and adapt formatting
+ *                      to large disks. Support up to 256kB defect lists with
+ *                      0xB7 (12byte) command if necessary and fallback to 0x37
+ *                      (10byte) in case of failure. Report truncation.
+ *              sizeof(buffer) (which is sizeof(char*) == 4 or 32 bit archs)
+ *                      was used incorrectly all over the place. Fixed.
+ *                                      [version 1.95]
+ * Douglas Gilbert (dgilbert at interlog dot com)
+ *    20020113  snprintf() type cleanup [version 1.96]
+ *    20021211  correct sginfo MODE_SELECT, protect against block devices
+ *              that answer sg's ioctls. [version 1.97]
+ *    20021228  scan for some "scd<n>" as well as "sr<n>" device names [1.98]
+ *    20021020  Update control page [1.99]
+ *
+ * Thomas Steudten (thomas at steudten dot com)
+ *    20040521  add -Fhead feature [version 2.04]
+ *
+ * Tim Hunt (tim at timhunt dot net)
+ *    20050427  increase number of mapped SCSI disks devices
+ *
+ * Dave Johnson (djj at ccv dot brown dot edu)
+ *    20051218  improve disk defect list handling
+ */
+
+
+/*
+ * N.B. This utility is in maintenance mode only. This means that serious
+ * bugs will be fixed but no new features or mode page changes will be
+ * added. Please use the sdparm utility.     D. Gilbert 20090316
+ */
+
+#define _XOPEN_SOURCE 500
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+static const char * version_str = "2.35 [20140403]";
+
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_io_linux.h"
+
+
+static int glob_fd;
+static char *device_name;
+
+#define MAX_SG_DEVS 8192
+#define MAX_RESP6_SIZE 252
+#define MAX_RESP10_SIZE (4*1024)
+#define MAX_BUFFER_SIZE MAX_RESP10_SIZE
+
+#define INQUIRY_RESP_INITIAL_LEN 36
+#define MAX_INQFIELD_LEN 17
+
+#define MAX_HEADS 127
+#define HEAD_SORT_TOKEN 0x55
+
+#define SIZEOF_BUFFER (16*1024)
+#define SIZEOF_BUFFER1 (16*1024)
+static unsigned char cbuffer[SIZEOF_BUFFER];
+static unsigned char cbuffer1[SIZEOF_BUFFER1];
+static unsigned char cbuffer2[SIZEOF_BUFFER1];
+
+static char defect = 0;
+static char defectformat = 0x4;
+static char grown_defect = 0;
+static char negate_sp_bit = 0;
+static char replace = 0;
+static char serial_number = 0;
+static char x_interface = 0;
+static char single_fetch = 0;
+
+static char mode6byte = 0;      /* defaults to 10 byte mode sense + select */
+static char trace_cmd = 0;
+
+struct mpage_info {
+    int page;
+    int subpage;
+    int page_control;
+    int peri_type;
+    int inq_byte6;      /* EncServ and MChngr bits of interest */
+    int resp_len;
+};
+
+/* declarations of functions decoding known mode pages */
+static int common_disconnect_reconnect(struct mpage_info * mpi,
+                                       const char * prefix);
+static int common_control(struct mpage_info * mpi, const char * prefix);
+static int common_control_extension(struct mpage_info * mpi,
+                                    const char * prefix);
+static int common_proto_spec_lu(struct mpage_info * mpi, const char * prefix);
+static int common_proto_spec_port(struct mpage_info * mpi,
+                                  const char * prefix);
+static int common_proto_spec_port_sp1(struct mpage_info * mpi,
+                                      const char * prefix);
+static int common_proto_spec_port_sp2(struct mpage_info * mpi,
+                                      const char * prefix);
+static int common_power_condition(struct mpage_info * mpi,
+                                  const char * prefix);
+static int common_informational(struct mpage_info * mpi, const char * prefix);
+static int disk_error_recovery(struct mpage_info * mpi, const char * prefix);
+static int disk_format(struct mpage_info * mpi, const char * prefix);
+static int disk_verify_error_recovery(struct mpage_info * mpi,
+                                      const char * prefix);
+static int disk_geometry(struct mpage_info * mpi, const char * prefix);
+static int disk_notch_parameters(struct mpage_info * mpi, const char * prefix);
+static int disk_cache(struct mpage_info * mpi, const char * prefix);
+static int disk_xor_control(struct mpage_info * mpi, const char * prefix);
+static int disk_background(struct mpage_info * mpi, const char * prefix);
+static int optical_memory(struct mpage_info * mpi, const char * prefix);
+static int cdvd_error_recovery(struct mpage_info * mpi, const char * prefix);
+static int cdvd_mrw(struct mpage_info * mpi, const char * prefix);
+static int cdvd_write_param(struct mpage_info * mpi, const char * prefix);
+static int cdvd_audio_control(struct mpage_info * mpi, const char * prefix);
+static int cdvd_timeout(struct mpage_info * mpi, const char * prefix);
+static int cdvd_device_param(struct mpage_info * mpi, const char * prefix);
+static int cdvd_cache(struct mpage_info * mpi, const char * prefix);
+static int cdvd_mm_capab(struct mpage_info * mpi, const char * prefix);
+static int cdvd_feature(struct mpage_info * mpi, const char * prefix);
+static int tape_data_compression(struct mpage_info * mpi, const char * prefix);
+static int tape_dev_config(struct mpage_info * mpi, const char * prefix);
+static int tape_medium_part1(struct mpage_info * mpi, const char * prefix);
+static int tape_medium_part2_4(struct mpage_info * mpi, const char * prefix);
+static int ses_services_manag(struct mpage_info * mpi, const char * prefix);
+static int spi4_training_config(struct mpage_info * mpi, const char * prefix);
+static int spi4_negotiated(struct mpage_info * mpi, const char * prefix);
+static int spi4_report_xfer(struct mpage_info * mpi, const char * prefix);
+
+enum page_class {PC_COMMON, PC_DISK, PC_TAPE, PC_CDVD, PC_SES, PC_SMC};
+
+struct mpage_name_func {
+    int page;
+    int subpage;
+    enum page_class pg_class;
+    const char * name;
+    int (*func)(struct mpage_info *, const char *);
+};
+
+#define MP_LIST_PAGES 0x3f
+#define MP_LIST_SUBPAGES 0xff
+
+static struct mpage_name_func mpage_common[] =
+{
+    { 0, 0, PC_COMMON, "Vendor (non-page format)", NULL},
+    { 2, 0, PC_COMMON, "Disconnect-Reconnect", common_disconnect_reconnect},
+    { 9, 0, PC_COMMON, "Peripheral device (obsolete)", NULL},
+    { 0xa, 0, PC_COMMON, "Control", common_control},
+    { 0xa, 1, PC_COMMON, "Control Extension", common_control_extension},
+    { 0x15, 0, PC_COMMON, "Extended", NULL},
+    { 0x16, 0, PC_COMMON, "Extended, device-type specific", NULL},
+    { 0x18, 0, PC_COMMON, "Protocol specific lu", common_proto_spec_lu},
+    { 0x19, 0, PC_COMMON, "Protocol specific port", common_proto_spec_port},
+    { 0x19, 1, PC_COMMON, "Protocol specific port, subpage 1 overload",
+      common_proto_spec_port_sp1},
+    { 0x19, 2, PC_COMMON, "Protocol specific port, subpage 2 overload",
+      common_proto_spec_port_sp2},
+/*    { 0x19, 2, PC_COMMON, "SPI-4 Saved Training configuration",
+        spi4_training_config}, */
+    { 0x19, 3, PC_COMMON, "SPI-4 Negotiated Settings", spi4_negotiated},
+    { 0x19, 4, PC_COMMON, "SPI-4 Report transfer capabilities",
+      spi4_report_xfer},
+    { 0x1a, 0, PC_COMMON, "Power Condition", common_power_condition},
+    { 0x1c, 0, PC_COMMON, "Informational Exceptions", common_informational},
+    { MP_LIST_PAGES, 0, PC_COMMON, "Return all pages", NULL},
+};
+static const int mpage_common_len = sizeof(mpage_common) /
+                                    sizeof(mpage_common[0]);
+
+static struct mpage_name_func mpage_disk[] =
+{
+    { 1, 0, PC_DISK, "Read-Write Error Recovery", disk_error_recovery},
+    { 3, 0, PC_DISK, "Format Device", disk_format},
+    { 4, 0, PC_DISK, "Rigid Disk Geometry", disk_geometry},
+    { 5, 0, PC_DISK, "Flexible Disk", NULL},
+    { 6, 0, PC_DISK, "Optical memory", optical_memory},
+    { 7, 0, PC_DISK, "Verify Error Recovery", disk_verify_error_recovery},
+    { 8, 0, PC_DISK, "Caching", disk_cache},
+    { 0xa, 0xf1, PC_DISK, "Parallel ATA control (SAT)", NULL},
+    { 0xb, 0, PC_DISK, "Medium Types Supported", NULL},
+    { 0xc, 0, PC_DISK, "Notch and Partition", disk_notch_parameters},
+    { 0x10, 0, PC_DISK, "XOR control", disk_xor_control},
+    { 0x1c, 1, PC_DISK, "Background control", disk_background},
+};
+static const int mpage_disk_len = sizeof(mpage_disk) / sizeof(mpage_disk[0]);
+
+static struct mpage_name_func mpage_cdvd[] =
+{
+    { 1, 0, PC_CDVD, "Read-Write Error Recovery (cdvd)",
+      cdvd_error_recovery},
+    { 3, 0, PC_CDVD, "MRW", cdvd_mrw},
+    { 5, 0, PC_CDVD, "Write parameters", cdvd_write_param},
+    { 8, 0, PC_CDVD, "Caching", cdvd_cache},
+    { 0xd, 0, PC_CDVD, "CD device parameters", cdvd_device_param},
+    { 0xe, 0, PC_CDVD, "CD audio control", cdvd_audio_control},
+    { 0x18, 0, PC_CDVD, "Feature set support & version", cdvd_feature},
+    { 0x1a, 0, PC_CDVD, "Power Condition", common_power_condition},
+    { 0x1c, 0, PC_CDVD, "Fault/failure reporting control",
+      common_informational},
+    { 0x1d, 0, PC_CDVD, "Time-out & protect", cdvd_timeout},
+    { 0x2a, 0, PC_CDVD, "MM capabilities & mechanical status", cdvd_mm_capab},
+};
+static const int mpage_cdvd_len = sizeof(mpage_cdvd) / sizeof(mpage_cdvd[0]);
+
+static struct mpage_name_func mpage_tape[] =
+{
+    { 1, 0, PC_TAPE, "Read-Write Error Recovery", disk_error_recovery},
+    { 0xf, 0, PC_TAPE, "Data compression", tape_data_compression},
+    { 0x10, 0, PC_TAPE, "Device configuration", tape_dev_config},
+    { 0x10, 1, PC_TAPE, "Device configuration extension", NULL},
+    { 0x11, 0, PC_TAPE, "Medium partition(1)", tape_medium_part1},
+    { 0x12, 0, PC_TAPE, "Medium partition(2)", tape_medium_part2_4},
+    { 0x13, 0, PC_TAPE, "Medium partition(3)", tape_medium_part2_4},
+    { 0x14, 0, PC_TAPE, "Medium partition(4)", tape_medium_part2_4},
+    { 0x1c, 0, PC_TAPE, "Informational Exceptions", common_informational},
+    { 0x1d, 0, PC_TAPE, "Medium configuration", NULL},
+};
+static const int mpage_tape_len = sizeof(mpage_tape) / sizeof(mpage_tape[0]);
+
+static struct mpage_name_func mpage_ses[] =
+{
+    { 0x14, 0, PC_SES, "Enclosure services management", ses_services_manag},
+};
+static const int mpage_ses_len = sizeof(mpage_ses) / sizeof(mpage_ses[0]);
+
+static struct mpage_name_func mpage_smc[] =
+{
+    { 0x1d, 0, PC_SMC, "Element address assignment", NULL},
+    { 0x1e, 0, PC_SMC, "Transport geometry parameters", NULL},
+    { 0x1f, 0, PC_SMC, "Device capabilities", NULL},
+    { 0x1f, 1, PC_SMC, "Extended device capabilities", NULL},
+};
+static const int mpage_smc_len = sizeof(mpage_smc) / sizeof(mpage_smc[0]);
+
+
+#define MAXPARM 64
+
+static int next_parameter;
+static int n_replacement_values;
+static uint64_t replacement_values[MAXPARM];
+static char is_hex[MAXPARM];
+
+#define SMODE_SENSE 0x1a
+#define SMODE_SENSE_10 0x5a
+#define SMODE_SELECT 0x15
+#define SMODE_SELECT_10 0x55
+
+#define MPHEADER6_LEN 4
+#define MPHEADER10_LEN 8
+
+
+/* forward declarations */
+static void usage(const char *);
+static void dump(void *buffer, unsigned int length);
+
+#define DXFER_NONE        0
+#define DXFER_FROM_DEVICE 1
+#define DXFER_TO_DEVICE   2
+
+
+struct scsi_cmnd_io
+{
+    unsigned char * cmnd;       /* ptr to SCSI command block (cdb) */
+    size_t  cmnd_len;           /* number of bytes in SCSI command */
+    int dxfer_dir;              /* DXFER_NONE, DXFER_FROM_DEVICE, or
+                                   DXFER_TO_DEVICE */
+    unsigned char * dxferp;     /* ptr to outgoing/incoming data */
+    size_t dxfer_len;           /* bytes to be transferred to/from dxferp */
+};
+
+#define SENSE_BUFF_LEN   64
+#define CMD_TIMEOUT   60000 /* 60,000 milliseconds (60 seconds) */
+#define EBUFF_SZ   256
+
+
+#define GENERAL_ERROR           1
+#define UNKNOWN_OPCODE          2
+#define BAD_CDB_FIELD           3
+#define UNSUPPORTED_PARAM       4
+#define DEVICE_ATTENTION        5
+#define DEVICE_NOT_READY        6
+
+#define DECODE_FAILED_TRY_HEX   9999
+
+/* Returns 0 -> ok, 1 -> general error, 2 -> unknown opcode,
+   3 -> unsupported field in cdb, 4 -> unsupported param in data-in */
+static int
+do_scsi_io(struct scsi_cmnd_io * sio)
+{
+    unsigned char sense_b[SENSE_BUFF_LEN];
+    struct sg_io_hdr io_hdr;
+    struct sg_scsi_sense_hdr ssh;
+    int res;
+
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = sio->cmnd_len;
+    io_hdr.mx_sb_len = sizeof(sense_b);
+    if (DXFER_NONE == sio->dxfer_dir)
+        io_hdr.dxfer_direction = SG_DXFER_NONE;
+    else
+        io_hdr.dxfer_direction = (DXFER_TO_DEVICE == sio->dxfer_dir) ?
+                                SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = sio->dxfer_len;
+    io_hdr.dxferp = sio->dxferp;
+    io_hdr.cmdp = sio->cmnd;
+    io_hdr.sbp = sense_b;
+    io_hdr.timeout = CMD_TIMEOUT;
+
+    if (trace_cmd) {
+        printf("  cdb:");
+        dump(sio->cmnd, sio->cmnd_len);
+    }
+    if ((trace_cmd > 1) && (DXFER_TO_DEVICE == sio->dxfer_dir)) {
+        printf("  additional data:\n");
+        dump(sio->dxferp, sio->dxfer_len);
+    }
+
+    if (ioctl(glob_fd, SG_IO, &io_hdr) < 0) {
+        perror("do_scsi_cmd: SG_IO error");
+        return GENERAL_ERROR;
+    }
+    res = sg_err_category3(&io_hdr);
+    switch (res) {
+    case SG_LIB_CAT_RECOVERED:
+        sg_chk_n_print3("do_scsi_cmd, continuing", &io_hdr, 1);
+        /* fall through */
+    case SG_LIB_CAT_CLEAN:
+        return 0;
+    default:
+        if (trace_cmd) {
+            char ebuff[EBUFF_SZ];
+
+            snprintf(ebuff, EBUFF_SZ, "do_scsi_io: opcode=0x%x", sio->cmnd[0]);
+            sg_chk_n_print3(ebuff, &io_hdr, 1);
+        }
+        if (sg_normalize_sense(&io_hdr, &ssh)) {
+            if (ILLEGAL_REQUEST == ssh.sense_key) {
+                if (0x20 == ssh.asc)
+                    return UNKNOWN_OPCODE;
+                else if (0x24 == ssh.asc)
+                    return BAD_CDB_FIELD;
+                else if (0x26 == ssh.asc)
+                    return UNSUPPORTED_PARAM;
+            } else if (UNIT_ATTENTION == ssh.sense_key)
+                return DEVICE_ATTENTION;
+            else if (NOT_READY == ssh.sense_key)
+                return DEVICE_NOT_READY;
+        }
+        return GENERAL_ERROR;
+    }
+}
+
+struct mpage_name_func * get_mpage_info(int page_no, int subpage_no,
+                                    struct mpage_name_func * mpp, int elems)
+{
+    int k;
+
+    for (k = 0; k < elems; ++k, ++mpp) {
+        if ((mpp->page == page_no) && (mpp->subpage == subpage_no))
+            return mpp;
+        if (mpp->page > page_no)
+            break;
+    }
+    return NULL;
+}
+
+enum page_class get_page_class(struct mpage_info * mpi)
+{
+    switch (mpi->peri_type)
+    {
+    case 0:
+    case 4:
+    case 7:
+    case 0xe:   /* should be RBC */
+        return PC_DISK;
+    case 1:
+    case 2:
+        return PC_TAPE;
+    case 8:
+        return PC_SMC;
+    case 5:
+        return PC_CDVD;
+    case 0xd:
+        return PC_SES;
+    default:
+        return PC_COMMON;
+    }
+}
+
+struct mpage_name_func * get_mpage_name_func(struct mpage_info * mpi)
+{
+    struct mpage_name_func * mpf = NULL;
+
+    switch (get_page_class(mpi))
+    {
+    case PC_DISK:
+        mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_disk,
+                             mpage_disk_len);
+        break;
+    case PC_CDVD:
+        mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_cdvd,
+                             mpage_cdvd_len);
+        break;
+    case PC_TAPE:
+        mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_tape,
+                             mpage_tape_len);
+        break;
+    case PC_SES:
+        mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_ses,
+                             mpage_ses_len);
+        break;
+    case PC_SMC:
+        mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_smc,
+                             mpage_smc_len);
+        break;
+    case PC_COMMON:
+        /* picked up it catch all next */
+        break;
+    }
+    if (NULL == mpf) {
+        if ((PC_SES != get_page_class(mpi)) && (mpi->inq_byte6 & 0x40)) {
+            /* check for attached enclosure services processor */
+            mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_ses,
+                                 mpage_ses_len);
+        }
+        if ((PC_SMC != get_page_class(mpi)) && (mpi->inq_byte6 & 0x8)) {
+            /* check for attached medium changer device */
+            mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_smc,
+                                 mpage_smc_len);
+        }
+    }
+    if (NULL == mpf)
+        mpf = get_mpage_info(mpi->page, mpi->subpage, mpage_common,
+                             mpage_common_len);
+    return mpf;
+}
+
+
+static char unkn_page_str[64];
+
+static const char *
+get_page_name(struct mpage_info * mpi)
+{
+    struct mpage_name_func * mpf;
+
+    if (MP_LIST_PAGES == mpi->page) {
+        if (MP_LIST_SUBPAGES == mpi->subpage)
+            return "List supported pages and subpages";
+        else
+            return "List supported pages";
+    }
+    mpf = get_mpage_name_func(mpi);
+    if ((NULL == mpf) || (NULL == mpf->name)) {
+        if (mpi->subpage)
+            snprintf(unkn_page_str, sizeof(unkn_page_str),
+                     "page number=0x%x, subpage number=0x%x",
+                     mpi->page, mpi->subpage);
+        else
+            snprintf(unkn_page_str, sizeof(unkn_page_str),
+                     "page number=0x%x", mpi->page);
+        return unkn_page_str;
+    }
+    return mpf->name;
+}
+
+static void
+dump(void *buffer, unsigned int length)
+{
+    unsigned int i;
+
+    printf("    ");
+    for (i = 0; i < length; i++) {
+#if 0
+        if (((unsigned char *) buffer)[i] > 0x20)
+            printf(" %c ", (unsigned int) ((unsigned char *) buffer)[i]);
+        else
+#endif
+            printf("%02x ", (unsigned int) ((unsigned char *) buffer)[i]);
+        if ((i % 16 == 15) && (i < (length - 1))) {
+            printf("\n    ");
+        }
+    }
+    printf("\n");
+
+}
+
+static int
+getnbyte(const unsigned char *pnt, int nbyte)
+{
+    unsigned int result;
+    int i;
+
+    if (nbyte > 4)
+        fprintf(stderr, "getnbyte() limited to 32 bits, nbyte=%d\n", nbyte);
+    result = 0;
+    for (i = 0; i < nbyte; i++)
+        result = (result << 8) | (pnt[i] & 0xff);
+    return result;
+}
+
+static int64_t
+getnbyte_ll(const unsigned char *pnt, int nbyte)
+{
+    int64_t result;
+    int i;
+
+    if (nbyte > 8)
+        fprintf(stderr, "getnbyte_ll() limited to 64 bits, nbyte=%d\n",
+                nbyte);
+    result = 0;
+    for (i = 0; i < nbyte; i++)
+        result = (result << 8) + (pnt[i] & 0xff);
+    return result;
+}
+
+static int
+putnbyte(unsigned char *pnt, unsigned int value,
+                    unsigned int nbyte)
+{
+    int i;
+
+    for (i = nbyte - 1; i >= 0; i--) {
+        pnt[i] = value & 0xff;
+        value = value >> 8;
+    }
+    return 0;
+}
+
+#define REASON_SZ 128
+
+static void
+check_parm_type(int i)
+{
+    char reason[REASON_SZ];
+
+    if (i == 1 && is_hex[next_parameter] != 1) {
+        snprintf(reason, REASON_SZ,
+                 "simple number (pos %i) instead of @ hexdatafield: %"
+                 PRIu64 , next_parameter, replacement_values[next_parameter]);
+        usage(reason);
+    }
+    if (i != 1 && is_hex[next_parameter]) {
+        snprintf(reason, REASON_SZ,
+                 "@ hexdatafield (pos %i) instead of a simple number: %"
+                 PRIu64 , next_parameter, replacement_values[next_parameter]);
+        usage(reason);
+    }
+}
+
+static void
+bitfield(unsigned char *pageaddr, const char * text, int mask, int shift)
+{
+    if (x_interface && replace) {
+        check_parm_type(0);
+        *pageaddr = (*pageaddr & ~(mask << shift)) |
+            ((replacement_values[next_parameter++] & mask) << shift);
+    } else if (x_interface)
+        printf("%d ", (*pageaddr >> shift) & mask);
+    else
+        printf("%-35s%d\n", text, (*pageaddr >> shift) & mask);
+}
+
+#if 0
+static void
+notbitfield(unsigned char *pageaddr, char * text, int mask,
+                        int shift)
+{
+    if (modifiable) {
+        bitfield(pageaddr, text, mask, shift);
+        return;
+    }
+    if (x_interface && replace) {
+        check_parm_type(0);
+        *pageaddr = (*pageaddr & ~(mask << shift)) |
+            (((!replacement_values[next_parameter++]) & mask) << shift);
+    } else if (x_interface)
+        printf("%d ", !((*pageaddr >> shift) & mask));
+    else
+        printf("%-35s%d\n", text, !((*pageaddr >> shift) & mask));
+}
+#endif
+
+static void
+intfield(unsigned char * pageaddr, int nbytes, const char * text)
+{
+    if (x_interface && replace) {
+        check_parm_type(0);
+        putnbyte(pageaddr, replacement_values[next_parameter++], nbytes);
+    } else if (x_interface)
+        printf("%d ", getnbyte(pageaddr, nbytes));
+    else
+        printf("%-35s%d\n", text, getnbyte(pageaddr, nbytes));
+}
+
+static void
+hexfield(unsigned char * pageaddr, int nbytes, const char * text)
+{
+    if (x_interface && replace) {
+        check_parm_type(0);
+        putnbyte(pageaddr, replacement_values[next_parameter++], nbytes);
+    } else if (x_interface)
+        printf("%d ", getnbyte(pageaddr, nbytes));
+    else
+        printf("%-35s0x%x\n", text, getnbyte(pageaddr, nbytes));
+}
+
+static void
+hexdatafield(unsigned char * pageaddr, int nbytes, const char * text)
+{
+    if (x_interface && replace) {
+        unsigned char *ptr;
+        unsigned tmp;
+
+        /* Though in main we ensured that a @string has the right format,
+           we have to check that we are working on a @ hexdata field */
+
+        check_parm_type(1);
+
+        ptr = (unsigned char *) (unsigned long)
+              (replacement_values[next_parameter++]);
+        ptr++;                  /* Skip @ */
+
+        while (*ptr) {
+            if (!nbytes)
+                goto illegal;
+            tmp = (*ptr >= 'a') ? (*ptr - 'a' + 'A') : *ptr;
+            tmp -= (tmp >= 'A') ? 'A' - 10 : '0';
+
+            *pageaddr = tmp << 4;
+            ptr++;
+
+            tmp = (*ptr >= 'a') ? (*ptr - 'a' + 'A') : *ptr;
+            tmp -= (tmp >= 'A') ? 'A' - 10 : '0';
+
+            *pageaddr++ += tmp;
+            ptr++;
+            nbytes--;
+        }
+
+        if (nbytes) {
+          illegal:
+            fputs("sginfo: incorrect number of bytes in @hexdatafield.\n",
+                  stdout);
+            exit(2);
+        }
+    } else if (x_interface) {
+        putchar('@');
+        while (nbytes-- > 0)
+            printf("%02x", *pageaddr++);
+        putchar(' ');
+    } else {
+        printf("%-35s0x", text);
+        while (nbytes-- > 0)
+            printf("%02x", *pageaddr++);
+        putchar('\n');
+    }
+}
+
+
+/* Offset into mode sense (6 or 10 byte) response that actual mode page
+ * starts at (relative to resp[0]). Returns -1 if problem */
+static int
+modePageOffset(const unsigned char * resp, int len, int modese_6)
+{
+    int bd_len;
+    int resp_len = 0;
+    int offset = -1;
+
+    if (resp) {
+        if (modese_6) {
+            resp_len = resp[0] + 1;
+            bd_len = resp[3];
+            offset = bd_len + MPHEADER6_LEN;
+        } else {
+            resp_len = (resp[0] << 8) + resp[1] + 2;
+            bd_len = (resp[6] << 8) + resp[7];
+            /* LongLBA doesn't change this calculation */
+            offset = bd_len + MPHEADER10_LEN;
+        }
+        if ((offset + 2) > len) {
+            printf("modePageOffset: raw_curr too small, offset=%d "
+                   "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len);
+            offset = -1;
+        } else if ((offset + 2) > resp_len) {
+            printf("modePageOffset: response length too short, resp_len=%d"
+                   " offset=%d bd_len=%d\n", resp_len, offset, bd_len);
+            offset = -1;
+        }
+    }
+    return offset;
+}
+
+/* Reads mode (sub-)page via 6 byte MODE SENSE, returns 0 if ok */
+static int
+get_mode_page6(struct mpage_info * mpi, int dbd, unsigned char * resp,
+               int sngl_fetch)
+{
+    int status, off;
+    unsigned char cmd[6];
+    struct scsi_cmnd_io sci;
+    int initial_len = (sngl_fetch ? MAX_RESP6_SIZE : 4);
+
+    memset(resp, 0, 4);
+    cmd[0] = SMODE_SENSE;       /* MODE SENSE (6) */
+    cmd[1] = 0x00 | (dbd ? 0x8 : 0); /* disable block descriptors bit */
+    cmd[2] = (mpi->page_control << 6) | mpi->page;
+    cmd[3] = mpi->subpage;      /* subpage code */
+    cmd[4] = initial_len;
+    cmd[5] = 0x00;              /* control */
+
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_FROM_DEVICE;
+    sci.dxfer_len = initial_len;
+    sci.dxferp = resp;
+    status = do_scsi_io(&sci);
+    if (status) {
+        if (mpi->subpage)
+            fprintf(stdout, ">>> Unable to read %s mode page 0x%x, subpage "
+                    "0x%x [mode_sense_6]\n", get_page_name(mpi), mpi->page,
+                    mpi->subpage);
+        else
+            fprintf(stdout, ">>> Unable to read %s mode page (0x%x) "
+                    "[mode_sense_6]\n", get_page_name(mpi), mpi->page);
+        return status;
+    }
+    mpi->resp_len = resp[0] + 1;
+    if (sngl_fetch) {
+        if (trace_cmd > 1) {
+            off = modePageOffset(resp, mpi->resp_len, 1);
+            if (off >= 0) {
+                printf("  cdb response:\n");
+                dump(resp, mpi->resp_len);
+            }
+        }
+        return status;
+    }
+
+    cmd[4] = mpi->resp_len;
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_FROM_DEVICE;
+    sci.dxfer_len = mpi->resp_len;
+    sci.dxferp = resp;
+    status = do_scsi_io(&sci);
+    if (status) {
+        if (mpi->subpage)
+            fprintf(stdout, ">>> Unable to read %s mode page 0x%x, subpage "
+                    "0x%x [mode_sense_6]\n", get_page_name(mpi), mpi->page,
+                    mpi->subpage);
+        else
+            fprintf(stdout, ">>> Unable to read %s mode page (0x%x) "
+                    "[mode_sense_6]\n", get_page_name(mpi), mpi->page);
+    } else if (trace_cmd > 1) {
+        off = modePageOffset(resp, mpi->resp_len, 1);
+        if (off >= 0) {
+            printf("  cdb response:\n");
+            dump(resp, mpi->resp_len);
+        }
+    }
+    return status;
+}
+
+/* Reads mode (sub-)page via 10 byte MODE SENSE, returns 0 if ok */
+static int
+get_mode_page10(struct mpage_info * mpi, int llbaa, int dbd,
+                unsigned char * resp, int sngl_fetch)
+{
+    int status, off;
+    unsigned char cmd[10];
+    struct scsi_cmnd_io sci;
+    int initial_len = (sngl_fetch ? MAX_RESP10_SIZE : 4);
+
+    memset(resp, 0, 4);
+    cmd[0] = SMODE_SENSE_10;     /* MODE SENSE (10) */
+    cmd[1] = 0x00 | (llbaa ? 0x10 : 0) | (dbd ? 0x8 : 0);
+    cmd[2] = (mpi->page_control << 6) | mpi->page;
+    cmd[3] = mpi->subpage;
+    cmd[4] = 0x00;              /* (reserved) */
+    cmd[5] = 0x00;              /* (reserved) */
+    cmd[6] = 0x00;              /* (reserved) */
+    cmd[7] = (initial_len >> 8) & 0xff;
+    cmd[8] = initial_len & 0xff;
+    cmd[9] = 0x00;              /* control */
+
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_FROM_DEVICE;
+    sci.dxfer_len = initial_len;
+    sci.dxferp = resp;
+    status = do_scsi_io(&sci);
+    if (status) {
+        if (mpi->subpage)
+            fprintf(stdout, ">>> Unable to read %s mode page 0x%x, subpage "
+                    "0x%x [mode_sense_10]\n", get_page_name(mpi), mpi->page,
+                    mpi->subpage);
+        else
+            fprintf(stdout, ">>> Unable to read %s mode page (0x%x) "
+                    "[mode_sense_10]\n", get_page_name(mpi), mpi->page);
+            return status;
+    }
+    mpi->resp_len = (resp[0] << 8) + resp[1] + 2;
+    if (sngl_fetch) {
+        if (trace_cmd > 1) {
+            off = modePageOffset(resp, mpi->resp_len, 0);
+            if (off >= 0) {
+                printf("  cdb response:\n");
+                dump(resp, mpi->resp_len);
+            }
+        }
+        return status;
+    }
+
+    cmd[7] = (mpi->resp_len >> 8) & 0xff;
+    cmd[8] = (mpi->resp_len & 0xff);
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_FROM_DEVICE;
+    sci.dxfer_len = mpi->resp_len;
+    sci.dxferp = resp;
+    status = do_scsi_io(&sci);
+    if (status) {
+        if (mpi->subpage)
+            fprintf(stdout, ">>> Unable to read %s mode page 0x%x, subpage "
+                    "0x%x [mode_sense_10]\n", get_page_name(mpi), mpi->page,
+                    mpi->subpage);
+        else
+            fprintf(stdout, ">>> Unable to read %s mode page (0x%x) "
+                    "[mode_sense_10]\n", get_page_name(mpi), mpi->page);
+    } else if (trace_cmd > 1) {
+        off = modePageOffset(resp, mpi->resp_len, 0);
+        if (off >= 0) {
+            printf("  cdb response:\n");
+            dump(resp, mpi->resp_len);
+        }
+    }
+    return status;
+}
+
+static int
+get_mode_page(struct mpage_info * mpi, int dbd, unsigned char * resp)
+{
+    int res;
+
+    if (mode6byte)
+        res = get_mode_page6(mpi, dbd, resp, single_fetch);
+    else
+        res = get_mode_page10(mpi, 0, dbd, resp, single_fetch);
+    if (UNKNOWN_OPCODE == res)
+        fprintf(stdout, ">>>>> Try command again with%s '-6' "
+                "argument\n", (mode6byte ? "out the" : " a"));
+    else if (mpi->subpage && (BAD_CDB_FIELD == res))
+        fprintf(stdout, ">>>>> device doesn't seem to support "
+                "subpages\n");
+    else if (DEVICE_ATTENTION == res)
+        fprintf(stdout, ">>>>> device reports UNIT ATTENTION, check it or"
+                " just try again\n");
+    else if (DEVICE_NOT_READY == res)
+        fprintf(stdout, ">>>>> device NOT READY, does it need media?\n");
+    return res;
+}
+
+/* Contents should point to the mode parameter header that we obtained
+   in a prior read operation.  This way we do not have to work out the
+   format of the beast. Assume 0 or 1 block descriptors. */
+static int
+put_mode_page6(struct mpage_info * mpi, const unsigned char * msense6_resp,
+               int sp_bit)
+{
+    int status;
+    int bdlen, resplen;
+    unsigned char cmd[6];
+    struct scsi_cmnd_io sci;
+
+    bdlen = msense6_resp[3];
+    resplen = msense6_resp[0] + 1;
+
+    cmd[0] = SMODE_SELECT;
+    cmd[1] = 0x10 | (sp_bit ? 1 : 0); /* always set PF bit */
+    cmd[2] = 0x00;
+    cmd[3] = 0x00;              /* (reserved) */
+    cmd[4] = resplen;           /* parameter list length */
+    cmd[5] = 0x00;              /* (reserved) */
+
+    memcpy(cbuffer1, msense6_resp, resplen);
+    cbuffer1[0] = 0;            /* Mask off the mode data length
+                                   - reserved field */
+    cbuffer1[2] = 0;            /* device-specific parameter is not defined
+                                   and/or reserved for mode select */
+
+#if 0   /* leave block descriptor alone */
+    if (bdlen > 0) {
+        memset(cbuffer1 + MPHEADER6_LEN, 0, 4);  /* clear 'number of blocks'
+                                                   for DAD device */
+        cbuffer1[MPHEADER6_LEN + 4] = 0; /* clear DAD density code. Why? */
+        /* leave DAD block length */
+    }
+#endif
+    cbuffer1[MPHEADER6_LEN + bdlen] &= 0x7f;   /* Mask PS bit */
+
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_TO_DEVICE;
+    sci.dxfer_len = resplen;
+    sci.dxferp = cbuffer1;
+    status = do_scsi_io(&sci);
+    if (status) {
+        if (mpi->subpage)
+            fprintf(stdout, ">>> Unable to store %s mode page 0x%x,"
+                    " subpage 0x%x [msel_6]\n", get_page_name(mpi),
+                    mpi->page, mpi->subpage);
+        else
+            fprintf(stdout, ">>> Unable to store %s mode page 0x%x [msel_6]\n",
+                    get_page_name(mpi), mpi->page);
+    }
+    return status;
+}
+
+/* Contents should point to the mode parameter header that we obtained
+   in a prior read operation.  This way we do not have to work out the
+   format of the beast. Assume 0 or 1 block descriptors. */
+static int
+put_mode_page10(struct mpage_info * mpi, const unsigned char * msense10_resp,
+                int sp_bit)
+{
+    int status;
+    int bdlen, resplen;
+    unsigned char cmd[10];
+    struct scsi_cmnd_io sci;
+
+    bdlen = (msense10_resp[6] << 8) + msense10_resp[7];
+    resplen = (msense10_resp[0] << 8) + msense10_resp[1] + 2;
+
+    cmd[0] = SMODE_SELECT_10;
+    cmd[1] = 0x10 | (sp_bit ? 1 : 0); /* always set PF bit */
+    cmd[2] = 0x00;              /* (reserved) */
+    cmd[3] = 0x00;              /* (reserved) */
+    cmd[4] = 0x00;              /* (reserved) */
+    cmd[5] = 0x00;              /* (reserved) */
+    cmd[6] = 0x00;              /* (reserved) */
+    cmd[7] = (resplen >> 8) & 0xff;
+    cmd[8] = resplen & 0xff;
+    cmd[9] = 0x00;              /* (reserved) */
+
+    memcpy(cbuffer1, msense10_resp, resplen);
+    cbuffer1[0] = 0;            /* Mask off the mode data length */
+    cbuffer1[1] = 0;            /* Mask off the mode data length */
+    cbuffer1[3] = 0;            /* device-specific parameter is not defined
+                                   and/or reserved for mode select */
+#if 0   /* leave block descriptor alone */
+    if (bdlen > 0) {
+        memset(cbuffer1 + MPHEADER10_LEN, 0, 4);  /* clear 'number of blocks'
+                                                    for DAD device */
+        cbuffer1[MPHEADER10_LEN + 4] = 0; /* clear DAD density code. Why? */
+        /* leave DAD block length */
+    }
+#endif
+    cbuffer1[MPHEADER10_LEN + bdlen] &= 0x7f;   /* Mask PS bit */
+
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_TO_DEVICE;
+    sci.dxfer_len = resplen;
+    sci.dxferp = cbuffer1;
+    status = do_scsi_io(&sci);
+    if (status) {
+        if (mpi->subpage)
+            fprintf(stdout, ">>> Unable to store %s mode page 0x%x,"
+                    " subpage 0x%x [msel_10]\n", get_page_name(mpi),
+                    mpi->page, mpi->subpage);
+        else
+            fprintf(stdout, ">>> Unable to store %s mode page 0x%x "
+                    "[msel_10]\n", get_page_name(mpi), mpi->page);
+    }
+    return status;
+}
+
+static int
+put_mode_page(struct mpage_info * mpi, const unsigned char * msense_resp)
+{
+    if (mode6byte)
+        return put_mode_page6(mpi, msense_resp, ! negate_sp_bit);
+    else
+        return put_mode_page10(mpi, msense_resp, ! negate_sp_bit);
+}
+
+static int
+setup_mode_page(struct mpage_info * mpi, int nparam, unsigned char * buff,
+                unsigned char ** o_pagestart)
+{
+    int status, offset, rem_pglen;
+    unsigned char * pgp;
+
+    status = get_mode_page(mpi, 0, buff);
+    if (status) {
+        printf("\n");
+        return status;
+    }
+    offset = modePageOffset(buff, mpi->resp_len, mode6byte);
+    if (offset < 0) {
+        fprintf(stdout, "mode page=0x%x has bad page format\n", mpi->page);
+        fprintf(stdout, "   perhaps '-z' switch may help\n");
+        return -1;
+    }
+    pgp = buff + offset;
+    *o_pagestart = pgp;
+    rem_pglen = (0x40 & pgp[0]) ? ((pgp[2] << 8) + pgp[3]) : pgp[1];
+
+    if (x_interface && replace) {
+        if ((nparam && (n_replacement_values != nparam)) ||
+            ((! nparam) && (n_replacement_values != rem_pglen))) {
+            fprintf(stdout, "Wrong number of replacement values (%i instead "
+                    "of %i)\n", n_replacement_values,
+                    nparam ? nparam : rem_pglen);
+            return 1;
+        }
+        next_parameter = 1;
+    }
+    return 0;
+}
+
+static int
+get_protocol_id(int port_not_lu, unsigned char * buff, int * proto_idp,
+                int * offp)
+{
+    int status, off, proto_id, spf;
+    struct mpage_info mp_i;
+    char b[64];
+
+    memset(&mp_i, 0, sizeof(mp_i));
+    mp_i.page = (port_not_lu ? 0x19 : 0x18);
+    /* N.B. getting port or lu specific mode page (not subpage) */
+    status = get_mode_page(&mp_i, 0, buff);
+    if (status)
+        return status;
+    off = modePageOffset(buff, mp_i.resp_len, mode6byte);
+    if (off < 0)
+        return off;
+    spf = (buff[off] & 0x40) ? 1 : 0;  /* subpages won't happen here */
+    proto_id = buff[off + (spf ? 5 : 2)] & 0xf;
+    if (trace_cmd > 0)
+        printf("Protocol specific %s, protocol_id=%s\n",
+               (port_not_lu ? "port" : "lu"),
+               sg_get_trans_proto_str(proto_id, sizeof(b), b));
+    if (proto_idp)
+        *proto_idp = proto_id;
+    if (offp)
+        *offp = off;
+    return 0;
+}
+
+static int
+disk_geometry(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 9, cbuffer, &pagestart);
+    if (status)
+        return status;
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------------------\n");
+    };
+    intfield(pagestart + 2, 3, "Number of cylinders");
+    intfield(pagestart + 5, 1, "Number of heads");
+    intfield(pagestart + 6, 3, "Starting cyl. write precomp");
+    intfield(pagestart + 9, 3, "Starting cyl. reduced current");
+    intfield(pagestart + 12, 2, "Device step rate");
+    intfield(pagestart + 14, 3, "Landing Zone Cylinder");
+    bitfield(pagestart + 17, "RPL", 3, 0);
+    intfield(pagestart + 18, 1, "Rotational Offset");
+    intfield(pagestart + 20, 2, "Rotational Rate");
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+common_disconnect_reconnect(struct mpage_info * mpi,
+                                       const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 11, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("------------------------------------\n");
+    };
+    intfield(pagestart + 2, 1, "Buffer full ratio");
+    intfield(pagestart + 3, 1, "Buffer empty ratio");
+    intfield(pagestart + 4, 2, "Bus Inactivity Limit (SAS: 100us)");
+    intfield(pagestart + 6, 2, "Disconnect Time Limit");
+    intfield(pagestart + 8, 2, "Connect Time Limit (SAS: 100us)");
+    intfield(pagestart + 10, 2, "Maximum Burst Size");
+    bitfield(pagestart + 12, "EMDP", 1, 7);
+    bitfield(pagestart + 12, "Fair Arbitration (fcp:faa,fab,fac)", 0x7, 4);
+    bitfield(pagestart + 12, "DIMM", 1, 3);
+    bitfield(pagestart + 12, "DTDC", 0x7, 0);
+    intfield(pagestart + 14, 2, "First Burst Size");
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+
+}
+
+static int
+common_control(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 21, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------\n");
+    }
+    bitfield(pagestart + 2, "TST", 0x7, 5);
+    bitfield(pagestart + 2, "TMF_ONLY", 1, 4);
+    bitfield(pagestart + 2, "D_SENSE", 1, 2);
+    bitfield(pagestart + 2, "GLTSD", 1, 1);
+    bitfield(pagestart + 2, "RLEC", 1, 0);
+    bitfield(pagestart + 3, "Queue Algorithm Modifier", 0xf, 4);
+    bitfield(pagestart + 3, "QErr", 0x3, 1);
+    bitfield(pagestart + 3, "DQue [obsolete]", 1, 0);
+    bitfield(pagestart + 4, "TAS", 1, 7);
+    bitfield(pagestart + 4, "RAC", 1, 6);
+    bitfield(pagestart + 4, "UA_INTLCK_CTRL", 0x3, 4);
+    bitfield(pagestart + 4, "SWP", 1, 3);
+    bitfield(pagestart + 4, "RAERP [obs.]", 1, 2);
+    bitfield(pagestart + 4, "UAAERP [obs.]", 1, 1);
+    bitfield(pagestart + 4, "EAERP [obs.]", 1, 0);
+    bitfield(pagestart + 5, "ATO", 1, 7);
+    bitfield(pagestart + 5, "TAS", 1, 6);
+    bitfield(pagestart + 5, "AUTOLOAD MODE", 0x7, 0);
+    intfield(pagestart + 6, 2, "Ready AER Holdoff Period [obs.]");
+    intfield(pagestart + 8, 2, "Busy Timeout Period");
+    intfield(pagestart + 10, 2, "Extended self-test completion time");
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+common_control_extension(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 4, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode subpage (0x%x,0x%x)\n", get_page_name(mpi), mpi->page,
+               mpi->subpage);
+        printf("--------------------------------------------\n");
+    }
+    bitfield(pagestart + 4, "TCMOS", 1, 2);
+    bitfield(pagestart + 4, "SCSIP", 1, 1);
+    bitfield(pagestart + 4, "IALUAE", 1, 0);
+    bitfield(pagestart + 5, "Initial Priority", 0xf, 0);
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+common_informational(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 10, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "PERF", 1, 7);
+    bitfield(pagestart + 2, "EBF", 1, 5);
+    bitfield(pagestart + 2, "EWASC", 1, 4);
+    bitfield(pagestart + 2, "DEXCPT", 1, 3);
+    bitfield(pagestart + 2, "TEST", 1, 2);
+    bitfield(pagestart + 2, "EBACKERR", 1, 1);
+    bitfield(pagestart + 2, "LOGERR", 1, 0);
+    bitfield(pagestart + 3, "MRIE", 0xf, 0);
+    intfield(pagestart + 4, 4, "Interval Timer");
+    intfield(pagestart + 8, 4, "Report Count");
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+disk_error_recovery(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 14, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "AWRE", 1, 7);
+    bitfield(pagestart + 2, "ARRE", 1, 6);
+    bitfield(pagestart + 2, "TB", 1, 5);
+    bitfield(pagestart + 2, "RC", 1, 4);
+    bitfield(pagestart + 2, "EER", 1, 3);
+    bitfield(pagestart + 2, "PER", 1, 2);
+    bitfield(pagestart + 2, "DTE", 1, 1);
+    bitfield(pagestart + 2, "DCR", 1, 0);
+    intfield(pagestart + 3, 1, "Read Retry Count");
+    intfield(pagestart + 4, 1, "Correction Span");
+    intfield(pagestart + 5, 1, "Head Offset Count");
+    intfield(pagestart + 6, 1, "Data Strobe Offset Count");
+    intfield(pagestart + 8, 1, "Write Retry Count");
+    intfield(pagestart + 10, 2, "Recovery Time Limit (ms)");
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+cdvd_error_recovery(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 10, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("------------------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "AWRE", 1, 7);
+    bitfield(pagestart + 2, "ARRE", 1, 6);
+    bitfield(pagestart + 2, "TB", 1, 5);
+    bitfield(pagestart + 2, "RC", 1, 4);
+    bitfield(pagestart + 2, "PER", 1, 2);
+    bitfield(pagestart + 2, "DTE", 1, 1);
+    bitfield(pagestart + 2, "DCR", 1, 0);
+    intfield(pagestart + 3, 1, "Read Retry Count");
+    bitfield(pagestart + 7, "EMCDR", 3, 0);
+    intfield(pagestart + 8, 1, "Write Retry Count");
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+cdvd_mrw(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 1, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("------------------------------------------------\n");
+    }
+    bitfield(pagestart + 3, "LBA space", 1, 0);
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+disk_notch_parameters(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 6, cbuffer, &pagestart);
+    if (status) {
+        fprintf(stdout, "Special case: only give 6 fields to '-XR' since"
+                " 'Pages Notched' is unchangeable\n");
+        return status;
+    }
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------------------\n");
+    };
+    bitfield(pagestart + 2, "Notched Drive", 1, 7);
+    bitfield(pagestart + 2, "Logical or Physical Notch", 1, 6);
+    intfield(pagestart + 4, 2, "Max # of notches");
+    intfield(pagestart + 6, 2, "Active Notch");
+    if (pagestart[2] & 0x40) {
+        intfield(pagestart + 8, 4, "Starting Boundary");
+        intfield(pagestart + 12, 4, "Ending Boundary");
+    } else {           /* Hex is more meaningful for physical notches */
+        hexfield(pagestart + 8, 4, "Starting Boundary");
+        hexfield(pagestart + 12, 4, "Ending Boundary");
+    }
+
+    if (x_interface && !replace) {
+#if 1
+        ;       /* do nothing, skip this field */
+#else
+        if (1 == mpi->page_control)     /* modifiable */
+            printf("0");
+        else
+            printf("0x%8.8x%8.8x", getnbyte(pagestart + 16, 4),
+                   getnbyte(pagestart + 20, 4));
+#endif
+    };
+    if (!x_interface)
+        printf("Pages Notched                      %8.8x %8.8x\n",
+               getnbyte(pagestart + 16, 4), getnbyte(pagestart + 20, 4));
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static const char *
+formatname(int format)
+{
+    switch(format) {
+        case 0x0: return "logical block addresses (32 bit)";
+        case 0x3: return "logical block addresses (64 bit)";
+        case 0x4: return "bytes from index [Cyl:Head:Off]\n"
+                "Offset -1 marks whole track as bad.\n";
+        case 0x5: return "physical blocks [Cyl:Head:Sect]\n"
+                "Sector -1 marks whole track as bad.\n";
+    }
+    return "Weird, unknown format";
+}
+
+static int
+read_defect_list(int grown_only)
+{
+    int i, len, reallen, table, k, defect_format;
+    int status = 0;
+    int header = 1;
+    int sorthead = 0;
+    unsigned char cmd[10];
+    unsigned char cmd12[12];
+    unsigned char *df = NULL;
+    unsigned char *bp = NULL;
+    unsigned char *heapp = NULL;
+    unsigned int  *headsp = NULL;
+    int trunc;
+    struct scsi_cmnd_io sci;
+
+    if (defectformat == HEAD_SORT_TOKEN) {
+        defectformat = 0x04;
+        sorthead = 1;
+        headsp = (unsigned int *)malloc(sizeof(unsigned int) * MAX_HEADS);
+        if (headsp == NULL) {
+           perror("malloc failed");
+           return status;
+        }
+        memset(headsp,0,sizeof(unsigned int) * MAX_HEADS);
+    }
+    for (table = grown_only; table < 2; table++) {
+        if (heapp) {
+            free(heapp);
+            heapp = NULL;
+        }
+        bp = cbuffer;
+        memset(bp, 0, 4);
+        trunc = 0;
+        reallen = -1;
+
+        cmd[0] = 0x37;          /* READ DEFECT DATA (10) */
+        cmd[1] = 0x00;
+        cmd[2] = (table ? 0x08 : 0x10) | defectformat;  /*  List, Format */
+        cmd[3] = 0x00;          /* (reserved) */
+        cmd[4] = 0x00;          /* (reserved) */
+        cmd[5] = 0x00;          /* (reserved) */
+        cmd[6] = 0x00;          /* (reserved) */
+        cmd[7] = 0x00;          /* Alloc len */
+        cmd[8] = 0x04;          /* Alloc len (size finder) */
+        cmd[9] = 0x00;          /* control */
+
+        sci.cmnd = cmd;
+        sci.cmnd_len = sizeof(cmd);
+        sci.dxfer_dir = DXFER_FROM_DEVICE;
+        sci.dxfer_len = 4;
+        sci.dxferp = bp;
+        i = do_scsi_io(&sci);
+        if (i) {
+            fprintf(stdout, ">>> Unable to read %s defect data.\n",
+                    (table ? "grown (GLIST)" : "primary (PLIST)"));
+            status |= i;
+            continue;
+        }
+        if (trace_cmd > 1) {
+            printf("  cdb response:\n");
+            dump(bp, 4);
+        }
+        /*
+         * Check validity of response:
+         * bp[0] reserved, must be zero
+         * bp[1] bits 7-5 reserved, must be zero
+         * bp[1] bits 4-3 should match table requested
+         */
+        if (0 != bp[0] || (table ? 0x08 : 0x10) != (bp[1] & 0xf8)) {
+            fprintf(stdout, ">>> Invalid header for %s defect list.\n",
+                    (table ? "grown (GLIST)" : "primary (PLIST)"));
+            status |= 1;
+            continue;
+        }
+        if (header) {
+            printf("Defect Lists\n"
+                   "------------\n");
+            header = 0;
+        }
+        len = (bp[2] << 8) + bp[3];
+        if (len < 0xfff8)
+            reallen = len;
+        else {
+            /*
+             * List length is at or over capacity of READ DEFECT DATA (10)
+             * Try to get actual length with READ DEFECT DATA (12)
+             */
+            bp = cbuffer;
+            memset(bp, 0, 8);
+            cmd12[0] = 0xB7;          /* READ DEFECT DATA (12) */
+            cmd12[1] = (table ? 0x08 : 0x10) | defectformat;/*  List, Format */
+            cmd12[2] = 0x00;          /* (reserved) */
+            cmd12[3] = 0x00;          /* (reserved) */
+            cmd12[4] = 0x00;          /* (reserved) */
+            cmd12[5] = 0x00;          /* (reserved) */
+            cmd12[6] = 0x00;          /* Alloc len */
+            cmd12[7] = 0x00;          /* Alloc len */
+            cmd12[8] = 0x00;          /* Alloc len */
+            cmd12[9] = 0x08;          /* Alloc len (size finder) */
+            cmd12[10] = 0x00;         /* reserved */
+            cmd12[11] = 0x00;         /* control */
+
+            sci.cmnd = cmd12;
+            sci.cmnd_len = sizeof(cmd12);
+            sci.dxfer_dir = DXFER_FROM_DEVICE;
+            sci.dxfer_len = 8;
+            sci.dxferp = bp;
+            i = do_scsi_io(&sci);
+            if (i) {
+                if (trace_cmd) {
+                    fprintf(stdout, ">>> No 12 byte command support, "
+                            "but list is too long for 10 byte version.\n"
+                            "List will be truncated at 8191 elements\n");
+                }
+                goto trytenbyte;
+            }
+            if (trace_cmd > 1) {
+                printf("  cdb response:\n");
+                dump(bp, 8);
+            }
+            /*
+             * Check validity of response:
+             *    bp[0], bp[2] and bp[3] reserved, must be zero
+             *    bp[1] bits 7-5 reserved, must be zero
+             *    bp[1] bits 4-3 should match table we requested
+             */
+            if (0 != bp[0] || 0 != bp[2] || 0 != bp[3] ||
+                    ((table ? 0x08 : 0x10) != (bp[1] & 0xf8))) {
+                if (trace_cmd)
+                    fprintf(stdout,
+                            ">>> Invalid header for %s defect list.\n",
+                            (table ? "grown (GLIST)" : "primary (PLIST)"));
+                goto trytenbyte;
+            }
+            len = (bp[4] << 24) + (bp[5] << 16) + (bp[6] << 8) + bp[7];
+            reallen = len;
+        }
+
+        if (len > 0) {
+            k = len + 8;              /* length of defect list + header */
+            if (k > (int)sizeof(cbuffer)) {
+                heapp = (unsigned char *)malloc(k);
+
+                if (len > 0x80000 && NULL == heapp) {
+                    len = 0x80000;      /* go large: 512 KB */
+                    k = len + 8;
+                    heapp = (unsigned char *)malloc(k);
+                }
+                if (heapp != NULL)
+                    bp = heapp;
+            }
+            if (len > 0xfff0 && heapp != NULL) {
+                cmd12[0] = 0xB7;          /* READ DEFECT DATA (12) */
+                cmd12[1] = (table ? 0x08 : 0x10) | defectformat;
+                                                /*  List, Format */
+                cmd12[2] = 0x00;          /* (reserved) */
+                cmd12[3] = 0x00;          /* (reserved) */
+                cmd12[4] = 0x00;          /* (reserved) */
+                cmd12[5] = 0x00;          /* (reserved) */
+                cmd12[6] = 0x00;          /* Alloc len */
+                cmd12[7] = (k >> 16) & 0xff;     /* Alloc len */
+                cmd12[8] = (k >> 8) & 0xff;      /* Alloc len */
+                cmd12[9] = (k & 0xff);    /* Alloc len */
+                cmd12[10] = 0x00;         /* reserved */
+                cmd12[11] = 0x00;         /* control */
+
+                sci.cmnd = cmd12;
+                sci.cmnd_len = sizeof(cmd12);
+                sci.dxfer_dir = DXFER_FROM_DEVICE;
+                sci.dxfer_len = k;
+                sci.dxferp = bp;
+                i = do_scsi_io(&sci);
+                if (i)
+                    goto trytenbyte;
+                if (trace_cmd > 1) {
+                    printf("  cdb response:\n");
+                    dump(bp, 8);
+                }
+                reallen = (bp[4] << 24) + (bp[5] << 16) + (bp[6] << 8) +
+                          bp[7];
+                if (reallen > len) {
+                    trunc = 1;
+                }
+                df = (unsigned char *) (bp + 8);
+            }
+            else {
+trytenbyte:
+                if (len > 0xfff8) {
+                    len = 0xfff8;
+                    trunc = 1;
+                }
+                k = len + 4;            /* length of defect list + header */
+                if (k > (int)sizeof(cbuffer) && NULL == heapp) {
+                    heapp = (unsigned char *)malloc(k);
+                    if (heapp != NULL)
+                        bp = heapp;
+                }
+                if (k > (int)sizeof(cbuffer) && NULL == heapp) {
+                    bp = cbuffer;
+                    k = sizeof(cbuffer);
+                    len = k - 4;
+                    trunc = 1;
+                }
+                cmd[0] = 0x37;          /* READ DEFECT DATA (10) */
+                cmd[1] = 0x00;
+                cmd[2] = (table ? 0x08 : 0x10) | defectformat;
+                                        /*  List, Format */
+                cmd[3] = 0x00;          /* (reserved) */
+                cmd[4] = 0x00;          /* (reserved) */
+                cmd[5] = 0x00;          /* (reserved) */
+                cmd[6] = 0x00;          /* (reserved) */
+                cmd[7] = (k >> 8);      /* Alloc len */
+                cmd[8] = (k & 0xff);    /* Alloc len */
+                cmd[9] = 0x00;          /* control */
+
+                sci.cmnd = cmd;
+                sci.cmnd_len = sizeof(cmd);
+                sci.dxfer_dir = DXFER_FROM_DEVICE;
+                sci.dxfer_len = k;
+                sci.dxferp = bp;
+                i = do_scsi_io(&sci);
+                df = (unsigned char *) (bp + 4);
+            }
+        }
+        if (i) {
+            fprintf(stdout, ">>> Unable to read %s defect data.\n",
+                    (table ? "grown (GLIST)" : "primary (PLIST)"));
+            status |= i;
+            continue;
+        }
+        else {
+            if (table && !status && !sorthead)
+                printf("\n");
+            defect_format = (bp[1] & 0x7);
+            if (-1 == reallen) {
+                printf("at least ");
+                reallen = len;
+            }
+            printf("%d entries (%d bytes) in %s table.\n",
+                   reallen / ((0 == defect_format) ? 4 : 8), reallen,
+                   table ? "grown (GLIST)" : "primary (PLIST)");
+            if (!sorthead)
+                printf("Format (%x) is: %s\n", defect_format,
+                   formatname(defect_format));
+            i = 0;
+            switch (defect_format) {
+            case 4:     /* bytes from index */
+                while (len > 0) {
+                    snprintf((char *)cbuffer1, 40, "%6d:%3u:%8d",
+                             getnbyte(df, 3), df[3], getnbyte(df + 4, 4));
+                    if (sorthead == 0)
+                        printf("%19s", (char *)cbuffer1);
+                    else
+                        if (df[3] < MAX_HEADS) headsp[df[3]]++;
+                    len -= 8;
+                    df += 8;
+                    i++;
+                    if (i >= 4 && !sorthead) {
+                        printf("\n");
+                        i = 0;
+                    }
+                    else if (!sorthead) printf("|");
+                }
+            case 5:     /* physical sector */
+                while (len > 0) {
+                    snprintf((char *)cbuffer1, 40, "%6d:%2u:%5d",
+                             getnbyte(df, 3),
+                             df[3], getnbyte(df + 4, 4));
+                    if (sorthead == 0)
+                        printf("%15s", (char *)cbuffer1);
+                    else
+                        if (df[3] < MAX_HEADS) headsp[df[3]]++;
+                    len -= 8;
+                    df += 8;
+                    i++;
+                    if (i >= 5 && !sorthead) {
+                        printf("\n");
+                        i = 0;
+                    }
+                    else if (!sorthead) printf("|");
+                }
+            case 0:     /* lba (32 bit) */
+                while (len > 0) {
+                    printf("%10d", getnbyte(df, 4));
+                    len -= 4;
+                    df += 4;
+                    i++;
+                    if (i >= 7) {
+                        printf("\n");
+                        i = 0;
+                    }
+                    else
+                        printf("|");
+                }
+            case 3:     /* lba (64 bit) */
+                while (len > 0) {
+                    printf("%15" PRId64 , getnbyte_ll(df, 8));
+                    len -= 8;
+                    df += 8;
+                    i++;
+                    if (i >= 5) {
+                        printf("\n");
+                        i = 0;
+                    }
+                    else
+                        printf("|");
+                }
+                break;
+            default:
+                printf("unknown defect list format: %d\n", defect_format);
+                break;
+            }
+            if (i && !sorthead)
+                printf("\n");
+        }
+        if (trunc)
+                printf("[truncated]\n");
+    }
+    if (heapp) {
+        free(heapp);
+        heapp = NULL;
+    }
+    if (sorthead) {
+        printf("Format is: [head:# entries for this head in list]\n\n");
+        for (i=0; i<MAX_HEADS; i++) {
+            if (headsp[i] > 0) {
+               printf("%3d: %u\n", i, headsp[i]);
+            }
+        }
+    }
+    printf("\n");
+    return status;
+}
+
+static int
+disk_cache(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 21, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------\n");
+    };
+    bitfield(pagestart + 2, "Initiator Control", 1, 7);
+    bitfield(pagestart + 2, "ABPF", 1, 6);
+    bitfield(pagestart + 2, "CAP", 1, 5);
+    bitfield(pagestart + 2, "DISC", 1, 4);
+    bitfield(pagestart + 2, "SIZE", 1, 3);
+    bitfield(pagestart + 2, "Write Cache Enabled", 1, 2);
+    bitfield(pagestart + 2, "MF", 1, 1);
+    bitfield(pagestart + 2, "Read Cache Disabled", 1, 0);
+    bitfield(pagestart + 3, "Demand Read Retention Priority", 0xf, 4);
+    bitfield(pagestart + 3, "Demand Write Retention Priority", 0xf, 0);
+    intfield(pagestart + 4, 2, "Disable Pre-fetch Transfer Length");
+    intfield(pagestart + 6, 2, "Minimum Pre-fetch");
+    intfield(pagestart + 8, 2, "Maximum Pre-fetch");
+    intfield(pagestart + 10, 2, "Maximum Pre-fetch Ceiling");
+    bitfield(pagestart + 12, "FSW", 1, 7);
+    bitfield(pagestart + 12, "LBCSS", 1, 6);
+    bitfield(pagestart + 12, "DRA", 1, 5);
+    bitfield(pagestart + 12, "NV_DIS", 1, 0);
+    intfield(pagestart + 13, 1, "Number of Cache Segments");
+    intfield(pagestart + 14, 2, "Cache Segment size");
+    intfield(pagestart + 17, 3, "Non-Cache Segment size");
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+disk_format(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 13, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------------\n");
+    };
+    intfield(pagestart + 2, 2, "Tracks per Zone");
+    intfield(pagestart + 4, 2, "Alternate sectors per zone");
+    intfield(pagestart + 6, 2, "Alternate tracks per zone");
+    intfield(pagestart + 8, 2, "Alternate tracks per lu");
+    intfield(pagestart + 10, 2, "Sectors per track");
+    intfield(pagestart + 12, 2, "Data bytes per physical sector");
+    intfield(pagestart + 14, 2, "Interleave");
+    intfield(pagestart + 16, 2, "Track skew factor");
+    intfield(pagestart + 18, 2, "Cylinder skew factor");
+    bitfield(pagestart + 20, "Supports Soft Sectoring", 1, 7);
+    bitfield(pagestart + 20, "Supports Hard Sectoring", 1, 6);
+    bitfield(pagestart + 20, "Removable Medium", 1, 5);
+    bitfield(pagestart + 20, "Surface", 1, 4);
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+
+}
+
+static int
+disk_verify_error_recovery(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 7, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "EER", 1, 3);
+    bitfield(pagestart + 2, "PER", 1, 2);
+    bitfield(pagestart + 2, "DTE", 1, 1);
+    bitfield(pagestart + 2, "DCR", 1, 0);
+    intfield(pagestart + 3, 1, "Verify Retry Count");
+    intfield(pagestart + 4, 1, "Verify Correction Span (bits)");
+    intfield(pagestart + 10, 2, "Verify Recovery Time Limit (ms)");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+#if 0
+static int
+peripheral_device_page(struct mpage_info * mpi, const char * prefix)
+{
+    static char *idents[] =
+    {
+        "X3.131: Small Computer System Interface",
+        "X3.91M-1987: Storage Module Interface",
+        "X3.170: Enhanced Small Device Interface",
+        "X3.130-1986; X3T9.3/87-002: IPI-2",
+        "X3.132-1987; X3.147-1988: IPI-3"
+    };
+    int status;
+    unsigned ident;
+    unsigned char *pagestart;
+    char *name;
+
+    status = setup_mode_page(mpi, 2, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("---------------------------------\n");
+    };
+
+#if 0
+    dump(pagestart, 20);
+    pagestart[1] += 2;          /*TEST */
+    cbuffer[8] += 2;             /*TEST */
+#endif
+
+    ident = getnbyte(pagestart + 2, 2);
+    if (ident < (sizeof(idents) / sizeof(char *)))
+         name = idents[ident];
+    else if (ident < 0x8000)
+        name = "Reserved";
+    else
+        name = "Vendor Specific";
+
+#ifdef DPG_CHECK_THIS_OUT
+    bdlen = pagestart[1] - 6;
+    if (bdlen < 0)
+        bdlen = 0;
+    else {
+        status = setup_mode_page(mpi, 2, cbuffer, &bdlen,
+                                 &pagestart);
+        if (status)
+            return status;
+    }
+
+    hexfield(pagestart + 2, 2, "Interface Identifier");
+    if (!x_interface) {
+        for (ident = 0; ident < 35; ident++)
+            putchar(' ');
+        puts(name);
+    }
+    hexdatafield(pagestart + 8, bdlen, "Vendor Specific Data");
+#endif
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    if (x_interface)
+        puts(name);
+    return 0;
+}
+#endif
+
+static int
+common_power_condition(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 4, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("--------------------------------\n");
+    }
+    bitfield(pagestart + 3, "Idle", 1, 1);
+    bitfield(pagestart + 3, "Standby", 1, 0);
+    intfield(pagestart + 4, 4, "Idle Condition counter (100ms)");
+    intfield(pagestart + 8, 4, "Standby Condition counter (100ms)");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+disk_xor_control(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 5, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("--------------------------------\n");
+    }
+    bitfield(pagestart + 2, "XORDS", 1, 1);
+    intfield(pagestart + 4, 4, "Maximum XOR write size");
+    intfield(pagestart + 12, 4, "Maximum regenerate size");
+    intfield(pagestart + 16, 4, "Maximum rebuild transfer size");
+    intfield(pagestart + 22, 2, "Rebuild delay");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+disk_background(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 4, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode subpage (0x%x,0x%x)\n", get_page_name(mpi), mpi->page,
+               mpi->subpage);
+        printf("--------------------------------------------\n");
+    }
+    bitfield(pagestart + 4, "Enable background medium scan", 1, 0);
+    bitfield(pagestart + 5, "Enable pre-scan", 1, 0);
+    intfield(pagestart + 6, 2, "BMS interval time (hour)");
+    intfield(pagestart + 8, 2, "Pre-scan timeout value (hour)");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+optical_memory(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 1, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("--------------------------------\n");
+    }
+    bitfield(pagestart + 2, "RUBR", 1, 0);
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+cdvd_write_param(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 20, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("--------------------------------\n");
+    }
+    bitfield(pagestart + 2, "BUFE", 1, 6);
+    bitfield(pagestart + 2, "LS_V", 1, 5);
+    bitfield(pagestart + 2, "Test Write", 1, 4);
+    bitfield(pagestart + 2, "Write Type", 0xf, 0);
+    bitfield(pagestart + 3, "MultiSession", 3, 6);
+    bitfield(pagestart + 3, "FP", 1, 5);
+    bitfield(pagestart + 3, "Copy", 1, 4);
+    bitfield(pagestart + 3, "Track Mode", 0xf, 0);
+    bitfield(pagestart + 4, "Data Block type", 0xf, 0);
+    intfield(pagestart + 5, 1, "Link size");
+    bitfield(pagestart + 7, "Initiator app. code", 0x3f, 0);
+    intfield(pagestart + 8, 1, "Session Format");
+    intfield(pagestart + 10, 4, "Packet size");
+    intfield(pagestart + 14, 2, "Audio Pause Length");
+    hexdatafield(pagestart + 16, 16, "Media Catalog number");
+    hexdatafield(pagestart + 32, 16, "Int. standard recording code");
+    hexdatafield(pagestart + 48, 1, "Subheader byte 1");
+    hexdatafield(pagestart + 49, 1, "Subheader byte 2");
+    hexdatafield(pagestart + 50, 1, "Subheader byte 3");
+    hexdatafield(pagestart + 51, 1, "Subheader byte 4");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+cdvd_audio_control(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 10, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("--------------------------------\n");
+    }
+    bitfield(pagestart + 2, "IMMED", 1, 2);
+    bitfield(pagestart + 2, "SOTC", 1, 1);
+    bitfield(pagestart + 8, "CDDA out port 0, channel select", 0xf, 0);
+    intfield(pagestart + 9, 1, "Channel port 0 volume");
+    bitfield(pagestart + 10, "CDDA out port 1, channel select", 0xf, 0);
+    intfield(pagestart + 11, 1, "Channel port 1 volume");
+    bitfield(pagestart + 12, "CDDA out port 2, channel select", 0xf, 0);
+    intfield(pagestart + 13, 1, "Channel port 2 volume");
+    bitfield(pagestart + 14, "CDDA out port 3, channel select", 0xf, 0);
+    intfield(pagestart + 15, 1, "Channel port 3 volume");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+cdvd_timeout(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 6, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------------------\n");
+    }
+    bitfield(pagestart + 4, "G3Enable", 1, 3);
+    bitfield(pagestart + 4, "TMOE", 1, 2);
+    bitfield(pagestart + 4, "DISP", 1, 1);
+    bitfield(pagestart + 4, "SWPP", 1, 0);
+    intfield(pagestart + 6, 2, "Group 1 minimum time-out");
+    intfield(pagestart + 8, 2, "Group 2 minimum time-out");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+cdvd_device_param(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 3, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("------------------------------------\n");
+    }
+    bitfield(pagestart + 3, "Inactivity timer multiplier", 0xf, 0);
+    intfield(pagestart + 4, 2, "MSF-S units per MSF_M unit");
+    intfield(pagestart + 6, 2, "MSF-F units per MSF_S unit");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+/* This is not a standard t10.org MMC mode page (it is now "protocol specific
+   lu" mode page). This definition was found in Hitachi GF-2050/GF-2055
+   DVD-RAM drive SCSI reference manual. */
+static int
+cdvd_feature(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 12, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("----------------------------------------------\n");
+    }
+    intfield(pagestart + 2, 2, "DVD feature set");
+    intfield(pagestart + 4, 2, "CD audio");
+    intfield(pagestart + 6, 2, "Embedded changer");
+    intfield(pagestart + 8, 2, "Packet SMART");
+    intfield(pagestart + 10, 2, "Persistent prevent(MESN)");
+    intfield(pagestart + 12, 2, "Event status notification");
+    intfield(pagestart + 14, 2, "Digital output");
+    intfield(pagestart + 16, 2, "CD sequential recordable");
+    intfield(pagestart + 18, 2, "DVD sequential recordable");
+    intfield(pagestart + 20, 2, "Random recordable");
+    intfield(pagestart + 22, 2, "Key management");
+    intfield(pagestart + 24, 2, "Partial recorded CD media read");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+cdvd_mm_capab(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 49, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "DVD-RAM read", 1, 5);
+    bitfield(pagestart + 2, "DVD-R read", 1, 4);
+    bitfield(pagestart + 2, "DVD-ROM read", 1, 3);
+    bitfield(pagestart + 2, "Method 2", 1, 2);
+    bitfield(pagestart + 2, "CD-RW read", 1, 1);
+    bitfield(pagestart + 2, "CD-R read", 1, 0);
+    bitfield(pagestart + 3, "DVD-RAM write", 1, 5);
+    bitfield(pagestart + 3, "DVD-R write", 1, 4);
+    bitfield(pagestart + 3, "DVD-ROM write", 1, 3);
+    bitfield(pagestart + 3, "Test Write", 1, 2);
+    bitfield(pagestart + 3, "CD-RW write", 1, 1);
+    bitfield(pagestart + 3, "CD-R write", 1, 0);
+    bitfield(pagestart + 4, "BUF", 1, 7);
+    bitfield(pagestart + 4, "MultiSession", 1, 6);
+    bitfield(pagestart + 4, "Mode 2 Form 2", 1, 5);
+    bitfield(pagestart + 4, "Mode 2 Form 1", 1, 4);
+    bitfield(pagestart + 4, "Digital port (2)", 1, 3);
+    bitfield(pagestart + 4, "Digital port (1)", 1, 2);
+    bitfield(pagestart + 4, "Composite", 1, 1);
+    bitfield(pagestart + 4, "Audio play", 1, 0);
+    bitfield(pagestart + 5, "Read bar code", 1, 7);
+    bitfield(pagestart + 5, "UPC", 1, 6);
+    bitfield(pagestart + 5, "ISRC", 1, 5);
+    bitfield(pagestart + 5, "C2 pointers supported", 1, 4);
+    bitfield(pagestart + 5, "R-W de-interleaved & corrected", 1, 3);
+    bitfield(pagestart + 5, "R-W supported", 1, 2);
+    bitfield(pagestart + 5, "CD-DA stream is accurate", 1, 1);
+    bitfield(pagestart + 5, "CD-DA commands supported", 1, 0);
+    bitfield(pagestart + 6, "Loading mechanism type", 7, 5);
+    bitfield(pagestart + 6, "Eject (individual or magazine)", 1, 3);
+    bitfield(pagestart + 6, "Prevent jumper", 1, 2);
+    bitfield(pagestart + 6, "Lock state", 1, 1);
+    bitfield(pagestart + 6, "Lock", 1, 0);
+    bitfield(pagestart + 7, "R-W in lead-in", 1, 5);
+    bitfield(pagestart + 7, "Side change capable", 1, 4);
+    bitfield(pagestart + 7, "S/W slot selection", 1, 3);
+    bitfield(pagestart + 7, "Changer supports disc present", 1, 2);
+    bitfield(pagestart + 7, "Separate channel mute", 1, 1);
+    bitfield(pagestart + 7, "Separate volume levels", 1, 0);
+    intfield(pagestart + 10, 2, "number of volume level supported");
+    intfield(pagestart + 12, 2, "Buffer size supported");
+    bitfield(pagestart + 17, "Length", 3, 4);
+    bitfield(pagestart + 17, "LSBF", 1, 3);
+    bitfield(pagestart + 17, "RCK", 1, 2);
+    bitfield(pagestart + 17, "BCKF", 1, 1);
+    intfield(pagestart + 22, 2, "Copy management revision supported");
+    bitfield(pagestart + 27, "Rotation control selected", 3, 0);
+    intfield(pagestart + 28, 2, "Current write speed selected");
+    intfield(pagestart + 30, 2, "# of lu speed performance tables");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+cdvd_cache(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 2, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("-----------------------\n");
+    };
+    bitfield(pagestart + 2, "Write Cache Enabled", 1, 2);
+    bitfield(pagestart + 2, "Read Cache Disabled", 1, 0);
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+tape_data_compression(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 6, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "DCE", 1, 7);
+    bitfield(pagestart + 2, "DCC", 1, 6);
+    bitfield(pagestart + 3, "DDE", 1, 7);
+    bitfield(pagestart + 3, "RED", 3, 5);
+    intfield(pagestart + 4, 4, "Compression algorithm");
+    intfield(pagestart + 8, 4, "Decompression algorithm");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+tape_dev_config(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 25, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "CAF", 1, 5);
+    bitfield(pagestart + 2, "Active format", 0x1f, 0);
+    intfield(pagestart + 3, 1, "Active partition");
+    intfield(pagestart + 4, 1, "Write object cbuffer full ratio");
+    intfield(pagestart + 5, 1, "Read object cbuffer full ratio");
+    intfield(pagestart + 6, 2, "Wire delay time");
+    bitfield(pagestart + 8, "OBR", 1, 7);
+    bitfield(pagestart + 8, "LOIS", 1, 6);
+    bitfield(pagestart + 8, "RSMK", 1, 5);
+    bitfield(pagestart + 8, "AVC", 1, 4);
+    bitfield(pagestart + 8, "SOCF", 3, 2);
+    bitfield(pagestart + 8, "ROBO", 1, 1);
+    bitfield(pagestart + 8, "REW", 1, 0);
+    intfield(pagestart + 9, 1, "Gap size");
+    bitfield(pagestart + 10, "EOD defined", 7, 5);
+    bitfield(pagestart + 10, "EEG", 1, 4);
+    bitfield(pagestart + 10, "SEW", 1, 3);
+    bitfield(pagestart + 10, "SWP", 1, 2);
+    bitfield(pagestart + 10, "BAML", 1, 1);
+    bitfield(pagestart + 10, "BAM", 1, 0);
+    intfield(pagestart + 11, 3, "Object cbuffer size at early warning");
+    intfield(pagestart + 14, 1, "Select data compression algorithm");
+    bitfield(pagestart + 15, "ASOCWP", 1, 2);
+    bitfield(pagestart + 15, "PERSWO", 1, 1);
+    bitfield(pagestart + 15, "PRMWP", 1, 0);
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+tape_medium_part1(struct mpage_info * mpi, const char * prefix)
+{
+    int status, off, len;
+    unsigned char *pagestart;
+
+    /* variable length mode page, need to know its response length */
+    status = get_mode_page(mpi, 0, cbuffer);
+    if (status)
+        return status;
+    off = modePageOffset(cbuffer, mpi->resp_len, mode6byte);
+    if (off < 0)
+        return off;
+    len = mpi->resp_len - off;
+
+    status = setup_mode_page(mpi, 12 + ((len - 10) / 2), cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    intfield(pagestart + 2, 1, "Maximum additional partitions");
+    intfield(pagestart + 3, 1, "Additional partitions defined");
+    bitfield(pagestart + 4, "FDP", 1, 7);
+    bitfield(pagestart + 4, "SDP", 1, 6);
+    bitfield(pagestart + 4, "IDP", 1, 5);
+    bitfield(pagestart + 4, "PSUM", 3, 3);
+    bitfield(pagestart + 4, "POFM", 1, 2);
+    bitfield(pagestart + 4, "CLEAR", 1, 1);
+    bitfield(pagestart + 4, "ADDP", 1, 0);
+    intfield(pagestart + 5, 1, "Medium format recognition");
+    bitfield(pagestart + 6, "Partition units", 0xf, 0);
+    intfield(pagestart + 8, 2, "Partition size");
+
+    for (off = 10; off < len; off += 2)
+        intfield(pagestart + off, 2, "Partition size");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+tape_medium_part2_4(struct mpage_info * mpi, const char * prefix)
+{
+    int status, off, len;
+    unsigned char *pagestart;
+
+    /* variable length mode page, need to know its response length */
+    status = get_mode_page(mpi, 0, cbuffer);
+    if (status)
+        return status;
+    off = modePageOffset(cbuffer, mpi->resp_len, mode6byte);
+    if (off < 0)
+        return off;
+    len = mpi->resp_len - off;
+
+    status = setup_mode_page(mpi, 1 + ((len - 4) / 2), cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    intfield(pagestart + 2, 2, "Partition size");
+
+    for (off = 4; off < len; off += 2)
+        intfield(pagestart + off, 2, "Partition size");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+ses_services_manag(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 2, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", get_page_name(mpi), mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    bitfield(pagestart + 5, "ENBLTC", 1, 0);
+    intfield(pagestart + 6, 2, "Maximum time to completion (100 ms units)");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+fcp_proto_spec_lu(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 1, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", "Fibre Channel logical unit",
+               mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    bitfield(pagestart + 3, "EPDC", 1, 0);
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+sas_proto_spec_lu(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 1, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", "SAS logical unit", mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "Transport Layer Retries", 1, 4);
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+common_proto_spec_lu(struct mpage_info * mpi, const char * prefix)
+{
+    int status, proto_id;
+
+    status = get_protocol_id(0, cbuffer, &proto_id, NULL);
+    if (status)
+        return status;
+    if (0 == proto_id)
+        return fcp_proto_spec_lu(mpi, prefix);
+    else if (6 == proto_id)
+        return sas_proto_spec_lu(mpi, prefix);
+    else
+        return DECODE_FAILED_TRY_HEX;
+}
+
+static int
+fcp_proto_spec_port(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 10, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", "Fibre Channel port control",
+               mpi->page);
+        printf("----------------------------------------------------\n");
+    }
+    bitfield(pagestart + 3, "DTFD", 1, 7);
+    bitfield(pagestart + 3, "PLPB", 1, 6);
+    bitfield(pagestart + 3, "DDIS", 1, 5);
+    bitfield(pagestart + 3, "DLM", 1, 4);
+    bitfield(pagestart + 3, "RHA", 1, 3);
+    bitfield(pagestart + 3, "ALWI", 1, 2);
+    bitfield(pagestart + 3, "DTIPE", 1, 1);
+    bitfield(pagestart + 3, "DTOLI", 1, 0);
+    bitfield(pagestart + 6, "RR_TOV units", 7, 0);
+    intfield(pagestart + 7, 1, "Resource recovery time-out");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+spi4_proto_spec_port(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 1, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", "SPI-4 port control", mpi->page);
+        printf("-----------------------------------\n");
+    }
+    intfield(pagestart + 4, 2, "Synchronous transfer time-out");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+/* Protocol specific mode page for SAS, short format (subpage 0) */
+static int
+sas_proto_spec_port(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 3, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode page (0x%x)\n", "SAS SSP port control", mpi->page);
+        printf("-------------------------------------\n");
+    }
+    bitfield(pagestart + 2, "Ready LED meaning", 0x1, 4);
+    intfield(pagestart + 4, 2, "I_T Nexus Loss time");
+    intfield(pagestart + 6, 2, "Initiator response time-out");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+common_proto_spec_port(struct mpage_info * mpi, const char * prefix)
+{
+    int status, proto_id;
+
+    status = get_protocol_id(1, cbuffer, &proto_id, NULL);
+    if (status)
+        return status;
+    if (0 == proto_id)
+        return fcp_proto_spec_port(mpi, prefix);
+    else if (1 == proto_id)
+        return spi4_proto_spec_port(mpi, prefix);
+    else if (6 == proto_id)
+        return sas_proto_spec_port(mpi, prefix);
+    else
+        return DECODE_FAILED_TRY_HEX;
+}
+
+static int
+spi4_margin_control(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 5, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode subpage (0x%x,0x%x)\n", "SPI-4 Margin control",
+               mpi->page, mpi->subpage);
+        printf("--------------------------------------------\n");
+    }
+    bitfield(pagestart + 5, "Protocol identifier", 0xf, 0);
+    bitfield(pagestart + 7, "Driver Strength", 0xf, 4);
+    bitfield(pagestart + 8, "Driver Asymmetry", 0xf, 4);
+    bitfield(pagestart + 8, "Driver Precompensation", 0xf, 0);
+    bitfield(pagestart + 9, "Driver Slew rate", 0xf, 4);
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+/* Protocol specific mode page for SAS, phy control + discover (subpage 1) */
+static int
+sas_phy_control_discover(struct mpage_info * mpi, const char * prefix)
+{
+    int status, off, num_phys, k;
+    unsigned char *pagestart;
+    unsigned char *p;
+
+   /* variable length mode page, need to know its response length */
+    status = get_mode_page(mpi, 0, cbuffer);
+    if (status)
+        return status;
+    off = modePageOffset(cbuffer, mpi->resp_len, mode6byte);
+    if (off < 0)
+        return off;
+    num_phys = cbuffer[off + 7];
+
+    status = setup_mode_page(mpi,  1 + (16 * num_phys), cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode subpage (0x%x,0x%x)\n", "SAS Phy Control and "
+               "Discover", mpi->page, mpi->subpage);
+        printf("--------------------------------------------\n");
+    }
+    intfield(pagestart + 7, 1, "Number of phys");
+    for (k = 0, p = pagestart + 8; k < num_phys; ++k, p += 48) {
+        intfield(p + 1, 1, "Phy Identifier");
+        bitfield(p + 4, "Attached Device type", 0x7, 4);
+        bitfield(p + 5, "Negotiated Logical Link rate", 0xf, 0);
+        bitfield(p + 6, "Attached SSP Initiator port", 0x1, 3);
+        bitfield(p + 6, "Attached STP Initiator port", 0x1, 2);
+        bitfield(p + 6, "Attached SMP Initiator port", 0x1, 1);
+        bitfield(p + 7, "Attached SSP Target port", 0x1, 3);
+        bitfield(p + 7, "Attached STP Target port", 0x1, 2);
+        bitfield(p + 7, "Attached SMP Target port", 0x1, 1);
+        hexdatafield(p + 8, 8, "SAS address");
+        hexdatafield(p + 16, 8, "Attached SAS address");
+        intfield(p + 24, 1, "Attached Phy identifier");
+        bitfield(p + 32, "Programmed Min Physical Link rate", 0xf, 4);
+        bitfield(p + 32, "Hardware Min Physical Link rate", 0xf, 0);
+        bitfield(p + 33, "Programmed Max Physical Link rate", 0xf, 4);
+        bitfield(p + 33, "Hardware Max Physical Link rate", 0xf, 0);
+    }
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+
+static int
+common_proto_spec_port_sp1(struct mpage_info * mpi, const char * prefix)
+{
+    int status, proto_id;
+
+    status = get_protocol_id(1, cbuffer, &proto_id, NULL);
+    if (status)
+        return status;
+    if (1 == proto_id)
+        return spi4_margin_control(mpi, prefix);
+    else if (6 == proto_id)
+        return sas_phy_control_discover(mpi, prefix);
+    else
+        return DECODE_FAILED_TRY_HEX;
+}
+
+static int
+spi4_training_config(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 27, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode subpage (0x%x,0x%x)\n", "training configuration",
+               mpi->page, mpi->subpage);
+        printf("----------------------------------------------------------\n");
+    }
+    hexdatafield(pagestart + 10, 4, "DB(0) value");
+    hexdatafield(pagestart + 14, 4, "DB(1) value");
+    hexdatafield(pagestart + 18, 4, "DB(2) value");
+    hexdatafield(pagestart + 22, 4, "DB(3) value");
+    hexdatafield(pagestart + 26, 4, "DB(4) value");
+    hexdatafield(pagestart + 30, 4, "DB(5) value");
+    hexdatafield(pagestart + 34, 4, "DB(6) value");
+    hexdatafield(pagestart + 38, 4, "DB(7) value");
+    hexdatafield(pagestart + 42, 4, "DB(8) value");
+    hexdatafield(pagestart + 46, 4, "DB(9) value");
+    hexdatafield(pagestart + 50, 4, "DB(10) value");
+    hexdatafield(pagestart + 54, 4, "DB(11) value");
+    hexdatafield(pagestart + 58, 4, "DB(12) value");
+    hexdatafield(pagestart + 62, 4, "DB(13) value");
+    hexdatafield(pagestart + 66, 4, "DB(14) value");
+    hexdatafield(pagestart + 70, 4, "DB(15) value");
+    hexdatafield(pagestart + 74, 4, "P_CRCA value");
+    hexdatafield(pagestart + 78, 4, "P1 value");
+    hexdatafield(pagestart + 82, 4, "BSY value");
+    hexdatafield(pagestart + 86, 4, "SEL value");
+    hexdatafield(pagestart + 90, 4, "RST value");
+    hexdatafield(pagestart + 94, 4, "REQ value");
+    hexdatafield(pagestart + 98, 4, "ACK value");
+    hexdatafield(pagestart + 102, 4, "ATN value");
+    hexdatafield(pagestart + 106, 4, "C/D value");
+    hexdatafield(pagestart + 110, 4, "I/O value");
+    hexdatafield(pagestart + 114, 4, "MSG value");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+/* SAS(2) SSP, shared protocol specific port mode subpage (subpage 2) */
+static int
+sas_shared_spec_port(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 1, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode subpage (0x%x,0x%x)\n", "SAS SSP shared protocol "
+               "specific port", mpi->page, mpi->subpage);
+        printf("-----------------------------------------------------\n");
+    }
+    intfield(pagestart + 6, 2, "Power loss timeout(ms)");
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+common_proto_spec_port_sp2(struct mpage_info * mpi, const char * prefix)
+{
+    int status, proto_id;
+
+    status = get_protocol_id(1, cbuffer, &proto_id, NULL);
+    if (status)
+        return status;
+    if (1 == proto_id)
+        return spi4_training_config(mpi, prefix);
+    else if (6 == proto_id)
+        return sas_shared_spec_port(mpi, prefix);
+    else
+        return DECODE_FAILED_TRY_HEX;
+}
+
+static int
+spi4_negotiated(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 7, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode subpage (0x%x,0x%x)\n", get_page_name(mpi), mpi->page,
+               mpi->subpage);
+        printf("--------------------------------------------\n");
+    }
+    intfield(pagestart + 6, 1, "Transfer period");
+    intfield(pagestart + 8, 1, "REQ/ACK offset");
+    intfield(pagestart + 9, 1, "Transfer width exponent");
+    bitfield(pagestart + 10, "Protocol option bits", 0x7f, 0);
+    bitfield(pagestart + 11, "Transceiver mode", 3, 2);
+    bitfield(pagestart + 11, "Sent PCOMP_EN", 1, 1);
+    bitfield(pagestart + 11, "Received PCOMP_EN", 1, 0);
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static int
+spi4_report_xfer(struct mpage_info * mpi, const char * prefix)
+{
+    int status;
+    unsigned char *pagestart;
+
+    status = setup_mode_page(mpi, 4, cbuffer, &pagestart);
+    if (status)
+        return status;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (!x_interface && !replace) {
+        printf("%s mode subpage (0x%x,0x%x)\n", get_page_name(mpi), mpi->page,
+               mpi->subpage);
+        printf("--------------------------------------------\n");
+    }
+    intfield(pagestart + 6, 1, "Mimimum transfer period factor");
+    intfield(pagestart + 8, 1, "Maximum REQ/ACK offset");
+    intfield(pagestart + 9, 1, "Maximum transfer width exponent");
+    bitfield(pagestart + 10, "Protocol option bits supported", 0xff, 0);
+
+    if (x_interface && replace)
+        return put_mode_page(mpi, cbuffer);
+    else
+        printf("\n");
+    return 0;
+}
+
+static void
+print_hex_page(struct mpage_info * mpi, const char * prefix,
+               unsigned char *pagestart, int off, int len)
+{
+    int k;
+    const char * pg_name;
+
+    if (prefix[0])
+        printf("%s", prefix);
+    if (! x_interface) {
+        pg_name = get_page_name(mpi);
+        if (mpi->subpage) {
+            if (pg_name && (unkn_page_str != pg_name))
+                printf("mode page: 0x%02x  subpage: 0x%02x   [%s]\n",
+                       mpi->page, mpi->subpage, pg_name);
+            else
+                printf("mode page: 0x%02x  subpage: 0x%02x\n", mpi->page,
+                   mpi->subpage);
+            printf("------------------------------\n");
+        } else {
+            if (pg_name && (unkn_page_str != pg_name))
+                printf("mode page: 0x%02x   [%s]\n", mpi->page,
+                       pg_name);
+            else
+                printf("mode page: 0x%02x\n", mpi->page);
+            printf("---------------\n");
+        }
+    }
+    for (k = off; k < len; k++)
+    {
+        char nm[8];
+
+        snprintf(nm, sizeof(nm), "0x%02x", k);
+        hexdatafield(pagestart + k, 1, nm);
+    }
+    printf("\n");
+}
+
+static int
+do_user_page(struct mpage_info * mpi, int decode_in_hex)
+{
+    int status = 0;
+    int len, off, res, done;
+    int offset = 0;
+    unsigned char *pagestart;
+    char prefix[96];
+    struct mpage_info local_mp_i;
+    struct mpage_name_func * mpf;
+    int multiple = ((MP_LIST_PAGES == mpi->page) ||
+                    (MP_LIST_SUBPAGES == mpi->subpage));
+
+    if (replace && multiple) {
+        printf("Can't list all (sub)pages and use replace (-R) together\n");
+        return 1;
+    }
+    status = get_mode_page(mpi, 0, cbuffer2);
+    if (status) {
+        printf("\n");
+        return status;
+    } else {
+        offset = modePageOffset(cbuffer2, mpi->resp_len, mode6byte);
+        if (offset < 0) {
+            fprintf(stdout, "mode page=0x%x has bad page format\n",
+                    mpi->page);
+            fprintf(stdout, "   perhaps '-z' switch may help\n");
+            return -1;
+        }
+        pagestart = cbuffer2 + offset;
+    }
+
+    memset(&local_mp_i, 0, sizeof(local_mp_i));
+    local_mp_i.page_control = mpi->page_control;
+    local_mp_i.peri_type = mpi->peri_type;
+    local_mp_i.inq_byte6 = mpi->inq_byte6;
+    local_mp_i.resp_len = mpi->resp_len;
+
+    do {
+        local_mp_i.page = (pagestart[0] & 0x3f);
+        local_mp_i.subpage = (pagestart[0] & 0x40) ? pagestart[1] : 0;
+        if(0 == local_mp_i.page) { /* page==0 vendor (unknown) format */
+            off = 0;
+            len = mpi->resp_len - offset;  /* should be last listed page */
+        } else if (local_mp_i.subpage) {
+            off = 4;
+            len = (pagestart[2] << 8) + pagestart[3] + 4;
+        } else {
+            off = 2;
+            len = pagestart[1] + 2;
+        }
+
+        prefix[0] = '\0';
+        done = 0;
+        if ((! decode_in_hex) && ((mpf = get_mpage_name_func(&local_mp_i))) &&
+            mpf->func) {
+            if (multiple && x_interface && !replace) {
+                if (local_mp_i.subpage)
+                    snprintf(prefix, sizeof(prefix), "sginfo -t 0x%x,0x%x"
+                             " -XR %s ", local_mp_i.page, local_mp_i.subpage,
+                             device_name);
+                else
+                    snprintf(prefix, sizeof(prefix), "sginfo -t 0x%x -XR %s ",
+                             local_mp_i.page, device_name);
+            }
+            res = mpf->func(&local_mp_i, prefix);
+            if (DECODE_FAILED_TRY_HEX != res) {
+                done = 1;
+                status |= res;
+            }
+        }
+        if (! done) {
+            if (x_interface && replace)
+                return put_mode_page(&local_mp_i, cbuffer2);
+            else {
+                if (multiple && x_interface && !replace) {
+                    if (local_mp_i.subpage)
+                        snprintf(prefix, sizeof(prefix), "sginfo -u 0x%x,0x%x"
+                                 " -XR %s ", local_mp_i.page,
+                                 local_mp_i.subpage, device_name);
+                    else
+                        snprintf(prefix, sizeof(prefix), "sginfo -u 0x%x -XR "
+                                 "%s ", local_mp_i.page, device_name);
+                }
+                print_hex_page(&local_mp_i, prefix, pagestart, off, len);
+            }
+        }
+        offset += len;
+        pagestart = cbuffer2 + offset;
+    } while (multiple && (offset < mpi->resp_len));
+    return status;
+}
+
+static void
+inqfieldname(unsigned char *deststr, const unsigned char *srcbuf, int maxlen)
+{
+        int i;
+
+        memset(deststr, '\0', MAX_INQFIELD_LEN);
+        for (i = maxlen - 1; i >= 0 && isspace(srcbuf[i]); --i)
+                ;
+        memcpy(deststr, srcbuf, i + 1);
+}
+
+static int
+do_inquiry(int * peri_type, int * resp_byte6, int inquiry_verbosity)
+{
+    int status;
+    unsigned char cmd[6];
+    unsigned char fieldname[MAX_INQFIELD_LEN];
+    unsigned char *pagestart;
+    struct scsi_cmnd_io sci;
+
+    memset(cbuffer, 0, INQUIRY_RESP_INITIAL_LEN);
+    cbuffer[0] = 0x7f;
+
+    cmd[0] = 0x12;              /* INQUIRY */
+    cmd[1] = 0x00;              /* evpd=0 */
+    cmd[2] = 0x00;              /* page code = 0 */
+    cmd[3] = 0x00;              /* (reserved) */
+    cmd[4] = INQUIRY_RESP_INITIAL_LEN;      /* allocation length */
+    cmd[5] = 0x00;              /* control */
+
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_FROM_DEVICE;
+    sci.dxfer_len = INQUIRY_RESP_INITIAL_LEN;
+    sci.dxferp = cbuffer;
+    status = do_scsi_io(&sci);
+    if (status) {
+        printf("Error doing INQUIRY (1)\n");
+        return status;
+    }
+    if (trace_cmd > 1) {
+        printf("  inquiry response:\n");
+        dump(cbuffer, INQUIRY_RESP_INITIAL_LEN);
+    }
+    pagestart = cbuffer;
+    if (peri_type)
+        *peri_type = pagestart[0] & 0x1f;
+    if (resp_byte6)
+        *resp_byte6 = pagestart[6];
+    if (0 == inquiry_verbosity)
+        return 0;
+    if ((pagestart[4] + 5) < INQUIRY_RESP_INITIAL_LEN) {
+        printf("INQUIRY response too short: expected 36 bytes, got %d\n",
+               pagestart[4] + 5);
+        return -EINVAL;
+    }
+
+    if (!x_interface && !replace) {
+        printf("INQUIRY response (cmd: 0x12)\n");
+        printf("----------------------------\n");
+    };
+    bitfield(pagestart + 0, "Device Type", 0x1f, 0);
+    if (2 == inquiry_verbosity) {
+        bitfield(pagestart + 0, "Peripheral Qualifier", 0x7, 5);
+        bitfield(pagestart + 1, "Removable", 1, 7);
+        bitfield(pagestart + 2, "Version", 0xff, 0);
+        bitfield(pagestart + 3, "NormACA", 1, 5);
+        bitfield(pagestart + 3, "HiSup", 1, 4);
+        bitfield(pagestart + 3, "Response Data Format", 0xf, 0);
+        bitfield(pagestart + 5, "SCCS", 1, 7);
+        bitfield(pagestart + 5, "ACC", 1, 6);
+        bitfield(pagestart + 5, "ALUA", 3, 4);
+        bitfield(pagestart + 5, "3PC", 1, 3);
+        bitfield(pagestart + 5, "Protect", 1, 0);
+        bitfield(pagestart + 6, "BQue", 1, 7);
+        bitfield(pagestart + 6, "EncServ", 1, 6);
+        bitfield(pagestart + 6, "MultiP", 1, 4);
+        bitfield(pagestart + 6, "MChngr", 1, 3);
+        bitfield(pagestart + 6, "Addr16", 1, 0);
+        bitfield(pagestart + 7, "Relative Address", 1, 7);
+        bitfield(pagestart + 7, "Wide bus 16", 1, 5);
+        bitfield(pagestart + 7, "Synchronous neg.", 1, 4);
+        bitfield(pagestart + 7, "Linked Commands", 1, 3);
+        bitfield(pagestart + 7, "Command Queueing", 1, 1);
+    }
+    if (x_interface)
+        printf("\n");
+
+    inqfieldname(fieldname, pagestart + 8, 8);
+    printf("%s%s\n", (!x_interface ? "Vendor:                    " : ""),
+           fieldname);
+
+    inqfieldname(fieldname, pagestart + 16, 16);
+    printf("%s%s\n", (!x_interface ? "Product:                   " : ""),
+           fieldname);
+
+    inqfieldname(fieldname, pagestart + 32, 4);
+    printf("%s%s\n", (!x_interface ? "Revision level:            " : ""),
+           fieldname);
+
+    printf("\n");
+    return status;
+
+}
+
+static int
+do_serial_number(void)
+{
+    int status, pagelen;
+    unsigned char cmd[6];
+    unsigned char *pagestart;
+    struct scsi_cmnd_io sci;
+    const unsigned char serial_vpd = 0x80;
+    const unsigned char supported_vpd = 0x0;
+
+    /* check supported VPD pages + unit serial number well formed */
+    cmd[0] = 0x12;              /* INQUIRY */
+    cmd[1] = 0x01;              /* evpd=1 */
+    cmd[2] = supported_vpd;
+    cmd[3] = 0x00;              /* (reserved) */
+    cmd[4] = 0x04;              /* allocation length */
+    cmd[5] = 0x00;              /* control */
+
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_FROM_DEVICE;
+    sci.dxfer_len = 4;
+    sci.dxferp = cbuffer;
+    status = do_scsi_io(&sci);
+    if (status) {
+        printf("No serial number (error doing INQUIRY, supported VPDs)\n\n");
+        return status;
+    }
+    if (! ((supported_vpd == cbuffer[1]) && (0 == cbuffer[2]))) {
+        printf("No serial number (bad format for supported VPDs)\n\n");
+        return -1;
+    }
+
+    cmd[0] = 0x12;              /* INQUIRY */
+    cmd[1] = 0x01;              /* evpd=1 */
+    cmd[2] = serial_vpd;
+    cmd[3] = 0x00;              /* (reserved) */
+    cmd[4] = 0x04;              /* allocation length */
+    cmd[5] = 0x00;              /* control */
+
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_FROM_DEVICE;
+    sci.dxfer_len = 4;
+    sci.dxferp = cbuffer;
+    status = do_scsi_io(&sci);
+    if (status) {
+        printf("No serial number (error doing INQUIRY, serial number)\n\n");
+        return status;
+    }
+    if (! ((serial_vpd == cbuffer[1]) && (0 == cbuffer[2]))) {
+        printf("No serial number (bad format for serial number)\n\n");
+        return -1;
+    }
+
+    pagestart = cbuffer;
+
+    pagelen = 4 + pagestart[3];
+
+    cmd[0] = 0x12;              /* INQUIRY */
+    cmd[1] = 0x01;              /* evpd=1 */
+    cmd[2] = serial_vpd;
+    cmd[3] = 0x00;              /* (reserved) */
+    cmd[4] = (unsigned char)pagelen; /* allocation length */
+    cmd[5] = 0x00;              /* control */
+
+    sci.cmnd = cmd;
+    sci.cmnd_len = sizeof(cmd);
+    sci.dxfer_dir = DXFER_FROM_DEVICE;
+    sci.dxfer_len = pagelen;
+    sci.dxferp = cbuffer;
+    status = do_scsi_io(&sci);
+    if (status) {
+        printf("No serial number (error doing INQUIRY, serial number)\n\n");
+        return status;
+    }
+    if (trace_cmd > 1) {
+        printf("  inquiry (vpd page 0x80) response:\n");
+        dump(cbuffer, pagelen);
+    }
+
+    pagestart[pagestart[3] + 4] = '\0';
+    printf("Serial Number '%s'\n\n", pagestart + 4);
+    return status;
+}
+
+
+typedef struct sg_map {
+    int bus;
+    int channel;
+    int target_id;
+    int lun;
+    char * dev_name;
+} Sg_map;
+
+typedef struct my_scsi_idlun
+{
+    int mux4;
+    int host_unique_id;
+
+} My_scsi_idlun;
+
+#define MDEV_NAME_SZ 256
+
+static void
+make_dev_name(char * fname, int k, int do_numeric)
+{
+    char buff[MDEV_NAME_SZ];
+    size_t len;
+
+    strncpy(fname, "/dev/sg", MDEV_NAME_SZ);
+    fname[MDEV_NAME_SZ - 1] = '\0';
+    len = strlen(fname);
+    if (do_numeric)
+        snprintf(fname + len, MDEV_NAME_SZ - len, "%d", k);
+    else {
+        if (k <= 26) {
+            buff[0] = 'a' + (char)k;
+            buff[1] = '\0';
+            strcat(fname, buff);
+        }
+        else
+            strcat(fname, "xxxx");
+    }
+}
+
+
+static Sg_map sg_map_arr[MAX_SG_DEVS + 1];
+
+#define MAX_HOLES 4
+
+/* Print out a list of the known devices on the system */
+static void
+show_devices(int raw)
+{
+    int k, j, fd, err, bus;
+    My_scsi_idlun m_idlun;
+    char name[MDEV_NAME_SZ];
+    char dev_name[MDEV_NAME_SZ];
+    char ebuff[EBUFF_SZ];
+    int do_numeric = 1;
+    int max_holes = MAX_HOLES;
+    DIR *dir_ptr;
+    struct dirent *entry;
+    char *tmpptr;
+
+    dir_ptr=opendir("/dev");
+    if ( dir_ptr == NULL ) {
+        perror("/dev");
+        exit(1);
+    }
+
+    j=0;
+    while ( (entry=readdir(dir_ptr)) != NULL ) {
+        switch(entry->d_type) {
+        case DT_LNK:
+        case DT_CHR:
+        case DT_BLK:
+                break;
+        default:
+                continue;
+        }
+
+        switch(entry->d_name[0]) {
+        case 's':
+        case 'n':
+                break;
+        default:
+                continue;
+        }
+
+        if ( strncmp("sg",entry->d_name,2) == 0 ) {
+                continue;
+        }
+        if ( strncmp("sd",entry->d_name,2) == 0 ) {
+            continue;
+        }
+        if ( isdigit(entry->d_name[strlen(entry->d_name)-1]) ) {
+            continue;
+        }
+
+        snprintf(dev_name, sizeof(dev_name),"/dev/%s",entry->d_name);
+
+        fd = open(dev_name, O_RDONLY | O_NONBLOCK);
+        if (fd < 0)
+            continue;
+        err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &(sg_map_arr[j].bus));
+        if (err < 0) {
+#if 0
+            snprintf(ebuff, EBUFF_SZ,
+                     "SCSI(1) ioctl on %s failed", dev_name);
+            perror(ebuff);
+#endif
+            close(fd);
+            continue;
+        }
+        err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
+        if (err < 0) {
+            snprintf(ebuff, EBUFF_SZ,
+                     "SCSI(2) ioctl on %s failed", dev_name);
+            perror(ebuff);
+            close(fd);
+            continue;
+        }
+        sg_map_arr[j].channel = (m_idlun.mux4 >> 16) & 0xff;
+        sg_map_arr[j].lun = (m_idlun.mux4 >> 8) & 0xff;
+        sg_map_arr[j].target_id = m_idlun.mux4 & 0xff;
+        tmpptr=(char *)malloc(strlen(dev_name)+1);
+        strncpy(tmpptr,dev_name,strlen(dev_name)+1);
+        sg_map_arr[j].dev_name = tmpptr;
+#if 0
+        printf("[scsi%d ch=%d id=%d lun=%d %s] ", sg_map_arr[j].bus,
+        sg_map_arr[j].channel, sg_map_arr[j].target_id, sg_map_arr[j].lun,
+        sg_map_arr[j].dev_name);
+#endif
+        printf("%s ", dev_name);
+        close(fd);
+        if (++j >= MAX_SG_DEVS)
+            break;
+    }
+    closedir(dir_ptr);
+
+    printf("\n"); /* <<<<<<<<<<<<<<<<<<<<< */
+    for (k = 0; k < MAX_SG_DEVS; k++) {
+        if ( raw ) {
+                sprintf(name,"/dev/raw/raw%d",k);
+                fd = open(name, O_RDWR | O_NONBLOCK);
+                if (fd < 0) {
+                        continue;
+                }
+        }
+        else {
+                make_dev_name(name, k, do_numeric);
+                fd = open(name, O_RDWR | O_NONBLOCK);
+        if (fd < 0) {
+            if ((ENOENT == errno) && (0 == k)) {
+                do_numeric = 0;
+                make_dev_name(name, k, do_numeric);
+                fd = open(name, O_RDWR | O_NONBLOCK);
+            }
+            if (fd < 0) {
+                if (EBUSY == errno)
+                    continue;   /* step over if O_EXCL already on it */
+                else {
+#if 0
+                    snprintf(ebuff, EBUFF_SZ,
+                             "open on %s failed (%d)", name, errno);
+                    perror(ebuff);
+#endif
+                    if (max_holes-- > 0)
+                        continue;
+                    else
+                        break;
+                }
+            }
+        }
+        }
+        max_holes = MAX_HOLES;
+        err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus);
+        if (err < 0) {
+            if ( ! raw ) {
+                snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed", name);
+                perror(ebuff);
+            }
+            close(fd);
+            continue;
+        }
+        err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
+        if (err < 0) {
+            if ( ! raw ) {
+                snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed", name);
+                perror(ebuff);
+            }
+            close(fd);
+            continue;
+        }
+#if 0
+        printf("[scsi%d ch=%d id=%d lun=%d %s]", bus,
+               (m_idlun.mux4 >> 16) & 0xff, m_idlun.mux4 & 0xff,
+               (m_idlun.mux4 >> 8) & 0xff, name);
+#endif
+        for (j = 0; sg_map_arr[j].dev_name; ++j) {
+            if ((bus == sg_map_arr[j].bus) &&
+                ((m_idlun.mux4 & 0xff) == sg_map_arr[j].target_id) &&
+                (((m_idlun.mux4 >> 16) & 0xff) == sg_map_arr[j].channel) &&
+                (((m_idlun.mux4 >> 8) & 0xff) == sg_map_arr[j].lun)) {
+                printf("%s [=%s  scsi%d ch=%d id=%d lun=%d]\n", name,
+                       sg_map_arr[j].dev_name, bus,
+                       ((m_idlun.mux4 >> 16) & 0xff), m_idlun.mux4 & 0xff,
+                       ((m_idlun.mux4 >> 8) & 0xff));
+                break;
+            }
+        }
+        if (NULL == sg_map_arr[j].dev_name)
+            printf("%s [scsi%d ch=%d id=%d lun=%d]\n", name, bus,
+                   ((m_idlun.mux4 >> 16) & 0xff), m_idlun.mux4 & 0xff,
+                   ((m_idlun.mux4 >> 8) & 0xff));
+        close(fd);
+    }
+    printf("\n");
+}
+
+#define DEVNAME_SZ 256
+
+static int
+open_sg_io_dev(char * devname)
+{
+    int fd, fdrw, err, bus, bbus, k, v;
+    My_scsi_idlun m_idlun, mm_idlun;
+    int do_numeric = 1;
+    char name[DEVNAME_SZ];
+    struct stat a_st;
+    int block_dev = 0;
+
+    strncpy(name, devname, DEVNAME_SZ);
+    name[DEVNAME_SZ - 1] = '\0';
+    fd = open(name, O_RDONLY | O_NONBLOCK);
+    if (fd < 0)
+        return fd;
+    if ((ioctl(fd, SG_GET_VERSION_NUM, &v) >= 0) && (v >= 30000)) {
+        fdrw = open(name, O_RDWR | O_NONBLOCK);
+        if (fdrw >= 0) {
+            close(fd);
+            return fdrw;
+        }
+        return fd;
+    }
+    if (fstat(fd, &a_st) < 0) {
+        fprintf(stderr, "could do fstat() on fd ??\n");
+        close(fd);
+        return -9999;
+    }
+    if (S_ISBLK(a_st.st_mode))
+        block_dev = 1;
+
+    if (block_dev || (ioctl(fd, SG_GET_TIMEOUT, 0) < 0)) {
+        err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus);
+        if (err < 0) {
+            fprintf(stderr, "A device name that understands SCSI commands "
+                    "is required\n");
+            close(fd);
+            return -9999;
+        }
+        err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
+        if (err < 0) {
+            fprintf(stderr, "A SCSI device name is required(2)\n");
+            close(fd);
+            return -9999;
+        }
+        close(fd);
+
+        for (k = 0; k < MAX_SG_DEVS; k++) {
+            make_dev_name(name, k, do_numeric);
+            fd = open(name, O_RDWR | O_NONBLOCK);
+            if (fd < 0) {
+                if ((ENOENT == errno) && (0 == k)) {
+                    do_numeric = 0;
+                    make_dev_name(name, k, do_numeric);
+                    fd = open(name, O_RDWR | O_NONBLOCK);
+                }
+                if (fd < 0) {
+                    if (EBUSY == errno)
+                        continue;   /* step over if O_EXCL already on it */
+                    else
+                        break;
+                }
+            }
+            err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bbus);
+            if (err < 0) {
+                perror("sg ioctl failed");
+                close(fd);
+                fd = -9999;
+            }
+            err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &mm_idlun);
+            if (err < 0) {
+                perror("sg ioctl failed");
+                close(fd);
+                fd = -9999;
+            }
+            if ((bus == bbus) &&
+                ((m_idlun.mux4 & 0xff) == (mm_idlun.mux4 & 0xff)) &&
+                (((m_idlun.mux4 >> 8) & 0xff) ==
+                                        ((mm_idlun.mux4 >> 8) & 0xff)) &&
+                (((m_idlun.mux4 >> 16) & 0xff) ==
+                                        ((mm_idlun.mux4 >> 16) & 0xff)))
+                break;
+            else {
+                close(fd);
+                fd = -9999;
+            }
+        }
+    }
+    if (fd >= 0) {
+        if ((ioctl(fd, SG_GET_VERSION_NUM, &v) < 0) || (v < 30000)) {
+            fprintf(stderr, "requires lk 2.4 (sg driver), lk 2.6 or lk 3 "
+                    "series\n");
+            close(fd);
+            return -9999;
+        }
+        close(fd);
+        return open(name, O_RDWR | O_NONBLOCK);
+    }
+    else
+        return fd;
+}
+
+static void
+usage(const char *errtext)
+{
+    if (errtext)
+        fprintf(stderr, "Error: sginfo: %s\n", errtext);
+    fprintf(stderr, "Usage: sginfo [-options] [device] "
+            "[replacement_values]\n");
+    fputs("\tAllowed options are:\n"
+          "\t-6    Do 6 byte mode sense and select commands (def: 10 "
+          "bytes).\n"
+          "\t-a    Display inquiry info, serial # and all mode pages.\n"
+          "\t-A    Similar to '-a' but displays all subpages as well.\n"
+          "\t-c    Access Caching Page.\n"
+          "\t-C    Access Control Mode Page.\n"
+          "\t-d    Display defect lists (default format: index).\n"
+          "\t-D    Access Disconnect-Reconnect Page.\n"
+          "\t-e    Access Read-Write Error Recovery page.\n"
+          "\t-E    Access Control Extension page.\n"
+          "\t-f    Access Format Device Page.\n"
+          "\t-Farg Format of the defect list:\n"
+          "\t\t-Flogical  - logical block addresses (32 bit)\n"
+          "\t\t-Flba64    - logical block addresses (64 bit)\n"
+          "\t\t-Fphysical - physical blocks\n"
+          "\t\t-Findex    - defect bytes from index\n"
+          "\t\t-Fhead     - sort by head\n", stdout);
+    fputs("\t-g    Access Rigid Disk Drive Geometry Page.\n"
+          "\t-G    Display 'grown' defect list (default format: index).\n"
+          "\t-i    Display information from INQUIRY command.\n"
+          "\t-I    Access Informational Exception page.\n"
+          "\t-l    List known scsi devices on the system [DEPRECATED]\n"
+          "\t-n    Access Notch and Partition Page.\n"
+          "\t-N    Negate (stop) storing to saved page (active with -R).\n"
+          "\t-P    Access Power Condition Page.\n"
+          "\t-r    List known raw scsi devices on the system\n"
+          "\t-s    Display serial number (from INQUIRY VPD page).\n"
+          "\t-t<pn[,sp]> Access mode page <pn> [subpage <sp>] and decode.\n"
+          "\t-T    Trace commands (for debugging, double for more)\n"
+          "\t-u<pn[,sp]> Access mode page <pn> [subpage <sp>], output in hex\n"
+          "\t-v    Show version number\n"
+          "\t-V    Access Verify Error Recovery Page.\n"
+          "\t-z    single fetch mode pages (rather than double fetch)\n"
+          "\n", stdout);
+    fputs("\tOnly one of the following three options can be specified.\n"
+   "\tNone of these three implies the current values are returned.\n", stdout);
+    fputs("\t-m    Access modifiable fields instead of current values\n"
+          "\t-M    Access manufacturer defaults instead of current values\n"
+          "\t-S    Access saved defaults instead of current values\n\n"
+          "\t-X    Use list (space separated values) rather than table.\n"
+    "\t-R    Replace parameters - best used with -X (expert use only)\n"
+    "\t      [replacement parameters placed after device on command line]\n\n",
+    stdout);
+    printf("\t      sginfo version: %s; See man page for more details.\n",
+           version_str);
+    exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+    int k, j, n;
+    unsigned int unum, unum2;
+    int decode_in_hex = 0;
+    char c;
+    char * cp;
+    int status = 0;
+    long tmp;
+    struct mpage_info mp_i;
+    int inquiry_verbosity = 0;
+    int show_devs = 0, show_raw = 0;
+    int found = 0;
+
+    if (argc < 2)
+        usage(NULL);
+    memset(&mp_i, 0, sizeof(mp_i));
+    while ((k = getopt(argc, argv, "6aAcCdDeEfgGiIlmMnNPrRsSTvVXzF:t:u:")) !=
+           EOF) {
+        c = (char)k;
+        switch (c) {
+        case '6':
+            mode6byte = 1;
+            break;
+        case 'a':
+            inquiry_verbosity = 1;
+            serial_number = 1;
+            mp_i.page = MP_LIST_PAGES;
+            break;
+        case 'A':
+            inquiry_verbosity = 1;
+            serial_number = 1;
+            mp_i.page = MP_LIST_PAGES;
+            mp_i.subpage = MP_LIST_SUBPAGES;
+            break;
+        case 'c':
+            mp_i.page = 0x8;
+            break;
+        case 'C':
+            mp_i.page = 0xa;
+            break;
+        case 'd':
+            defect = 1;
+            break;
+        case 'D':
+            mp_i.page = 0x2;
+            break;
+        case 'e':
+            mp_i.page = 0x1;
+            break;
+        case 'E':
+            mp_i.page = 0xa;
+            mp_i.subpage = 0x1;
+            break;
+        case 'f':
+            mp_i.page = 0x3;
+            break;
+        case 'F':
+            if (!strcasecmp(optarg, "logical"))
+                defectformat = 0x0;
+            else if (!strcasecmp(optarg, "lba64"))
+                defectformat = 0x3;
+            else if (!strcasecmp(optarg, "physical"))
+                defectformat = 0x5;
+            else if (!strcasecmp(optarg, "index"))
+                defectformat = 0x4;
+            else if (!strcasecmp(optarg, "head"))
+                defectformat = HEAD_SORT_TOKEN;
+            else
+                usage("Illegal -F parameter, must be one of logical, "
+                      "physical, index or head");
+            break;
+        case 'g':
+            mp_i.page = 0x4;
+            break;
+        case 'G':
+            grown_defect = 1;
+            break;
+        case 'i':       /* just vendor, product and revision for '-i -i' */
+            inquiry_verbosity = (2 == inquiry_verbosity) ? 1 : 2;
+            break;
+        case 'I':
+            mp_i.page = 0x1c;
+            break;
+        case 'l':
+            show_devs = 1;
+            break;
+        case 'm': /* modifiable page control */
+            if (0 == mp_i.page_control)
+                mp_i.page_control = 1;
+            else
+                usage("can only have one of 'm', 'M' and 'S'");
+            break;
+        case 'M': /* manufacturer's==default page control */
+            if (0 == mp_i.page_control)
+                mp_i.page_control = 2;
+            else
+                usage("can only have one of 'M', 'm' and 'S'");
+            break;
+        case 'n':
+            mp_i.page = 0xc;
+            break;
+        case 'N':
+            negate_sp_bit = 1;
+            break;
+        case 'P':
+            mp_i.page = 0x1a;
+            break;
+        case 'r':
+            show_raw = 1;
+            break;
+        case 'R':
+            replace = 1;
+            break;
+        case 's':
+            serial_number = 1;
+            break;
+        case 'S': /* saved page control */
+            if (0 == mp_i.page_control)
+                mp_i.page_control = 3;
+            else
+                usage("can only have one of 'S', 'm' and 'M'");
+            break;
+        case 'T':
+            trace_cmd++;
+            break;
+        case 't':
+        case 'u':
+            if ('u' == c)
+                decode_in_hex = 1;
+            while (' ' == *optarg)
+                optarg++;
+            if ('0' == *optarg) {
+                unum = 0;
+                unum2 = 0;
+                j = sscanf(optarg, "0x%x,0x%x", &unum, &unum2);
+                mp_i.page = unum;
+                if (1 == j) {
+                    cp = strchr(optarg, ',');
+                    if (cp && (1 == sscanf(cp, ",%d", &mp_i.subpage)))
+                        j = 2;
+                } else
+                    mp_i.subpage = unum2;
+            } else
+                j = sscanf(optarg, "%d,%d", &mp_i.page, &mp_i.subpage);
+            if (1 == j)
+                mp_i.subpage = 0;
+            else if (j < 1)
+                usage("argument following '-u' should be of form "
+                      "<pg>[,<subpg>]");
+            if ((mp_i.page < 0) || (mp_i.page > MP_LIST_PAGES) ||
+                (mp_i.subpage < 0) || (mp_i.subpage > MP_LIST_SUBPAGES))
+                usage("mode pages range from 0 .. 63, subpages from "
+                      "1 .. 255");
+            found = 1;
+            break;
+        case 'v':
+            fprintf(stdout, "sginfo version: %s\n", version_str);
+            return 0;
+        case 'V':
+            mp_i.page = 0x7;
+            break;
+        case 'X':
+            x_interface = 1;
+            break;
+        case 'z':
+            single_fetch = 1;
+            break;
+        case '?':
+            usage("Unknown option");
+            break;
+        default:
+            fprintf(stdout, "Unknown option '-%c' (ascii 0x%02x)\n", c, c);
+            usage("bad option");
+        }
+    }
+
+    if (replace && !x_interface)
+        usage("-R requires -X");
+    if (replace && mp_i.page_control)
+        usage("-R not allowed for -m, -M or -S");
+    if (x_interface && replace && ((MP_LIST_PAGES == mp_i.page) ||
+                        (MP_LIST_SUBPAGES == mp_i.subpage)))
+        usage("-XR can be used only with exactly one page.");
+
+    if (replace && (3 != mp_i.page_control)) {
+        memset (is_hex, 0, 32);
+        for (j = 1; j < argc - optind; j++) {
+            if (strncmp(argv[optind + j], "0x", 2) == 0) {
+                char *pnt = argv[optind + j] + 2;
+                replacement_values[j] = 0;
+        /* This is a kluge, but we can handle 64 bit quantities this way. */
+                while (*pnt) {
+                    if (*pnt >= 'a' && *pnt <= 'f')
+                        *pnt -= 32;
+                    replacement_values[j] = (replacement_values[j] << 4) |
+                        (*pnt > '9' ? (*pnt - 'A' + 10) : (*pnt - '0'));
+                    pnt++;
+                }
+                continue;
+            }
+            if (argv[optind + j][0] == '@') {
+        /*Ensure that this string contains an even number of hex-digits */
+                int len = strlen(argv[optind + j] + 1);
+
+                if ((len & 1) || (len != (int)strspn(argv[optind + j] + 1,
+                                                "0123456789ABCDEFabcdef")))
+                            usage("Odd number of chars or non-hex digit in "
+                                  "@hexdatafield");
+
+                replacement_values[j] = (unsigned long) argv[optind + j];
+                is_hex[j] = 1;
+                continue;
+            }
+            /* Using a tmp here is silly but the most clean approach */
+            n = sscanf(argv[optind + j], "%ld", &tmp);
+            replacement_values[j] = ((1 == n) ? tmp : 0);
+        }
+        n_replacement_values = argc - optind - 1;
+    }
+    if (show_devs) {
+        show_devices(0);
+        exit(0);
+    }
+    if (show_raw) {
+        show_devices(1);
+        exit(0);
+    }
+    if (optind >= argc)
+        usage("no device name given");
+    glob_fd = open_sg_io_dev(device_name = argv[optind]);
+    if (glob_fd < 0) {
+        if (-9999 == glob_fd)
+            fprintf(stderr, "Couldn't find sg device corresponding to %s\n",
+                    device_name);
+        else {
+            perror("sginfo(open)");
+            fprintf(stderr, "file=%s, or no corresponding sg device found\n",
+                    device_name);
+            fprintf(stderr, "Is sg driver loaded?\n");
+        }
+        exit(1);
+    }
+
+#if 0
+    if (!x_interface)
+        printf("\n");
+#endif
+    if (! (found || mp_i.page || mp_i.subpage || inquiry_verbosity ||
+           serial_number)) {
+        if (trace_cmd > 0)
+            fprintf(stdout, "nothing selected so do a short INQUIRY\n");
+        inquiry_verbosity = 1;
+    }
+
+    status |= do_inquiry(&mp_i.peri_type, &mp_i.inq_byte6,
+                         inquiry_verbosity);
+    if (serial_number)
+        do_serial_number();     /* ignore error */
+    if (mp_i.page > 0)
+        status |= do_user_page(&mp_i, decode_in_hex);
+    if (defect)
+        status |= read_defect_list(0);
+    if (grown_defect)
+        status |= read_defect_list(1);
+
+    return status ? 1 : 0;
+}
diff --git a/sg3_utils/src/sgm_dd.c b/sg3_utils/src/sgm_dd.c
new file mode 100644
index 0000000..90f2938
--- /dev/null
+++ b/sg3_utils/src/sgm_dd.c
@@ -0,0 +1,1372 @@
+/* A utility program for copying files. Specialised for "files" that
+ * represent devices that understand the SCSI command set.
+ *
+ * Copyright (C) 1999 - 2016 D. Gilbert and P. Allworth
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+
+   This program is a specialisation of the Unix "dd" command in which
+   either the input or the output file is a scsi generic device or a
+   raw device. The block size ('bs') is assumed to be 512 if not given.
+   This program complains if 'ibs' or 'obs' are given with a value
+   that differs from 'bs' (or the default 512).
+   If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
+   not given or 'of=-' then stdout assumed.
+
+   A non-standard argument "bpt" (blocks per transfer) is added to control
+   the maximum number of blocks in each transfer. The default value is 128.
+   For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
+   in this case) is transferred to or from the sg device in a single SCSI
+   command.
+
+   This version uses memory-mapped transfers (i.e. mmap() call from the user
+   space) to speed transfers. If both sides of copy are sg devices
+   then only the read side will be mmap-ed, while the write side will
+   use normal IO.
+
+   This version is designed for the linux kernel 2.4, 2.6 and 3 series.
+*/
+
+#define _XOPEN_SOURCE 500
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <linux/major.h>
+#include <linux/fs.h>   /* <sys/mount.h> */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "1.44 20151219";
+
+#define DEF_BLOCK_SIZE 512
+#define DEF_BLOCKS_PER_TRANSFER 128
+#define DEF_BLOCKS_PER_2048TRANSFER 32
+#define DEF_SCSI_CDBSZ 10
+#define MAX_SCSI_CDBSZ 16
+
+#define ME "sgm_dd: "
+
+/* #define SG_DEBUG */
+
+#ifndef SG_FLAG_MMAP_IO
+#define SG_FLAG_MMAP_IO 4
+#endif
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define READ_CAP_REPLY_LEN 8
+#define RCAP16_REPLY_LEN 32
+
+#ifndef SERVICE_ACTION_IN
+#define SERVICE_ACTION_IN     0x9e
+#endif
+#ifndef SAI_READ_CAPACITY_16
+#define SAI_READ_CAPACITY_16  0x10
+#endif
+
+
+#define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
+
+#ifndef RAW_MAJOR
+#define RAW_MAJOR 255   /*unlikey value */
+#endif
+
+#define FT_OTHER 1              /* filetype other than one of following */
+#define FT_SG 2                 /* filetype is sg char device */
+#define FT_RAW 4                /* filetype is raw char device */
+#define FT_DEV_NULL 8           /* either "/dev/null" or "." as filename */
+#define FT_ST 16                /* filetype is st char device (tape) */
+#define FT_BLOCK 32             /* filetype is a block device */
+#define FT_ERROR 64             /* couldn't "stat" file */
+
+#define DEV_NULL_MINOR_NUM 3
+
+#define MIN_RESERVED_SIZE 8192
+
+static int sum_of_resids = 0;
+
+static int64_t dd_count = -1;
+static int64_t req_count = 0;
+static int64_t in_full = 0;
+static int in_partial = 0;
+static int64_t out_full = 0;
+static int out_partial = 0;
+static int verbose = 0;
+
+static int do_time = 0;
+static int start_tm_valid = 0;
+static struct timeval start_tm;
+static int blk_sz = 0;
+
+static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
+
+struct flags_t {
+    int append;
+    int dio;
+    int direct;
+    int dpo;
+    int dsync;
+    int excl;
+    int fua;
+};
+
+
+static void
+install_handler(int sig_num, void (*sig_handler) (int sig))
+{
+    struct sigaction sigact;
+    sigaction (sig_num, NULL, &sigact);
+    if (sigact.sa_handler != SIG_IGN)
+    {
+        sigact.sa_handler = sig_handler;
+        sigemptyset (&sigact.sa_mask);
+        sigact.sa_flags = 0;
+        sigaction (sig_num, &sigact, NULL);
+    }
+}
+
+static void
+print_stats()
+{
+    if (0 != dd_count)
+        pr2serr("  remaining block count=%" PRId64 "\n", dd_count);
+    pr2serr("%" PRId64 "+%d records in\n", in_full - in_partial, in_partial);
+    pr2serr("%" PRId64 "+%d records out\n", out_full - out_partial,
+            out_partial);
+}
+
+static void
+calc_duration_throughput(int contin)
+{
+    struct timeval end_tm, res_tm;
+    double a, b;
+
+    if (start_tm_valid && (start_tm.tv_sec || start_tm.tv_usec)) {
+        gettimeofday(&end_tm, NULL);
+        res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+        res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+        if (res_tm.tv_usec < 0) {
+            --res_tm.tv_sec;
+            res_tm.tv_usec += 1000000;
+        }
+        a = res_tm.tv_sec;
+        a += (0.000001 * res_tm.tv_usec);
+        b = (double)blk_sz * (req_count - dd_count);
+        pr2serr("time to transfer data%s: %d.%06d secs",
+                (contin ? " so far" : ""), (int)res_tm.tv_sec,
+                (int)res_tm.tv_usec);
+        if ((a > 0.00001) && (b > 511))
+            pr2serr(" at %.2f MB/sec\n", b / (a * 1000000.0));
+        else
+            pr2serr("\n");
+    }
+}
+
+static void
+interrupt_handler(int sig)
+{
+    struct sigaction sigact;
+
+    sigact.sa_handler = SIG_DFL;
+    sigemptyset (&sigact.sa_mask);
+    sigact.sa_flags = 0;
+    sigaction (sig, &sigact, NULL);
+    pr2serr("Interrupted by signal,");
+    print_stats ();
+    if (do_time)
+        calc_duration_throughput(0);
+    kill (getpid (), sig);
+}
+
+static void
+siginfo_handler(int sig)
+{
+    if (sig) { ; }      /* unused, dummy to suppress warning */
+    pr2serr("Progress report, continuing ...\n");
+    print_stats();
+    if (do_time)
+        calc_duration_throughput(1);
+}
+
+static int
+dd_filetype(const char * filename)
+{
+    struct stat st;
+    size_t len = strlen(filename);
+
+    if ((1 == len) && ('.' == filename[0]))
+        return FT_DEV_NULL;
+    if (stat(filename, &st) < 0)
+        return FT_ERROR;
+    if (S_ISCHR(st.st_mode)) {
+        if ((MEM_MAJOR == major(st.st_rdev)) &&
+            (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
+            return FT_DEV_NULL;
+        if (RAW_MAJOR == major(st.st_rdev))
+            return FT_RAW;
+        if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
+            return FT_SG;
+        if (SCSI_TAPE_MAJOR == major(st.st_rdev))
+            return FT_ST;
+    } else if (S_ISBLK(st.st_mode))
+        return FT_BLOCK;
+    return FT_OTHER;
+}
+
+static char *
+dd_filetype_str(int ft, char * buff)
+{
+    int off = 0;
+
+    if (FT_DEV_NULL & ft)
+        off += snprintf(buff + off, 32, "null device ");
+    if (FT_SG & ft)
+        off += snprintf(buff + off, 32, "SCSI generic (sg) device ");
+    if (FT_BLOCK & ft)
+        off += snprintf(buff + off, 32, "block device ");
+    if (FT_ST & ft)
+        off += snprintf(buff + off, 32, "SCSI tape device ");
+    if (FT_RAW & ft)
+        off += snprintf(buff + off, 32, "raw device ");
+    if (FT_OTHER & ft)
+        off += snprintf(buff + off, 32, "other (perhaps ordinary file) ");
+    if (FT_ERROR & ft)
+        off += snprintf(buff + off, 32, "unable to 'stat' file ");
+    return buff;
+}
+
+static void
+usage()
+{
+    pr2serr("Usage: sgm_dd  [bs=BS] [count=COUNT] [ibs=BS] [if=IFILE]"
+            " [iflag=FLAGS]\n"
+            "               [obs=BS] [of=OFILE] [oflag=FLAGS] "
+            "[seek=SEEK] [skip=SKIP]\n"
+            "               [--help] [--version]\n\n");
+    pr2serr("               [bpt=BPT] [cdbsz=6|10|12|16] [dio=0|1] "
+            "[fua=0|1|2|3]\n"
+            "               [sync=0|1] [time=0|1] [verbose=VERB]\n\n"
+            "  where:\n"
+            "    bpt         is blocks_per_transfer (default is 128)\n"
+            "    bs          must be device block size (default 512)\n"
+            "    cdbsz       size of SCSI READ or WRITE cdb (default is 10)\n"
+            "    count       number of blocks to copy (def: device size)\n"
+            "    dio         0->indirect IO on write, 1->direct IO on write\n"
+            "                (only when read side is sg device (using mmap))\n"
+            "    fua         force unit access: 0->don't(def), 1->OFILE, "
+            "2->IFILE,\n"
+            "                3->OFILE+IFILE\n"
+            "    if          file or device to read from (def: stdin)\n");
+    pr2serr("    iflag       comma separated list from: [direct,dpo,dsync,"
+            "excl,fua,\n"
+            "                null]\n"
+            "    of          file or device to write to (def: stdout), "
+            "OFILE of '.'\n"
+            "                treated as /dev/null\n"
+            "    oflag       comma separated list from: [append,dio,direct,"
+            "dpo,dsync,\n"
+            "                excl,fua,null]\n"
+            "    seek        block position to start writing to OFILE\n"
+            "    skip        block position to start reading from IFILE\n"
+            "    sync        0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
+            "after copy\n"
+            "    time        0->no timing(def), 1->time plus calculate "
+            "throughput\n"
+            "    verbose     0->quiet(def), 1->some noise, 2->more noise, "
+            "etc\n"
+            "    --help      print usage message then exit\n"
+            "    --version   print version information then exit\n\n"
+            "Copy from IFILE to OFILE, similar to dd command\n"
+            "specialized for SCSI devices for which mmap-ed IO attempted\n");
+}
+
+/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
+static int
+scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
+{
+    int res;
+    unsigned int ui;
+    unsigned char rcBuff[RCAP16_REPLY_LEN];
+    int verb;
+
+    verb = (verbose ? verbose - 1: 0);
+    res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, 0,
+                           verb);
+    if (0 != res)
+        return res;
+
+    if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
+        (0xff == rcBuff[3])) {
+
+        res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, 0,
+                               verb);
+        if (0 != res)
+            return res;
+        *num_sect = sg_get_unaligned_be64(rcBuff + 0) + 1;
+        *sect_sz = sg_get_unaligned_be32(rcBuff + 8);
+    } else {
+        ui = sg_get_unaligned_be32(rcBuff + 0);
+        /* take care not to sign extend values > 0x7fffffff */
+        *num_sect = (int64_t)ui + 1;
+        *sect_sz = sg_get_unaligned_be32(rcBuff + 4);
+    }
+    if (verbose)
+        pr2serr("      number of blocks=%" PRId64 " [0x%" PRIx64 "], block "
+                "size=%d\n", *num_sect, *num_sect, *sect_sz);
+    return 0;
+}
+
+/* Return of 0 -> success, -1 -> failure. BLKGETSIZE64, BLKGETSIZE and */
+/* BLKSSZGET macros problematic (from <linux/fs.h> or <sys/mount.h>). */
+static int
+read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
+{
+#ifdef BLKSSZGET
+    if ((ioctl(sg_fd, BLKSSZGET, sect_sz) < 0) && (*sect_sz > 0)) {
+        perror("BLKSSZGET ioctl error");
+        return -1;
+    } else {
+ #ifdef BLKGETSIZE64
+        uint64_t ull;
+
+        if (ioctl(sg_fd, BLKGETSIZE64, &ull) < 0) {
+
+            perror("BLKGETSIZE64 ioctl error");
+            return -1;
+        }
+        *num_sect = ((int64_t)ull / (int64_t)*sect_sz);
+        if (verbose)
+            pr2serr("      [bgs64] number of blocks=%" PRId64 " [0x%" PRIx64
+                    "], block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ #else
+        unsigned long ul;
+
+        if (ioctl(sg_fd, BLKGETSIZE, &ul) < 0) {
+            perror("BLKGETSIZE ioctl error");
+            return -1;
+        }
+        *num_sect = (int64_t)ul;
+        if (verbose)
+            pr2serr("      [bgs] number of blocks=%" PRId64 " [0x%" PRIx64
+                    "], block size=%d\n", *num_sect, *num_sect, *sect_sz);
+ #endif
+    }
+    return 0;
+#else
+    if (verbose)
+        pr2serr("      BLKSSZGET+BLKGETSIZE ioctl not available\n");
+    *num_sect = 0;
+    *sect_sz = 0;
+    return -1;
+#endif
+}
+
+static int
+sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
+                  int64_t start_block, int write_true, int fua, int dpo)
+{
+    int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+    int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
+    int sz_ind;
+
+    memset(cdbp, 0, cdb_sz);
+    if (dpo)
+        cdbp[1] |= 0x10;
+    if (fua)
+        cdbp[1] |= 0x8;
+    switch (cdb_sz) {
+    case 6:
+        sz_ind = 0;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
+        cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
+        if (blocks > 256) {
+            pr2serr(ME "for 6 byte commands, maximum number of blocks is "
+                    "256\n");
+            return 1;
+        }
+        if ((start_block + blocks - 1) & (~0x1fffff)) {
+            pr2serr(ME "for 6 byte commands, can't address blocks beyond "
+                    "%d\n", 0x1fffff);
+            return 1;
+        }
+        if (dpo || fua) {
+            pr2serr(ME "for 6 byte commands, neither dpo nor fua bits "
+                    "supported\n");
+            return 1;
+        }
+        break;
+    case 10:
+        sz_ind = 1;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
+        sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7);
+        if (blocks & (~0xffff)) {
+            pr2serr(ME "for 10 byte commands, maximum number of blocks is "
+                    "%d\n", 0xffff);
+            return 1;
+        }
+        break;
+    case 12:
+        sz_ind = 2;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
+        sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6);
+        break;
+    case 16:
+        sz_ind = 3;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be64((uint64_t)start_block, cdbp + 2);
+        sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10);
+        break;
+    default:
+        pr2serr(ME "expected cdb size of 6, 10, 12, or 16 but got %d\n",
+                cdb_sz);
+        return 1;
+    }
+    return 0;
+}
+
+/* Returns 0 -> successful, various SG_LIB_CAT_* positive values,
+ * -2 -> recoverable (ENOMEM), -1 -> unrecoverable error */
+static int
+sg_read(int sg_fd, unsigned char * buff, int blocks, int64_t from_block,
+        int bs, int cdbsz, int fua, int dpo, int do_mmap)
+{
+    unsigned char rdCmd[MAX_SCSI_CDBSZ];
+    unsigned char senseBuff[SENSE_BUFF_LEN];
+    struct sg_io_hdr io_hdr;
+    int k, res;
+
+    if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, dpo)) {
+        pr2serr(ME "bad rd cdb build, from_block=%" PRId64 ", blocks=%d\n",
+                from_block, blocks);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = cdbsz;
+    io_hdr.cmdp = rdCmd;
+    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_hdr.dxfer_len = bs * blocks;
+    if (! do_mmap)
+        io_hdr.dxferp = buff;
+    io_hdr.mx_sb_len = SENSE_BUFF_LEN;
+    io_hdr.sbp = senseBuff;
+    io_hdr.timeout = DEF_TIMEOUT;
+    io_hdr.pack_id = (int)from_block;
+    if (do_mmap)
+        io_hdr.flags |= SG_FLAG_MMAP_IO;
+    if (verbose > 2) {
+        pr2serr("    read cdb: ");
+        for (k = 0; k < cdbsz; ++k)
+            pr2serr("%02x ", rdCmd[k]);
+        pr2serr("\n");
+    }
+
+#if 1
+    while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        sleep(1);
+    if (res < 0) {
+        perror(ME "SG_IO error (sg_read)");
+        return -1;
+    }
+#else
+    while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        if (ENOMEM == errno)
+            return -2;
+        perror("reading (wr) on sg device, error");
+        return -1;
+    }
+
+    while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        perror("reading (rd) on sg device, error");
+        return -1;
+    }
+#endif
+    if (verbose > 2)
+        pr2serr("      duration=%u ms\n", io_hdr.duration);
+    res =  sg_err_category3(&io_hdr);
+    switch (res) {
+    case SG_LIB_CAT_CLEAN:
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        sg_chk_n_print3("Reading, continuing", &io_hdr, verbose > 1);
+        break;
+    case SG_LIB_CAT_NOT_READY:
+    case SG_LIB_CAT_MEDIUM_HARD:
+        return res;
+    case SG_LIB_CAT_ABORTED_COMMAND:
+    case SG_LIB_CAT_UNIT_ATTENTION:
+    case SG_LIB_CAT_ILLEGAL_REQ:
+    default:
+        sg_chk_n_print3("reading", &io_hdr, verbose > 1);
+        return res;
+    }
+    sum_of_resids += io_hdr.resid;
+#ifdef SG_DEBUG
+    pr2serr("duration=%u ms\n", io_hdr.duration);
+#endif
+    return 0;
+}
+
+/* Returns 0 -> successful, various SG_LIB_CAT_* positive values,
+ * -2 -> recoverable (ENOMEM), -1 -> unrecoverable error */
+static int
+sg_write(int sg_fd, unsigned char * buff, int blocks, int64_t to_block,
+         int bs, int cdbsz, int fua, int dpo, int do_mmap, int * diop)
+{
+    unsigned char wrCmd[MAX_SCSI_CDBSZ];
+    unsigned char senseBuff[SENSE_BUFF_LEN];
+    struct sg_io_hdr io_hdr;
+    int k, res;
+
+    if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, dpo)) {
+        pr2serr(ME "bad wr cdb build, to_block=%" PRId64 ", blocks=%d\n",
+                to_block, blocks);
+        return SG_LIB_SYNTAX_ERROR;
+    }
+
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = cdbsz;
+    io_hdr.cmdp = wrCmd;
+    io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+    io_hdr.dxfer_len = bs * blocks;
+    if (! do_mmap)
+        io_hdr.dxferp = buff;
+    io_hdr.mx_sb_len = SENSE_BUFF_LEN;
+    io_hdr.sbp = senseBuff;
+    io_hdr.timeout = DEF_TIMEOUT;
+    io_hdr.pack_id = (int)to_block;
+    if (do_mmap)
+        io_hdr.flags |= SG_FLAG_MMAP_IO;
+    else if (diop && *diop)
+        io_hdr.flags |= SG_FLAG_DIRECT_IO;
+    if (verbose > 2) {
+        pr2serr("    write cdb: ");
+        for (k = 0; k < cdbsz; ++k)
+            pr2serr("%02x ", wrCmd[k]);
+        pr2serr("\n");
+    }
+
+#if 1
+    while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        sleep(1);
+    if (res < 0) {
+        perror(ME "SG_IO error (sg_write)");
+        return -1;
+    }
+#else
+    while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        if (ENOMEM == errno)
+            return -2;
+        perror("writing (wr) on sg device, error");
+        return -1;
+    }
+
+    while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        perror("writing (rd) on sg device, error");
+        return -1;
+    }
+#endif
+    if (verbose > 2)
+        pr2serr("      duration=%u ms\n", io_hdr.duration);
+    res = sg_err_category3(&io_hdr);
+    switch (res) {
+    case SG_LIB_CAT_CLEAN:
+        break;
+    case SG_LIB_CAT_RECOVERED:
+        sg_chk_n_print3("Writing, continuing", &io_hdr, verbose > 1);
+        break;
+    case SG_LIB_CAT_NOT_READY:
+    case SG_LIB_CAT_MEDIUM_HARD:
+        return res;
+    case SG_LIB_CAT_ABORTED_COMMAND:
+    case SG_LIB_CAT_UNIT_ATTENTION:
+    case SG_LIB_CAT_ILLEGAL_REQ:
+    default:
+        sg_chk_n_print3("writing", &io_hdr, verbose > 1);
+        return res;
+    }
+    if (diop && *diop &&
+        ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+        *diop = 0;      /* flag that dio not done (completely) */
+    return 0;
+}
+
+static int
+process_flags(const char * arg, struct flags_t * fp)
+{
+    char buff[256];
+    char * cp;
+    char * np;
+
+    strncpy(buff, arg, sizeof(buff));
+    buff[sizeof(buff) - 1] = '\0';
+    if ('\0' == buff[0]) {
+        pr2serr("no flag found\n");
+        return 1;
+    }
+    cp = buff;
+    do {
+        np = strchr(cp, ',');
+        if (np)
+            *np++ = '\0';
+        if (0 == strcmp(cp, "append"))
+            fp->append = 1;
+        else if (0 == strcmp(cp, "dio"))
+            fp->dio = 1;
+        else if (0 == strcmp(cp, "direct"))
+            fp->direct = 1;
+        else if (0 == strcmp(cp, "dpo"))
+            fp->dpo = 1;
+        else if (0 == strcmp(cp, "dsync"))
+            fp->dsync = 1;
+        else if (0 == strcmp(cp, "excl"))
+            fp->excl = 1;
+        else if (0 == strcmp(cp, "fua"))
+            fp->fua = 1;
+        else if (0 == strcmp(cp, "null"))
+            ;
+        else {
+            pr2serr("unrecognised flag: %s\n", cp);
+            return 1;
+        }
+        cp = np;
+    } while (cp);
+    return 0;
+}
+
+
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+#define EBUFF_SZ 512
+
+
+int
+main(int argc, char * argv[])
+{
+    int64_t skip = 0;
+    int64_t seek = 0;
+    int ibs = 0;
+    int obs = 0;
+    int bpt = DEF_BLOCKS_PER_TRANSFER;
+    int bpt_given = 0;
+    char str[STR_SZ];
+    char * key;
+    char * buf;
+    char inf[INOUTF_SZ];
+    int in_type = FT_OTHER;
+    char outf[INOUTF_SZ];
+    int out_type = FT_OTHER;
+    int res, k, t;
+    int infd, outfd, blocks;
+    unsigned char * wrkPos;
+    unsigned char * wrkBuff = NULL;
+    unsigned char * wrkMmap = NULL;
+    int64_t in_num_sect = -1;
+    int in_res_sz = 0;
+    int64_t out_num_sect = -1;
+    int out_res_sz = 0;
+    int scsi_cdbsz_in = DEF_SCSI_CDBSZ;
+    int scsi_cdbsz_out = DEF_SCSI_CDBSZ;
+    int cdbsz_given = 0;
+    int do_coe = 0;     /* dummy, just accept + ignore */
+    int do_sync = 0;
+    int num_dio_not_done = 0;
+    int in_sect_sz, out_sect_sz;
+    int n, flags;
+    char ebuff[EBUFF_SZ];
+    char b[80];
+    int blocks_per;
+    size_t psz;
+    struct flags_t in_flags;
+    struct flags_t out_flags;
+    int ret = 0;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+    psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
+#else
+    psz = 4096;     /* give up, pick likely figure */
+#endif
+    inf[0] = '\0';
+    outf[0] = '\0';
+    memset(&in_flags, 0, sizeof(in_flags));
+    memset(&out_flags, 0, sizeof(out_flags));
+
+    for (k = 1; k < argc; k++) {
+        if (argv[k])
+            strncpy(str, argv[k], STR_SZ);
+        else
+            continue;
+        for (key = str, buf = key; *buf && *buf != '=';)
+            buf++;
+        if (*buf)
+            *buf++ = '\0';
+        if (0 == strcmp(key,"bpt")) {
+            bpt = sg_get_num(buf);
+            if (-1 == bpt) {
+                pr2serr(ME "bad argument to 'bpt'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            bpt_given = 1;
+        } else if (0 == strcmp(key,"bs")) {
+            blk_sz = sg_get_num(buf);
+            if (-1 == blk_sz) {
+                pr2serr(ME "bad argument to 'bs'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"cdbsz")) {
+            scsi_cdbsz_in = sg_get_num(buf);
+            scsi_cdbsz_out = scsi_cdbsz_in;
+            cdbsz_given = 1;
+        } else if (0 == strcmp(key,"coe")) {
+            do_coe = sg_get_num(buf);   /* dummy, just accept + ignore */
+            if (do_coe) { ; }   /* unused, dummy to suppress warning */
+        } else if (0 == strcmp(key,"count")) {
+            if (0 != strcmp("-1", buf)) {
+                dd_count = sg_get_llnum(buf);
+                if (-1LL == dd_count) {
+                    pr2serr(ME "bad argument to 'count'\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }   /* treat 'count=-1' as calculate count (same as not given) */
+        } else if (0 == strcmp(key,"dio"))
+            out_flags.dio = sg_get_num(buf);
+        else if (0 == strcmp(key,"fua")) {
+            n = sg_get_num(buf);
+            if (n & 1)
+                out_flags.fua = 1;
+            if (n & 2)
+                in_flags.fua = 1;
+        } else if (0 == strcmp(key,"ibs")) {
+            ibs = sg_get_num(buf);
+            if (-1 == ibs) {
+                pr2serr(ME "bad argument to 'ibs'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (strcmp(key,"if") == 0) {
+            if ('\0' != inf[0]) {
+                pr2serr("Second 'if=' argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(inf, buf, INOUTF_SZ);
+        } else if (0 == strcmp(key, "iflag")) {
+            if (process_flags(buf, &in_flags)) {
+                pr2serr(ME "bad argument to 'iflag'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (strcmp(key,"of") == 0) {
+            if ('\0' != outf[0]) {
+                pr2serr("Second 'of=' argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(outf, buf, INOUTF_SZ);
+        } else if (0 == strcmp(key, "oflag")) {
+            if (process_flags(buf, &out_flags)) {
+                pr2serr(ME "bad argument to 'oflag'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"obs")) {
+            obs = sg_get_num(buf);
+            if (-1 == obs) {
+                pr2serr(ME "bad argument to 'obs'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"seek")) {
+            seek = sg_get_llnum(buf);
+            if (-1LL == seek) {
+                pr2serr(ME "bad argument to 'seek'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"skip")) {
+            skip = sg_get_llnum(buf);
+            if (-1LL == skip) {
+                pr2serr(ME "bad argument to 'skip'\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"sync"))
+            do_sync = sg_get_num(buf);
+        else if (0 == strcmp(key,"time"))
+            do_time = sg_get_num(buf);
+        else if (0 == strncmp(key, "verb", 4))
+            verbose = sg_get_num(buf);
+        else if ((0 == strncmp(key, "--help", 7)) ||
+                 (0 == strcmp(key, "-h")) || (0 == strcmp(key, "-?"))) {
+            usage();
+            return 0;
+        } else if ((0 == strncmp(key, "--vers", 6)) ||
+                   (0 == strcmp(key, "-V"))) {
+            pr2serr(ME ": %s\n", version_str);
+            return 0;
+        }
+        else {
+            pr2serr("Unrecognized option '%s'\n", key);
+            pr2serr("For more information use '--help'\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (blk_sz <= 0) {
+        blk_sz = DEF_BLOCK_SIZE;
+        pr2serr("Assume default 'bs' (block size) of %d bytes\n", blk_sz);
+    }
+    if ((ibs && (ibs != blk_sz)) || (obs && (obs != blk_sz))) {
+        pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((skip < 0) || (seek < 0)) {
+        pr2serr("skip and seek cannot be negative\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((out_flags.append > 0) && (seek > 0)) {
+        pr2serr("Can't use both append and seek switches\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (bpt < 1) {
+        pr2serr("bpt must be greater than 0\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    /* defaulting transfer size to 128*2048 for CD/DVDs is too large
+       for the block layer in lk 2.6 and results in an EIO on the
+       SG_IO ioctl. So reduce it in that case. */
+    if ((blk_sz >= 2048) && (0 == bpt_given))
+        bpt = DEF_BLOCKS_PER_2048TRANSFER;
+
+#ifdef SG_DEBUG
+    pr2serr(ME "if=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%" PRId64
+            "\n", inf, skip, outf, seek, dd_count);
+#endif
+    install_handler (SIGINT, interrupt_handler);
+    install_handler (SIGQUIT, interrupt_handler);
+    install_handler (SIGPIPE, interrupt_handler);
+    install_handler (SIGUSR1, siginfo_handler);
+
+    infd = STDIN_FILENO;
+    outfd = STDOUT_FILENO;
+    if (inf[0] && ('-' != inf[0])) {
+        in_type = dd_filetype(inf);
+        if (verbose)
+            pr2serr(" >> Input file type: %s\n",
+                    dd_filetype_str(in_type, ebuff));
+
+        if (FT_ERROR == in_type) {
+            pr2serr(ME "unable to access %s\n", inf);
+            return SG_LIB_FILE_ERROR;
+        } else if (FT_ST == in_type) {
+            pr2serr(ME "unable to use scsi tape device %s\n", inf);
+            return SG_LIB_FILE_ERROR;
+        } else if (FT_SG == in_type) {
+            flags = O_RDWR | O_NONBLOCK;
+            if (in_flags.direct)
+                flags |= O_DIRECT;
+            if (in_flags.excl)
+                flags |= O_EXCL;
+            if (in_flags.dsync)
+                flags |= O_SYNC;
+            if ((infd = open(inf, flags)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for sg reading", inf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+            res = ioctl(infd, SG_GET_VERSION_NUM, &t);
+            if ((res < 0) || (t < 30122)) {
+                pr2serr(ME "sg driver prior to 3.1.22\n");
+                return SG_LIB_FILE_ERROR;
+            }
+            in_res_sz = blk_sz * bpt;
+            if (0 != (in_res_sz % psz)) /* round up to next page */
+                in_res_sz = ((in_res_sz / psz) + 1) * psz;
+            if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) {
+                perror(ME "SG_GET_RESERVED_SIZE error");
+                return SG_LIB_FILE_ERROR;
+            }
+            if (t < MIN_RESERVED_SIZE)
+                t = MIN_RESERVED_SIZE;
+            if (in_res_sz > t) {
+                if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) {
+                    perror(ME "SG_SET_RESERVED_SIZE error");
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+            wrkMmap = (unsigned char *)mmap(NULL, in_res_sz,
+                                 PROT_READ | PROT_WRITE, MAP_SHARED, infd, 0);
+            if (MAP_FAILED == wrkMmap) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "error using mmap() on file: %s", inf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+        } else {
+            flags = O_RDONLY;
+            if (in_flags.direct)
+                flags |= O_DIRECT;
+            if (in_flags.excl)
+                flags |= O_EXCL;
+            if (in_flags.dsync)
+                flags |= O_SYNC;
+            if ((infd = open(inf, flags)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for reading", inf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+            else if (skip > 0) {
+                off64_t offset = skip;
+
+                offset *= blk_sz;       /* could exceed 32 bits here! */
+                if (lseek64(infd, offset, SEEK_SET) < 0) {
+                    snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
+                             "required position on %s", inf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+                if (verbose)
+                    pr2serr("  >> skip: lseek64 SEEK_SET, byte offset=0x%"
+                            PRIx64 "\n", (uint64_t)offset);
+            }
+        }
+    }
+
+    if (outf[0] && ('-' != outf[0])) {
+        out_type = dd_filetype(outf);
+        if (verbose)
+            pr2serr(" >> Output file type: %s\n",
+                    dd_filetype_str(out_type, ebuff));
+
+        if (FT_ST == out_type) {
+            pr2serr(ME "unable to use scsi tape device %s\n", outf);
+            return SG_LIB_FILE_ERROR;
+        }
+        else if (FT_SG == out_type) {
+            flags = O_RDWR | O_NONBLOCK;
+            if (out_flags.direct)
+                flags |= O_DIRECT;
+            if (out_flags.excl)
+                flags |= O_EXCL;
+            if (out_flags.dsync)
+                flags |= O_SYNC;
+            if ((outfd = open(outf, flags)) < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
+                         "sg writing", outf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+            res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
+            if ((res < 0) || (t < 30122)) {
+                pr2serr(ME "sg driver prior to 3.1.22\n");
+                return SG_LIB_FILE_ERROR;
+            }
+            if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) {
+                perror(ME "SG_GET_RESERVED_SIZE error");
+                return SG_LIB_FILE_ERROR;
+            }
+           if (t < MIN_RESERVED_SIZE)
+                t = MIN_RESERVED_SIZE;
+            out_res_sz = blk_sz * bpt;
+            if (out_res_sz > t) {
+                if (ioctl(outfd, SG_SET_RESERVED_SIZE, &out_res_sz) < 0) {
+                    perror(ME "SG_SET_RESERVED_SIZE error");
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+            if (NULL == wrkMmap) {
+                wrkMmap = (unsigned char *)mmap(NULL, out_res_sz,
+                                PROT_READ | PROT_WRITE, MAP_SHARED, outfd, 0);
+                if (MAP_FAILED == wrkMmap) {
+                    snprintf(ebuff, EBUFF_SZ,
+                             ME "error using mmap() on file: %s", outf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+        }
+        else if (FT_DEV_NULL == out_type)
+            outfd = -1; /* don't bother opening */
+        else {
+            if (FT_RAW != out_type) {
+                flags = O_WRONLY | O_CREAT;
+                if (out_flags.direct)
+                    flags |= O_DIRECT;
+                if (out_flags.excl)
+                    flags |= O_EXCL;
+                if (out_flags.dsync)
+                    flags |= O_SYNC;
+                if (out_flags.append)
+                    flags |= O_APPEND;
+                if ((outfd = open(outf, flags, 0666)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                             ME "could not open %s for writing", outf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+            else {
+                if ((outfd = open(outf, O_WRONLY)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ, ME "could not open %s "
+                             "for raw writing", outf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+            if (seek > 0) {
+                off64_t offset = seek;
+
+                offset *= blk_sz;       /* could exceed 32 bits here! */
+                if (lseek64(outfd, offset, SEEK_SET) < 0) {
+                    snprintf(ebuff, EBUFF_SZ, ME "couldn't seek to "
+                             "required position on %s", outf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+                if (verbose)
+                    pr2serr("   >> seek: lseek64 SEEK_SET, byte offset=0x%"
+                            PRIx64 "\n", (uint64_t)offset);
+            }
+        }
+    }
+    if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
+        pr2serr("Won't default both IFILE to stdin _and_ OFILE to as "
+                "stdout\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (dd_count < 0) {
+        in_num_sect = -1;
+        if (FT_SG == in_type) {
+            res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
+            if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+                pr2serr("Unit attention(in), continuing\n");
+                res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
+            } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
+                pr2serr("Aborted command(in), continuing\n");
+                res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
+            }
+            if (0 != res) {
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                pr2serr("Read capacity (if=%s): %s\n", inf, b);
+                in_num_sect = -1;
+            }
+        } else if (FT_BLOCK == in_type) {
+            if (0 != read_blkdev_capacity(infd, &in_num_sect, &in_sect_sz)) {
+                pr2serr("Unable to read block capacity on %s\n", inf);
+                in_num_sect = -1;
+            }
+            if (blk_sz != in_sect_sz) {
+                pr2serr("block size on %s confusion; bs=%d, from device=%d\n",
+                        inf, blk_sz, in_sect_sz);
+                in_num_sect = -1;
+            }
+        }
+        if (in_num_sect > skip)
+            in_num_sect -= skip;
+
+        out_num_sect = -1;
+        if (FT_SG == out_type) {
+            res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
+            if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+                pr2serr("Unit attention(out), continuing\n");
+                res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
+            } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
+                pr2serr("Aborted command(out), continuing\n");
+                res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
+            }
+            if (0 != res) {
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                pr2serr("Read capacity (of=%s): %s\n", inf, b);
+                out_num_sect = -1;
+            }
+        } else if (FT_BLOCK == out_type) {
+            if (0 != read_blkdev_capacity(outfd, &out_num_sect,
+                                          &out_sect_sz)) {
+                pr2serr("Unable to read block capacity on %s\n", outf);
+                out_num_sect = -1;
+            }
+            if (blk_sz != out_sect_sz) {
+                pr2serr("block size on %s confusion: bs=%d, from device=%d\n",
+                        outf, blk_sz, out_sect_sz);
+                out_num_sect = -1;
+            }
+        }
+        if (out_num_sect > seek)
+            out_num_sect -= seek;
+#ifdef SG_DEBUG
+        pr2serr("Start of loop, count=%" PRId64 ", in_num_sect=%" PRId64 ", "
+                "out_num_sect=%" PRId64 "\n", dd_count, in_num_sect,
+                out_num_sect);
+#endif
+        if (in_num_sect > 0) {
+            if (out_num_sect > 0)
+                dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
+                                                          in_num_sect;
+            else
+                dd_count = in_num_sect;
+        }
+        else
+            dd_count = out_num_sect;
+    }
+
+    if (dd_count < 0) {
+        pr2serr("Couldn't calculate count, please give one\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (! cdbsz_given) {
+        if ((FT_SG == in_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_in) &&
+            (((dd_count + skip) > UINT_MAX) || (bpt > USHRT_MAX))) {
+            pr2serr("Note: SCSI command size increased to 16 bytes (for "
+                    "'if')\n");
+            scsi_cdbsz_in = MAX_SCSI_CDBSZ;
+        }
+        if ((FT_SG == out_type) && (MAX_SCSI_CDBSZ != scsi_cdbsz_out) &&
+            (((dd_count + seek) > UINT_MAX) || (bpt > USHRT_MAX))) {
+            pr2serr("Note: SCSI command size increased to 16 bytes (for "
+                    "'of')\n");
+            scsi_cdbsz_out = MAX_SCSI_CDBSZ;
+        }
+    }
+
+    if (out_flags.dio && (FT_SG != in_type)) {
+        out_flags.dio = 0;
+        pr2serr(">>> dio only performed on 'of' side when 'if' is an sg "
+                "device\n");
+    }
+    if (out_flags.dio) {
+        int fd;
+        char c;
+
+        if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
+            if (1 == read(fd, &c, 1)) {
+                if ('0' == c)
+                    pr2serr(">>> %s set to '0' but should be set to '1' for "
+                            "direct IO\n", proc_allow_dio);
+            }
+            close(fd);
+        }
+    }
+
+    if (wrkMmap) {
+        wrkPos = wrkMmap;
+    } else {
+        if ((FT_RAW == in_type) || (FT_RAW == out_type)) {
+            wrkBuff = (unsigned char *)malloc(blk_sz * bpt + psz);
+            if (0 == wrkBuff) {
+                pr2serr("Not enough user memory for raw\n");
+                return SG_LIB_FILE_ERROR;
+            }
+            /* perhaps use posix_memalign() instead */
+            wrkPos = (unsigned char *)(((uintptr_t)wrkBuff + psz - 1) &
+                                       (~(psz - 1)));
+        }
+        else {
+            wrkBuff = (unsigned char *)malloc(blk_sz * bpt);
+            if (0 == wrkBuff) {
+                pr2serr("Not enough user memory\n");
+                return SG_LIB_FILE_ERROR;
+            }
+            wrkPos = wrkBuff;
+        }
+    }
+
+    blocks_per = bpt;
+#ifdef SG_DEBUG
+    pr2serr("Start of loop, count=%" PRId64 ", blocks_per=%d\n", dd_count,
+            blocks_per);
+#endif
+    if (do_time) {
+        start_tm.tv_sec = 0;
+        start_tm.tv_usec = 0;
+        gettimeofday(&start_tm, NULL);
+        start_tm_valid = 1;
+    }
+    req_count = dd_count;
+
+    if (verbose && (dd_count > 0) && (0 == out_flags.dio) &&
+        (FT_SG == in_type) && (FT_SG == out_type))
+        pr2serr("Since both 'if' and 'of' are sg devices, only do mmap-ed "
+                "transfers on 'if'\n");
+
+    while (dd_count > 0) {
+        blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
+        if (FT_SG == in_type) {
+            ret = sg_read(infd, wrkPos, blocks, skip, blk_sz, scsi_cdbsz_in,
+                          in_flags.fua, in_flags.dpo, 1);
+            if ((SG_LIB_CAT_UNIT_ATTENTION == ret) ||
+                (SG_LIB_CAT_ABORTED_COMMAND == ret)) {
+                pr2serr("Unit attention or aborted command, continuing "
+                        "(r)\n");
+                ret = sg_read(infd, wrkPos, blocks, skip, blk_sz,
+                              scsi_cdbsz_in, in_flags.fua, in_flags.dpo, 1);
+            }
+            if (0 != ret) {
+                pr2serr("sg_read failed, skip=%" PRId64 "\n", skip);
+                break;
+            }
+            else
+                in_full += blocks;
+        }
+        else {
+            while (((res = read(infd, wrkPos, blocks * blk_sz)) < 0) &&
+                   ((EINTR == errno) || (EAGAIN == errno)))
+                ;
+            if (verbose > 2)
+                pr2serr("read(unix): count=%d, res=%d\n", blocks * blk_sz,
+                        res);
+            if (ret < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%" PRId64 " ",
+                         skip);
+                perror(ebuff);
+                ret = -1;
+                break;
+            }
+            else if (res < blocks * blk_sz) {
+                dd_count = 0;
+                blocks = res / blk_sz;
+                if ((res % blk_sz) > 0) {
+                    blocks++;
+                    in_partial++;
+                }
+            }
+            in_full += blocks;
+        }
+
+        if (0 == blocks)
+            break;      /* read nothing so leave loop */
+
+        if (FT_SG == out_type) {
+            int do_mmap = (FT_SG == in_type) ? 0 : 1;
+            int dio_res = out_flags.dio;
+
+            ret = sg_write(outfd, wrkPos, blocks, seek, blk_sz, scsi_cdbsz_out,
+                           out_flags.fua, out_flags.dpo, do_mmap, &dio_res);
+            if ((SG_LIB_CAT_UNIT_ATTENTION == ret) ||
+                (SG_LIB_CAT_ABORTED_COMMAND == ret)) {
+                pr2serr("Unit attention or aborted command, continuing (w)\n");
+                dio_res = out_flags.dio;
+                ret = sg_write(outfd, wrkPos, blocks, seek, blk_sz,
+                               scsi_cdbsz_out, out_flags.fua, out_flags.dpo,
+                               do_mmap, &dio_res);
+            }
+            if (0 != ret) {
+                pr2serr("sg_write failed, seek=%" PRId64 "\n", seek);
+                break;
+            }
+            else {
+                out_full += blocks;
+                if (out_flags.dio && (0 == dio_res))
+                    num_dio_not_done++;
+            }
+        }
+        else if (FT_DEV_NULL == out_type)
+            out_full += blocks; /* act as if written out without error */
+        else {
+            while (((res = write(outfd, wrkPos, blocks * blk_sz)) < 0) &&
+                   ((EINTR == errno) || (EAGAIN == errno)))
+                ;
+            if (verbose > 2)
+                pr2serr("write(unix): count=%d, res=%d\n", blocks * blk_sz,
+                        res);
+            if (res < 0) {
+                snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%" PRId64 " ",
+                         seek);
+                perror(ebuff);
+                break;
+            }
+            else if (res < blocks * blk_sz) {
+                pr2serr("output file probably full, seek=%" PRId64 " ", seek);
+                blocks = res / blk_sz;
+                out_full += blocks;
+                if ((res % blk_sz) > 0)
+                    out_partial++;
+                break;
+            }
+            else
+                out_full += blocks;
+        }
+        if (dd_count > 0)
+            dd_count -= blocks;
+        skip += blocks;
+        seek += blocks;
+    }
+
+    if (do_time)
+        calc_duration_throughput(0);
+    if (do_sync) {
+        if (FT_SG == out_type) {
+            pr2serr(">> Synchronizing cache on %s\n", outf);
+            res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0);
+            if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+                pr2serr("Unit attention(out), continuing\n");
+                res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0);
+            }
+            if (0 != res) {
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                pr2serr("Synchronize cache(out): %s\n", b);
+            }
+        }
+    }
+
+    if (wrkBuff) free(wrkBuff);
+    if (STDIN_FILENO != infd)
+        close(infd);
+    if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type))
+        close(outfd);
+    if (0 != dd_count) {
+        pr2serr("Some error occurred,");
+        if (0 == ret)
+            ret = SG_LIB_CAT_OTHER;
+    }
+    print_stats();
+    if (sum_of_resids)
+        pr2serr(">> Non-zero sum of residual counts=%d\n", sum_of_resids);
+    if (num_dio_not_done)
+        pr2serr(">> dio requested but _not_ done %d times\n",
+                num_dio_not_done);
+    return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/src/sgp_dd.c b/sg3_utils/src/sgp_dd.c
new file mode 100644
index 0000000..661925d
--- /dev/null
+++ b/sg3_utils/src/sgp_dd.c
@@ -0,0 +1,1659 @@
+/* A utility program for copying files. Specialised for "files" that
+ * represent devices that understand the SCSI command set.
+ *
+ * Copyright (C) 1999 - 2016 D. Gilbert and P. Allworth
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is a specialisation of the Unix "dd" command in which
+ * one or both of the given files is a scsi generic device or a raw
+ * device. A block size ('bs') is assumed to be 512 if not given. This
+ * program complains if 'ibs' or 'obs' are given with some other value
+ * than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
+ * 'of' is not given or 'of=-' then stdout assumed.
+ *
+ * A non-standard argument "bpt" (blocks per transfer) is added to control
+ * the maximum number of blocks in each transfer. The default value is 128.
+ * For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
+ * in this case) are transferred to or from the sg device in a single SCSI
+ * command.
+ *
+ * This version is designed for the linux kernel 2.4, 2.6 and 3 series.
+ */
+
+#define _XOPEN_SOURCE 500
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <linux/major.h>
+#include <linux/fs.h>   /* <sys/mount.h> */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_io_linux.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+
+static const char * version_str = "5.52 20160121";
+
+#define DEF_BLOCK_SIZE 512
+#define DEF_BLOCKS_PER_TRANSFER 128
+#define DEF_BLOCKS_PER_2048TRANSFER 32
+#define DEF_SCSI_CDBSZ 10
+#define MAX_SCSI_CDBSZ 16
+
+#define ME "sgp_dd: "
+
+/* #define SG_DEBUG */
+
+#define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
+#define READ_CAP_REPLY_LEN 8
+#define RCAP16_REPLY_LEN 32
+
+#ifndef SERVICE_ACTION_IN
+#define SERVICE_ACTION_IN     0x9e
+#endif
+#ifndef SAI_READ_CAPACITY_16
+#define SAI_READ_CAPACITY_16  0x10
+#endif
+
+#define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
+
+#define SGP_READ10 0x28
+#define SGP_WRITE10 0x2a
+#define DEF_NUM_THREADS 4
+#define MAX_NUM_THREADS SG_MAX_QUEUE
+
+#ifndef RAW_MAJOR
+#define RAW_MAJOR 255   /*unlikely value */
+#endif
+
+#define FT_OTHER 1              /* filetype other than one of the following */
+#define FT_SG 2                 /* filetype is sg char device */
+#define FT_RAW 4                /* filetype is raw char device */
+#define FT_DEV_NULL 8           /* either "/dev/null" or "." as filename */
+#define FT_ST 16                /* filetype is st char device (tape) */
+#define FT_BLOCK 32             /* filetype is a block device */
+#define FT_ERROR 64             /* couldn't "stat" file */
+
+#define DEV_NULL_MINOR_NUM 3
+
+#define EBUFF_SZ 512
+
+struct flags_t {
+    int append;
+    int coe;
+    int dio;
+    int direct;
+    int dpo;
+    int dsync;
+    int excl;
+    int fua;
+};
+
+typedef struct request_collection
+{       /* one instance visible to all threads */
+    int infd;
+    int64_t skip;
+    int in_type;
+    int cdbsz_in;
+    struct flags_t in_flags;
+    int64_t in_blk;                 /* -\ next block address to read */
+    int64_t in_count;               /*  | blocks remaining for next read */
+    int64_t in_rem_count;           /*  | count of remaining in blocks */
+    int in_partial;                   /*  | */
+    int in_stop;                      /*  | */
+    pthread_mutex_t in_mutex;         /* -/ */
+    int outfd;
+    int64_t seek;
+    int out_type;
+    int cdbsz_out;
+    struct flags_t out_flags;
+    int64_t out_blk;                /* -\ next block address to write */
+    int64_t out_count;              /*  | blocks remaining for next write */
+    int64_t out_rem_count;          /*  | count of remaining out blocks */
+    int out_partial;                  /*  | */
+    int out_stop;                     /*  | */
+    pthread_mutex_t out_mutex;        /*  | */
+    pthread_cond_t out_sync_cv;       /* -/ hold writes until "in order" */
+    int bs;
+    int bpt;
+    int dio_incomplete;         /* -\ */
+    int sum_of_resids;          /*  | */
+    pthread_mutex_t aux_mutex;  /* -/ (also serializes some printf()s */
+    int debug;
+} Rq_coll;
+
+typedef struct request_element
+{       /* one instance per worker thread */
+    int infd;
+    int outfd;
+    int wr;
+    int64_t blk;
+    int num_blks;
+    unsigned char * buffp;
+    unsigned char * alloc_bp;
+    struct sg_io_hdr io_hdr;
+    unsigned char cmd[MAX_SCSI_CDBSZ];
+    unsigned char sb[SENSE_BUFF_LEN];
+    int bs;
+    int dio_incomplete;
+    int resid;
+    int cdbsz_in;
+    int cdbsz_out;
+    struct flags_t in_flags;
+    struct flags_t out_flags;
+    int debug;
+} Rq_elem;
+
+static sigset_t signal_set;
+static pthread_t sig_listen_thread_id;
+
+static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio";
+
+static void sg_in_operation(Rq_coll * clp, Rq_elem * rep);
+static void sg_out_operation(Rq_coll * clp, Rq_elem * rep);
+static int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks);
+static void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks);
+static int sg_start_io(Rq_elem * rep);
+static int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp);
+
+#define STRERR_BUFF_LEN 128
+
+static pthread_mutex_t strerr_mut = PTHREAD_MUTEX_INITIALIZER;
+
+static int do_time = 0;
+static Rq_coll rcoll;
+static struct timeval start_tm;
+static int64_t dd_count = -1;
+static int num_threads = DEF_NUM_THREADS;
+static int do_sync = 0;
+static int exit_status = 0;
+
+
+static void
+calc_duration_throughput(int contin)
+{
+    struct timeval end_tm, res_tm;
+    double a, b;
+
+    gettimeofday(&end_tm, NULL);
+    res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+    res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+    if (res_tm.tv_usec < 0) {
+        --res_tm.tv_sec;
+        res_tm.tv_usec += 1000000;
+    }
+    a = res_tm.tv_sec;
+    a += (0.000001 * res_tm.tv_usec);
+    b = (double)rcoll.bs * (dd_count - rcoll.out_rem_count);
+    pr2serr("time to transfer data %s %d.%06d secs",
+            (contin ? "so far" : "was"), (int)res_tm.tv_sec,
+            (int)res_tm.tv_usec);
+    if ((a > 0.00001) && (b > 511))
+        pr2serr(", %.2f MB/sec\n", b / (a * 1000000.0));
+    else
+        pr2serr("\n");
+}
+
+static void
+print_stats(const char * str)
+{
+    int64_t infull, outfull;
+
+    if (0 != rcoll.out_rem_count)
+        pr2serr("  remaining block count=%" PRId64 "\n",
+                rcoll.out_rem_count);
+    infull = dd_count - rcoll.in_rem_count;
+    pr2serr("%s%" PRId64 "+%d records in\n", str,
+            infull - rcoll.in_partial, rcoll.in_partial);
+
+    outfull = dd_count - rcoll.out_rem_count;
+    pr2serr("%s%" PRId64 "+%d records out\n", str,
+            outfull - rcoll.out_partial, rcoll.out_partial);
+}
+
+static void
+interrupt_handler(int sig)
+{
+    struct sigaction sigact;
+
+    sigact.sa_handler = SIG_DFL;
+    sigemptyset(&sigact.sa_mask);
+    sigact.sa_flags = 0;
+    sigaction(sig, &sigact, NULL);
+    pr2serr("Interrupted by signal,");
+    if (do_time)
+        calc_duration_throughput(0);
+    print_stats("");
+    kill(getpid (), sig);
+}
+
+static void
+siginfo_handler(int sig)
+{
+    if (sig) { ; }      /* unused, dummy to suppress warning */
+    pr2serr("Progress report, continuing ...\n");
+    if (do_time)
+        calc_duration_throughput(1);
+    print_stats("  ");
+}
+
+static void
+install_handler(int sig_num, void (*sig_handler) (int sig))
+{
+    struct sigaction sigact;
+    sigaction (sig_num, NULL, &sigact);
+    if (sigact.sa_handler != SIG_IGN)
+    {
+        sigact.sa_handler = sig_handler;
+        sigemptyset (&sigact.sa_mask);
+        sigact.sa_flags = 0;
+        sigaction (sig_num, &sigact, NULL);
+    }
+}
+
+/* Make safe_strerror() thread safe */
+static char *
+tsafe_strerror(int code, char * ebp)
+{
+    char * cp;
+
+    pthread_mutex_lock(&strerr_mut);
+    cp = safe_strerror(code);
+    strncpy(ebp, cp, STRERR_BUFF_LEN);
+    pthread_mutex_unlock(&strerr_mut);
+
+    ebp[STRERR_BUFF_LEN - 1] = '\0';
+    return ebp;
+}
+
+
+/* Following macro from D.R. Butenhof's POSIX threads book:
+ * ISBN 0-201-63392-2 . [Highly recommended book.] Changed __FILE__
+ * to __func__ */
+#define err_exit(code,text) do { \
+    char strerr_buff[STRERR_BUFF_LEN]; \
+    pr2serr("%s at \"%s\":%d: %s\n", \
+        text, __func__, __LINE__, tsafe_strerror(code, strerr_buff)); \
+    exit(1); \
+    } while (0)
+
+
+static int
+dd_filetype(const char * filename)
+{
+    struct stat st;
+    size_t len = strlen(filename);
+
+    if ((1 == len) && ('.' == filename[0]))
+        return FT_DEV_NULL;
+    if (stat(filename, &st) < 0)
+        return FT_ERROR;
+    if (S_ISCHR(st.st_mode)) {
+        if ((MEM_MAJOR == major(st.st_rdev)) &&
+            (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
+            return FT_DEV_NULL;
+        if (RAW_MAJOR == major(st.st_rdev))
+            return FT_RAW;
+        if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
+            return FT_SG;
+        if (SCSI_TAPE_MAJOR == major(st.st_rdev))
+            return FT_ST;
+    } else if (S_ISBLK(st.st_mode))
+        return FT_BLOCK;
+    return FT_OTHER;
+}
+
+static void
+usage()
+{
+    pr2serr("Usage: sgp_dd  [bs=BS] [count=COUNT] [ibs=BS] [if=IFILE]"
+            " [iflag=FLAGS]\n"
+            "               [obs=BS] [of=OFILE] [oflag=FLAGS] "
+            "[seek=SEEK] [skip=SKIP]\n"
+            "               [--help] [--version]\n\n");
+    pr2serr("               [bpt=BPT] [cdbsz=6|10|12|16] [coe=0|1] "
+            "[deb=VERB] [dio=0|1]\n"
+            "               [fua=0|1|2|3] [sync=0|1] [thr=THR] "
+            "[time=0|1] [verbose=VERB]\n"
+            "  where:\n"
+            "    bpt         is blocks_per_transfer (default is 128)\n"
+            "    bs          must be device block size (default 512)\n"
+            "    cdbsz       size of SCSI READ or WRITE cdb (default is 10)\n"
+            "    coe         continue on error, 0->exit (def), "
+            "1->zero + continue\n"
+            "    count       number of blocks to copy (def: device size)\n"
+            "    deb         for debug, 0->none (def), > 0->varying degrees "
+            "of debug\n");
+    pr2serr("    dio         is direct IO, 1->attempt, 0->indirect IO (def)\n"
+            "    fua         force unit access: 0->don't(def), 1->OFILE, "
+            "2->IFILE,\n"
+            "                3->OFILE+IFILE\n"
+            "    if          file or device to read from (def: stdin)\n"
+            "    iflag       comma separated list from: [coe,dio,direct,dpo,"
+            "dsync,excl,\n"
+            "                fua, null]\n"
+            "    of          file or device to write to (def: stdout), "
+            "OFILE of '.'\n"
+            "                treated as /dev/null\n"
+            "    oflag       comma separated list from: [append,coe,dio,"
+            "direct,dpo,dsync,\n"
+            "                excl,fua,null]\n"
+            "    seek        block position to start writing to OFILE\n"
+            "    skip        block position to start reading from IFILE\n"
+            "    sync        0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
+            "after copy\n"
+            "    thr         is number of threads, must be > 0, default 4, "
+            "max 16\n"
+            "    time        0->no timing(def), 1->time plus calculate "
+            "throughput\n"
+            "    verbose     same as 'deb=VERB': increase verbosity\n"
+            "    --help      output this usage message then exit\n"
+            "    --version   output version string then exit\n"
+            "Copy from IFILE to OFILE, similar to dd command\n"
+            "specialized for SCSI devices, uses multiple POSIX threads\n");
+}
+
+static void
+guarded_stop_in(Rq_coll * clp)
+{
+    pthread_mutex_lock(&clp->in_mutex);
+    clp->in_stop = 1;
+    pthread_mutex_unlock(&clp->in_mutex);
+}
+
+static void
+guarded_stop_out(Rq_coll * clp)
+{
+    pthread_mutex_lock(&clp->out_mutex);
+    clp->out_stop = 1;
+    pthread_mutex_unlock(&clp->out_mutex);
+}
+
+static void
+guarded_stop_both(Rq_coll * clp)
+{
+    guarded_stop_in(clp);
+    guarded_stop_out(clp);
+}
+
+/* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
+static int
+scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
+{
+    int res;
+    unsigned char rcBuff[RCAP16_REPLY_LEN];
+
+    res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, 0, 0);
+    if (0 != res)
+        return res;
+
+    if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
+        (0xff == rcBuff[3])) {
+
+        res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, 0, 0);
+        if (0 != res)
+            return res;
+        *num_sect = sg_get_unaligned_be64(rcBuff + 0) + 1;
+        *sect_sz = sg_get_unaligned_be32(rcBuff + 8);
+    } else {
+        /* take care not to sign extend values > 0x7fffffff */
+        *num_sect = (int64_t)sg_get_unaligned_be32(rcBuff + 0) + 1;
+        *sect_sz = sg_get_unaligned_be32(rcBuff + 4);
+    }
+    return 0;
+}
+
+/* Return of 0 -> success, -1 -> failure. BLKGETSIZE64, BLKGETSIZE and */
+/* BLKSSZGET macros problematic (from <linux/fs.h> or <sys/mount.h>). */
+static int
+read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
+{
+#ifdef BLKSSZGET
+    if ((ioctl(sg_fd, BLKSSZGET, sect_sz) < 0) && (*sect_sz > 0)) {
+        perror("BLKSSZGET ioctl error");
+        return -1;
+    } else {
+ #ifdef BLKGETSIZE64
+        uint64_t ull;
+
+        if (ioctl(sg_fd, BLKGETSIZE64, &ull) < 0) {
+
+            perror("BLKGETSIZE64 ioctl error");
+            return -1;
+        }
+        *num_sect = ((int64_t)ull / (int64_t)*sect_sz);
+ #else
+        unsigned long ul;
+
+        if (ioctl(sg_fd, BLKGETSIZE, &ul) < 0) {
+            perror("BLKGETSIZE ioctl error");
+            return -1;
+        }
+        *num_sect = (int64_t)ul;
+ #endif
+    }
+    return 0;
+#else
+    *num_sect = 0;
+    *sect_sz = 0;
+    return -1;
+#endif
+}
+
+static void *
+sig_listen_thread(void * v_clp)
+{
+    Rq_coll * clp = (Rq_coll *)v_clp;
+    int sig_number;
+
+    while (1) {
+        sigwait(&signal_set, &sig_number);
+        if (SIGINT == sig_number) {
+            pr2serr(ME "interrupted by SIGINT\n");
+            guarded_stop_both(clp);
+            pthread_cond_broadcast(&clp->out_sync_cv);
+        }
+    }
+    return NULL;
+}
+
+static void
+cleanup_in(void * v_clp)
+{
+    Rq_coll * clp = (Rq_coll *)v_clp;
+
+    pr2serr("thread cancelled while in mutex held\n");
+    clp->in_stop = 1;
+    pthread_mutex_unlock(&clp->in_mutex);
+    guarded_stop_out(clp);
+    pthread_cond_broadcast(&clp->out_sync_cv);
+}
+
+static void
+cleanup_out(void * v_clp)
+{
+    Rq_coll * clp = (Rq_coll *)v_clp;
+
+    pr2serr("thread cancelled while out mutex held\n");
+    clp->out_stop = 1;
+    pthread_mutex_unlock(&clp->out_mutex);
+    guarded_stop_in(clp);
+    pthread_cond_broadcast(&clp->out_sync_cv);
+}
+
+static void *
+read_write_thread(void * v_clp)
+{
+    Rq_coll * clp;
+    Rq_elem rel;
+    Rq_elem * rep = &rel;
+    size_t psz = 0;
+    int sz;
+    volatile int stop_after_write = 0;
+    int64_t seek_skip;
+    int blocks, status;
+
+    clp = (Rq_coll *)v_clp;
+    sz = clp->bpt * clp->bs;
+    seek_skip =  clp->seek - clp->skip;
+    memset(rep, 0, sizeof(Rq_elem));
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+    psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
+#else
+    psz = 4096;     /* give up, pick likely figure */
+#endif
+    if (NULL == (rep->alloc_bp = (unsigned char *)malloc(sz + psz)))
+        err_exit(ENOMEM, "out of memory creating user buffers\n");
+    rep->buffp = (unsigned char *)(((uintptr_t)rep->alloc_bp + psz - 1) &
+                                   (~(psz - 1)));
+    /* Follow clp members are constant during lifetime of thread */
+    rep->bs = clp->bs;
+    rep->infd = clp->infd;
+    rep->outfd = clp->outfd;
+    rep->debug = clp->debug;
+    rep->cdbsz_in = clp->cdbsz_in;
+    rep->cdbsz_out = clp->cdbsz_out;
+    rep->in_flags = clp->in_flags;
+    rep->out_flags = clp->out_flags;
+
+    while(1) {
+        status = pthread_mutex_lock(&clp->in_mutex);
+        if (0 != status) err_exit(status, "lock in_mutex");
+        if (clp->in_stop || (clp->in_count <= 0)) {
+            /* no more to do, exit loop then thread */
+            status = pthread_mutex_unlock(&clp->in_mutex);
+            if (0 != status) err_exit(status, "unlock in_mutex");
+            break;
+        }
+        blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count;
+        rep->wr = 0;
+        rep->blk = clp->in_blk;
+        rep->num_blks = blocks;
+        clp->in_blk += blocks;
+        clp->in_count -= blocks;
+
+        pthread_cleanup_push(cleanup_in, (void *)clp);
+        if (FT_SG == clp->in_type)
+            sg_in_operation(clp, rep); /* lets go of in_mutex mid operation */
+        else {
+            stop_after_write = normal_in_operation(clp, rep, blocks);
+            status = pthread_mutex_unlock(&clp->in_mutex);
+            if (0 != status) err_exit(status, "unlock in_mutex");
+        }
+        pthread_cleanup_pop(0);
+
+        status = pthread_mutex_lock(&clp->out_mutex);
+        if (0 != status) err_exit(status, "lock out_mutex");
+        if (FT_DEV_NULL != clp->out_type) {
+            while ((! clp->out_stop) &&
+                   ((rep->blk + seek_skip) != clp->out_blk)) {
+                /* if write would be out of sequence then wait */
+                pthread_cleanup_push(cleanup_out, (void *)clp);
+                status = pthread_cond_wait(&clp->out_sync_cv, &clp->out_mutex);
+                if (0 != status) err_exit(status, "cond out_sync_cv");
+                pthread_cleanup_pop(0);
+            }
+        }
+
+        if (clp->out_stop || (clp->out_count <= 0)) {
+            if (! clp->out_stop)
+                clp->out_stop = 1;
+            status = pthread_mutex_unlock(&clp->out_mutex);
+            if (0 != status) err_exit(status, "unlock out_mutex");
+            break;
+        }
+        if (stop_after_write)
+            clp->out_stop = 1;
+        rep->wr = 1;
+        rep->blk = clp->out_blk;
+        clp->out_blk += blocks;
+        clp->out_count -= blocks;
+
+        if (0 == rep->num_blks) {
+            clp->out_stop = 1;
+            stop_after_write = 1;
+            status = pthread_mutex_unlock(&clp->out_mutex);
+            if (0 != status) err_exit(status, "unlock out_mutex");
+            break;      /* read nothing so leave loop */
+        }
+
+        pthread_cleanup_push(cleanup_out, (void *)clp);
+        if (FT_SG == clp->out_type)
+            sg_out_operation(clp, rep); /* releases out_mutex mid operation */
+        else if (FT_DEV_NULL == clp->out_type) {
+            /* skip actual write operation */
+            clp->out_rem_count -= blocks;
+            status = pthread_mutex_unlock(&clp->out_mutex);
+            if (0 != status) err_exit(status, "unlock out_mutex");
+        }
+        else {
+            normal_out_operation(clp, rep, blocks);
+            status = pthread_mutex_unlock(&clp->out_mutex);
+            if (0 != status) err_exit(status, "unlock out_mutex");
+        }
+        pthread_cleanup_pop(0);
+
+        if (stop_after_write)
+            break;
+        pthread_cond_broadcast(&clp->out_sync_cv);
+    } /* end of while loop */
+    if (rep->alloc_bp) free(rep->alloc_bp);
+    status = pthread_mutex_lock(&clp->in_mutex);
+    if (0 != status) err_exit(status, "lock in_mutex");
+    if (! clp->in_stop)
+        clp->in_stop = 1;  /* flag other workers to stop */
+    status = pthread_mutex_unlock(&clp->in_mutex);
+    if (0 != status) err_exit(status, "unlock in_mutex");
+    pthread_cond_broadcast(&clp->out_sync_cv);
+    return stop_after_write ? NULL : clp;
+}
+
+static int
+normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
+{
+    int res;
+    int stop_after_write = 0;
+    char strerr_buff[STRERR_BUFF_LEN];
+
+    /* enters holding in_mutex */
+    while (((res = read(clp->infd, rep->buffp, blocks * clp->bs)) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        if (clp->in_flags.coe) {
+            memset(rep->buffp, 0, rep->num_blks * rep->bs);
+            pr2serr(">> substituted zeros for in blk=%" PRId64 " for %d "
+                    "bytes, %s\n", rep->blk,
+                    rep->num_blks * rep->bs,
+                    tsafe_strerror(errno, strerr_buff));
+            res = rep->num_blks * clp->bs;
+        }
+        else {
+            pr2serr("error in normal read, %s\n",
+                    tsafe_strerror(errno, strerr_buff));
+            clp->in_stop = 1;
+            guarded_stop_out(clp);
+            return 1;
+        }
+    }
+    if (res < blocks * clp->bs) {
+        int o_blocks = blocks;
+        stop_after_write = 1;
+        blocks = res / clp->bs;
+        if ((res % clp->bs) > 0) {
+            blocks++;
+            clp->in_partial++;
+        }
+        /* Reverse out + re-apply blocks on clp */
+        clp->in_blk -= o_blocks;
+        clp->in_count += o_blocks;
+        rep->num_blks = blocks;
+        clp->in_blk += blocks;
+        clp->in_count -= blocks;
+    }
+    clp->in_rem_count -= blocks;
+    return stop_after_write;
+}
+
+static void
+normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
+{
+    int res;
+    char strerr_buff[STRERR_BUFF_LEN];
+
+    /* enters holding out_mutex */
+    while (((res = write(clp->outfd, rep->buffp, rep->num_blks * clp->bs))
+            < 0) && ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        if (clp->out_flags.coe) {
+            pr2serr(">> ignored error for out blk=%" PRId64 " for %d bytes, "
+                    "%s\n", rep->blk, rep->num_blks * rep->bs,
+                    tsafe_strerror(errno, strerr_buff));
+            res = rep->num_blks * clp->bs;
+        }
+        else {
+            pr2serr("error normal write, %s\n",
+                    tsafe_strerror(errno, strerr_buff));
+            guarded_stop_in(clp);
+            clp->out_stop = 1;
+            return;
+        }
+    }
+    if (res < blocks * clp->bs) {
+        blocks = res / clp->bs;
+        if ((res % clp->bs) > 0) {
+            blocks++;
+            clp->out_partial++;
+        }
+        rep->num_blks = blocks;
+    }
+    clp->out_rem_count -= blocks;
+}
+
+static int
+sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
+                  int64_t start_block, int write_true, int fua, int dpo)
+{
+    int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+    int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
+    int sz_ind;
+
+    memset(cdbp, 0, cdb_sz);
+    if (dpo)
+        cdbp[1] |= 0x10;
+    if (fua)
+        cdbp[1] |= 0x8;
+    switch (cdb_sz) {
+    case 6:
+        sz_ind = 0;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
+        cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
+        if (blocks > 256) {
+            pr2serr(ME "for 6 byte commands, maximum number of blocks is "
+                    "256\n");
+            return 1;
+        }
+        if ((start_block + blocks - 1) & (~0x1fffff)) {
+            pr2serr(ME "for 6 byte commands, can't address blocks beyond "
+                    "%d\n", 0x1fffff);
+            return 1;
+        }
+        if (dpo || fua) {
+            pr2serr(ME "for 6 byte commands, neither dpo nor fua bits "
+                    "supported\n");
+            return 1;
+        }
+        break;
+    case 10:
+        sz_ind = 1;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
+        sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7);
+        if (blocks & (~0xffff)) {
+            pr2serr(ME "for 10 byte commands, maximum number of blocks is "
+                    "%d\n", 0xffff);
+            return 1;
+        }
+        break;
+    case 12:
+        sz_ind = 2;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
+        sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6);
+        break;
+    case 16:
+        sz_ind = 3;
+        cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+                                               rd_opcode[sz_ind]);
+        sg_put_unaligned_be64((uint64_t)start_block, cdbp + 2);
+        sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10);
+        break;
+    default:
+        pr2serr(ME "expected cdb size of 6, 10, 12, or 16 but got %d\n",
+                cdb_sz);
+        return 1;
+    }
+    return 0;
+}
+
+static void
+sg_in_operation(Rq_coll * clp, Rq_elem * rep)
+{
+    int res;
+    int status;
+
+    /* enters holding in_mutex */
+    while (1) {
+        res = sg_start_io(rep);
+        if (1 == res)
+            err_exit(ENOMEM, "sg starting in command");
+        else if (res < 0) {
+            pr2serr(ME "inputting to sg failed, blk=%" PRId64 "\n", rep->blk);
+            status = pthread_mutex_unlock(&clp->in_mutex);
+            if (0 != status) err_exit(status, "unlock in_mutex");
+            guarded_stop_both(clp);
+            return;
+        }
+        /* Now release in mutex to let other reads run in parallel */
+        status = pthread_mutex_unlock(&clp->in_mutex);
+        if (0 != status) err_exit(status, "unlock in_mutex");
+
+        res = sg_finish_io(rep->wr, rep, &clp->aux_mutex);
+        switch (res) {
+        case SG_LIB_CAT_ABORTED_COMMAND:
+        case SG_LIB_CAT_UNIT_ATTENTION:
+            /* try again with same addr, count info */
+            /* now re-acquire in mutex for balance */
+            /* N.B. This re-read could now be out of read sequence */
+            status = pthread_mutex_lock(&clp->in_mutex);
+            if (0 != status) err_exit(status, "lock in_mutex");
+            break;
+        case SG_LIB_CAT_MEDIUM_HARD:
+            if (0 == clp->in_flags.coe) {
+                pr2serr("error finishing sg in command (medium)\n");
+                if (exit_status <= 0)
+                    exit_status = res;
+                guarded_stop_both(clp);
+                return;
+            } else {
+                memset(rep->buffp, 0, rep->num_blks * rep->bs);
+                pr2serr(">> substituted zeros for in blk=%" PRId64 " for %d "
+                        "bytes\n", rep->blk, rep->num_blks * rep->bs);
+            }
+            /* fall through */
+        case 0:
+            if (rep->dio_incomplete || rep->resid) {
+                status = pthread_mutex_lock(&clp->aux_mutex);
+                if (0 != status) err_exit(status, "lock aux_mutex");
+                clp->dio_incomplete += rep->dio_incomplete;
+                clp->sum_of_resids += rep->resid;
+                status = pthread_mutex_unlock(&clp->aux_mutex);
+                if (0 != status) err_exit(status, "unlock aux_mutex");
+            }
+            status = pthread_mutex_lock(&clp->in_mutex);
+            if (0 != status) err_exit(status, "lock in_mutex");
+            clp->in_rem_count -= rep->num_blks;
+            status = pthread_mutex_unlock(&clp->in_mutex);
+            if (0 != status) err_exit(status, "unlock in_mutex");
+            return;
+        default:
+            pr2serr("error finishing sg in command (%d)\n", res);
+            if (exit_status <= 0)
+                exit_status = res;
+            guarded_stop_both(clp);
+            return;
+        }
+    }
+}
+
+static void
+sg_out_operation(Rq_coll * clp, Rq_elem * rep)
+{
+    int res;
+    int status;
+
+    /* enters holding out_mutex */
+    while (1) {
+        res = sg_start_io(rep);
+        if (1 == res)
+            err_exit(ENOMEM, "sg starting out command");
+        else if (res < 0) {
+            pr2serr(ME "outputting from sg failed, blk=%" PRId64 "\n",
+                    rep->blk);
+            status = pthread_mutex_unlock(&clp->out_mutex);
+            if (0 != status) err_exit(status, "unlock out_mutex");
+            guarded_stop_both(clp);
+            return;
+        }
+        /* Now release in mutex to let other reads run in parallel */
+        status = pthread_mutex_unlock(&clp->out_mutex);
+        if (0 != status) err_exit(status, "unlock out_mutex");
+
+        res = sg_finish_io(rep->wr, rep, &clp->aux_mutex);
+        switch (res) {
+        case SG_LIB_CAT_ABORTED_COMMAND:
+        case SG_LIB_CAT_UNIT_ATTENTION:
+            /* try again with same addr, count info */
+            /* now re-acquire out mutex for balance */
+            /* N.B. This re-write could now be out of write sequence */
+            status = pthread_mutex_lock(&clp->out_mutex);
+            if (0 != status) err_exit(status, "lock out_mutex");
+            break;
+        case SG_LIB_CAT_MEDIUM_HARD:
+            if (0 == clp->out_flags.coe) {
+                pr2serr("error finishing sg out command (medium)\n");
+                if (exit_status <= 0)
+                    exit_status = res;
+                guarded_stop_both(clp);
+                return;
+            } else
+                pr2serr(">> ignored error for out blk=%" PRId64 " for %d "
+                        "bytes\n", rep->blk, rep->num_blks * rep->bs);
+            /* fall through */
+        case 0:
+            if (rep->dio_incomplete || rep->resid) {
+                status = pthread_mutex_lock(&clp->aux_mutex);
+                if (0 != status) err_exit(status, "lock aux_mutex");
+                clp->dio_incomplete += rep->dio_incomplete;
+                clp->sum_of_resids += rep->resid;
+                status = pthread_mutex_unlock(&clp->aux_mutex);
+                if (0 != status) err_exit(status, "unlock aux_mutex");
+            }
+            status = pthread_mutex_lock(&clp->out_mutex);
+            if (0 != status) err_exit(status, "lock out_mutex");
+            clp->out_rem_count -= rep->num_blks;
+            status = pthread_mutex_unlock(&clp->out_mutex);
+            if (0 != status) err_exit(status, "unlock out_mutex");
+            return;
+        default:
+            pr2serr("error finishing sg out command (%d)\n", res);
+            if (exit_status <= 0)
+                exit_status = res;
+            guarded_stop_both(clp);
+            return;
+        }
+    }
+}
+
+static int
+sg_start_io(Rq_elem * rep)
+{
+    struct sg_io_hdr * hp = &rep->io_hdr;
+    int fua = rep->wr ? rep->out_flags.fua : rep->in_flags.fua;
+    int dpo = rep->wr ? rep->out_flags.dpo : rep->in_flags.dpo;
+    int dio = rep->wr ? rep->out_flags.dio : rep->in_flags.dio;
+    int cdbsz = rep->wr ? rep->cdbsz_out : rep->cdbsz_in;
+    int res;
+
+    if (sg_build_scsi_cdb(rep->cmd, cdbsz, rep->num_blks, rep->blk,
+                          rep->wr, fua, dpo)) {
+        pr2serr(ME "bad cdb build, start_blk=%" PRId64 ", blocks=%d\n",
+                rep->blk, rep->num_blks);
+        return -1;
+    }
+    memset(hp, 0, sizeof(struct sg_io_hdr));
+    hp->interface_id = 'S';
+    hp->cmd_len = cdbsz;
+    hp->cmdp = rep->cmd;
+    hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
+    hp->dxfer_len = rep->bs * rep->num_blks;
+    hp->dxferp = rep->buffp;
+    hp->mx_sb_len = sizeof(rep->sb);
+    hp->sbp = rep->sb;
+    hp->timeout = DEF_TIMEOUT;
+    hp->usr_ptr = rep;
+    hp->pack_id = (int)rep->blk;
+    if (dio)
+        hp->flags |= SG_FLAG_DIRECT_IO;
+    if (rep->debug > 8) {
+        pr2serr("sg_start_io: SCSI %s, blk=%" PRId64 " num_blks=%d\n",
+               rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
+        sg_print_command(hp->cmdp);
+    }
+
+    while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
+                         sizeof(struct sg_io_hdr))) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        if (ENOMEM == errno)
+            return 1;
+        perror("starting io on sg device, error");
+        return -1;
+    }
+    return 0;
+}
+
+/* 0 -> successful, SG_LIB_CAT_UNIT_ATTENTION or SG_LIB_CAT_ABORTED_COMMAND
+   -> try again, SG_LIB_CAT_NOT_READY, SG_LIB_CAT_MEDIUM_HARD,
+   -1 other errors */
+static int
+sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
+{
+    int res, status;
+    struct sg_io_hdr io_hdr;
+    struct sg_io_hdr * hp;
+#if 0
+    static int testing = 0;     /* thread dubious! */
+#endif
+
+    memset(&io_hdr, 0 , sizeof(struct sg_io_hdr));
+    /* FORCE_PACK_ID active set only read packet with matching pack_id */
+    io_hdr.interface_id = 'S';
+    io_hdr.dxfer_direction = wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
+    io_hdr.pack_id = (int)rep->blk;
+
+    while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr,
+                        sizeof(struct sg_io_hdr))) < 0) &&
+           ((EINTR == errno) || (EAGAIN == errno)))
+        ;
+    if (res < 0) {
+        perror("finishing io on sg device, error");
+        return -1;
+    }
+    if (rep != (Rq_elem *)io_hdr.usr_ptr)
+        err_exit(0, "sg_finish_io: bad usr_ptr, request-response mismatch\n");
+    memcpy(&rep->io_hdr, &io_hdr, sizeof(struct sg_io_hdr));
+    hp = &rep->io_hdr;
+
+    res = sg_err_category3(hp);
+    switch (res) {
+        case SG_LIB_CAT_CLEAN:
+            break;
+        case SG_LIB_CAT_RECOVERED:
+            sg_chk_n_print3((wr ? "writing continuing":
+                                       "reading continuing"), hp, 0);
+            break;
+        case SG_LIB_CAT_ABORTED_COMMAND:
+        case SG_LIB_CAT_UNIT_ATTENTION:
+            if (rep->debug > 8)
+                sg_chk_n_print3((wr ? "writing": "reading"), hp, 0);
+            return res;
+        case SG_LIB_CAT_NOT_READY:
+        default:
+            {
+                char ebuff[EBUFF_SZ];
+
+                snprintf(ebuff, EBUFF_SZ, "%s blk=%" PRId64,
+                         wr ? "writing": "reading", rep->blk);
+                status = pthread_mutex_lock(a_mutp);
+                if (0 != status) err_exit(status, "lock aux_mutex");
+                sg_chk_n_print3(ebuff, hp, 0);
+                status = pthread_mutex_unlock(a_mutp);
+                if (0 != status) err_exit(status, "unlock aux_mutex");
+                return res;
+            }
+    }
+#if 0
+    if (0 == (++testing % 100)) return -1;
+#endif
+    if ((wr ? rep->out_flags.dio : rep->in_flags.dio) &&
+        ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+        rep->dio_incomplete = 1; /* count dios done as indirect IO */
+    else
+        rep->dio_incomplete = 0;
+    rep->resid = hp->resid;
+    if (rep->debug > 8)
+        pr2serr("sg_finish_io: completed %s\n", wr ? "WRITE" : "READ");
+    return 0;
+}
+
+static int
+sg_prepare(int fd, int bs, int bpt)
+{
+    int res, t;
+
+    res = ioctl(fd, SG_GET_VERSION_NUM, &t);
+    if ((res < 0) || (t < 30000)) {
+        pr2serr(ME "sg driver prior to 3.x.y\n");
+        return 1;
+    }
+    res = 0;
+    t = bs * bpt;
+    res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
+    if (res < 0)
+        perror(ME "SG_SET_RESERVED_SIZE error");
+    t = 1;
+    res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
+    if (res < 0)
+        perror(ME "SG_SET_FORCE_PACK_ID error");
+    return 0;
+}
+
+static int
+process_flags(const char * arg, struct flags_t * fp)
+{
+    char buff[256];
+    char * cp;
+    char * np;
+
+    strncpy(buff, arg, sizeof(buff));
+    buff[sizeof(buff) - 1] = '\0';
+    if ('\0' == buff[0]) {
+        pr2serr("no flag found\n");
+        return 1;
+    }
+    cp = buff;
+    do {
+        np = strchr(cp, ',');
+        if (np)
+            *np++ = '\0';
+        if (0 == strcmp(cp, "append"))
+            fp->append = 1;
+        else if (0 == strcmp(cp, "coe"))
+            fp->coe = 1;
+        else if (0 == strcmp(cp, "dio"))
+            fp->dio = 1;
+        else if (0 == strcmp(cp, "direct"))
+            fp->direct = 1;
+        else if (0 == strcmp(cp, "dpo"))
+            fp->dpo = 1;
+        else if (0 == strcmp(cp, "dsync"))
+            fp->dsync = 1;
+        else if (0 == strcmp(cp, "excl"))
+            fp->excl = 1;
+        else if (0 == strcmp(cp, "fua"))
+            fp->fua = 1;
+        else if (0 == strcmp(cp, "null"))
+            ;
+        else {
+            pr2serr("unrecognised flag: %s\n", cp);
+            return 1;
+        }
+        cp = np;
+    } while (cp);
+    return 0;
+}
+
+
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+
+
+int
+main(int argc, char * argv[])
+{
+    int64_t skip = 0;
+    int64_t seek = 0;
+    int ibs = 0;
+    int obs = 0;
+    int bpt_given = 0;
+    int cdbsz_given = 0;
+    char str[STR_SZ];
+    char * key;
+    char * buf;
+    char inf[INOUTF_SZ];
+    char outf[INOUTF_SZ];
+    int res, k;
+    int64_t in_num_sect = 0;
+    int64_t out_num_sect = 0;
+    pthread_t threads[MAX_NUM_THREADS];
+    int in_sect_sz, out_sect_sz, status, n, flags;
+    void * vp;
+    char ebuff[EBUFF_SZ];
+
+    memset(&rcoll, 0, sizeof(Rq_coll));
+    rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
+    rcoll.in_type = FT_OTHER;
+    rcoll.out_type = FT_OTHER;
+    rcoll.cdbsz_in = DEF_SCSI_CDBSZ;
+    rcoll.cdbsz_out = DEF_SCSI_CDBSZ;
+    inf[0] = '\0';
+    outf[0] = '\0';
+
+    for (k = 1; k < argc; k++) {
+        if (argv[k]) {
+            strncpy(str, argv[k], STR_SZ);
+            str[STR_SZ - 1] = '\0';
+        }
+        else
+            continue;
+        for (key = str, buf = key; *buf && *buf != '=';)
+            buf++;
+        if (*buf)
+            *buf++ = '\0';
+        if (0 == strcmp(key,"bpt")) {
+            rcoll.bpt = sg_get_num(buf);
+            if (-1 == rcoll.bpt) {
+                pr2serr(ME "bad argument to 'bpt='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            bpt_given = 1;
+        } else if (0 == strcmp(key,"bs")) {
+            rcoll.bs = sg_get_num(buf);
+            if (-1 == rcoll.bs) {
+                pr2serr(ME "bad argument to 'bs='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"cdbsz")) {
+            rcoll.cdbsz_in = sg_get_num(buf);
+            rcoll.cdbsz_out = rcoll.cdbsz_in;
+            cdbsz_given = 1;
+        } else if (0 == strcmp(key,"coe")) {
+            rcoll.in_flags.coe = sg_get_num(buf);
+            rcoll.out_flags.coe = rcoll.in_flags.coe;
+        } else if (0 == strcmp(key,"count")) {
+            if (0 != strcmp("-1", buf)) {
+                dd_count = sg_get_llnum(buf);
+                if (-1LL == dd_count) {
+                    pr2serr(ME "bad argument to 'count='\n");
+                    return SG_LIB_SYNTAX_ERROR;
+                }
+            }   /* treat 'count=-1' as calculate count (same as not given) */
+        } else if ((0 == strncmp(key,"deb", 3)) ||
+                   (0 == strncmp(key,"verb", 4)))
+            rcoll.debug = sg_get_num(buf);
+        else if (0 == strcmp(key,"dio")) {
+            rcoll.in_flags.dio = sg_get_num(buf);
+            rcoll.out_flags.dio = rcoll.in_flags.dio;
+        } else if (0 == strcmp(key,"fua")) {
+            n = sg_get_num(buf);
+            if (n & 1)
+                rcoll.out_flags.fua = 1;
+            if (n & 2)
+                rcoll.in_flags.fua = 1;
+        } else if (0 == strcmp(key,"ibs")) {
+            ibs = sg_get_num(buf);
+            if (-1 == ibs) {
+                pr2serr(ME "bad argument to 'ibs='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (strcmp(key,"if") == 0) {
+            if ('\0' != inf[0]) {
+                pr2serr("Second 'if=' argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(inf, buf, INOUTF_SZ);
+        } else if (0 == strcmp(key, "iflag")) {
+            if (process_flags(buf, &rcoll.in_flags)) {
+                pr2serr(ME "bad argument to 'iflag='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"obs")) {
+            obs = sg_get_num(buf);
+            if (-1 == obs) {
+                pr2serr(ME "bad argument to 'obs='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (strcmp(key,"of") == 0) {
+            if ('\0' != outf[0]) {
+                pr2serr("Second 'of=' argument??\n");
+                return SG_LIB_SYNTAX_ERROR;
+            } else
+                strncpy(outf, buf, INOUTF_SZ);
+        } else if (0 == strcmp(key, "oflag")) {
+            if (process_flags(buf, &rcoll.out_flags)) {
+                pr2serr(ME "bad argument to 'oflag='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"seek")) {
+            seek = sg_get_llnum(buf);
+            if (-1LL == seek) {
+                pr2serr(ME "bad argument to 'seek='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"skip")) {
+            skip = sg_get_llnum(buf);
+            if (-1LL == skip) {
+                pr2serr(ME "bad argument to 'skip='\n");
+                return SG_LIB_SYNTAX_ERROR;
+            }
+        } else if (0 == strcmp(key,"sync"))
+            do_sync = sg_get_num(buf);
+        else if (0 == strcmp(key,"thr"))
+            num_threads = sg_get_num(buf);
+        else if (0 == strcmp(key,"time"))
+            do_time = sg_get_num(buf);
+        else if ((0 == strncmp(key, "--help", 7)) ||
+                 (0 == strncmp(key, "-h", 2)) ||
+                 (0 == strcmp(key, "-?"))) {
+            usage();
+            return 0;
+        } else if ((0 == strncmp(key, "--vers", 6)) ||
+                   (0 == strcmp(key, "-V"))) {
+            pr2serr(ME ": %s\n", version_str);
+            return 0;
+        }
+        else {
+            pr2serr("Unrecognized option '%s'\n", key);
+            pr2serr("For more information use '--help'\n");
+            return SG_LIB_SYNTAX_ERROR;
+        }
+    }
+    if (rcoll.bs <= 0) {
+        rcoll.bs = DEF_BLOCK_SIZE;
+        pr2serr("Assume default 'bs' (block size) of %d bytes\n", rcoll.bs);
+    }
+    if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) {
+        pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((skip < 0) || (seek < 0)) {
+        pr2serr("skip and seek cannot be negative\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if ((rcoll.out_flags.append > 0) && (seek > 0)) {
+        pr2serr("Can't use both append and seek switches\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (rcoll.bpt < 1) {
+        pr2serr("bpt must be greater than 0\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    /* defaulting transfer size to 128*2048 for CD/DVDs is too large
+       for the block layer in lk 2.6 and results in an EIO on the
+       SG_IO ioctl. So reduce it in that case. */
+    if ((rcoll.bs >= 2048) && (0 == bpt_given))
+        rcoll.bpt = DEF_BLOCKS_PER_2048TRANSFER;
+    if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) {
+        pr2serr("too few or too many threads requested\n");
+        usage();
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (rcoll.debug)
+        pr2serr(ME "if=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%"
+                PRId64 "\n", inf, skip, outf, seek, dd_count);
+
+    install_handler(SIGINT, interrupt_handler);
+    install_handler(SIGQUIT, interrupt_handler);
+    install_handler(SIGPIPE, interrupt_handler);
+    install_handler(SIGUSR1, siginfo_handler);
+
+    rcoll.infd = STDIN_FILENO;
+    rcoll.outfd = STDOUT_FILENO;
+    if (inf[0] && ('-' != inf[0])) {
+        rcoll.in_type = dd_filetype(inf);
+
+        if (FT_ERROR == rcoll.in_type) {
+            pr2serr(ME "unable to access %s\n", inf);
+            return SG_LIB_FILE_ERROR;
+        } else if (FT_ST == rcoll.in_type) {
+            pr2serr(ME "unable to use scsi tape device %s\n", inf);
+            return SG_LIB_FILE_ERROR;
+        } else if (FT_SG == rcoll.in_type) {
+            flags = O_RDWR;
+            if (rcoll.in_flags.direct)
+                flags |= O_DIRECT;
+            if (rcoll.in_flags.excl)
+                flags |= O_EXCL;
+            if (rcoll.in_flags.dsync)
+                flags |= O_SYNC;
+
+            if ((rcoll.infd = open(inf, flags)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for sg reading", inf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+            if (sg_prepare(rcoll.infd, rcoll.bs, rcoll.bpt))
+                return SG_LIB_FILE_ERROR;
+        }
+        else {
+            flags = O_RDONLY;
+            if (rcoll.in_flags.direct)
+                flags |= O_DIRECT;
+            if (rcoll.in_flags.excl)
+                flags |= O_EXCL;
+            if (rcoll.in_flags.dsync)
+                flags |= O_SYNC;
+
+            if ((rcoll.infd = open(inf, flags)) < 0) {
+                snprintf(ebuff, EBUFF_SZ,
+                         ME "could not open %s for reading", inf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+            else if (skip > 0) {
+                off64_t offset = skip;
+
+                offset *= rcoll.bs;       /* could exceed 32 here! */
+                if (lseek64(rcoll.infd, offset, SEEK_SET) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                        ME "couldn't skip to required position on %s", inf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+        }
+    }
+    if (outf[0] && ('-' != outf[0])) {
+        rcoll.out_type = dd_filetype(outf);
+
+        if (FT_ST == rcoll.out_type) {
+            pr2serr(ME "unable to use scsi tape device %s\n", outf);
+            return SG_LIB_FILE_ERROR;
+        }
+        else if (FT_SG == rcoll.out_type) {
+            flags = O_RDWR;
+            if (rcoll.out_flags.direct)
+                flags |= O_DIRECT;
+            if (rcoll.out_flags.excl)
+                flags |= O_EXCL;
+            if (rcoll.out_flags.dsync)
+                flags |= O_SYNC;
+
+            if ((rcoll.outfd = open(outf, flags)) < 0) {
+                snprintf(ebuff,  EBUFF_SZ,
+                         ME "could not open %s for sg writing", outf);
+                perror(ebuff);
+                return SG_LIB_FILE_ERROR;
+            }
+
+            if (sg_prepare(rcoll.outfd, rcoll.bs, rcoll.bpt))
+                return SG_LIB_FILE_ERROR;
+        }
+        else if (FT_DEV_NULL == rcoll.out_type)
+            rcoll.outfd = -1; /* don't bother opening */
+        else {
+            if (FT_RAW != rcoll.out_type) {
+                flags = O_WRONLY | O_CREAT;
+                if (rcoll.out_flags.direct)
+                    flags |= O_DIRECT;
+                if (rcoll.out_flags.excl)
+                    flags |= O_EXCL;
+                if (rcoll.out_flags.dsync)
+                    flags |= O_SYNC;
+                if (rcoll.out_flags.append)
+                    flags |= O_APPEND;
+
+                if ((rcoll.outfd = open(outf, flags, 0666)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                             ME "could not open %s for writing", outf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+            else {      /* raw output file */
+                if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                             ME "could not open %s for raw writing", outf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+            if (seek > 0) {
+                off64_t offset = seek;
+
+                offset *= rcoll.bs;       /* could exceed 32 bits here! */
+                if (lseek64(rcoll.outfd, offset, SEEK_SET) < 0) {
+                    snprintf(ebuff, EBUFF_SZ,
+                        ME "couldn't seek to required position on %s", outf);
+                    perror(ebuff);
+                    return SG_LIB_FILE_ERROR;
+                }
+            }
+        }
+    }
+    if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) {
+        pr2serr("Won't default both IFILE to stdin _and_ OFILE to stdout\n");
+        pr2serr("For more information use '--help'\n");
+        return SG_LIB_SYNTAX_ERROR;
+    }
+    if (dd_count < 0) {
+        in_num_sect = -1;
+        if (FT_SG == rcoll.in_type) {
+            res = scsi_read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
+            if (2 == res) {
+                pr2serr("Unit attention, media changed(in), continuing\n");
+                res = scsi_read_capacity(rcoll.infd, &in_num_sect,
+                                         &in_sect_sz);
+            }
+            if (0 != res) {
+                if (res == SG_LIB_CAT_INVALID_OP)
+                    pr2serr("read capacity not supported on %s\n", inf);
+                else if (res == SG_LIB_CAT_NOT_READY)
+                    pr2serr("read capacity failed, %s not ready\n", inf);
+                else
+                    pr2serr("Unable to read capacity on %s\n", inf);
+                in_num_sect = -1;
+            }
+        } else if (FT_BLOCK == rcoll.in_type) {
+            if (0 != read_blkdev_capacity(rcoll.infd, &in_num_sect,
+                                          &in_sect_sz)) {
+                pr2serr("Unable to read block capacity on %s\n", inf);
+                in_num_sect = -1;
+            }
+            if (rcoll.bs != in_sect_sz) {
+                pr2serr("block size on %s confusion; bs=%d, from device=%d\n",
+                        inf, rcoll.bs, in_sect_sz);
+                in_num_sect = -1;
+            }
+        }
+        if (in_num_sect > skip)
+            in_num_sect -= skip;
+
+        out_num_sect = -1;
+        if (FT_SG == rcoll.out_type) {
+            res = scsi_read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
+            if (2 == res) {
+                pr2serr("Unit attention, media changed(out), continuing\n");
+                res = scsi_read_capacity(rcoll.outfd, &out_num_sect,
+                                         &out_sect_sz);
+            }
+            if (0 != res) {
+                if (res == SG_LIB_CAT_INVALID_OP)
+                    pr2serr("read capacity not supported on %s\n", outf);
+                else if (res == SG_LIB_CAT_NOT_READY)
+                    pr2serr("read capacity failed, %s not ready\n", outf);
+                else
+                    pr2serr("Unable to read capacity on %s\n", outf);
+                out_num_sect = -1;
+            }
+        } else if (FT_BLOCK == rcoll.out_type) {
+            if (0 != read_blkdev_capacity(rcoll.outfd, &out_num_sect,
+                                          &out_sect_sz)) {
+                pr2serr("Unable to read block capacity on %s\n", outf);
+                out_num_sect = -1;
+            }
+            if (rcoll.bs != out_sect_sz) {
+                pr2serr("block size on %s confusion: bs=%d, from device=%d\n",
+                        outf, rcoll.bs, out_sect_sz);
+                out_num_sect = -1;
+            }
+        }
+        if (out_num_sect > seek)
+            out_num_sect -= seek;
+
+        if (in_num_sect > 0) {
+            if (out_num_sect > 0)
+                dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
+                                                          in_num_sect;
+            else
+                dd_count = in_num_sect;
+        }
+        else
+            dd_count = out_num_sect;
+    }
+    if (rcoll.debug > 1)
+        pr2serr("Start of loop, count=%" PRId64 ", in_num_sect=%" PRId64
+                ", out_num_sect=%" PRId64 "\n", dd_count, in_num_sect,
+                out_num_sect);
+    if (dd_count < 0) {
+        pr2serr("Couldn't calculate count, please give one\n");
+        return SG_LIB_CAT_OTHER;
+    }
+    if (! cdbsz_given) {
+        if ((FT_SG == rcoll.in_type) && (MAX_SCSI_CDBSZ != rcoll.cdbsz_in) &&
+            (((dd_count + skip) > UINT_MAX) || (rcoll.bpt > USHRT_MAX))) {
+            pr2serr("Note: SCSI command size increased to 16 bytes (for "
+                    "'if')\n");
+            rcoll.cdbsz_in = MAX_SCSI_CDBSZ;
+        }
+        if ((FT_SG == rcoll.out_type) && (MAX_SCSI_CDBSZ != rcoll.cdbsz_out) &&
+            (((dd_count + seek) > UINT_MAX) || (rcoll.bpt > USHRT_MAX))) {
+            pr2serr("Note: SCSI command size increased to 16 bytes (for "
+                    "'of')\n");
+            rcoll.cdbsz_out = MAX_SCSI_CDBSZ;
+        }
+    }
+
+    rcoll.in_count = dd_count;
+    rcoll.in_rem_count = dd_count;
+    rcoll.skip = skip;
+    rcoll.in_blk = skip;
+    rcoll.out_count = dd_count;
+    rcoll.out_rem_count = dd_count;
+    rcoll.seek = seek;
+    rcoll.out_blk = seek;
+    status = pthread_mutex_init(&rcoll.in_mutex, NULL);
+    if (0 != status) err_exit(status, "init in_mutex");
+    status = pthread_mutex_init(&rcoll.out_mutex, NULL);
+    if (0 != status) err_exit(status, "init out_mutex");
+    status = pthread_mutex_init(&rcoll.aux_mutex, NULL);
+    if (0 != status) err_exit(status, "init aux_mutex");
+    status = pthread_cond_init(&rcoll.out_sync_cv, NULL);
+    if (0 != status) err_exit(status, "init out_sync_cv");
+
+    sigemptyset(&signal_set);
+    sigaddset(&signal_set, SIGINT);
+    status = pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
+    if (0 != status) err_exit(status, "pthread_sigmask");
+    status = pthread_create(&sig_listen_thread_id, NULL,
+                            sig_listen_thread, (void *)&rcoll);
+    if (0 != status) err_exit(status, "pthread_create, sig...");
+
+    if (do_time) {
+        start_tm.tv_sec = 0;
+        start_tm.tv_usec = 0;
+        gettimeofday(&start_tm, NULL);
+    }
+
+/* vvvvvvvvvvv  Start worker threads  vvvvvvvvvvvvvvvvvvvvvvvv */
+    if ((rcoll.out_rem_count > 0) && (num_threads > 0)) {
+        /* Run 1 work thread to shake down infant retryable stuff */
+        status = pthread_mutex_lock(&rcoll.out_mutex);
+        if (0 != status) err_exit(status, "lock out_mutex");
+        status = pthread_create(&threads[0], NULL, read_write_thread,
+                                (void *)&rcoll);
+        if (0 != status) err_exit(status, "pthread_create");
+        if (rcoll.debug)
+            pr2serr("Starting worker thread k=0\n");
+
+        /* wait for any broadcast */
+        pthread_cleanup_push(cleanup_out, (void *)&rcoll);
+        status = pthread_cond_wait(&rcoll.out_sync_cv, &rcoll.out_mutex);
+        if (0 != status) err_exit(status, "cond out_sync_cv");
+        pthread_cleanup_pop(0);
+        status = pthread_mutex_unlock(&rcoll.out_mutex);
+        if (0 != status) err_exit(status, "unlock out_mutex");
+
+        /* now start the rest of the threads */
+        for (k = 1; k < num_threads; ++k) {
+            status = pthread_create(&threads[k], NULL, read_write_thread,
+                                    (void *)&rcoll);
+            if (0 != status) err_exit(status, "pthread_create");
+            if (rcoll.debug)
+                pr2serr("Starting worker thread k=%d\n", k);
+        }
+
+        /* now wait for worker threads to finish */
+        for (k = 0; k < num_threads; ++k) {
+            status = pthread_join(threads[k], &vp);
+            if (0 != status) err_exit(status, "pthread_join");
+            if (rcoll.debug)
+                pr2serr("Worker thread k=%d terminated\n", k);
+        }
+    }
+
+    if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec))
+        calc_duration_throughput(0);
+
+    if (do_sync) {
+        if (FT_SG == rcoll.out_type) {
+            pr2serr(">> Synchronizing cache on %s\n", outf);
+            res = sg_ll_sync_cache_10(rcoll.outfd, 0, 0, 0, 0, 0, 0, 0);
+            if (SG_LIB_CAT_UNIT_ATTENTION == res) {
+                pr2serr("Unit attention(out), continuing\n");
+                res = sg_ll_sync_cache_10(rcoll.outfd, 0, 0, 0, 0, 0, 0, 0);
+            }
+            if (0 != res)
+                pr2serr("Unable to synchronize cache\n");
+        }
+    }
+
+    status = pthread_cancel(sig_listen_thread_id);
+    if (0 != status) err_exit(status, "pthread_cancel");
+    if (STDIN_FILENO != rcoll.infd)
+        close(rcoll.infd);
+    if ((STDOUT_FILENO != rcoll.outfd) && (FT_DEV_NULL != rcoll.out_type))
+        close(rcoll.outfd);
+    res = exit_status;
+    if (0 != rcoll.out_count) {
+        pr2serr(">>>> Some error occurred, remaining blocks=%" PRId64 "\n",
+                rcoll.out_count);
+        if (0 == res)
+            res = SG_LIB_CAT_OTHER;
+    }
+    print_stats("");
+    if (rcoll.dio_incomplete) {
+        int fd;
+        char c;
+
+        pr2serr(">> Direct IO requested but incomplete %d times\n",
+                rcoll.dio_incomplete);
+        if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
+            if (1 == read(fd, &c, 1)) {
+                if ('0' == c)
+                    pr2serr(">>> %s set to '0' but should be set to '1' for "
+                            "direct IO\n", proc_allow_dio);
+            }
+            close(fd);
+        }
+    }
+    if (rcoll.sum_of_resids)
+        pr2serr(">> Non-zero sum of residual counts=%d\n",
+               rcoll.sum_of_resids);
+    return (res >= 0) ? res : SG_LIB_CAT_OTHER;
+}
diff --git a/sg3_utils/suse/sg3_utils.changes b/sg3_utils/suse/sg3_utils.changes
new file mode 100644
index 0000000..4279a29
--- /dev/null
+++ b/sg3_utils/suse/sg3_utils.changes
@@ -0,0 +1,393 @@
+-------------------------------------------------------------------
+Thu Jan 23 15:00:00 EST 2014 - dgilbert@interlog.com
+
+- import Suse build files into sg3_utils in the suse directory
+  * change suse spec file to be patch-less
+  * henceforth see ChangeLog in main directory
+
+-------------------------------------------------------------------
+Thu Jan 23 08:57:56 CET 2014 - hare@suse.de
+
+- Update to inofficial release 1.38r546
+  * sg_ses: error and warning message cleanup
+    - fix --data=- problem with large buffers
+    - new --data=@FN to read hex data from file FN
+    - add --maxlen= option
+  * sg_inq:
+    - add LU_CONG to standard inquiry response
+    - sync version descriptors dated 20131126
+    - fix overflow in encode_whitespaces
+  * sg_vpd: add LU_CONG to standard inquiry response output
+    - decode Third Party Copy (tpc) page
+  * sg_persist: add PROUT: Replace Lost Reservation (spc4r36)
+  * sg_readcap: for --16 show physical block size if
+  * sg_xcopy:
+    - environment variables: XCOPY_TO_SRC and
+      XCOPY_TO_DST indicate where xcopy command is sent
+    - change default to send xcopy to dst (was src)
+    - improve CL handling of short options (e.g. '-vv')
+  * sg_write_same: repeat if unit attention
+  * sg_rtpg: fix indexing bug with --extended option
+  * sg_lib_data: sync asc/ascq codes with T10 dated 20131110
+  * sg_cmds_extra: fix sa bug in sg_ll_3party_copy_out()
+- Update tarball to 1.38b7r537
+- Add sg3_utils-1.38r546.patch
+
+-------------------------------------------------------------------
+Mon Nov  4 01:59:38 UTC 2013 - jengelh@inai.de
+
+- Update to new upstream release 1.37
+* sg_compare_and_write: add --quiet option to suppress miscompare
+  report
+* sg_persist: fix core dump on -Q option
+* sg_unmap: fix core dump on -g option
+* sg_ses: add --nickname and --nickid options
+- Remove sg3_utils-Fixup-T10-Vendor-designator-display.patch
+  (merged upstream)
+
+-------------------------------------------------------------------
+Sun Aug 25 18:45:14 CEST 2013 - ohering@suse.de
+
+- Fixup T10 Vendor designator display (bnc#805059)
+  sg3_utils-Fixup-T10-Vendor-designator-display.patch
+- In rescan-scsi-bus.sh, check if the HBA driver exports issue_lip
+  in sysfs before using it (bnc#780946)
+  sg3_utils-check-if-hba-supports-issue-lip.patch
+
+-------------------------------------------------------------------
+Thu Jun 13 14:15:26 UTC 2013 - jengelh@inai.de
+
+- Implement shlib packaging guidelines; rename sg3_utils-devel
+  to libsgutils-devel (upstream recommendation)
+- More robust make install call; remove redundant %clean section;
+  simplify file lists
+
+-------------------------------------------------------------------
+Tue Jun 11 08:56:39 UTC 2013 - rmilasan@suse.com
+
+- Update to version 1.36
+  - sg_vpd: Protocol-specific port information VPD page
+    for SAS SSP, persistent connection (spl3r2), power
+    disable (spl3r3)
+    - block device characteristics: add FUAB bit
+  - sg_xcopy: handle more descriptor types; handle zero
+    maximum segment length; allow list IDs to be disabled;
+    improve skip/seek handling; allow xcopy on destination
+  - sg_reset: and --no-esc option to stop reset escalation
+    - clean up cli, add long option names
+  - sg_luns: add --test=ALUN option for decoding LUNs
+    - decoded luns output in decimal or hex (if -HH given)
+    - add '--linux' option to show Linux LUN after T10
+      representation, can map one to the other
+  - sg_inq: add --vendor option to show standard inquiry's
+    vendor specific fields in ASCII
+    - take resid into account with response output
+  - sg_sync: add --16 (for 16 byte command) and --timeout=
+  - sg_logs: add data compression page (ssc4)
+  - sg_sat_set_features: increase --lba from 1 to 4 bytes
+  - sg_write_same: add --ndob option (sbc3r35d)
+  - sg_map: mark as deprecated
+  - sginfo: mark as deprecated, especially -l (list)
+  - sg_lib: improve snprintf handling
+  - sg_lib_data: sync asc/ascq codes with T10 20130117
+  - sg_cmds (lib): if noisy given, give more UA info
+  - make code more C++ friendly
+
+-------------------------------------------------------------------
+Tue Mar 12 09:13:45 CET 2013 - hare@suse.de
+
+- Update to version 1.35
+  - sg_compare_and_write: new utility
+  - sg_inq+sg_vpd: block device characteristics VPD page:
+    add product_type, WABEREQ, WACEREQ and VBULS fields
+  - sg_inq: more --export option changes for udev
+  - sg_vpd: add more rdac vendor specific vpd pages
+  - sg_verify: add --ebytchk option for sbc3r34 changes
+  - sg_stpg: --offline option: fix 'Invalid state 0xe'
+  - sg_ses: Door Lock element changed to Door element and
+    abbreviation changed from 'dl' to 'do' (ses3r05)
+  - archive/rescan-scsi-bus.sh: upgrade to version 1.53hr
+    - move rescan-scsi-bus.sh to scripts directory
+  - sync to sbc3r34
+  - sg_lib: sg_ll_verify10+16 expand BYTCHK to 2 bit field
+  - sg_pt_win32, sg_scan(win32): changes for cygwin 1.7.17
+  - clean up man page summary lines
+  - sg_xcopy: new dd like utility for extended copy command
+  - sg_copy_results: new utility for receive copy results
+  - sg_verify: add 16 byte cdb, bytchk (data-out buffer)
+    and group number support
+  - sync to spc4r36 and sbc3r32
+  - sg_inq: add --export so sg_inq can replace udev's scsi_id
+    - decode old EMC Symmetrix abuse of VPD page 0x83
+  - sg_vpd: decode old EMC Symmetrix abuse of VPD page 0x83
+  - sg_ses: increase max dpage response size to 64 KB
+    - allow ident,locate on enclosure controller
+    - more sanity for additional element status descriptor
+  - sg_sanitize: add --ause, --fail and --test=
+  - sg_luns: add long extended flat space addressing format
+  - sg_logs: add ATA pass-through results lpage (SAT-2)
+  - sg_rtpg: add --extended option
+  - sg_senddiag: list rebuild assist diag page name
+  - sg_pt_linux: expand DID_ (host_byte) codes
+    - cope with a transport error plus sense data
+    - prefer major() over MAJOR() macro
+  - sg_lib: fix sg_get_command_name() service actions
+    - report sdat_ovfl bit (if set) in sense data
+    - decode extended_copy and receive_copy service actions
+    - decode read_buffer and write_buffer modes
+    - decode ATA PT fixed format sense (SAT-2)
+  - sg_cmds_extra: add sg_ll_report_tgt_prt_grp2()
+  - ./configure options:
+    - change --enable-no-linux-bsg to --disable-linuxbsg
+    - add --disable-scsistrings to reduce utility sizes
+
+-------------------------------------------------------------------
+Wed Jul  4 07:01:46 UTC 2012 - cfarrell@suse.com
+
+- license update: GPL-2.0+ and BSD-3-Clause
+  Show aggregation and make compatible with Fedora declaration
+
+-------------------------------------------------------------------
+Sun Apr 22 11:50:44 UTC 2012 - puzel@suse.com
+
+- Update to version 1.33
+  - sg_ses: major rework of indexes (again), now two level
+  - sg_write_buffer: new --specific option for mode specific
+    field; new mode 13 (spc4r32)
+  - sg_vpd: add hp3par volume info vendor VPD page
+    - fix 'scsi ports' [0x88] page problem
+    - add 'sinq' pseudo page for standard inquiry response
+    - add power consumption page
+  - sg_format: add --poll= option for request sense polling
+    - improve handling of disks > 2 TB and DIF (protection)
+  - sg_logs: LB provision lpage extra (sbc3r28)
+  - sg_modes: application tag mpage subcode 0xf0->0x2
+  - sg_write_same: no prot fields when wrprotect=0
+  - sg_get_lba_status: reflect change in sbc3r25 to Parameter
+    Data Length response field (offset reduced from 8 to 4)
+  - sg_inq, sg_vpd: sync with spc4r33
+  - win32: change DataBufferOffset type per MSDN; caused
+    problem with 64 bit machines (with buffered interface)
+  - sg_luns: tweak documentation for vendor specific reports
+  - add man pages for scsi_loging_level, scsi_mandat,
+    scsi_satl and scsi_temperature
+
+-------------------------------------------------------------------
+Mon Jan 16 19:59:42 UTC 2012 - tabraham@novell.com
+
+- Update to version 1.32
+  + sg_sanitize: new utility for command added in sb3r27
+  + sg_sat_identify: add '--ident' to output WWN
+  + sg_ses: major rework of descriptor output
+    + add --index, --descriptor, --join, --clear, --get, and --set
+      options
+  + sg_raw: exit status corrections
+  + sg_decode_sense: add --nospace and --hex options
+  + sg_logs: fix bug with large --maxlen
+    + zero response length when resid implies it is invalid
+    + add scope field to lb provisioning lpage (sb3r27)
+  + sg_inq: sync version descriptors with spc4r31
+  + sb_lib_data: sync asc/ascq codes with spc4r31
+  + sg_vpd: add LBPRZ field in LP provisioning VPD page
+  + sg_format: allow format of pdt 7 (some MO drives)
+  + sg_cmd_basic: sg_cmds_process_resp() handle status good
+    with a sense key other than no_sense (e.g. completed)
+  + add README.iscsi
+
+- Updated rescan-scsi-bus.sh to v1.56
+
+-------------------------------------------------------------------
+Thu Mar 10 08:47:43 UTC 2011 - coolo@novell.com
+
+- fix file list
+
+-------------------------------------------------------------------
+Fri Feb 18 16:41:32 CET 2011 - hare@suse.de
+
+- Update to version 1.31:
+  + sg_decode_sense: new utility to decode sense data
+  + sg_vpd: LB provisioning + Block limits pages (sbc3r26)
+  + sync asc/ascq and version descriptors with spc4r28
+  + sg_get_config, sg_rmsn, sg_verify: add --readonly option
+  + sg_lib: implement forwarded sense data descriptor
+    - decode user data segment referral sense data descriptor
+  + sg_lib, sg_turs, sg_format: more precision for progress
+    indication (two places after decimal point)
+  + sg_lib(win32): add runtime selection of SPT direct or
+    indirect interface
+    - sg_read_buffer+sg_write_buffer: set SPT direct
+ + add examples/forwarded_sense.txt + examples/ref_sense.txt
+
+- Changes from version 1.30:
+  + sg_referrals: new utility for REPORT REFERRALS
+  + sbc3r25 renames 'thin' provisioning' to 'logical block
+    provisioning': changes in sg_format, sg_inq, sg_logs,
+    sg_modes, sg_readcap, sg_vpd
+  + sg_inq: update version descriptor list to spc4r27
+    - extended inquiry vpd page add extended self test
+      completion minutes field
+  + sg_lib: sync asc/ascq list to spc4r27
+    - dStrHex(): trim excess trailing spaces
+  + sg_read_long: add --readonly option (open() is rw)
+  + sg_raw: add --readonly option (open() is rw)
+    - allow bidirectional commands
+  + sg_vpd: rdac vendor page [0xc8] parse corrections
+    - extended inquiry vpd page add extended self test
+    -completion minutes field
+  + sg_ses: expand --data (in) buffer to 2048 bytes
+  + sg_opcodes: add extended parameter data for TMFs (spc4r26)
+  + sg_dd: clean count calculation, document nocache flag
+    - treat bsg devices as implicit sg_io
+  + sg_write_same: if READ CAPACITY(16) fails try 10 byte variant
+    - anticipate approval of proposal to allow UNMAP and ANCHOR
+      bits to be set on WRITE SAME(10) with '--10' option
+  + sg3_utils man page: sections added for OS device names
+
+-------------------------------------------------------------------
+Fri Aug 13 11:42:50 CEST 2010 - dimstar@opensuse.org
+
+- Update to version 1.29:
+  + sg_rtpg: new logical block dependent state and bit (spc4r23)
+  + sg_start: add '--readonly' option for ATA disks
+  + sg_lib: update asc/ascq list to spc4r23
+  + sg_inq: update version descriptor list to spc4r23
+  + sg_vpd: block device characteristics page: fix form factor
+    - update Extended Inquiry VPD page to spc4r23
+    - update Block Limits VPD page to sbc3r22
+    - update Thin Provisioning VPD page to sbc3r22
+    - Automation device serial number and Data transfer device
+      element VPD pages (ssc4r01)
+    - add Referrals VPD page (sbc3r22)
+  + sg_logs: add thin provisioning and solid state media log pages
+    - addition of IBM LTO specific log pages
+  + sg_modes: new page names from ssc4r01
+  + sg_ses: sync with ses3r02 (SAS-2.1 connector types)
+  + sg_unmap: add '--anchor' option (sbc3r22)
+  + sg_write_same: add '--anchor' option (sbc3r22)
+  + sg_pt interface: add set_scsi_pt_flags() to permit passing
+    through SCSI_PT_FLAGS_QUEUE_AT_TAIL and AT_HEAD flags
+  + add examples/sg_queue_tst+bsg_queue_tst for SG_FLAG_Q_AT_TAIL
+  + add AM_MAINTAINER_MODE to configure.ac to lessen build issues
+  + add BSD_LICENSE file to this and lib directories, refer to
+    it from source and header files. Some source has GPL license
+- Changes from version 1.28:
+  + sg_unmap: new utility for thin provisioning
+    - add examples/sg_unmap_example.txt
+  + sg_get_lba_status: new utility for thin provisioning
+  + sg_read_block_limits: new utility for tape drives
+  + sg_logs: add cache memory statistics log (sub)page
+  + sg_vpd, sg_inq: extend Block limits VPD page (sbc3r19)
+  + sg_vpd: add Thin provisioning VPD page (sbc3r20) and
+            TapeAlert supported flags VPD page
+  + sg_inq: note VPD page support better in sg_vpd
+  + sg_persist: add transport specific transportID format
+    - allow transportIDs to be read from named file
+  + sg_opcodes: allow --opcode= option to take OP and SA
+    values (comma seperated)
+    - tweak print format, remove test code
+  + sg_requests: remove test code in progress calculation
+  + sg_reset: add target reset option
+  + sg_luns: reduce default maxlen to 8192 (for FreeBSD)
+  + sg_raw: extend max cdb length from 16 to 256 bytes
+    - align heap allocs to page boundaries
+  + sg_lib: sg_set_binary_mode() needs config.h included
+    - add progress indication sense data descriptor (0xa)
+    - change SG3_UTILS_* constants to SG_LIB_*
+    - decode service actions within persistent reserve in/out
+    - sync with spc4r21
+  + sg_cmds_extra: add sg_ll_unmap() and sg_ll_get_lba_status()
+  + sg_pt_linux: fix check condition but empty sense buffer;
+    - major() macro grief, if present include <linux/kdev_t.h> and
+      use MAJOR() instead
+  + scripts/sas_disk_blink: moved from this package to sdparm
+  + utils/hxascdmp: in Windows set binary mode on read files
+  + examples/sg_persist_tst.sh: add PRIN read full status command
+  + sg_raw,sg_write_buffer,sg_write_long,sg_write_same: in Windows
+    set binary mode on read files
+  + sg_pt_win32: default to non-direct variant of SPT interface
+    - use './configure --enable-win32-spt-direct' to override
+    - non-direct data length set to 16 KB, extended if required
+  + debian: incorporate patch from debian sid
+
+-------------------------------------------------------------------
+Mon Jun 28 06:38:35 UTC 2010 - jengelh@medozas.de
+
+- use %_smp_mflags
+
+-------------------------------------------------------------------
+Tue Jul 21 14:00:16 CEST 2009 - hare@suse.de
+
+- Clean up spec file and remove obsolete cruft
+
+-------------------------------------------------------------------
+Fri Apr 17 20:15:58 CEST 2009 - crrodriguez@suse.de
+
+- remove static libraries and "la" files
+
+-------------------------------------------------------------------
+Mon Jan 26 15:30:31 CET 2009 - hare@suse.de
+
+- Fixes to rescan-scsi-bus.sh:
+  * Implement '--forcerescan' to force a rescan of existing devices
+  * Handle LUN changes correctly
+  * Check variables before evaluation
+
+-------------------------------------------------------------------
+Wed Oct 29 11:05:47 CET 2008 - garloff@suse.de
+
+- rescan-scsi-bus.sh 1.29:
+  * Fix error in script (returning "" does not work)
+  * Support systems without /proc/scsi
+- Don't install INSTALL
+
+-------------------------------------------------------------------
+Tue Sep 30 14:11:15 CEST 2008 - hare@suse.de
+
+-  Add %insserv_prereq (bnc#423204)
+
+-------------------------------------------------------------------
+Fri Sep 12 20:29:08 CEST 2008 - garloff@suse.de
+
+- Update rescan-scsi-bus.sh script to 1.28:
+  * Merge fixes from Hannes
+  * Minor cleanups
+  * Sort hosts numerically
+
+-------------------------------------------------------------------
+Tue Aug 12 18:25:43 CEST 2008 - garloff@suse.de
+
+- Update to sg3_utils-1.27:
+  * Adapted to linux-2.6.26 (sg_map26)
+  * sg_dd uses flock (rw -- if that fails ro)
+  * sg_get_config: OSSC feature (mmc6r02)
+- Update to sg3_utils-1.26:
+  * Minor fixes and enhancements to
+    sg_sat_phy_event, sg_ses, sg_get_config, sg_verify, sg_vpd,
+    sg_inq, sg_modes, sg_start, sg_request, sg_luns, sg_dd,
+    sg_opcodes, sg_turs.
+  * sg_lib: asc/ascq update for spc4r15, osd2r03 service actions,
+    sense key specific unit attn queue overflow decoding, ...
+  * Restructuring: sg_lib -> sg_lib_data, sg_inq_data, (u)int64_t,
+    sg_io_linux -> lib/.
+  * Documentation enhancements.
+
+-------------------------------------------------------------------
+Wed Jul 16 09:55:33 CEST 2008 - hare@suse.de
+
+- Use correct length parameter for sg_inq (bnc#363438)
+
+-------------------------------------------------------------------
+Fri May 23 10:22:31 CEST 2008 - hare@suse.de
+
+- Use 'Provides' to clean update dependency
+
+-------------------------------------------------------------------
+Fri May  9 17:31:33 CEST 2008 - schwab@suse.de
+
+- Use autoreconf -i.
+
+-------------------------------------------------------------------
+Thu Apr 24 14:14:14 CEST 2008 - hare@suse.de
+
+- Split off from original scsi package.
+
diff --git a/sg3_utils/suse/sg3_utils.spec b/sg3_utils/suse/sg3_utils.spec
new file mode 100644
index 0000000..2606139
--- /dev/null
+++ b/sg3_utils/suse/sg3_utils.spec
@@ -0,0 +1,125 @@
+#
+# spec file for package sg3_utils
+#
+# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
+#
+#
+# No patches, this is the maintainer's version for Suse targets.
+# Patch lines would appear after the "Source:" line and look like:
+#   Patch1:         sg3_utils-1.38r546.patch
+# then under the "%setup -q" line there would be one or more lines:
+#   %patch1 -p1
+
+
+Name:           sg3_utils
+%define lname	libsgutils2-2
+Version:        1.41
+Release:        0
+Summary:        A collection of tools that send SCSI commands to devices
+License:        GPL-2.0+ and BSD-3-Clause
+Group:          Hardware/Other
+Url:            http://sg.danny.cz/sg/sg3_utils.html
+
+Source:         http://sg.danny.cz/sg/p/%name-%{version}.tar.xz
+BuildRoot:      %{_tmppath}/%{name}-%{version}-build
+BuildRequires:  xz
+Requires(pre):  %insserv_prereq
+Provides:       scsi
+Provides:       sg_utils
+Obsoletes:      scsi <= 1.7_2.38_1.25_0.19_1.02_0.93
+
+%description
+The sg3_utils package contains utilities that send SCSI commands to
+devices. As well as devices on transports traditionally associated with
+SCSI (e.g. Fibre Channel (FCP), Serial Attached SCSI (SAS) and the SCSI
+Parallel Interface(SPI)) many other devices use SCSI command sets.
+ATAPI cd/dvd drives and SATA disks that connect via a translation layer
+or a bridge device are examples of devices that use SCSI command sets.
+
+%package -n %lname
+Summary:        Library to hold functions common to the SCSI utilities
+License:        BSD-3-Clause
+Group:          System/Libraries
+
+%description -n %lname
+The sg3_utils package contains utilities that send SCSI commands to
+devices. As well as devices on transports traditionally associated with
+SCSI (e.g. Fibre Channel (FCP), Serial Attached SCSI (SAS) and the SCSI
+Parallel Interface(SPI)) many other devices use SCSI command sets.
+ATAPI cd/dvd drives and SATA disks that connect via a translation layer
+or a bridge device are examples of devices that use SCSI command sets.
+
+This subpackage contains the library of common sg_utils code, such as
+SCSI error processing.
+
+%package -n libsgutils-devel
+Summary:        A collection of tools that send SCSI commands to devices
+License:        BSD-3-Clause
+Group:          Development/Libraries/C and C++
+Requires:       %lname = %version
+# Added for 13.1
+Obsoletes:      %name-devel < %version-%release
+Provides:       %name-devel = %version-%release
+
+%description -n libsgutils-devel
+The sg3_utils package contains utilities that send SCSI commands to
+devices. As well as devices on transports traditionally associated with
+SCSI (e.g. Fibre Channel (FCP), Serial Attached SCSI (SAS) and the SCSI
+Parallel Interface(SPI)) many other devices use SCSI command sets.
+ATAPI cd/dvd drives and SATA disks that connect via a translation layer
+or a bridge device are examples of devices that use SCSI command sets.
+
+This subpackage contains libraries and header files for developing
+applications that want to make use of libsgutils.
+
+%prep
+%setup -q
+
+%build
+%configure --disable-static --with-pic
+make %{?_smp_mflags}
+
+%install
+make install DESTDIR="%buildroot"
+install -m 755 scripts/scsi_logging_level $RPM_BUILD_ROOT%{_bindir}
+install -m 755 scripts/rescan-scsi-bus.sh $RPM_BUILD_ROOT%{_bindir}
+%{__rm} -f %{buildroot}%{_libdir}/*.la
+
+%post   -p /sbin/ldconfig -n %lname
+
+%postun -p /sbin/ldconfig -n %lname
+
+%files
+%defattr(-,root,root)
+%doc README README.sg_start
+%doc ChangeLog CREDITS NEWS
+%_bindir/sg_*
+%_bindir/scsi_*
+%_bindir/sginfo
+%_bindir/sgp_dd
+%_bindir/sgm_dd
+%_bindir/scsi_logging_level
+%_bindir/rescan-scsi-bus.sh
+%_mandir/man8/*.8*
+
+%files -n %lname
+%defattr(-,root,root)
+%_libdir/libsgutils2.so.2*
+
+%files -n libsgutils-devel
+%defattr(-,root,root)
+%_libdir/libsgutils2.so
+%_includedir/scsi/
+
+%changelog
diff --git a/sg3_utils/utils/Makefile b/sg3_utils/utils/Makefile
new file mode 100644
index 0000000..aa11a7d
--- /dev/null
+++ b/sg3_utils/utils/Makefile
@@ -0,0 +1,63 @@
+SHELL = /bin/sh
+
+PREFIX=/usr
+INSTDIR=$(DESTDIR)/$(PREFIX)/bin
+MANDIR=$(DESTDIR)/$(PREFIX)/share/man
+
+CC = gcc
+LD = gcc
+
+EXECS = hxascdmp
+EXTRA_EXECS = hxascdmp sg_chk_asc tst_sg_lib
+
+MAN_PGS = hxascdmp.1
+MAN_PREF = man1
+
+CFLAGS = -g -O2 -W -Wall -iquote ../include
+# CFLAGS = -g -O2 -W -iquote ../include -pedantic -std=c99
+
+LDFLAGS =
+
+all: $(EXECS)
+
+depend dep:
+	for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+	done > .depend
+
+clean:
+	/bin/rm -f *.o $(EXTRA_EXECS) core .depend
+
+hxascdmp: hxascdmp.o
+	$(LD) -o $@ $(LDFLAGS) $^
+
+# building sg_chk_asc depends on a prior successful make in ../lib
+sg_chk_asc: sg_chk_asc.o ../lib/sg_lib.o ../lib/sg_lib_data.o
+	$(LD) -o $@ $(LDFLAGS) $^
+
+tst_sg_lib: tst_sg_lib.o ../lib/sg_lib.o ../lib/sg_lib_data.o
+	$(LD) -o $@ $(LDFLAGS) $^
+
+
+install: $(EXECS)
+	install -d $(INSTDIR)
+	for name in $^; \
+	 do install -s -o root -g root -m 755 $$name $(INSTDIR); \
+	done
+	install -d $(MANDIR)/$(MAN_PREF)
+	for mp in $(MAN_PGS); \
+	 do install -o root -g root -m 644 $$mp $(MANDIR)/$(MAN_PREF); \
+	 gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \
+	done
+
+uninstall:
+	dists="$(EXECS)"; \
+	for name in $$dists; do \
+	 rm -f $(INSTDIR)/$$name; \
+	done
+	for mp in $(MAN_PGS); do \
+	 rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \
+	done
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/sg3_utils/utils/Makefile.cygwin b/sg3_utils/utils/Makefile.cygwin
new file mode 100644
index 0000000..86e9fa3
--- /dev/null
+++ b/sg3_utils/utils/Makefile.cygwin
@@ -0,0 +1,33 @@
+# Assumes Makefile is used in a cygwin shell
+
+SHELL = /bin/sh
+
+CC = gcc
+LD = gcc
+
+EXECS =	hxascdmp
+
+EXE_S =	hxascdmp.exe
+
+# OS_FLAGS = -DSG_LIB_WIN32 -DSPTD
+OS_FLAGS = -DSG_LIB_WIN32
+LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+EXTRA_FLAGS = $(OS_FLAGS) $(LARGE_FILE_FLAGS)
+
+# CFLAGS = -O2 -Wall -W $(EXTRA_FLAGS)
+CFLAGS = -g -O2 -Wall -W $(EXTRA_FLAGS)
+# CFLAGS = -g -O2 -Wall -W -pedantic -std=c99 $(EXTRA_FLAGS)
+
+LDFLAGS = 
+
+all: $(EXECS)
+
+clean:
+	rm *.o $(EXE_S)
+
+.c.o:
+	$(CC) $(INCLUDES) $(CFLAGS) $(S_CFLAGS) -c -o $@ $<
+
+hxascdmp: hxascdmp.o
+	$(LD) -o $@ $(LDFLAGS) $@.o
+
diff --git a/sg3_utils/utils/Makefile.freebsd b/sg3_utils/utils/Makefile.freebsd
new file mode 100644
index 0000000..6b9b134
--- /dev/null
+++ b/sg3_utils/utils/Makefile.freebsd
@@ -0,0 +1,56 @@
+SHELL = /bin/sh
+
+PREFIX=/usr/local
+INSTDIR=$(DESTDIR)/$(PREFIX)/bin
+MANDIR=$(DESTDIR)/$(PREFIX)/man
+
+CC = gcc
+LD = gcc
+
+EXECS = hxascdmp
+# EXECS = hxascdmp sg_chk_asc
+
+MAN_PGS = 
+MAN_PREF = man8
+
+CFLAGS = -g -O2 -W 
+# CFLAGS = -g -O2 -W -pedantic -std=c99
+
+LDFLAGS =
+
+all: $(EXECS)
+
+depend dep:
+	for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+	done > .depend
+
+clean:
+	/bin/rm -f *.o $(EXECS) core .depend
+
+hxascdmp: hxascdmp.o
+	$(LD) -o $@ $(LDFLAGS) $@.o
+
+sg_chk_asc: sg_chk_asc.o ../sg_lib.o ../sg_lib_data.o
+	$(LD) -o $@ $(LDFLAGS) $@.o ../sg_lib.o
+
+
+install: $(EXECS)
+	install -d $(INSTDIR)
+	for name in $(EXECS); \
+	 do install -s -m 755 $$name $(INSTDIR); \
+	done
+	install -d $(MANDIR)/$(MAN_PREF)
+	for mp in $(MAN_PGS); \
+	 do install -m 644 $$mp $(MANDIR)/$(MAN_PREF); \
+	 gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \
+	done
+
+uninstall:
+	dists="$(EXECS)"; \
+	for name in $$dists; do \
+	 rm -f $(INSTDIR)/$$name; \
+	done
+	for mp in $(MAN_PGS); do \
+	 rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \
+	done
+
diff --git a/sg3_utils/utils/Makefile.mingw b/sg3_utils/utils/Makefile.mingw
new file mode 100644
index 0000000..6fed348
--- /dev/null
+++ b/sg3_utils/utils/Makefile.mingw
@@ -0,0 +1,33 @@
+# Assumes makefile is used in a MSYS shell with a MinGW compiler available.
+
+SHELL = /bin/sh
+
+CC = gcc
+LD = gcc
+
+EXECS =	hxascdmp
+
+EXE_S =	hxascdmp.exe
+
+# OS_FLAGS = -DSG_LIB_WIN32 -DSG_LIB_MINGW -DSPTD
+OS_FLAGS = -DSG_LIB_WIN32 -DSG_LIB_MINGW
+LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+EXTRA_FLAGS = $(OS_FLAGS) $(LARGE_FILE_FLAGS)
+
+# CFLAGS = -O2 -Wall -W $(EXTRA_FLAGS)
+CFLAGS = -g -O2 -Wall -W $(EXTRA_FLAGS)
+# CFLAGS = -g -O2 -Wall -W -pedantic -std=c99 $(EXTRA_FLAGS)
+
+LDFLAGS = 
+
+all: $(EXECS)
+
+clean:
+	rm *.o $(EXE_S)
+
+.c.o:
+	$(CC) $(INCLUDES) $(CFLAGS) $(S_CFLAGS) -c -o $@ $<
+
+hxascdmp: hxascdmp.o
+	$(LD) -o $@ $(LDFLAGS) $@.o
+
diff --git a/sg3_utils/utils/Makefile.solaris b/sg3_utils/utils/Makefile.solaris
new file mode 100644
index 0000000..b49115a
--- /dev/null
+++ b/sg3_utils/utils/Makefile.solaris
@@ -0,0 +1,55 @@
+SHELL = /bin/sh
+
+PREFIX=/usr/local
+INSTDIR=$(DESTDIR)/$(PREFIX)/bin
+MANDIR=$(DESTDIR)/$(PREFIX)/man
+
+CC = gcc
+LD = gcc
+
+EXECS = hxascdmp
+# EXECS = hxascdmp sg_chk_asc
+
+MAN_PGS = 
+MAN_PREF = man8
+
+CFLAGS = -g -O2 -W 
+# CFLAGS = -g -O2 -W -pedantic -std=c99
+
+LDFLAGS =
+
+all: $(EXECS)
+
+depend dep:
+	for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+	done > .depend
+
+clean:
+	/bin/rm -f *.o $(EXECS) core .depend
+
+hxascdmp: hxascdmp.o
+	$(LD) -o $@ $(LDFLAGS) $@.o
+
+sg_chk_asc: sg_chk_asc.o ../sg_lib.o
+	$(LD) -o $@ $(LDFLAGS) $@.o ../sg_lib.o
+
+
+install: $(EXECS)
+	install -d $(INSTDIR)
+	for name in $(EXECS); \
+	 do install -s -f $(INSTDIR) $$name; \
+	done
+	install -d $(MANDIR)/$(MAN_PREF)
+	for mp in $(MAN_PGS); \
+	 do install -m 644 -f $(MANDIR)/$(MAN_PREF) $$mp; \
+	done
+
+uninstall:
+	dists="$(EXECS)"; \
+	for name in $$dists; do \
+	 rm -f $(INSTDIR)/$$name; \
+	done
+	for mp in $(MAN_PGS); do \
+	 rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \
+	done
+
diff --git a/sg3_utils/utils/README b/sg3_utils/utils/README
new file mode 100644
index 0000000..ce2d698
--- /dev/null
+++ b/sg3_utils/utils/README
@@ -0,0 +1,29 @@
+This directory contains these utilities:
+  - hxascdmp: takes a binary stream and converts it to hexadecimal ASCII
+    which is sent to stdout. The incoming binary stream can either be
+    from a file or, in the absence of a file name, from stdin. Similar to
+    the Unix "od" command. By default, it decodes 16 bytes per line with
+    an ASCII interpretation to the right of each line. See its
+    hxascdmp(1) man page.
+  - sg_chk_asc: utility decodes the SCSI additional sense code table
+    found at http://www.t10.org/lists/asc-num.txt and checks it
+    against the table found in sg_lib_data.c in the lib/ subdirectory.
+    It is designed to keep the table in sg_lib_data.c in "sync" with the
+    table at the t10.org web site.
+
+
+By default, the Makefile only builds the hxascdmp utility. The 'Makefile'
+file (i.e. with no suffix) builds for Linux; the 'Makefile.freebsd' file
+builds for FreeBSD (e.g. 'make -f Makefile.freebsd'); the
+'Makefile.solaris' file builds for Solaris; the 'Makefile.mingw' builds
+in the Windows MinGW environment (e.g.  msys shell); and 'Makefile.cygwin'
+builds in the Windows Cygwin environment.
+
+To build sg_chk_asc the sg_lib.o and sg_lib_data.o files must be present
+(i.e. compiled) in the lib/ subdirectory. One way to meet that requirement
+is to execute './configure' in the main directory then 'cd lib ; make '.
+Then return to this directory and do 'make sg_chk_asc'.
+
+
+Douglas Gilbert
+30th March 2010 
diff --git a/sg3_utils/utils/hxascdmp.1 b/sg3_utils/utils/hxascdmp.1
new file mode 100644
index 0000000..16300aa
--- /dev/null
+++ b/sg3_utils/utils/hxascdmp.1
@@ -0,0 +1,106 @@
+.TH HXASCDMP "1" "February 2014" "sg3_utils\-1.38" SG3_UTILS
+.SH NAME
+hxascdmp \- hexadecimal ASCII dump
+.SH SYNOPSIS
+.B hxascdmp
+[\fI\-b=BPL\fR] [\fI\-h\fR] [\fI\-H\fR] [\fI\-N\fR] [\fI\-V\fR]
+[\fIFILE+\fR]
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+This utility reads one or more \fIFILE\fR names and dumps them in hexadecimal
+and ASCII to stdout. If no \fIFILE\fR is given then stdin is read instead;
+reading continues (or stalls) until an EOF is received.
+.PP
+The default format is to start each line with the hexadecimal address (offset
+from the start of file) followed by 16 hexadecimal bytes separated by a
+single space (apart from the 8th and 9th bytes which are separated by two
+spaces). If the \fI\-H\fR is not given, there is then a string of 16 ASCII
+characters corresponding to the hexadecimal bytes earlier in the line; only
+bytes in the range 0x20 to 0x7e are printed in ASCII, other bytes values are
+printed as '.' . If the \fI\-H\fR is not given, each \fIFILE\fR name that
+appears on the command line is printed on a separate line prior to that
+file's hexadecimal ASCII dump.
+.PP
+If the \fI\-N\fR option is given then no address is printed out and each
+line starts with the next hexadecimal byte.
+.PP
+This utility is pretty close to the 'hexdump -C' variant of BSD's
+.B hexdump(1)
+command.
+.SH OPTIONS
+.TP
+\fB\-b\fR=\fIBPL\fR
+where \fIBPL\fR specifies the number of bytes per line. The default value is
+16. 16 bytes per line is just enough to allow the address, 16 bytes in
+hexadecimal followed by 16 bytes as ASCII to fit on a standard 80 column
+wide terminal.
+.TP
+\fB\-h\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR
+output hexadecimal only (i.e. don't place an ASCII representation at the
+end of each line).
+.TP
+\fB\-N\fR
+no address; so each line starts with the next hexadecimal byte.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+In Windows the given file (or files) are set to binary mode.
+.SH EXIT STATUS
+The exit status of hxascdmp is 0 when it is successful. If any of the
+given \fIFILE\fR names cannot be opened then the exit status is 1.
+.SH EXAMPLES
+First we manufacture a short file with a mix of data in it: mostly ASCII with
+some control characters and 0xaa (which the echo command only accepts in
+octal (0252):
+.PP
+   $ echo -e "three blind mice,\t\r\0252" > 3bm.txt
+.PP
+Now we use this utility to see exactly what is in the file. To avoid
+problems with line wrapping, the bytes per line option is set to 8:
+.PP
+   $ hxascdmp -b=8 3bm.txt
+.br
+ASCII hex dump of file: 3bm.txt
+.br
+ 00      74 68 72 65  65 20 62 6c   three bl
+.br
+ 08      69 6e 64 20  6d 69 63 65   ind mice
+.br
+ 10      2c 09 0d aa  0a            ,....
+.PP
+Using the same file, use this utility to output only hexadecimal formatted
+16 bytes per line.
+.PP
+   $ hxascdmp -H 3bm.txt
+.br
+hex dump of file: 3bm.txt
+.br
+ 00      74 68 72 65 65 20 62 6c  69 6e 64 20 6d 69 63 65
+.br
+ 10      2c 09 0d aa 0a
+.PP
+For comparison the hexdump utility gives similar output:
+.PP
+   $ hexdump -C 3bm.txt
+.br
+00000000  74 68 72 65 65 20 62 6c  69 6e 64 20 6d 69 63 65  |three blind mice|
+.br
+00000010  2c 09 0d aa 0a                                    |,....|
+.br
+00000015
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2004\-2014 Douglas Gilbert
+.br
+This software is distributed under a FreeBSD license. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.B hexdump(1)
diff --git a/sg3_utils/utils/hxascdmp.c b/sg3_utils/utils/hxascdmp.c
new file mode 100644
index 0000000..077fd93
--- /dev/null
+++ b/sg3_utils/utils/hxascdmp.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2004-2014 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#define DEF_BYTES_PER_LINE 16
+
+static int bytes_per_line = DEF_BYTES_PER_LINE;
+
+static const char * version_str = "1.14 20140213";
+
+#define CHARS_PER_HEX_BYTE 3
+#define BINARY_START_COL 6
+#define MAX_LINE_LENGTH 257
+
+
+#ifdef SG_LIB_MINGW
+/* Non Unix OSes distinguish between text and binary files.
+   Set text mode on fd. Does nothing in Unix. Returns negative number on
+   failure. */
+int
+sg_set_text_mode(int fd)
+{
+    return setmode(fd, O_TEXT);
+}
+
+/* Set binary mode on fd. Does nothing in Unix. Returns negative number on
+   failure. */
+int
+sg_set_binary_mode(int fd)
+{
+    return setmode(fd, O_BINARY);
+}
+
+#else
+/* For Unix the following functions are dummies. */
+int
+sg_set_text_mode(int fd)
+{
+    return fd;  /* fd should be >= 0 */
+}
+
+int
+sg_set_binary_mode(int fd)
+{
+    return fd;
+}
+#endif
+
+/* Returns the number of times 'ch' is found in string 's' given the
+ * string's length. */
+static int
+num_chs_in_str(const char * s, int slen, int ch)
+{
+    int res = 0;
+
+    while (--slen >= 0) {
+        if (ch == s[slen])
+            ++res;
+    }
+    return res;
+}
+
+static void
+dStrHex(const char* str, int len, long start, int noAddr)
+{
+    const char* p = str;
+    unsigned char c;
+    char buff[MAX_LINE_LENGTH];
+    long a = start;
+    int bpstart, cpstart;
+    int j, k, line_length, nl, cpos, bpos, midline_space;
+
+    if (noAddr) {
+        bpstart = 0;
+        cpstart = ((CHARS_PER_HEX_BYTE * bytes_per_line) + 1) + 5;
+    } else {
+        bpstart = BINARY_START_COL;
+        cpstart = BINARY_START_COL +
+                        ((CHARS_PER_HEX_BYTE * bytes_per_line) + 1) + 5;
+    }
+    cpos = cpstart;
+    bpos = bpstart;
+    midline_space = ((bytes_per_line + 1) / 2);
+
+    if (len <= 0)
+        return;
+    line_length = BINARY_START_COL +
+                  (bytes_per_line * (1 + CHARS_PER_HEX_BYTE)) + 7;
+    if (line_length >= MAX_LINE_LENGTH) {
+        fprintf(stderr, "bytes_per_line causes maximum line length of %d "
+                        "to be exceeded\n", MAX_LINE_LENGTH);
+        return;
+    }
+    memset(buff, ' ', line_length);
+    buff[line_length] = '\0';
+    if (0 == noAddr) {
+        k = sprintf(buff + 1, "%.2lx", a);
+        buff[k + 1] = ' ';
+    }
+
+    for(j = 0; j < len; j++) {
+        nl = (0 == (j % bytes_per_line));
+        if ((j > 0) && nl) {
+            printf("%s\n", buff);
+            bpos = bpstart;
+            cpos = cpstart;
+            a += bytes_per_line;
+            memset(buff,' ', line_length);
+            if (0 == noAddr) {
+                k = sprintf(buff + 1, "%.2lx", a);
+                buff[k + 1] = ' ';
+            }
+        }
+        c = *p++;
+        bpos += (nl && noAddr) ?  0 : CHARS_PER_HEX_BYTE;
+        if ((bytes_per_line > 4) && ((j % bytes_per_line) == midline_space))
+            bpos++;
+        sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
+        buff[bpos + 2] = ' ';
+        if ((c < ' ') || (c >= 0x7f))
+            c='.';
+        buff[cpos++] = c;
+    }
+    if (cpos > cpstart)
+        printf("%s\n", buff);
+}
+
+static void
+dStrHexOnly(const char* str, int len, long start, int noAddr)
+{
+    const char* p = str;
+    unsigned char c;
+    char buff[MAX_LINE_LENGTH];
+    long a = start;
+    int bpstart, bpos, nl;
+    int midline_space = ((bytes_per_line + 1) / 2);
+    int j, k, line_length;
+
+    if (len <= 0)
+        return;
+    bpstart = (noAddr ? 0 : BINARY_START_COL);
+    bpos = bpstart;
+    line_length = (noAddr ? 0 : BINARY_START_COL) +
+                  (bytes_per_line * CHARS_PER_HEX_BYTE) + 4;
+    if (line_length >= MAX_LINE_LENGTH) {
+        fprintf(stderr, "bytes_per_line causes maximum line length of %d "
+                        "to be exceeded\n", MAX_LINE_LENGTH);
+        return;
+    }
+    memset(buff, ' ', line_length);
+    buff[line_length] = '\0';
+    if (0 == noAddr) {
+        k = sprintf(buff + 1, "%.2lx", a);
+        buff[k + 1] = ' ';
+    }
+
+    for(j = 0; j < len; j++) {
+        nl = (0 == (j % bytes_per_line));
+        if ((j > 0) && nl) {
+            printf("%s\n", buff);
+            bpos = bpstart;
+            a += bytes_per_line;
+            memset(buff,' ', line_length);
+            if (0 == noAddr) {
+                k = sprintf(buff + 1, "%.2lx", a);
+                buff[k + 1] = ' ';
+            }
+        }
+        c = *p++;
+        bpos += (nl && noAddr) ? 0 : CHARS_PER_HEX_BYTE;
+        if ((bytes_per_line > 4) && ((j % bytes_per_line) == midline_space))
+            bpos++;
+        sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
+        buff[bpos + 2] = ' ';
+    }
+    if (bpos > bpstart)
+        printf("%s\n", buff);
+}
+
+static void
+usage()
+{
+    fprintf(stderr, "Usage: hxascdmp [-b=<n>] [-h] [-H] [-N] [-V] [-?] "
+            "[<file>+]\n");
+    fprintf(stderr, "  where:\n");
+    fprintf(stderr, "    -b=<n>     bytes per line to display "
+                    "(def: 16)\n");
+    fprintf(stderr, "    -h         print this usage message\n");
+    fprintf(stderr, "    -H         print hex only (i.e. no ASCII "
+            "to right)\n");
+    fprintf(stderr, "    -N         no address, start in first column\n");
+    fprintf(stderr, "    -V         print version string then exits\n");
+    fprintf(stderr, "    -?         print this usage message\n");
+    fprintf(stderr, "    <file>+    reads file(s) and outputs each "
+                    "as hex ASCII\n");
+    fprintf(stderr, "               if no <file> then reads stdin\n\n");
+    fprintf(stderr, "Sends hex ASCII dump of stdin/file to stdout\n");
+}
+
+int
+main(int argc, const char ** argv)
+{
+    char buff[8192];
+    int num = 8192;
+    long start = 0;
+    int res, k, u, len, n;
+    int inFile = STDIN_FILENO;
+    int doHelp = 0;
+    int doHex = 0;
+    int noAddr = 0;
+    int doVersion = 0;
+    int hasFilename = 0;
+    int ret = 0;
+    const char * cp;
+
+    for (k = 1; k < argc; k++) {
+        cp = argv[k];
+        len = strlen(cp);
+        if (0 == strncmp("-b=", cp, 3)) {
+            res = sscanf(cp + 3, "%d", &u);
+            if ((1 != res) || (u < 1)) {
+                fprintf(stderr, "Bad value after '-b=' option\n");
+                usage();
+                return 1;
+            }
+            bytes_per_line = u;
+        } else if ((len > 1) && ('-' == cp[0]) && ('-' != cp[1])) {
+            res = 0;
+            n = num_chs_in_str(cp + 1, len - 1, 'h');
+            doHelp += n;
+            res += n;
+            n = num_chs_in_str(cp + 1, len - 1, 'H');
+            doHex += n;
+            res += n;
+            n = num_chs_in_str(cp + 1, len - 1, 'N');
+            noAddr += n;
+            res += n;
+            n = num_chs_in_str(cp + 1, len - 1, 'V');
+            doVersion += n;
+            res += n;
+            n = num_chs_in_str(cp + 1, len - 1, '?');
+            doHelp += n;
+            res += n;
+            if (0 == res) {
+                fprintf(stderr, "No option recognized in str: %s\n", cp);
+                usage();
+                return 1;
+            }
+        } else if (0 == strcmp("-?", argv[k]))
+            ++doHelp;
+        else if (*argv[k] == '-') {
+            fprintf(stderr, "unknown switch: %s\n", argv[k]);
+            usage();
+            return 1;
+        } else {
+            hasFilename = 1;
+            break;
+        }
+    }
+    if (doVersion) {
+        printf("%s\n", version_str);
+        return 0;
+    }
+    if (doHelp) {
+        usage();
+        return 0;
+    }
+
+    /* Make sure num to fetch is integral multiple of bytes_per_line */
+    if (0 != (num % bytes_per_line))
+        num = (num / bytes_per_line) * bytes_per_line;
+
+    if (hasFilename) {
+        for ( ; k < argc; k++)
+        {
+            inFile = open(argv[k], O_RDONLY);
+            if (inFile < 0) {
+                fprintf(stderr, "Couldn't open file: %s\n", argv[k]);
+                ret = 1;
+            } else {
+                sg_set_binary_mode(inFile);
+                start = 0;
+                if (! doHex)
+                    printf("ASCII hex dump of file: %s\n", argv[k]);
+                while ((res = read(inFile, buff, num)) > 0) {
+                    if (doHex)
+                        dStrHexOnly(buff, res, start, noAddr);
+                    else
+                        dStrHex(buff, res, start, noAddr);
+                    start += (long)res;
+                }
+            }
+            close(inFile);
+        }
+    } else {
+        sg_set_binary_mode(inFile);
+        while ((res = read(inFile, buff, num)) > 0) {
+            if (doHex)
+                dStrHexOnly(buff, res, start, noAddr);
+            else
+                dStrHex(buff, res, start, noAddr);
+            start += (long)res;
+        }
+    }
+    return ret;
+}
diff --git a/sg3_utils/utils/sg_chk_asc.c b/sg3_utils/utils/sg_chk_asc.c
new file mode 100644
index 0000000..cb30075
--- /dev/null
+++ b/sg3_utils/utils/sg_chk_asc.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2006-2015 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "sg_lib.h"
+
+/* A utility program for the Linux OS SCSI subsystem.
+ *
+ * This program takes a asc_ascq.txt file from www.t10.org and
+ * checks it against the additional sense codes held in the
+ * sg_lib.c file.
+ * The online version of the asc_ascq codes can be found at:
+ * http://www.t10.org/lists/asc-num.txt
+ */
+
+static char * version_str = "1.05 20150105";
+
+
+#define MAX_LINE_LEN 1024
+
+
+static struct option long_options[] = {
+        {"help", 0, 0, 'h'},
+        {"verbose", 0, 0, 'v'},
+        {"version", 0, 0, 'V'},
+        {0, 0, 0, 0},
+};
+
+static void usage()
+{
+    fprintf(stderr, "Usage: "
+            "sg_chk_asc [--help] [--offset=POS] [--verbose] [--version]\n"
+            "                  <asc_ascq_file>\n"
+            "  where:\n"
+            "    --help|-h          print out usage message\n"
+            "    --offset=POS|-o POS    line position in file where "
+            "text starts\n"
+            "                           origin 0 (def: 24 (was 25))\n"
+            "    --verbose|-v       increase verbosity\n"
+            "    --version|-V       print version string and exit\n\n"
+            "Checks asc/ascq codes in <asc_ascq_file> against the sg3_utils "
+            "library.\nThe additional sense code (asc_ascq) can be found at\n"
+            "www.t10.org/lists/asc-num.txt .\n"
+           );
+
+}
+
+int main(int argc, char * argv[])
+{
+    int k, j, res, c, num, len, asc, ascq;
+    FILE * fp;
+    int offset = 24;
+    int verbose = 0;
+    char file_name[256];
+    char line[MAX_LINE_LEN];
+    char b[MAX_LINE_LEN];
+    char bb[MAX_LINE_LEN];
+    char * cp;
+    int ret = 1;
+
+    memset(file_name, 0, sizeof file_name);
+    memset(line, 0, sizeof file_name);
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "ho:vV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'o':
+            offset = sg_get_num(optarg);
+            if (offset < 0) {
+                fprintf(stderr, "bad argument to --offset\n");
+                return 1;
+            }
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            fprintf(stderr, "version: %s\n", version_str);
+            return 0;
+        default:
+            fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
+            usage();
+            return 1;
+        }
+    }
+    if (optind < argc) {
+        if ('\0' == file_name[0]) {
+            strncpy(file_name, argv[optind], sizeof(file_name) - 1);
+            file_name[sizeof(file_name) - 1] = '\0';
+            ++optind;
+        }
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                fprintf(stderr, "Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return 1;
+        }
+    }
+
+    if (0 == file_name[0]) {
+        fprintf(stderr, "missing file name!\n");
+        usage();
+        return 1;
+    }
+    fp = fopen(file_name, "r");
+    if (NULL == fp) {
+        fprintf(stderr, "open error: %s: %s\n", file_name,
+                safe_strerror(errno));
+        return 1;
+    }
+    for (k = 0; (cp = fgets(line, sizeof(line) - 1, fp)); ++k) {
+        len = strlen(line);
+        if (len < 1)
+            continue;
+        if (! isdigit(line[0]))
+            continue;
+        num = sscanf(line, "%xh/%xh", &asc, &ascq);
+        if (1 == num)
+            ascq = -1;
+        if (num < 1) {
+            if (verbose)
+                fprintf(stderr, "Badly formed line number %d (num=%d)\n",
+                        k + 1, num);
+            continue;
+        }
+        if (len < 26)
+            continue;
+#if 0
+strncpy(b , line, sizeof(b) - 1);
+b[sizeof(b) - 1] = '\0';
+num = strlen(b);
+if (0xd == b[num - 2]) {
+    b[num - 2] = '\0';
+    b[num - 1] = '\0';
+}
+printf("\"%s\",\n", b);
+#endif
+        strncpy(b , line + offset, sizeof(b) - 1);
+        b[sizeof(b) - 1] = '\0';
+        num = strlen(b);
+        if (0xd == b[num - 2]) {
+            b[num - 2] = '\0';
+            b[num - 1] = '\0';
+        }
+        num = strlen(b);
+        for (j = 0; j < num; ++j)
+            b[j] = toupper(b[j]);
+
+        bb[0] = '\0';
+        if (ascq >= 0) {
+            cp = sg_get_asc_ascq_str(asc, ascq, sizeof(bb) - 1, bb);
+            if (NULL == cp) {
+                fprintf(stderr, "no entry for %x,%x : %s\n", asc, ascq, b);
+                continue;
+            }
+            num = strlen(cp);
+// fprintf(stderr, "file: asc=%x  acsq=%x  strlen=%d %s\n", asc, ascq, num,
+//         cp);
+//            if (num < 20)
+//                continue;
+            if ((num > 6) &&
+                ((0 == memcmp("ASC", cp, 3)) ||
+                 (0 == memcmp("vendor", cp, 6)))) {
+                fprintf(stderr, "%x,%x differ, ref: %s, sg_lib_data: "
+                        "<missing>\n", asc, ascq, b);
+                continue;
+            }
+            if (num > 20) {
+                cp += 18;
+                num -= 18;
+                for (j = 0; j < num; ++j)
+                    cp[j] = toupper(cp[j]);
+            }
+            if (0 != strcmp(b, cp))
+                fprintf(stderr, "%x,%x differ, ref: %s, sg_lib_data: "
+                        "%s\n", asc, ascq, b, cp);
+        }
+    }
+    if (NULL == cp) {
+        if (feof(fp)) {
+            if (verbose > 2)
+                fprintf(stderr, "EOF detected\n");
+        } else
+            fprintf(stderr, "fgets: %s\n", safe_strerror(errno));
+    } else
+        fprintf(stderr, "%s\n", line);
+
+    res = fclose(fp);
+    if (EOF == res) {
+        fprintf(stderr, "close error: %s\n", safe_strerror(errno));
+        return 1;
+    }
+    return ret;
+}
diff --git a/sg3_utils/utils/tst_sg_lib.c b/sg3_utils/utils/tst_sg_lib.c
new file mode 100644
index 0000000..569f176
--- /dev/null
+++ b/sg3_utils/utils/tst_sg_lib.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2013-2016 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 <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <errno.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#include "sg_lib.h"
+#include "sg_unaligned.h"
+
+/* A utility program to test sg_libs string handling, specifically
+ * related to snprintf().
+ *
+ */
+
+static char * version_str = "1.03 20160128";
+
+
+#define MAX_LINE_LEN 1024
+
+
+static struct option long_options[] = {
+        {"dstrhex",  no_argument, 0, 'd'},
+        {"help", no_argument, 0, 'h'},
+        {"leadin",  required_argument, 0, 'l'},
+        {"printf", no_argument, 0, 'p'},
+        {"sense", no_argument, 0, 's'},
+        {"unaligned", no_argument, 0, 'u'},
+        {"verbose", no_argument, 0, 'v'},
+        {"version", no_argument, 0, 'V'},
+        {0, 0, 0, 0},   /* sentinel */
+};
+
+static const unsigned char desc_sense_data1[] = {
+   /* unrec_err, excessive_writes, sdat_ovfl, additional_len=? */
+    0x72, 0x1, 0x3, 0x2, 0x80, 0x0, 0x0, 12+12+8+4+8+4+28,
+   /* Information: 0x11223344556677bb */
+    0x0, 0xa, 0x80, 0x0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xbb,
+   /* command specific: 0x3344556677bbccff */
+    0x1, 0xa, 0x0, 0x0, 0x33, 0x44, 0x55, 0x66, 0x77, 0xbb, 0xcc, 0xff,
+   /* sense key specific: SKSV=1, actual_count=257 (hex: 0x101) */
+    0x2, 0x6, 0x0, 0x0, 0x80, 0x1, 0x1, 0x0,
+   /* field replaceable code=0x45 */
+    0x3, 0x2, 0x0, 0x45,
+   /* another progress report indicator */
+    0xa, 0x6, 0x2, 0x1, 0x2, 0x0, 0x32, 0x01,
+   /* incorrect length indicator (ILI) */
+    0x5, 0x2, 0x0, 0x20,
+   /* user data degment referral */
+    0xb, 26, 0x1, 0x0,
+        0,0,0,1, 0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,
+                 0x1,0x2,0x3,0x4,0x55,0x6,0x7,0x8,
+        2,0,0x12,0x34,
+    };
+
+static const unsigned char desc_sense_data2[] = {
+   /* ill_req, inv fld in para list, additional_len=? */
+    0x72, 0x5, 0x26, 0x0, 0x0, 0x0, 0x0, 8+4,
+   /* sense key specific: SKSV=1, C/D*=0, bitp=7 bytep=34 */
+    0x2, 0x6, 0x0, 0x0, 0x8f, 0x0, 0x34, 0x0,
+   /* field replaceable code=0x45 */
+    0x3, 0x2, 0x0, 0x45,
+    };
+
+static const unsigned char desc_sense_data3[] = {
+   /* medium err, vibration induced ..., additional_len=? */
+    0x72, 0x3, 0x9, 0x5, 0x0, 0x0, 0x0, 32+16,
+   /* 0xd: block dev: sense key specific: SKSV=1, retry_count=257, fru=0x45
+    * info=0x1122334455, command_specific=0x1   */
+    0xd, 0x1e, 0xa0, 0x0, 0x80, 0x1, 0x1, 0x45,
+    0x0, 0x0, 0x0, 0x11, 0x22, 0x33, 0x44, 0x55,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+    /* following sbc3 (standard) and sbc4r10 inconsistency; add padding */
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    /* 0xe: reason: send_to_given+henceforth, lu, naa-5, 0x5333333000001f40 */
+    0xe, 0xe, 0x0, 0x1, 0x1, 0x3, 0x0, 0x8,
+        0x53, 0x33, 0x33, 0x30, 0x0, 0x0, 0x1f, 0x40,
+    };
+
+static const unsigned char desc_sense_data4[] = {
+   /* ill_req, inv fld in para list, additional_len=? */
+    0x72, 0x5, 0x26, 0x0, 0x0, 0x0, 0x0, 24,
+   /* Forwarded sense data, FSDT=0, sd_src=7, f_status=2 */
+    0xc, 22, 0x7, 0x2,
+   /* ill_req, inv fld in para list, additional_len=? */
+    0x72, 0x5, 0x26, 0x0, 0x0, 0x0, 0x0, 8+4,
+   /* sense key specific: SKSV=1, C/D*=0, bitp=7 bytep=34 */
+    0x2, 0x6, 0x0, 0x0, 0x8f, 0x0, 0x34, 0x0,
+   /* field replaceable code=0x45 */
+    0x3, 0x2, 0x0, 0x45,
+    };
+
+static const unsigned char desc_sense_data5[] = {
+   /* no_sense, ATA info available */
+    0x72, 0x0, 0x0, 0x1d, 0x0, 0x0, 0x0, 14+14,
+   /* ATA descriptor extend=1 */
+    0x9, 0xc, 0x1, 0x0, 0x34, 0x12, 0x44, 0x11,
+    0x55, 0x22, 0x66, 0x33, 0x1, 0x0,
+   /* ATA descriptor extend=0 */
+    0x9, 0xc, 0x0, 0x0, 0x34, 0x12, 0x44, 0x11,
+    0x55, 0x22, 0x66, 0x33, 0x1, 0x0,
+    };
+
+static const unsigned char desc_sense_data6[] = {
+   /* UA, req, subsidiary bindinganged */
+    0x72, 0x6, 0x3f, 0x1a, 0x0, 0x0, 0x0, 26+12+12,
+    /* 0xe: designator, reason: preferred admin lu, uuid */
+    0xe, 0x18, 0x0, 0x4, 0x1, 0xa, 0x0, 0x12,
+        0x10, 0x0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+        0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
+        0xfe, 0xdc,
+    /* 0x0: Information(valid): lun */
+    0x0, 0xa, 0x80, 0x0,
+    0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+    /* 0x1: Command specific: 0x1 */
+    0x1, 0xa, 0x0, 0x0,
+    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
+    };
+
+static const char * leadin = NULL;
+
+
+static void
+usage()
+{
+    fprintf(stderr,
+            "Usage: tst_sg_lib [--dstrhex] [--help] [--leadin=STR] "
+            "[--printf]\n"
+            "                  [--sense] [--unaligned] [--verbose] "
+            "[--version]\n"
+            "  where: --dstrhex|-d       test dStrHex* variants\n"
+            "         --help|-h          print out usage message\n"
+            "         --leadin=STR|-l STR    every line output by --sense "
+            "should\n"
+            "                                be prefixed by STR\n"
+            "         --printf|-p        test library printf variants\n"
+            "         --sense|-s         test sense data handling\n"
+            "         --unaligned|-u     test unaligned data handling\n"
+            "         --verbose|-v       increase verbosity\n"
+            "         --version|-V       print version string and exit\n\n"
+            "Test various parts of sg_lib, see options. Sense data tests "
+            "overlap\nsomewhat with examples/sg_sense_test .\n"
+           );
+
+}
+
+/* Want safe, 'n += snprintf(b + n ...)' like function. If cp_max_len is 1
+ * then assume cp is pointing to a null char and do nothing. Returns number
+ * number of chars placed in cp excluding the trailing null char. So for
+ * cp_max_len > 0 the return value is always < cp_max_len; for cp_max_len
+ * <= 0 the return value is 0 (and no chars are written to cp). */
+static int
+my_snprintf(char * cp, int cp_max_len, const char * fmt, ...)
+{
+    va_list args;
+    int n;
+
+    if (cp_max_len < 2)
+        return 0;
+    va_start(args, fmt);
+    n = vsnprintf(cp, cp_max_len, fmt, args);
+    va_end(args);
+    return (n < cp_max_len) ? n : (cp_max_len - 1);
+}
+
+
+int
+main(int argc, char * argv[])
+{
+    int k, c, n, len;
+    int do_dstrhex = 0;
+    int do_printf = 0;
+    int do_sense = 0;
+    int do_unaligned = 0;
+    int did_something = 0;
+    int verbose = 0;
+    int ret = 0;
+    char b[2048];
+    char bb[128];
+
+    while (1) {
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "dhl:psuvV", long_options,
+                        &option_index);
+        if (c == -1)
+            break;
+
+        switch (c) {
+        case 'd':
+            ++do_dstrhex;
+            break;
+        case 'h':
+        case '?':
+            usage();
+            return 0;
+        case 'l':
+            leadin = optarg;
+            break;
+        case 'p':
+            ++do_printf;
+            break;
+        case 's':
+            ++do_sense;
+            break;
+        case 'u':
+            ++do_unaligned;
+            break;
+        case 'v':
+            ++verbose;
+            break;
+        case 'V':
+            fprintf(stderr, "version: %s\n", version_str);
+            return 0;
+        default:
+            fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
+            usage();
+            return 1;
+        }
+    }
+    if (optind < argc) {
+        if (optind < argc) {
+            for (; optind < argc; ++optind)
+                fprintf(stderr, "Unexpected extra argument: %s\n",
+                        argv[optind]);
+            usage();
+            return 1;
+        }
+    }
+
+    if (do_sense ) {
+        ++did_something;
+        printf("desc_sense_data test1:\n");
+        sg_print_sense(leadin, desc_sense_data1,
+                       (int)sizeof(desc_sense_data1), verbose);
+        printf("\n");
+#if 1
+        printf("sg_get_sense_str(ds_data1):\n");
+        sg_get_sense_str(leadin, desc_sense_data1,
+                         sizeof(desc_sense_data1), verbose, sizeof(b), b);
+        printf("sg_get_sense_str: strlen(b)->%zd\n", strlen(b));
+        printf("%s", b);
+        printf("\n");
+#endif
+        printf("desc_sense_data test2\n");
+        sg_print_sense(leadin, desc_sense_data2,
+                       (int)sizeof(desc_sense_data2), verbose);
+        printf("\n");
+        printf("desc_sense block dev combo plus designator test3\n");
+        sg_print_sense(leadin, desc_sense_data3,
+                       (int)sizeof(desc_sense_data3), verbose);
+        printf("\n");
+        printf("desc_sense forwarded sense test4\n");
+        sg_print_sense(leadin, desc_sense_data4,
+                       (int)sizeof(desc_sense_data4), verbose);
+        printf("\n");
+        printf("desc_sense ATA Info test5\n");
+        sg_print_sense(leadin, desc_sense_data5,
+                       (int)sizeof(desc_sense_data5), verbose);
+        printf("\n");
+        printf("desc_sense UA subsidiary binfing changed test6\n");
+        sg_print_sense(leadin, desc_sense_data6,
+                       (int)sizeof(desc_sense_data6), verbose);
+        printf("\n");
+        printf("\n");
+    }
+
+    if (do_printf) {
+        ++did_something;
+        printf("Testing my_snprintf():\n");
+        b[0] = '\0';
+        len = sizeof(b);
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = -1;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = 0;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = 1;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = 2;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = 3;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = 4;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = 5;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = 6;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+
+        b[0] = '\0';
+        len = 7;
+        n = my_snprintf(b, len, "%s", "test");
+        printf("my_snprintf(,%d,,\"test\") -> %d; strlen(b) -> %zd\n",
+               len, n, strlen(b));
+        if (strlen(b) > 0)
+            printf("Resulting string: %s\n", b);
+    }
+    if (do_dstrhex) {
+        char b[] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+                    0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+                    0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58};
+
+        ++did_something;
+        for (k = 0; k < 18; ++k) {
+            printf("k=%d:\n", k);
+            dStrHex(b, k, 0);
+            dStrHex(b, k, 1);
+            dStrHex(b, k, -1);
+            dStrHexStr(b, k, "dStrHexStr:^", 0, sizeof(bb), bb);
+            printf("%s", bb);
+            printf("\n");
+        }
+    }
+    if (do_unaligned) {
+        uint16_t u16 = 0x55aa;
+        uint16_t u16r;
+        uint32_t u24 = 0x224488;
+        uint32_t u24r;
+        uint32_t u32 = 0x224488ff;
+        uint32_t u32r;
+        uint64_t u48 = 0x112233445566ULL;
+        uint64_t u48r;
+        uint64_t u64 = 0x1122334455667788ULL;
+        uint64_t u64r;
+        uint8_t u8[64];
+
+        ++did_something;
+        if (verbose)
+            memset(u8, 0, sizeof(u8));
+        printf("u16=0x%" PRIx16 "\n", u16);
+        sg_put_unaligned_le16(u16, u8);
+        printf("  le16:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 2, -1);
+        u16r = sg_get_unaligned_le16(u8);
+        printf("  u16r=0x%" PRIx16 "\n", u16r);
+        sg_put_unaligned_be16(u16, u8);
+        printf("  be16:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 2, -1);
+        u16r = sg_get_unaligned_be16(u8);
+        printf("  u16r=0x%" PRIx16 "\n\n", u16r);
+
+        printf("u24=0x%" PRIx32 "\n", u24);
+        sg_put_unaligned_le24(u24, u8);
+        printf("  le24:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 3, -1);
+        u24r = sg_get_unaligned_le24(u8);
+        printf("  u24r=0x%" PRIx32 "\n", u24r);
+        sg_put_unaligned_be24(u24, u8);
+        printf("  be24:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 3, -1);
+        u24r = sg_get_unaligned_be24(u8);
+        printf("  u24r=0x%" PRIx32 "\n\n", u24r);
+
+        printf("u32=0x%" PRIx32 "\n", u32);
+        sg_put_unaligned_le32(u32, u8);
+        printf("  le32:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 4, -1);
+        u32r = sg_get_unaligned_le32(u8);
+        printf("  u32r=0x%" PRIx32 "\n", u32r);
+        sg_put_unaligned_be32(u32, u8);
+        printf("  be32:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 4, -1);
+        u32r = sg_get_unaligned_be32(u8);
+        printf("  u32r=0x%" PRIx32 "\n\n", u32r);
+
+        printf("u48=0x%" PRIx64 "\n", u48);
+        sg_put_unaligned_le48(u48, u8);
+        printf("  le48:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 6, -1);
+        u48r = sg_get_unaligned_le48(u8);
+        printf("  u48r=0x%" PRIx64 "\n", u48r);
+        sg_put_unaligned_be48(u48, u8);
+        printf("  be48:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 6, -1);
+        u48r = sg_get_unaligned_be48(u8);
+        printf("  u48r=0x%" PRIx64 "\n\n", u48r);
+
+        printf("u64=0x%" PRIx64 "\n", u64);
+        sg_put_unaligned_le64(u64, u8);
+        printf("  le64:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 8, -1);
+        u64r = sg_get_unaligned_le64(u8);
+        printf("  u64r=0x%" PRIx64 "\n", u64r);
+        sg_put_unaligned_be64(u64, u8);
+        printf("  be64:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 8, -1);
+        u64r = sg_get_unaligned_be64(u8);
+        printf("  u64r=0x%" PRIx64 "\n\n", u64r);
+        printf("  be[8]:\n");
+        dStrHex((const char *)u8, verbose ? 10 : 8, -1);
+        u64r = sg_get_unaligned_be(8, u8);
+        printf("  u64r[8]=0x%" PRIx64 "\n\n", u64r);
+        printf("  le[8]:\n");
+        u64r = sg_get_unaligned_le(8, u8);
+        printf("  u64r[8]=0x%" PRIx64 "\n\n", u64r);
+
+    }
+
+    if (0 == did_something)
+        printf("Looks like no tests done, check usage with '-h'\n");
+    return ret;
+}