sg_vpd: more JSON work

git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@958 6180dd3e-e324-4e3e-922d-17de1ae2f315
diff --git a/ChangeLog b/ChangeLog
index 05c629e..800c2e0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@
 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 pre-release sg3_utils-1.48 [20220701] [svn: r957]
+Changelog for pre-release sg3_utils-1.48 [20220705] [svn: r958]
   - some utilities: add experimental --json[=JO] option
   - sg_z_act_query: new utility for sending either a
     Zone activate or Zone query command
diff --git a/doc/sg3_utils_json.8 b/doc/sg3_utils_json.8
index 8ada814..454dfa7 100644
--- a/doc/sg3_utils_json.8
+++ b/doc/sg3_utils_json.8
@@ -15,14 +15,17 @@
 human-readable output.
 .PP
 JSON is an open standard file format that can be used for data exchange
-between systems. See https://en.wikipedia.org/wiki/JSON . JSON comes in
-many flavours and this one uses the json-builder C implementation found
-at https://github.com/json-parser/json-builder which implements four simple
-JSON data types: string, integer, boolean and null. Its other data types
-are JSON object and JSON array. This project uses the "snake" convention
-for JSON object names: all in lower case with separate words joined with
-a single underscore (e.g. "starting_lba"). The json-builder library uses
-the SPDX-License-Identifier: BSD-2-Clause which is the same license as the
+between systems. See https://en.wikipedia.org/wiki/JSON . JSON comes in many
+flavours and this one uses the json-builder C implementation found at
+https://github.com/json-parser/json-builder which implements four simple JSON
+data types: string, integer, boolean and null. Its other data types are JSON
+object and JSON array.
+.PP
+This project uses the "snake_case" convention for JSON object names: all in
+lower case letters or numerals with individual words joined with a single
+underscore (e.g. "starting_lba"). There should be no leading or trailing
+underscore characters. The json-builder library uses the
+SPDX\-License\-Identifier: BSD\-2\-Clause which is the same license as the
 bulk of the utilities in the sg3_utils package.
 .PP
 The json-builder library is relatively lightweight (700 lines of C code) and
@@ -45,7 +48,7 @@
 have an optional argument. In its shorter form it may either be \fI\-j\fR or
 \fI\-J\fR (lower case preferred if not already in use). The shorter form may
 also take an argument but there must not be a space (or whitespace) between
-\fI\-j\fR and that argument.  
+\fI\-j\fR and that argument.
 .SH ENVIRONMENT VARIABLES
 The SG3_UTILS_JSON_OPTS environment variable allows the user to override the
 default values of the \fIJO\fR settings. Those settings can again be overridden
@@ -105,6 +108,10 @@
 value and a "abbreviated_name_expansion" field with a string value that
 should make the T10 name clearer (e.g. "Sense Key Specific Valid").
 .br
+The "abbreviated name expansion" control character is also used to flag
+fields that are obsolete, usually noting the standard or revision of a
+draft standard when they became obsolete.
+.br
 This boolean control character is default off (false).
 .TP
 \fBe\fR
@@ -126,7 +133,20 @@
 value instead receive a a sub\-object containing at least a "i" field with
 the integer value and a "hex" field with the corresponding hex value in a
 JSON string. That hex string has no hex decorations (i.e. no leading '0x'
-nor traing 'h').
+nor trailing 'h').
+.br
+This boolean control character is default off (false).
+.TP
+\fBk\fR
+this is a boolean control character for finer control of non\-pretty printed
+JSON output. If the 'p' control character is set on (true) then this option
+has no effect.
+.br
+If the 'p' control character is set off (false) and this control character is
+set off (false) then the single line JSON output contains some spaces for
+readability. If the 'p' control character is set off (false) and this control
+character is set on (true) then the JSON single line JSON output is "packed"
+removing all unnecessary spaces.
 .br
 This boolean control character is default off (false).
 .TP
@@ -163,6 +183,9 @@
 current tab setting to indicate the level of nesting. Basically the pretty
 printed form is for human consumption.
 .br
+There are two forms of non\-pretty printed output, see the "packed" control
+character ['k'].
+.br
 This boolean control character is default on (true).
 .TP
 \fBs\fR
diff --git a/doc/sg_modes.8 b/doc/sg_modes.8
index 162f5f9..4fef24a 100644
--- a/doc/sg_modes.8
+++ b/doc/sg_modes.8
@@ -1,4 +1,4 @@
-.TH SG_MODES "8" "September 2020" "sg3_utils\-1.45" SG3_UTILS
+.TH SG_MODES "8" "July 2022" "sg3_utils\-1.45" SG3_UTILS
 .SH NAME
 sg_modes \- reads mode pages with SCSI MODE SENSE command
 .SH SYNOPSIS
@@ -20,9 +20,9 @@
 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. The SPC\-4
-standard (and SPC\-5 drafts) include a note stating that implementers should
-migrate away from the SCSI MODE SELECT(6) and MODE SENSE(6) commands in
-favour of the 10 byte variants (e.g. MODE SENSE(10)).
+standard (and the SPC\-5 standard) include a note stating that implementers
+should migrate away from the SCSI MODE SELECT(6) and MODE SENSE(6) commands
+in favour of the 10 byte variants (e.g. MODE SENSE(10)).
 .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
@@ -301,7 +301,7 @@
 .SH "REPORTING BUGS"
 Report bugs to <dgilbert at interlog dot com>.
 .SH COPYRIGHT
-Copyright \(co 2000\-2020 Douglas Gilbert
+Copyright \(co 2000\-2022 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/doc/sg_persist.8 b/doc/sg_persist.8
index 36d784b..23a0c97 100644
--- a/doc/sg_persist.8
+++ b/doc/sg_persist.8
@@ -35,11 +35,10 @@
 .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\-5 whose most recent draft is revision
-18 dated 4 January 2018) are sections 5.14 (titled "Reservations"),
-6.16 (for the PRIN command) and 6.17 (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.
+Primary Commands document (i.e. SPC\-5 ANSI INCITS 502\-2020) are sections
+8.14 (titled "Reservations"), 9.16 (for the PRIN command) and 9.17 (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
@@ -245,7 +244,7 @@
 \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
+a single TransportID as defined in SPC\-5. 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 an equivalent
 to the hex byte form: "6,0,0,0,5,0,c5,0,5,b3,20,1"). The \fITIDS\fR argument
diff --git a/include/sg_pr2serr.h b/include/sg_pr2serr.h
index 43a85e0..23cad94 100644
--- a/include/sg_pr2serr.h
+++ b/include/sg_pr2serr.h
@@ -73,7 +73,8 @@
     bool pr_exit_status;        /* 'e' (def: true) */
     bool pr_hex;                /* 'h' (def: false) */
     bool pr_leadin;             /* 'l' (def: true) */
-    bool pr_output;             /* 'o' (def: false) */
+    bool pr_out_hr;             /* 'o' (def: false) */
+    bool pr_packed;             /* 'k' (def: false) only when !pr_pretty */
     bool pr_pretty;             /* 'p' (def: true) */
     bool pr_string;             /* 's' (def: true) */
     char pr_format;             /*  (def: '\0') */
@@ -82,17 +83,17 @@
     /* the following hold state information */
     int first_bad_char;         /* = '\0' */
     sgj_opaque_p basep;         /* base JSON object pointer */
-    sgj_opaque_p outputp;       /* 'output' named JSON array pointer */
+    sgj_opaque_p out_hrp;       /* JSON array pointer when pr_out_hr set */
     sgj_opaque_p userp;         /* for temporary usage */
 } sgj_state;
 
 /* If jsp in non-NULL and jsp->pr_as_json is true then this call is ignored
- * unless jsp->pr_output is true. Otherwise this function prints to stdout
- * like printf(fmt, ...); note that no LF is added. In the jsp->pr_output
- * is true case, nothing is printed to stdout but instead is placed into the
- * JSON 'output' array (jsp->outputp) after some preprocessing. That
- * preprocessing involves removing a leading LF from 'fmt' (if present) and
- * up to two trailing LF characters. */
+ * unless jsp->pr_out_hrp is true. Otherwise this function prints to stdout
+ * like printf(fmt, ...); note that no LF is added. In the jsp->pr_out_hrp is
+ * true case, nothing is printed to stdout but instead is placed into a JSON
+ * array (jsp->out_hrp) after some preprocessing. That preprocessing involves
+ * removing a leading LF from 'fmt' (if present) and up to two trailing LF
+ * characters. */
 void sgj_pr_hr(sgj_state * jsp, const char * fmt, ...) __printf(2, 3);
 
 /* Initializes the state object pointed to by jsp based on the argument
@@ -110,7 +111,7 @@
  * in-core tree. If jsp is NULL nothing further happens. Otherwise the pointer
  * to be returned is placed in jsp->basep. If jsp->pr_leadin is true and
  * util_name is non-NULL then a "utility_invoked" JSON object is made with
- * "name", and "version_date" object fields. If the jsp->pr_output field is
+ * "name", and "version_date" object fields. If the jsp->pr_out_hr field is
  * true a named array called "output" is added to the "utility_invoked" object
  * (creating it in the case when jsp->pr_leadin is false) and a pointer to
  * that array object is placed in jsp->objectp . The returned pointer is not
@@ -134,6 +135,8 @@
  * and if not, used. */
 sgj_opaque_p sgj_new_named_object(sgj_state * jsp, sgj_opaque_p jop,
                                   const char * name);
+sgj_opaque_p sgj_new_snake_named_object(sgj_state * jsp, sgj_opaque_p jop,
+                                        const char * conv2sname);
 
 /* If jsp is NULL or jsp->pr_as_json is false nothing happens and NULL is
  * returned. Otherwise it creates a new named object (whose name is what
@@ -143,6 +146,8 @@
  * and if not, used. */
 sgj_opaque_p sgj_new_named_array(sgj_state * jsp, sgj_opaque_p jop,
                                  const char * name);
+sgj_opaque_p sgj_new_snake_named_array(sgj_state * jsp, sgj_opaque_p jop,
+                                       const char * conv2sname);
 
 /* If either jsp or value is NULL or jsp->pr_as_json is false then nothing
  * happens and NULL is returned. The insertion point is at jop but if it is
@@ -190,9 +195,9 @@
                           const char * name, sgj_opaque_p ua_jop);
 
 /* The '_hr_js_' refers to generating output both for human readable and/or
- * JSON with a single invocation. If jsp is non_NULL and jsp->pr_output is
+ * JSON with a single invocation. If jsp is non_NULL and jsp->pr_out_hr is
  * true then both JSON and human readable output is formed (and the latter is
- * placed in the jsp->outputp JSON array). The human readable form will have
+ * placed in the jsp->out_hrp JSON array). The human readable form will have
  * leadin_sp spaces followed by 'name' then a separator, then 'value' with a
  * trailing LF. If 'name' is NULL then it and the separator are ignored. If
  * there is JSON output, then leadin_sp and sep are ignored. If 'jop' is NULL
@@ -260,10 +265,10 @@
                             const char * ane_s);
 
 /* Breaks up the string pointed to by 'sp' into lines and adds them to the
- * jsp->outputp array. Treat '\n' in sp as line breaks. Consumes characters
+ * jsp->out_hrp array. Treat '\n' in sp as line breaks. Consumes characters
  * from sp until either a '\0' is found or slen is exhausted. Add each line
- * to jsp->outputp JSON array (if conditions met). */
-void sgj_pr_str_output(sgj_state * jsp, const char * sp, int slen);
+ * to jsp->out_hrp JSON array (if conditions met). */
+void sgj_pr_str_out_hr(sgj_state * jsp, const char * sp, int slen);
 
 /* This function only produces JSON output if jsp is non-NULL and
  * jsp->pr_as_json is true. 'sbp' is assumed to point to sense data as
@@ -292,7 +297,7 @@
 /* If jsp is NULL or jsp->basep is NULL then this function does nothing.
  * This function does bottom up, heap freeing of all the in-core JSON
  * objects and arrays attached to the root JSON object assumed to be
- * found at jsp->basep . After this call jsp->basep, jsp->outputp and
+ * found at jsp->basep . After this call jsp->basep, jsp->out_hrp and
  * jsp->userp will all be set to NULL.  */
 void sgj_finish(sgj_state * jsp);
 
diff --git a/inhex/vpd_sdeb.hex b/inhex/vpd_sdeb.hex
new file mode 100644
index 0000000..ba5cd4e
--- /dev/null
+++ b/inhex/vpd_sdeb.hex
@@ -0,0 +1,99 @@
+#
+# The VPD responses in this file where generated from a dummy device
+# (ramdisk) associated with the Linux scsi_debug driver as follows:
+#        sg_vpd -a /dev/sg0 -HHHH
+
+# Supported VPD pages VPD page
+00 00 00 0c 00 80 83 84  85 86 87 88 89 b0 b1 b2
+
+# Unit serial number VPD page
+00 80 00 04 32 30 30 30
+
+# Device identification VPD page
+00 83 00 70 02 01 00 1c  4c 69 6e 75 78 20 20 20
+73 63 73 69 5f 64 65 62  75 67 20 20 20 20 20 20
+32 30 30 30 01 03 00 08  33 33 33 30 00 00 07 d0
+61 94 00 04 00 00 00 01  61 93 00 08 32 22 22 20
+00 00 07 ce 61 95 00 04  00 00 01 00 61 a3 00 08
+32 22 22 20 00 00 07 cd  63 a8 00 18 6e 61 61 2e
+33 32 32 32 32 32 32 30  30 30 30 30 30 37 43 44
+00 00 00 00
+
+# Software interface identification VPD page
+00 84 00 12 22 22 22 00  bb 00 22 22 22 00 bb 01
+22 22 22 00 bb 02
+
+# Management network addresses VPD page
+00 85 00 44 01 00 00 20  68 74 74 70 73 3a 2f 2f
+77 77 77 2e 6b 65 72 6e  65 6c 2e 6f 72 67 2f 63
+6f 6e 66 69 67 00 00 00  04 00 00 1c 68 74 74 70
+3a 2f 2f 77 77 77 2e 6b  65 72 6e 65 6c 2e 6f 72
+67 2f 6c 6f 67 00 00 00
+
+# extended INQUIRY data VPD page
+00 86 00 3c 00 07 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+
+# Mode page policy VPD page
+00 87 00 08 02 00 80 00  18 00 82 00
+
+# SCSI Ports VPD page
+00 88 00 30 00 00 00 01  00 00 00 00 00 00 00 0c
+61 93 00 08 32 22 22 20  00 00 07 ce 00 00 00 02
+00 00 00 00 00 00 00 0c  61 93 00 08 32 22 22 20
+00 00 07 cf
+
+# ATA information VPD page
+00 89 02 38 00 00 00 00  6c 69 6e 75 78 20 20 20
+53 41 54 20 73 63 73 69  5f 64 65 62 75 67 20 20
+31 32 33 34 34 00 00 00  01 00 00 00 00 00 00 00
+01 00 00 00 00 00 00 00  ec 00 00 00 5a 0c ff 3f
+37 c8 10 00 00 00 00 00  3f 00 00 00 00 00 00 00
+58 58 58 58 58 58 58 58  20 20 20 20 20 20 20 20
+20 20 20 20 00 00 00 40  04 00 2e 33 38 31 20 20
+20 20 54 53 38 33 30 30  33 31 53 41 20 20 20 20
+20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20
+20 20 20 20 20 20 20 20  20 20 10 80 00 00 00 2f
+00 00 00 02 00 02 07 00  ff ff 01 00 3f 00 c1 ff
+3e 00 10 01 b0 f8 50 09  00 00 07 00 03 00 78 00
+78 00 f0 00 78 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 02 00 00 00  00 00 00 00 7e 00 1b 00
+6b 34 01 7d 03 40 69 34  01 3c 03 40 7f 40 00 00
+00 00 fe fe 00 00 00 00  00 fe 00 00 00 00 00 00
+00 00 00 00 b0 f8 50 09  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 01 00 b0 f8
+50 09 b0 f8 50 09 20 20  02 00 b6 42 00 80 8a 00
+06 3c 0a 3c ff ff c6 07  00 01 00 08 f0 0f 00 10
+02 00 30 00 00 00 00 00  00 00 06 fe 00 00 02 00
+50 00 8a 00 4f 95 00 00  21 00 0b 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 a5 51
+
+# Block limits VPD page
+00 b0 00 3c 00 00 00 01  00 00 40 00 00 00 04 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01
+00 00 00 00 00 00 00 00  00 00 ff ff 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+
+# Block device characteristics VPD page
+00 b1 00 3c 00 01 00 05  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+
+# Logical block provisioning VPD page
+00 b2 00 04 00 00 00 00
diff --git a/lib/sg_pr2serr.c b/lib/sg_pr2serr.c
index be07a2e..310ea78 100644
--- a/lib/sg_pr2serr.c
+++ b/lib/sg_pr2serr.c
@@ -49,6 +49,8 @@
     4                                   /* indent size */
 };
 
+static int sgj_name_to_snake(const char * in, char * out, int maxlen_out);
+
 
 /* Users of the sg_pr2serr.h header need this function definition */
 int
@@ -116,11 +118,14 @@
         case 'h':
             jsp->pr_hex = ! prev_negate;
             break;
+        case 'k':
+            jsp->pr_packed = ! prev_negate;
+            break;
         case 'l':
             jsp->pr_leadin = ! prev_negate;
             break;
         case 'o':
-            jsp->pr_output = ! prev_negate;
+            jsp->pr_out_hr = ! prev_negate;
             break;
         case 'p':
             jsp->pr_pretty = ! prev_negate;
@@ -178,6 +183,8 @@
     n +=  sg_scnpr(b + n, blen - n,
                    "      h    show 'hex' fields\n");
     n +=  sg_scnpr(b + n, blen - n,
+                   "      k    packed, only non-pretty printed output\n");
+    n +=  sg_scnpr(b + n, blen - n,
                    "      l    show lead-in fields (invocation "
                    "information)\n");
     n +=  sg_scnpr(b + n, blen - n,
@@ -212,11 +219,12 @@
 char *
 sg_json_settings(sgj_state * jsp, char * b, int blen)
 {
-    snprintf(b, blen, "%d%sa%se%sh%sl%so%sp%ss%sv", jsp->pr_indent_size,
+    snprintf(b, blen, "%d%sa%se%sh%sk%sl%so%sp%ss%sv", jsp->pr_indent_size,
              jsp->pr_ane ? "" : "-", jsp->pr_exit_status ? "" : "-",
-             jsp->pr_hex ? "" : "-", jsp->pr_leadin ? "" : "-",
-             jsp->pr_output ? "" : "-", jsp->pr_pretty ? "" : "-",
-             jsp->pr_string ? "" : "-", jsp->verbose ? "" : "-");
+             jsp->pr_hex ? "" : "-", jsp->pr_packed ? "" : "-",
+             jsp->pr_leadin ? "" : "-", jsp->pr_out_hr ? "" : "-",
+             jsp->pr_pretty ? "" : "-", jsp->pr_string ? "" : "-",
+             jsp->verbose ? "" : "-");
     return b;
 }
 
@@ -228,7 +236,8 @@
     jsp->pr_exit_status = true;
     jsp->pr_hex = false;
     jsp->pr_leadin = true;
-    jsp->pr_output = false;
+    jsp->pr_out_hr = false;
+    jsp->pr_packed = false;     /* 'k' control character, needs '-p' */
     jsp->pr_pretty = true;
     jsp->pr_string = true;
     jsp->pr_format = 0;
@@ -244,7 +253,7 @@
 
     sgj_def_opts(jsp);
     jsp->basep = NULL;
-    jsp->outputp = NULL;
+    jsp->out_hrp = NULL;
     jsp->userp = NULL;
 
     cp = getenv(sgj_opts_ev);
@@ -286,8 +295,11 @@
                          (json_value *)jap);
         if (util_name) {
             jap = json_array_new(0);
-            for (k = 0; k < argc; ++k)
-                json_array_push((json_value *)jap, json_string_new(argv[k]));
+            if (argv) {
+                for (k = 0; k < argc; ++k)
+                    json_array_push((json_value *)jap,
+                                    json_string_new(argv[k]));
+            }
             jv2p = json_object_push((json_value *)jvp, "utility_invoked",
                                     json_object_new(0));
             json_object_push((json_value *)jv2p, "name",
@@ -313,19 +325,19 @@
                              json_string_new(b));
         }
     } else {
-        if (jsp->pr_output && util_name)
+        if (jsp->pr_out_hr && util_name)
             jv2p = json_object_push((json_value *)jvp, "utility_invoked",
                                     json_object_new(0));
     }
-    if (jsp->pr_output && jv2p) {
-        jsp->outputp = json_object_push((json_value *)jv2p, "output",
+    if (jsp->pr_out_hr && jv2p) {
+        jsp->out_hrp = json_object_push((json_value *)jv2p, "output",
                                         json_array_new(0));
         if (jsp->pr_leadin && (jsp->verbose > 3)) {
             char * bp = (char *)calloc(4096, 1);
 
             if (bp) {
                 sg_json_usage(0, bp, 4096);
-                sgj_pr_str_output(jsp, bp, strlen(bp));
+                sgj_pr_str_out_hr(jsp, bp, strlen(bp));
                 free(bp);
             }
         }
@@ -359,7 +371,8 @@
     if (jsp->pr_indent_size != def_out_settings.indent_size)
         out_settings.indent_size = jsp->pr_indent_size;
     if (! jsp->pr_pretty)
-        out_settings.mode = json_serialize_mode_single_line;
+        out_settings.mode = jsp->pr_packed ? json_serialize_mode_packed :
+                                json_serialize_mode_single_line;
 
     len = json_measure_ex(jvp, out_settings);
     if (len < 1)
@@ -372,6 +385,7 @@
             pr2serr("%s: unable to get %zu bytes on heap\n", __func__, len);
         return;
     }
+
     json_serialize_ex(b, jvp, out_settings);
     if (jsp->verbose > 3)
         fprintf(fp, "json serialized:\n");
@@ -384,7 +398,7 @@
     if (jsp && jsp->basep) {
         json_builder_free((json_value *)jsp->basep);
         jsp->basep = NULL;
-        jsp->outputp = NULL;
+        jsp->out_hrp = NULL;
         jsp->userp = NULL;
     }
 }
@@ -401,7 +415,7 @@
 {
     va_list args;
 
-    if (jsp->pr_as_json && jsp->pr_output) {
+    if (jsp->pr_as_json && jsp->pr_out_hr) {
         size_t len;
         char b[256];
 
@@ -420,7 +434,7 @@
             /* remove leading linefeed, if present */
             if ((len > 0) && ('\n' == b[0]))
                 ++cp;
-            json_array_push((json_value *)jsp->outputp, json_string_new(cp));
+            json_array_push((json_value *)jsp->out_hrp, json_string_new(cp));
         }
         va_end(args);
     } else if (jsp->pr_as_json) {
@@ -445,6 +459,22 @@
     return resp;
 }
 
+sgj_opaque_p
+sgj_new_snake_named_object(sgj_state * jsp, sgj_opaque_p jop,
+                           const char * conv2sname)
+{
+    if (jsp && jsp->pr_as_json && conv2sname) {
+        int olen = strlen(conv2sname);
+        char * sname = malloc(olen + 8);
+        int nlen = sgj_name_to_snake(conv2sname, sname, olen + 8);
+
+        if (nlen > 0)
+            return json_object_push((json_value *)(jop ? jop : jsp->basep),
+                                    sname, json_object_new(0));
+    }
+    return NULL;
+}
+
 /* jop will 'own' returned value (if non-NULL) */
 sgj_opaque_p
 sgj_new_named_array(sgj_state * jsp, sgj_opaque_p jop, const char * name)
@@ -457,6 +487,22 @@
     return resp;
 }
 
+sgj_opaque_p
+sgj_new_snake_named_array(sgj_state * jsp, sgj_opaque_p jop,
+                          const char * conv2sname)
+{
+    if (jsp && jsp->pr_as_json && conv2sname) {
+        int olen = strlen(conv2sname);
+        char * sname = malloc(olen + 8);
+        int nlen = sgj_name_to_snake(conv2sname, sname, olen + 8);
+
+        if (nlen > 0)
+            return json_object_push((json_value *)(jop ? jop : jsp->basep),
+                                    sname, json_array_new(0));
+    }
+    return NULL;
+}
+
 /* Newly created object is un-attached to jsp->basep tree */
 sgj_opaque_p
 sgj_new_unattached_object(sgj_state * jsp)
@@ -490,14 +536,19 @@
 sgj_add_nv_s_len(sgj_state * jsp, sgj_opaque_p jop, const char * name,
                  const char * value, int slen)
 {
+    int k;
+
     if (jsp && jsp->pr_as_json && value && (slen >= 0)) {
+        for (k = 0; k < slen; ++k) {    /* don't want '\0' in value string */
+            if (0 == value[k])
+                break;
+        }
         if (name)
             return json_object_push((json_value *)(jop ? jop : jsp->basep),
-                                    name, json_string_new_length(slen,
-                                                                 value));
+                                    name, json_string_new_length(k, value));
         else
             return json_array_push((json_value *)(jop ? jop : jsp->basep),
-                                   json_string_new_length(slen, value));
+                                   json_string_new_length(k, value));
     } else
         return NULL;
 }
@@ -555,8 +606,7 @@
     if ((NULL == jsp) || (NULL == name) || (! jsp->pr_as_json))
         return;
     else if (jsp->pr_hex)  {
-        sgj_opaque_p jo2p =
-                 sgj_new_named_object(jsp, jop, name);
+        sgj_opaque_p jo2p = sgj_new_named_object(jsp, jop, name);
         char b[64];
 
         if (NULL == jo2p)
@@ -577,8 +627,8 @@
     if ((NULL == jsp) || (! jsp->pr_as_json))
         return;
     else if (jsp->pr_string) {
-        sgj_opaque_p jo2p =
-                 sgj_new_named_object(jsp, jop, name);
+        sgj_opaque_p jo2p = sgj_new_named_object(jsp, jop, name);
+
         if (NULL == jo2p)
             return;
         sgj_add_nv_i(jsp, jo2p, "i", (int64_t)val_i);
@@ -598,8 +648,7 @@
         sgj_add_nv_i(jsp, jop, name, val_i);
     else {
         char b[64];
-        sgj_opaque_p jo2p =
-                 sgj_new_named_object(jsp, jop, name);
+        sgj_opaque_p jo2p = sgj_new_named_object(jsp, jop, name);
 
         if (NULL == jo2p)
             return;
@@ -713,18 +762,18 @@
 }
 
 /* Treat '\n' in sp as line breaks. Consumes characters from sp until either
- * a '\0' is found or slen is exhausted. Add each line to jsp->outputp JSON
+ * a '\0' is found or slen is exhausted. Add each line to jsp->out_hrp JSON
  * array (if conditions met). */
 void
-sgj_pr_str_output(sgj_state * jsp, const char * sp, int slen)
+sgj_pr_str_out_hr(sgj_state * jsp, const char * sp, int slen)
 {
     char c;
     int k, n;
     const char * prev_sp = sp;
     const char * cur_sp = sp;
 
-    if ((NULL == jsp) || (NULL == jsp->outputp) || (! jsp->pr_as_json) ||
-        (! jsp->pr_output))
+    if ((NULL == jsp) || (NULL == jsp->out_hrp) || (! jsp->pr_as_json) ||
+        (! jsp->pr_out_hr))
         return;
     for (k = 0; k < slen; ++k, ++cur_sp) {
         c = *cur_sp;
@@ -732,20 +781,25 @@
             break;
         else if ('\n' == c) {
             n = cur_sp - prev_sp;
-            /* when name is NULL, add to array (jsp->outputp) */
-            sgj_add_nv_s_len(jsp, jsp->outputp, NULL, prev_sp, n);
+            /* when name is NULL, add to array (jsp->out_hrp) */
+            sgj_add_nv_s_len(jsp, jsp->out_hrp, NULL, prev_sp, n);
             prev_sp = cur_sp + 1;
         }
     }
     if (prev_sp < cur_sp) {
         n = cur_sp - prev_sp;
-        sgj_add_nv_s_len(jsp, jsp->outputp, NULL, prev_sp, n);
+        sgj_add_nv_s_len(jsp, jsp->out_hrp, NULL, prev_sp, n);
     }
 }
 
-/* Returns number of characters placed in 'out' excluding trailing NULL */
+/* This function tries to convert the 'in' C string to "snake_case"
+ * convention so the output 'out' only contains lower case ASCII letters,
+ * numerals and "_" as a separator. Any leading or trailing underscores
+ * are removed as are repeated underscores (e.g. "_Snake __ case" becomes
+ * "snake_case").  Returns number of characters placed in 'out' excluding
+ * the trailing NULL */
 static int
-sgj_jsonify_name(const char * in, char * out, int maxlen_out)
+sgj_name_to_snake(const char * in, char * out, int maxlen_out)
 {
     bool prev_underscore = false;
     int c, k, j, inlen;
@@ -800,7 +854,7 @@
         b[n] = ' ';
     b[n] = '\0';
     if (NULL == name) {
-        if ((! as_json) || (jsp && jsp->pr_output)) {
+        if ((! as_json) || (jsp && jsp->pr_out_hr)) {
             switch (jtype) {
             case json_string:
                 sg_scnpr(b + n, blen - n, "%s", jvp->u.string.ptr);
@@ -819,9 +873,9 @@
             printf("%s\n", b);
         }
         if (NULL == jop) {
-            if (as_json && jsp->pr_output) {
+            if (as_json && jsp->pr_out_hr) {
                 eaten = true;
-                json_array_push((json_value *)jsp->outputp,
+                json_array_push((json_value *)jsp->out_hrp,
                                 jvp ? jvp : json_null_new());
             }
         } else {        /* assume jop points to named array */
@@ -841,14 +895,14 @@
 
         if (NULL == jop)
             jop = jsp->basep;
-        k = sgj_jsonify_name(name, jname, sizeof(jname));
+        k = sgj_name_to_snake(name, jname, sizeof(jname));
         if (k > 0) {
             eaten = true;
             json_object_push((json_value *)jop, jname,
                              jvp ? jvp : json_null_new());
         }
     }
-    if (jvp && ((as_json && jsp->pr_output) || (! as_json))) {
+    if (jvp && ((as_json && jsp->pr_out_hr) || (! as_json))) {
         switch (sep) {
         case SGJ_SEP_NONE:
             break;
@@ -894,8 +948,8 @@
             break;
         }
     }
-    if (as_json && jsp->pr_output)
-        json_array_push((json_value *)jsp->outputp, json_string_new(b));
+    if (as_json && jsp->pr_out_hr)
+        json_array_push((json_value *)jsp->out_hrp, json_string_new(b));
     else if (! as_json)
         printf("%s\n", b);
     if (jvp && (! eaten))
@@ -1292,7 +1346,7 @@
                         "Not all referrals");
     dp += 4;
     jap = sgj_new_named_array(jsp, jop,
-                              "user_data_segment_referral_descriptor");
+                              "user_data_segment_referral_descriptor_list");
     for (k = 0, f = 1; (k + 4) < dlen; k += g, dp += g, ++f) {
         int ntpgd = dp[3];
 
@@ -1309,7 +1363,8 @@
         sgj_add_nv_ihex(jsp, jo2p, "first_user_date_sgment_lba", ull);
         ull = sg_get_unaligned_be64(dp + 12);
         sgj_add_nv_ihex(jsp, jo2p, "last_user_date_sgment_lba", ull);
-        ja2p = sgj_new_named_array(jsp, jo2p, "target_port_group_descriptor");
+        ja2p = sgj_new_named_array(jsp, jo2p,
+                                   "target_port_group_descriptor_list");
         for (j = 0; j < ntpgd; ++j) {
             jo3p = sgj_new_unattached_object(jsp);
             tp = dp + 20 + (j * 4);
@@ -1358,7 +1413,7 @@
     add_sb_len = sshp->additional_length;
     add_sb_len = (add_sb_len < sb_len) ? add_sb_len : sb_len;
     sense_key = sshp->sense_key;
-    jap = sgj_new_named_array(jsp, jop, "sense_data_descriptor");
+    jap = sgj_new_named_array(jsp, jop, "sense_data_descriptor_list");
 
     for (descp = sbp, k = 0; (k < add_sb_len);
          k += desc_len, descp += desc_len) {
diff --git a/src/sg_decode_sense.c b/src/sg_decode_sense.c
index 4f50303..83f1d1f 100644
--- a/src/sg_decode_sense.c
+++ b/src/sg_decode_sense.c
@@ -505,10 +505,10 @@
         } else {
             if (as_json) {
                 sgj_get_sense(jsp, jop, op->sense, op->sense_len);
-                if (jsp->pr_output) {
+                if (jsp->pr_out_hr) {
                     sg_get_sense_str(NULL, op->sense, op->sense_len,
                                      op->verbose, blen, b);
-                     sgj_pr_str_output(jsp, b, strlen(b));
+                     sgj_pr_str_out_hr(jsp, b, strlen(b));
                 }
             } else {
                 sg_get_sense_str(NULL, op->sense, op->sense_len,
diff --git a/src/sg_inq.c b/src/sg_inq.c
index 3a09877..c902251 100644
--- a/src/sg_inq.c
+++ b/src/sg_inq.c
@@ -8,7 +8,7 @@
  * SPDX-License-Identifier: GPL-2.0-or-later
  *
  * This program outputs information provided by a SCSI INQUIRY command.
- * It is mainly based on the SCSI SPC-5 document at https://www.t10.org .
+ * It is mainly based on the SCSI SPC-6 document at https://www.t10.org .
  *
  * Acknowledgment:
  *    - Martin Schwenke <martin at meltin dot net> added the raw switch and
diff --git a/src/sg_inq_data.c b/src/sg_inq_data.c
index 9458d6b..f01bc5f 100644
--- a/src/sg_inq_data.c
+++ b/src/sg_inq_data.c
@@ -34,7 +34,7 @@
     "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",
+    "SPC-5",            /* ANSI INCITS 502-2020 */
     "ecma=1, [8h]",
     "ecma=1, [9h]",
     "ecma=1, [Ah]",
diff --git a/src/sg_opcodes.c b/src/sg_opcodes.c
index 8e29ea3..4d8fe0c 100644
--- a/src/sg_opcodes.c
+++ b/src/sg_opcodes.c
@@ -1386,7 +1386,7 @@
             sgj_add_nv_b(jsp, jop, "qaes", rsoc_buff[1] & 0x4);
             sgj_add_nv_b(jsp, jop, "qtss", rsoc_buff[1] & 0x2);
             sgj_add_nv_b(jsp, jop, "itnrs", rsoc_buff[1] & 0x1);
-            if (! jsp->pr_output)
+            if (! jsp->pr_out_hr)
                 goto fini;
         }
         sgj_pr_hr(jsp, "\nTask Management Functions supported by device:\n");
diff --git a/src/sg_vpd.c b/src/sg_vpd.c
index ff9e9cf..89c982e 100644
--- a/src/sg_vpd.c
+++ b/src/sg_vpd.c
@@ -42,7 +42,7 @@
 
 */
 
-static const char * version_str = "1.73 20220701";  /* spc6r06 + sbc5r01 */
+static const char * version_str = "1.73 20220705";  /* spc6r06 + sbc5r01 */
 
 #define MY_NAME "sg_decode_sense"
 
@@ -116,7 +116,7 @@
 static int svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue,
                                  int off);
 
-static int decode_dev_ids(const char * print_if_found, int num_leading,
+static int filter_dev_ids(const char * print_if_found, int num_leading,
                           uint8_t * buff, int len, int m_assoc,
                           int m_desig_type, int m_code_set,
                           struct opts_t * op, sgj_opaque_p jop);
@@ -198,7 +198,7 @@
      "protection types (SBC)"},
     {VPD_SCSI_FEATURE_SETS, 0, -1, "sfs", "SCSI feature sets"},
     {VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"},
-    {VPD_NOPE_WANT_STD_INQ, 0, -1, "sinq", "Standard inquiry response"},
+    {VPD_NOPE_WANT_STD_INQ, 0, -1, "sinq", "Standard inquiry data format"},
     {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)"},
@@ -439,7 +439,7 @@
     "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",
+    "SPC-5",            /* ANSI INCITS 502-2020 */
     "ecma=1, [8h]",
     "ecma=1, [9h]",
     "ecma=1, [Ah]",
@@ -450,61 +450,186 @@
     "reserved [Fh]",
 };
 
-static void
-std_inq_decode(uint8_t * b, int len, int verbose)
+static const char *
+pqual_str(int pqual)
 {
-    int pqual, n;
+    switch (pqual) {
+    case 0:
+        return "LU accessible";
+    case 1:
+        return "LU temporarily unavailable";
+    case 3:
+        return "LU not accessible via this port";
+    default:
+        return "value reserved by T10";
+    }
+}
 
-    if (len < 4)
+static const char *
+hot_pluggable_str(int hp)
+{
+    switch (hp) {
+    case 0:
+        return "No information";
+    case 1:
+        return "target device designed to be removed from SCSI domain";
+    case 2:
+        return "target device not designed to be removed from SCSI domain";
+    default:
+        return "value reserved by T10";
+    }
+}
+
+static const char *
+tpgs_str(int tpgs)
+{
+    switch (tpgs) {
+    case 1:
+        return "only implicit asymmetric logical unit access";
+    case 2:
+        return "only explicit asymmetric logical unit access";
+    case 3:
+        return "both explicit and implicit asymmetric logical unit access";
+    case 0:
+    default:
+        return "not supported";
+    }
+}
+
+static void
+std_inq_decode(uint8_t * b, int len, struct opts_t * op, sgj_opaque_p jop)
+{
+    uint8_t ver;
+    int pqual, pdt, hp, tpgs, j, n;
+    sgj_state * jsp = &op->json_st;
+    sgj_opaque_p jo2p;
+    const char * cp;
+    char c[256];
+    static const int clen = sizeof(c);
+    static const char * np = "Standard INQUIRY data format:";
+
+    if (len < 4) {
+        pr2serr("%s: len [%d] too short\n", __func__, len);
         return;
+    }
     pqual = (b[0] & 0xe0) >> 5;
-    printf("standard INQUIRY:");
+    pdt = b[0] & PDT_MASK;
+    hp = (b[1] >> 4) & 0x3;
+    ver = b[2];
+    sgj_pr_hr(jsp, "%s", np);
     if (0 == pqual)
-        printf("\n");
-    else if (1 == pqual)
-        printf(" [PQ indicates LU temporarily unavailable]\n");
-    else if (3 == pqual)
-        printf(" [PQ indicates LU not accessible via this port]\n");
-    else
-        printf(" [reserved or vendor specific qualifier [%d]]\n", pqual);
-    printf("  PQual=%d  PDT=%d  RMB=%d  LU_CONG=%d  hot_pluggable=%d  "
-           "version=0x%02x ", pqual, b[0] & PDT_MASK, !!(b[1] & 0x80),
-               !!(b[1] & 0x40), (b[1] >> 4) & 0x3, (unsigned int)b[2]);
-    printf(" [%s]\n", sg_ansi_version_arr[b[2] & 0xf]);
-    printf("  [AERC=%d]  [TrmTsk=%d]  NormACA=%d  HiSUP=%d "
+        sgj_pr_hr(jsp, "\n");
+    else {
+        cp = pqual_str(pqual);
+
+        if (pqual < 3)
+            sgj_pr_hr(jsp, " [PQ indicates %s]\n", cp);
+        else
+            sgj_pr_hr(jsp, " [PQ indicates %s [0x%x] ]\n", cp, pqual);
+    }
+    sgj_pr_hr(jsp, "  PQual=%d  PDT=%d  RMB=%d  LU_CONG=%d  hot_pluggable="
+              "%d  version=0x%02x  [%s]\n", pqual, pdt, !!(b[1] & 0x80),
+              !!(b[1] & 0x40), hp, ver, sg_ansi_version_arr[ver & 0xf]);
+    sgj_pr_hr(jsp, "  [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));
+        goto skip1;
+    j = b[4] + 5;
+    if (op->verbose > 2)
+        pr2serr(">> requested %d bytes, %d bytes available\n", len, j);
+    sgj_pr_hr(jsp, "  SCCS=%d  ACC=%d  TPGS=%d  3PC=%d  Protect=%d  "
+              "[BQue=%d]\n", !!(b[5] & 0x80), !!(b[5] & 0x40),
+              ((b[5] & 0x30) >> 4), !!(b[5] & 0x08), !!(b[5] & 0x01),
+              !!(b[6] & 0x80));
+    n = 0;
+    n += sg_scnpr(c + n, clen - n, "EncServ=%d  ", !!(b[6] & 0x40));
     if (b[6] & 0x10)
-        printf("MultiP=1 (VS=%d)  ", !!(b[6] & 0x20));
+        n += sg_scnpr(c + n, clen - n, "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));
+        n += sg_scnpr(c + n, clen - n, "MultiP=0  ");
+    n += sg_scnpr(c + n, clen - n, "[MChngr=%d]  [ACKREQQ=%d]  Addr16=%d",
+                  !!(b[6] & 0x08), !!(b[6] & 0x04), !!(b[6] & 0x01));
+    sgj_pr_hr(jsp, "  %s\n", c);
+    sgj_pr_hr(jsp, "  [RelAdr=%d]  WBus16=%d  Sync=%d  [Linked=%d]  "
+              "[TranDis=%d]  CmdQue=%d\n", !!(b[7] & 0x80), !!(b[7] & 0x20),
+              !!(b[7] & 0x10), !!(b[7] & 0x08), !!(b[7] & 0x04),
+              !!(b[7] & 0x02));
+    if (len < 36)
+        goto skip1;
+    sgj_pr_hr(jsp, "  Vendor_identification: %.8s\n", b + 8);
+    sgj_pr_hr(jsp, "  Product_identification: %.16s\n", b + 16);
+    sgj_pr_hr(jsp, "  Product_revision_level: %.4s\n", b + 32);
+skip1:
+    if (! jsp->pr_as_json || (len < 8))
+        return;
+    jo2p = sgj_new_snake_named_object(jsp, jop, np);
+    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier", pqual, NULL,
+                       pqual_str(pqual));
+    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type", pdt, NULL,
+                       sg_get_pdt_str(pdt, clen, c));
+    sgj_add_nv_ihex_ane(jsp, jo2p, "rmb", !!(b[1] & 0x80), false,
+                        "Removable Medium Bit");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "lu_cong", !!(b[1] & 0x40), false,
+                        "Logical Unit Conglomerate");
+    sgj_add_nv_ihexstr(jsp, jo2p, "hot_pluggable", hp, NULL,
+                       hot_pluggable_str(hp));
+    snprintf(c, clen, "%s", (ver > 0xf) ? "old or reserved version code" :
+                                          sg_ansi_version_arr[ver]);
+    sgj_add_nv_ihexstr(jsp, jo2p, "version", ver, NULL, c);
+    sgj_add_nv_ihex_ane(jsp, jo2p, "aerc", !!(b[3] & 0x80), false,
+                        "Asynchronous Event Reporting Capability (obsolete "
+                        "SPC-3)");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "trmtsk", !!(b[3] & 0x40), false,
+                        "Terminate Task (obsolete SPC-2)");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "normaca", !!(b[3] & 0x20), false,
+                        "Normal ACA (Auto Contingent Allegiance)");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "hisup", !!(b[3] & 0x10), false,
+                        "Hierarchial Support");
+    sgj_add_nv_ihex(jsp, jo2p, "response_data_format", b[3] & 0xf);
+    sgj_add_nv_ihex_ane(jsp, jo2p, "sccs", !!(b[5] & 0x80), false,
+                        "SCC (SCSI Storage Commands) Supported");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "acc", !!(b[5] & 0x40), false,
+                        "Access Commands Coordinator (obsolete SPC-5)");
+    tpgs = (b[5] >> 4) & 0x3;
+    sgj_add_nv_ihexstr_ane(jsp, jo2p, "tpgs", tpgs, false, NULL,
+                           tpgs_str(tpgs), "Target Port Group Support");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "3pc", !!(b[5] & 0x8), false,
+                        "Third Party Copy");
+    sgj_add_nv_ihex(jsp, jo2p, "protect", !!(b[5] & 0x1));
+    /* Skip SPI specific flags which have been obsolete for a while) */
+    sgj_add_nv_ihex_ane(jsp, jo2p, "bque", !!(b[6] & 0x80), false,
+                        "Basic task management model (obsolete SPC-4)");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "encserv", !!(b[6] & 0x40), false,
+                        "Enclousure Services supported");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "multip", !!(b[6] & 0x10), false,
+                        "Multiple SCSI port");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "mchngr", !!(b[6] & 0x8), false,
+                        "Medium changer (obsolete SPC-4)");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "reladr", !!(b[7] & 0x80), false,
+                        "Relative Addressing (obsolete in SPC-4)");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "linked", !!(b[7] & 0x8), false,
+                        "Linked Commands (obsolete in SPC-4)");
+    sgj_add_nv_ihex_ane(jsp, jo2p, "cmdque", !!(b[7] & 0x2), false,
+                        "Command Management Model (command queuing)");
+    if (len < 16)
+        return;
+    snprintf(c, clen, "%.8s", b + 8);
+    sgj_add_nv_s(jsp, jo2p, "t10_vendor_identification", c);
+    if (len < 32)
+        return;
+    snprintf(c, clen, "%.16s", b + 16);
+    sgj_add_nv_s(jsp, jo2p, "product_identification", c);
     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);
+    snprintf(c, clen, "%.4s", b + 32);
+    sgj_add_nv_s(jsp, jo2p, "product_revision_level", c);
 }
 
 static void
-decode_id_vpd_variants(uint8_t * buff, int len, int subvalue,
-                       struct opts_t * op, sgj_opaque_p jop)
+device_id_vpd_variants(uint8_t * buff, int len, int subvalue,
+                       struct opts_t * op, sgj_opaque_p jap)
 {
     int m_a, m_d, m_cs, blen;
     uint8_t * b;
@@ -519,24 +644,24 @@
     m_d = -1;
     m_cs = -1;
     if (0 == subvalue) {
-        decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
-                       VPD_ASSOC_LU, m_d, m_cs, op, jop);
-        decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b, blen,
-                       VPD_ASSOC_TPORT, m_d, m_cs, op, jop);
-        decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b, blen,
-                       VPD_ASSOC_TDEVICE, m_d, m_cs, op, jop);
+        filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
+                       VPD_ASSOC_LU, m_d, m_cs, op, jap);
+        filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b, blen,
+                       VPD_ASSOC_TPORT, m_d, m_cs, op, jap);
+        filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b, blen,
+                       VPD_ASSOC_TDEVICE, m_d, m_cs, op, jap);
     } else if (VPD_DI_SEL_AS_IS == subvalue)
-        decode_dev_ids(NULL, 0, b, blen, m_a, m_d, m_cs, op, jop);
+        filter_dev_ids(NULL, 0, b, blen, m_a, m_d, m_cs, op, jap);
     else {
         if (VPD_DI_SEL_LU & subvalue)
-            decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
-                           VPD_ASSOC_LU, m_d, m_cs, op, jop);
+            filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
+                           VPD_ASSOC_LU, m_d, m_cs, op, jap);
         if (VPD_DI_SEL_TPORT & subvalue)
-            decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b,
-                           blen, VPD_ASSOC_TPORT, m_d, m_cs, op, jop);
+            filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b,
+                           blen, VPD_ASSOC_TPORT, m_d, m_cs, op, jap);
         if (VPD_DI_SEL_TARGET & subvalue)
-            decode_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0,
-                           b, blen, VPD_ASSOC_TDEVICE, m_d, m_cs, op, jop);
+            filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0,
+                           b, blen, VPD_ASSOC_TDEVICE, m_d, m_cs, op, jap);
     }
 }
 
@@ -561,13 +686,18 @@
 
 /* VPD_MAN_NET_ADDR */
 static void
-decode_net_man_vpd(uint8_t * buff, int len, int do_hex)
+decode_net_man_vpd(uint8_t * buff, int len, struct opts_t * op,
+                   sgj_opaque_p jap)
 {
-    int k, bump, na_len;
+    int k, bump, na_len, assoc, nst;
+    sgj_state * jsp = &op->json_st;
+    sgj_opaque_p jo2p;
     uint8_t * bp;
+    const char * assoc_str;
+    const char * nst_str;
 
-    if ((1 == do_hex) || (do_hex > 2)) {
-        hex2stdout(buff, len, (1 == do_hex) ? 0 : -1);
+    if ((1 == op->do_hex) || (op->do_hex > 2)) {
+        hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
         return;
     }
     if (len < 4) {
@@ -578,23 +708,35 @@
     len -= 4;
     bp = buff + 4;
     for (k = 0; k < len; k += bump, bp += bump) {
-        printf("  %s, Service type: %s\n",
-               sg_get_desig_assoc_str((bp[0] >> 5) & 0x3),
-               network_service_type_arr[bp[0] & 0x1f]);
+        assoc = (bp[0] >> 5) & 0x3;
+        assoc_str = sg_get_desig_assoc_str(assoc);
+        nst = bp[0] & 0x1f;
+        nst_str = network_service_type_arr[nst];
+        sgj_pr_hr(jsp, "  %s, Service type: %s\n", assoc_str, nst_str);
         na_len = sg_get_unaligned_be16(bp + 2);
+        if (jsp->pr_as_json) {
+            jo2p = sgj_new_unattached_object(jsp);
+            sgj_add_nv_ihexstr(jsp, jo2p, "association", assoc, NULL,
+                               assoc_str);
+            sgj_add_nv_ihexstr(jsp, jo2p, "service_type", nst, NULL,
+                               nst_str);
+            sgj_add_nv_s_len(jsp, jo2p, "network_address",
+			     (const char *)(bp + 4), na_len);
+            sgj_add_nv_o(jsp, jap, NULL /* name */, jo2p);
+        }
+        if (na_len > 0) {
+            if (op->do_hex > 1) {
+                printf("    Network address:\n");
+                hex2stdout((bp + 4), na_len, 0);
+            } else
+                sgj_pr_hr(jsp, "    %s\n", bp + 4);
+        }
         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");
-                hex2stdout((bp + 4), na_len, 0);
-            } else
-                printf("    %s\n", bp + 4);
-        }
     }
 }
 
@@ -650,9 +792,12 @@
 /* VPD_SCSI_PORTS */
 static void
 decode_scsi_ports_vpd(uint8_t * buff, int len, struct opts_t * op,
-                      sgj_opaque_p jop)
+                      sgj_opaque_p jap)
 {
     int k, bump, rel_port, ip_tid_len, tpd_len;
+    sgj_state * jsp = &op->json_st;
+    sgj_opaque_p jo2p = NULL;
+    sgj_opaque_p ja2p = NULL;
     uint8_t * bp;
 
     if ((1 == op->do_hex) || (op->do_hex > 2)) {
@@ -667,7 +812,9 @@
     bp = buff + 4;
     for (k = 0; k < len; k += bump, bp += bump) {
         rel_port = sg_get_unaligned_be16(bp + 2);
-        printf("  Relative port=%d\n", rel_port);
+        sgj_pr_hr(jsp, "  Relative port=%d\n", rel_port);
+        jo2p = sgj_new_unattached_object(jsp);
+        sgj_add_nv_i(jsp, jo2p, "relative_port", rel_port);
         ip_tid_len = sg_get_unaligned_be16(bp + 6);
         bump = 8 + ip_tid_len;
         if ((k + bump) > len) {
@@ -677,13 +824,14 @@
         }
         if (ip_tid_len > 0) {
             if (op->do_hex > 1) {
-                printf("    Initiator port transport id:\n");
+                sgj_pr_hr(jsp, "    Initiator port transport id:\n");
                 hex2stdout((bp + 8), ip_tid_len, 1);
             } else {
                 char b[1024];
 
-                printf("%s", sg_decode_transportid_str("    ", bp + 8,
-                                         ip_tid_len, true, sizeof(b), b));
+                sgj_pr_hr(jsp, "%s",
+                          sg_decode_transportid_str("    ", bp + 8,
+                                            ip_tid_len, true, sizeof(b), b));
             }
         }
         tpd_len = sg_get_unaligned_be16(bp + bump + 2);
@@ -694,23 +842,31 @@
         }
         if (tpd_len > 0) {
             if (op->do_hex > 1) {
-                printf("    Target port descriptor(s):\n");
+                sgj_pr_hr(jsp, "    Target port descriptor(s):\n");
                 hex2stdout(bp + bump + 4, tpd_len, 1);
             } else {
                 if ((0 == op->do_quiet) || (ip_tid_len > 0))
-                    printf("    Target port descriptor(s):\n");
-                decode_dev_ids("", 2 /* leading spaces */, bp + bump + 4,
-                               tpd_len, VPD_ASSOC_TPORT, -1, -1, op, jop);
+                    sgj_pr_hr(jsp, "    Target port descriptor(s):\n");
+                if (jsp->pr_as_json) {
+                    sgj_opaque_p jo3p = sgj_new_named_object(jsp, jo2p,
+                                                             "target_port");
+
+                    ja2p = sgj_new_named_array(jsp, jo3p,
+                                               "designation_descriptor_list");
+                }
+                filter_dev_ids("", 2 /* leading spaces */, bp + bump + 4,
+                               tpd_len, VPD_ASSOC_TPORT, -1, -1, op, ja2p);
             }
         }
         bump += tpd_len + 4;
+        sgj_add_nv_o(jsp, jap, NULL, jo2p);
     }
 }
 
 /* Prints outs an abridged set of device identification designators
    selected by association, designator type and/or code set. */
 static int
-decode_dev_ids_quiet(uint8_t * buff, int len, int m_assoc,
+filter_dev_ids_quiet(uint8_t * buff, int len, int m_assoc,
                      int m_desig_type, int m_code_set)
 {
     int k, m, p_id, c_set, piv, desig_type, i_len, naa, off, u;
@@ -904,14 +1060,14 @@
     return 0;
 }
 
-/* Prints outs designation descriptors (dd_s)selected by association,
+/* Prints outs designation descriptors (dd_s) selected by association,
    designator type and/or code set. */
 static int
-decode_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff,
+filter_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff,
                int len, int m_assoc, int m_desig_type, int m_code_set,
-               struct opts_t * op, sgj_opaque_p jop)
+               struct opts_t * op, sgj_opaque_p jap)
 {
-    bool printed, sgj_output;
+    bool printed, sgj_out_hr;
     int assoc, off, u, i_len;
     const uint8_t * bp;
     sgj_state * jsp = &op->json_st;
@@ -920,7 +1076,7 @@
     static const int blen = sizeof(b);
 
     if (op->do_quiet && (! jsp->pr_as_json))
-        return decode_dev_ids_quiet(buff, len, m_assoc, m_desig_type,
+        return filter_dev_ids_quiet(buff, len, m_assoc, m_desig_type,
                                     m_code_set);
     if (num_leading > (int)(sizeof(sp) - 2))
         num_leading = sizeof(sp) - 2;
@@ -946,14 +1102,15 @@
                     "     remaining response length=%d\n", (len - off));
             return SG_LIB_CAT_MALFORMED;
         }
-        sgj_output = false;
+        sgj_out_hr = false;
         if (jsp->pr_as_json) {
-            sgj_opaque_p jo2p =
-                sgj_new_named_object(jsp, jop, "designation_descriptor");
+            sgj_opaque_p jo2p;
 
+            jo2p = sgj_new_unattached_object(jsp);
             sgj_get_designation_descriptor(jsp, jo2p, bp, i_len + 4);
-            if (jsp->pr_output)
-                sgj_output = true;
+            sgj_add_nv_o(jsp, jap, NULL /* name */, jo2p);
+            if (jsp->pr_out_hr)
+                sgj_out_hr = true;
             else
                 continue;
         }
@@ -962,23 +1119,23 @@
             printed = true;
             if (strlen(print_if_found) > 0) {
                 snprintf(b, blen, "  %s:", print_if_found);
-                if (sgj_output)
-                    sgj_pr_str_output(jsp, b, strlen(b));
+                if (sgj_out_hr)
+                    sgj_pr_str_out_hr(jsp, b, strlen(b));
                 else
                     printf("%s\n", b);
             }
         }
         if (NULL == print_if_found) {
             snprintf(b, blen, "  %s%s:", sp, sg_get_desig_assoc_str(assoc));
-            if (sgj_output)
-                sgj_pr_str_output(jsp, b, strlen(b));
+            if (sgj_out_hr)
+                sgj_pr_str_out_hr(jsp, b, strlen(b));
             else
                 printf("%s\n", b);
         }
         sg_get_designation_descriptor_str(sp, bp, i_len + 4, false,
                                           op->do_long, blen, b);
-        if (sgj_output)
-            sgj_pr_str_output(jsp, b, strlen(b));
+        if (sgj_out_hr)
+            sgj_pr_str_out_hr(jsp, b, strlen(b));
         else
             printf("%s", b);
     }
@@ -1132,17 +1289,28 @@
 
 /* VPD_SOFTW_INF_ID */
 static void
-decode_softw_inf_id(uint8_t * buff, int len, int do_hex)
+decode_softw_inf_id(uint8_t * buff, int len, struct opts_t * op,
+                    sgj_opaque_p jap)
 {
-    if (do_hex) {
-        hex2stdout(buff, len, (1 == do_hex) ? 0 : -1);
+    sgj_state * jsp = &op->json_st;
+    sgj_opaque_p jop;
+    uint64_t ieee_id;
+
+    if (op->do_hex) {
+        hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
         return;
     }
     len -= 4;
     buff += 4;
-    for ( ; len > 5; len -= 6, buff += 6)
-        printf("    IEEE identifier: 0x%" PRIx64 "\n",
-               sg_get_unaligned_be48(buff + 0));
+    for ( ; len > 5; len -= 6, buff += 6) {
+        ieee_id = sg_get_unaligned_be48(buff + 0);
+        sgj_pr_hr(jsp, "    IEEE identifier: 0x%" PRIx64 "\n", ieee_id);
+        if (jsp->pr_as_json) {
+            jop = sgj_new_unattached_object(jsp);
+            sgj_add_nv_ihex(jsp, jop, "ieee_identifier", ieee_id);
+            sgj_add_nv_o(jsp, jap, NULL /* name */, jop);
+        }
+   }
 }
 
 /* VPD_ATA_INFO */
@@ -1263,8 +1431,8 @@
     int k, j, res, bump, csd_len;
     uint16_t constit_type;
     const uint8_t * bp;
-    const char * dcp = "Device constituents VPD page";
     char b[64];
+    static const char * dcp = "Device constituents VPD page";
 
     if ((1 == op->do_hex) || (op->do_hex > 2)) {
         hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
@@ -1365,7 +1533,7 @@
     int k, bump;
     uint8_t * bp;
     unsigned int value;
-    const char * pcp = "Power consumption VPD page";
+    static const char * pcp = "Power consumption VPD page";
 
     if ((1 == do_hex) || (do_hex > 2)) {
         hex2stdout(buff, len, (1 == do_hex) ? 1 : -1);
@@ -2817,15 +2985,22 @@
     bool allow_name, allow_if_found, long_notquiet, qt;
     bool vpd_supported = false;
     bool inhex_active = (-1 == sg_fd);
-    int len, pdt, num, k, resid, alloc_len, pn, vb;
+    int len, pdt, pqual, num, k, resid, alloc_len, pn, vb;
     int res = 0;
     const struct svpd_values_name_t * vnp;
     sgj_state * jsp = &op->json_st;
     uint8_t * rp;
+    sgj_opaque_p jap = NULL;
+    sgj_opaque_p jo2p = NULL;
     const char * np;
     const char * pre = (prefix ? prefix : "");;
+    const char * pdt_str;
+    bool as_json = jsp->pr_as_json;
+    bool not_json = ! as_json;
     char obuff[DEF_ALLOC_LEN];
-    char b[48];
+    char b[120];
+    char d[48];
+    static const int blen = sizeof(b);
 
     vb = op->verbose;
     qt = op->do_quiet;
@@ -2868,6 +3043,10 @@
             return sg_convert_errno(EDOM);
         }
     }
+    pdt = rp[0] & PDT_MASK;
+    pdt_str = sg_get_pdt_str(pdt, sizeof(d), d);
+    pqual = (rp[0] & 0xe0) >> 5;
+
     switch(pn) {
     case VPD_NOPE_WANT_STD_INQ:    /* -2 (want standard inquiry response) */
         if (!inhex_active) {
@@ -2890,55 +3069,79 @@
                 dStrRaw(rp, alloc_len);
             else if (op->do_hex) {
                 if (! op->do_quiet && (op->do_hex < 3))
-                    printf("Standard Inquiry response:\n");
+                    sgj_pr_hr(jsp, "Standard Inquiry data format:\n");
                 hex2stdout(rp, alloc_len, (1 == op->do_hex) ? 0 : -1);
             } else
-                std_inq_decode(rp, alloc_len, vb);
+                std_inq_decode(rp, alloc_len, op, jop);
             return 0;
         }
         break;
     case VPD_SUPPORTED_VPDS:    /* 0x0 */
         np = "Supported VPD pages VPD page:";
         if (allow_name)
-            printf("%s%s\n", pre, np);
+            sgj_pr_hr(jsp, "%s%s\n", pre, np);
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
             if (! allow_name && allow_if_found)
-                printf("%s%s\n", pre, np);
+                sgj_pr_hr(jsp, "%s%s\n", pre, np);
             if (op->do_raw)
                 dStrRaw(rp, len);
             else if (op->do_hex)
                 hex2stdout(rp, len, (1 == op->do_hex) ? 0 : -1);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
-                    printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                    sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
+                              "%s]\n", pqual, pdt_str);
                 num = rp[3];
                 if (num > (len - 4))
                     num = (len - 4);
+                if (as_json) {
+                    jo2p = sgj_new_snake_named_object(jsp, jop, np);
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier",
+                                       pqual, NULL, pqual_str(pqual));
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
+                                       pdt, NULL, pdt_str);
+                    sgj_add_nv_ihex(jsp, jo2p, "page_code", pn);
+                    jap = sgj_new_named_array(jsp, jo2p,
+                                              "supported_vpd_page_list");
+                }
                 for (k = 0; k < num; ++k) {
                     pn = rp[4 + k];
+                    snprintf(b, blen, "0x%02x", pn);
                     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);
+                            sgj_pr_hr(jsp, "  %s  %s [%s]\n", b,
+                                      vnp->name, vnp->acron);
                         else
-                            printf("  %s [%s]\n", vnp->name, vnp->acron);
+                            sgj_pr_hr(jsp, "  %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);
+                                sgj_pr_hr(jsp, "  %s  %s [%s]\n", b,
+                                          vnp->name, vnp->acron);
                             else
-                                printf("  %s [%s]\n", vnp->name, vnp->acron);
+                                sgj_pr_hr(jsp, "  %s [%s]\n", vnp->name,
+                                          vnp->acron);
                         } else
-                            printf("  0x%x\n", pn);
+                            sgj_pr_hr(jsp, "  %s\n", b);
                     } else
-                        printf("  0x%x\n", pn);
+                        sgj_pr_hr(jsp, "  %s\n", b);
+                    if (as_json) {
+                        jo2p = sgj_new_unattached_object(jsp);
+                        sgj_add_nv_i(jsp, jo2p, "i", pn);
+                        sgj_add_nv_s(jsp, jo2p, "hex", b + 2);
+                        if (vnp) {
+                            sgj_add_nv_s(jsp, jo2p, "name", vnp->name);
+                            sgj_add_nv_s(jsp, jo2p, "acronym", vnp->acron);
+                        } else {
+                            sgj_add_nv_s(jsp, jo2p, "name", "unknown");
+                            sgj_add_nv_s(jsp, jo2p, "acronym", "unknown");
+                        }
+                        sgj_add_nv_o(jsp, jap, NULL /* name */, jo2p);
+                    }
                 }
             }
             return 0;
@@ -2946,28 +3149,33 @@
         break;
     case VPD_UNIT_SERIAL_NUM:   /* 0x80 */
         np = "Unit serial number VPD page:";
-        if (allow_name)
-            printf("%s%s\n", pre, np);
+        if (allow_name && not_json)
+            sgj_pr_hr(jsp, "%s%s\n", pre, np);
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
             if (! allow_name && allow_if_found)
-                printf("%s%s\n", pre, np);
+                sgj_pr_hr(jsp, "%s%s\n", pre, np);
             if (op->do_raw)
                 dStrRaw(rp, len);
             else if (op->do_hex)
                 hex2stdout(rp, len, (1 == op->do_hex) ? 0 : -1);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
-                    printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                    sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
+                              "%s]\n", pqual, pdt_str);
                 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);
+                jo2p = sgj_new_snake_named_object(jsp, jop, np);
+                sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier",
+                                   pqual, NULL, pqual_str(pqual));
+                sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
+                                   pdt, NULL, pdt_str);
+                sgj_add_nv_ihex(jsp, jo2p, "page_code", pn);
+                sgj_pr_hr_js_vs(jsp, jo2p, 2, "unit_serial_number",
+                                SGJ_SEP_COLON_1_SPACE, obuff);
             }
             return 0;
         }
@@ -2985,12 +3193,20 @@
             else if (op->do_hex)
                 hex2stdout(rp, len, (1 == op->do_hex) ? 0 : -1);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
-                              "%s]\n", (rp[0] & 0xe0) >> 5,
-                              sg_get_pdt_str(pdt, sizeof(b), b));
-                decode_id_vpd_variants(rp, len, subvalue, op, jop);
+                              "%s]\n", pqual, pdt_str);
+                if (as_json) {
+                    jo2p = sgj_new_snake_named_object(jsp, jop, np);
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier",
+                                       pqual, NULL, pqual_str(pqual));
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
+                                       pdt, NULL, pdt_str);
+                    sgj_add_nv_ihex(jsp, jo2p, "page_code", pn);
+                    jap = sgj_new_named_array(jsp, jo2p,
+                                              "designation_descriptor_list");
+                }
+                device_id_vpd_variants(rp, len, subvalue, op, jap);
             }
             return 0;
         }
@@ -2998,20 +3214,28 @@
     case VPD_SOFTW_INF_ID:      /* 0x84 */
         np = "Software interface identification VPD page:";
         if (allow_name)
-            printf("%s%s\n", pre, np);
+            sgj_pr_hr(jsp, "%s%s\n", pre, np);
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
             if (! allow_name && allow_if_found)
-                printf("%s%s\n", pre, np);
+                sgj_pr_hr(jsp, "%s%s\n", pre, np);
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 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);
+                    sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
+                              "%s]\n", pqual, pdt_str);
+                if (as_json) {
+                    jo2p = sgj_new_snake_named_object(jsp, jop, np);
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier",
+                                       pqual, NULL, pqual_str(pqual));
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
+                                       pdt, NULL, pdt_str);
+                    sgj_add_nv_ihex(jsp, jo2p, "page_code", pn);
+                    jap = sgj_new_named_array(jsp, jo2p,
+                                      "software_interface_identifier_list");
+                }
+                decode_softw_inf_id(rp, len, op, jap);
             }
             return 0;
         }
@@ -3019,15 +3243,26 @@
     case VPD_MAN_NET_ADDR:      /* 0x85 */
         np= "Management network addresses VPD page:";
         if (allow_name)
-            printf("%s%s\n", pre, np);
+            sgj_pr_hr(jsp, "%s%s\n", pre, np);
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
             if (! allow_name && allow_if_found)
-                printf("%s%s\n", pre, np);
+                sgj_pr_hr(jsp, "%s%s\n", pre, np);
             if (op->do_raw)
                 dStrRaw(rp, len);
-            else
-                decode_net_man_vpd(rp, len, op->do_hex);
+            else {
+                if (as_json) {
+                    jo2p = sgj_new_snake_named_object(jsp, jop, np);
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier",
+                                       pqual, NULL, pqual_str(pqual));
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
+                                       pdt, NULL, pdt_str);
+                    sgj_add_nv_ihex(jsp, jo2p, "page_code", pn);
+                    jap = sgj_new_named_array(jsp, jo2p,
+                                      "network_services_descriptor_list");
+                }
+                decode_net_man_vpd(rp, len, op, jap);
+            }
             return 0;
         }
         break;
@@ -3054,11 +3289,9 @@
                     } else
                         protect = !!(sir.byte_5 & 0x1); /* SPC-3 and later */
                 }
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_x_inq_vpd(rp, len, op->do_hex, long_notquiet, protect);
             }
             return 0;
@@ -3075,11 +3308,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_mode_policy_vpd(rp, len, op->do_hex);
             }
             return 0;
@@ -3088,20 +3319,28 @@
     case VPD_SCSI_PORTS:        /* 0x88 */
         np = "SCSI Ports VPD page:";
         if (allow_name)
-            printf("%s%s\n", pre, np);
+            sgj_pr_hr(jsp, "%s%s\n", pre, np);
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
             if (! allow_name && allow_if_found)
-                printf("%s%s\n", pre, np);
+                sgj_pr_hr(jsp, "%s%s\n", pre, np);
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 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, jop);
+                    sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
+                              "%s]\n", pqual, pdt_str);
+                if (as_json) {
+                    jo2p = sgj_new_snake_named_object(jsp, jop, np);
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier",
+                                       pqual, NULL, pqual_str(pqual));
+                    sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
+                                       pdt, NULL, pdt_str);
+                    sgj_add_nv_ihex(jsp, jo2p, "page_code", pn);
+                    jap = sgj_new_named_array(jsp, jo2p,
+                                              "scsi_port_descriptor_list");
+                }
+                decode_scsi_ports_vpd(rp, len, op, jap);
             }
             return 0;
         }
@@ -3126,11 +3365,9 @@
             else if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_ata_info_vpd(rp, len, long_notquiet, op->do_hex);
             }
             return 0;
@@ -3147,11 +3384,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_power_condition(rp, len, op->do_hex);
             }
             return 0;
@@ -3183,11 +3418,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_power_consumption_vpd(rp, len, op->do_hex);
             }
             return 0;
@@ -3206,11 +3439,9 @@
             else if (1 == op->do_hex)
                 hex2stdout(rp, len, 0);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_3party_copy_vpd(rp, len, op->do_hex, pdt, vb);
             }
             return 0;
@@ -3227,11 +3458,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rsp_buff[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_proto_lu_vpd(rp, len, op->do_hex);
             }
             return 0;
@@ -3248,11 +3477,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_proto_port_vpd(rp, len, op->do_hex);
             }
             return 0;
@@ -3269,11 +3496,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_feature_sets_vpd(rp, len, op);
             }
             return 0;
@@ -3282,7 +3507,6 @@
     case 0xb0:  /* depends on pdt */
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Block limits VPD page (SBC):";
@@ -3304,11 +3528,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_b0_vpd(rp, len, op->do_hex, pdt);
             }
             return 0;
@@ -3319,7 +3541,6 @@
     case 0xb1:  /* depends on pdt */
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Block device characteristics VPD page (SBC):";
@@ -3346,8 +3567,7 @@
             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));
+                           pqual, pdt_str);
                 decode_b1_vpd(rp, len, op->do_hex, pdt);
             }
             return 0;
@@ -3358,7 +3578,6 @@
     case 0xb2:          /* VPD page depends on pdt */
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Logical block provisioning VPD page (SBC):";
@@ -3379,8 +3598,7 @@
             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));
+                           pqual, pdt_str);
                 decode_b2_vpd(rp, len, pdt, op);
             }
             return 0;
@@ -3412,8 +3630,7 @@
             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));
+                           pqual, pdt_str);
                 decode_b3_vpd(rp, len, op->do_hex, pdt);
             }
             return 0;
@@ -3424,7 +3641,6 @@
     case 0xb4:          /* VPD page depends on pdt */
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Supported block lengths and protection types VPD page "
@@ -3446,8 +3662,7 @@
             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));
+                           pqual, pdt_str);
                 decode_b4_vpd(rp, len, op->do_hex, pdt);
             }
             return 0;
@@ -3458,7 +3673,6 @@
     case 0xb5:          /* VPD page depends on pdt */
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Block device characteristics extension VPD page (SBC):";
@@ -3479,8 +3693,7 @@
             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));
+                           pqual, pdt_str);
                 decode_b5_vpd(rp, len, op->do_hex, pdt);
             }
             return 0;
@@ -3491,7 +3704,6 @@
     case VPD_ZBC_DEV_CHARS:       /* 0xb6 for both pdt=0 and pdt=0x14 */
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Zoned block device characteristics VPD page (SBC, "
@@ -3510,8 +3722,7 @@
             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));
+                           pqual, pdt_str);
                 decode_zbdch_vpd(rp, len, op->do_hex);
             }
             return 0;
@@ -3522,7 +3733,6 @@
     case 0xb7:
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Block limits extension VPD page (SBC):";
@@ -3538,11 +3748,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_b7_vpd(rp, len, op->do_hex, pdt);
             }
             return 0;
@@ -3553,7 +3761,6 @@
     case 0xb8:          /* VPD_FORMAT_PRESETS */
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Format presets VPD page (SBC):";
@@ -3569,11 +3776,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_format_presets_vpd(rp, len, op->do_hex);
             }
             return 0;
@@ -3584,7 +3789,6 @@
     case 0xb9:          /* VPD_CON_POS_RANGE */
         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
         if (0 == res) {
-            pdt = rp[0] & PDT_MASK;
             switch (pdt) {
             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
                 np = "Concurrent positioning ranges VPD page (SBC):";
@@ -3600,11 +3804,9 @@
             if (op->do_raw)
                 dStrRaw(rp, len);
             else {
-                pdt = rp[0] & PDT_MASK;
                 if (vb || long_notquiet)
                     printf("   [PQual=%d  Peripheral device type: %s]\n",
-                           (rp[0] & 0xe0) >> 5,
-                           sg_get_pdt_str(pdt, sizeof(b), b));
+                           pqual, pdt_str);
                 decode_con_pos_range_vpd(rp, len, op->do_hex);
             }
             return 0;
@@ -3689,6 +3891,9 @@
         int prev_pn = -1;
 
         res = 0;
+        if (op->page_given && (VPD_NOPE_WANT_STD_INQ == op->vpd_pn))
+            return svpd_decode_t10(-1, op, jop, 0, 0, NULL);
+
         for (k = 0, off = 0; off < in_len; ++k, off += bump) {
             rp = rsp_buff + off;
             pn = rp[1];
@@ -3698,6 +3903,8 @@
                         pn, bump);
                 bump = in_len - off;
             }
+            if (op->page_given && (pn != op->vpd_pn))
+                continue;
             if (pn <= prev_pn) {
                 pr2serr("%s: prev_pn=0x%x, this pn=0x%x, not ascending so "
                         "exit\n", __func__, prev_pn, pn);
@@ -3875,6 +4082,7 @@
                 return SG_LIB_SYNTAX_ERROR;
             } else
                 op->page_str = optarg;
+            op->page_given = true;
             break;
         case 'q':
             op->do_quiet = true;
@@ -3987,19 +4195,22 @@
         jop = sgj_start(MY_NAME, version_str, argc, argv, jsp);
 
     if (op->page_str) {
-        if ((0 == strcmp("-1", op->page_str)) ||
-            (0 == strcmp("-2", op->page_str)))
+        if ('-' == op->page_str[0])
             op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
         else if (isalpha((uint8_t)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);
-                    ret = SG_LIB_SYNTAX_ERROR;
-                    goto fini;
+                    if (0 == strcmp("stdinq", op->page_str)) {
+                        vnp = sdp_find_vpd_by_acron("sinq");
+                    } else {
+                        pr2serr("abbreviation doesn't match a VPD page\n");
+                        printf("Available standard VPD pages:\n");
+                        enumerate_vpds(1, 1);
+                        ret = SG_LIB_SYNTAX_ERROR;
+                        goto fini;
+                    }
                 }
             }
             op->vpd_pn = vnp->value;
@@ -4146,7 +4357,7 @@
     if (op->inhex_fn) {
         if ((0 == op->maxlen) || (inhex_len < op->maxlen))
             op->maxlen = inhex_len;
-        if (op->do_all)
+        if (op->do_all || op->page_given)
             res = svpd_decode_all(-1, op, jop);
         else {
             res = svpd_decode_t10(-1, op, jop, subvalue, 0, NULL);
diff --git a/src/sg_vpd.h b/src/sg_vpd.h
index 6720571..f01ef12 100644
--- a/src/sg_vpd.h
+++ b/src/sg_vpd.h
@@ -32,6 +32,7 @@
     bool do_force;
     bool do_long;
     bool do_quiet;
+    bool page_given;
     bool verbose_given;
     bool version_given;
     int do_hex;
diff --git a/testing/tst_sg_lib.c b/testing/tst_sg_lib.c
index 51c109e..2f44d2f 100644
--- a/testing/tst_sg_lib.c
+++ b/testing/tst_sg_lib.c
@@ -367,10 +367,10 @@
         }
 #if 1
         if (as_json) {
-            sgj_pr_str_output(jsp, "sg_get_sense_str(ds_data1)", 999);
+            sgj_pr_str_out_hr(jsp, "sg_get_sense_str(ds_data1)", 999);
             sg_get_sense_str(leadin, desc_sense_data1,
                              sizeof(desc_sense_data1), vb, b_len, b);
-            sgj_pr_str_output(jsp, b, strlen(b));
+            sgj_pr_str_out_hr(jsp, b, strlen(b));
 
         } else {
             printf("sg_get_sense_str(ds_data1):\n");