aboutsummaryrefslogtreecommitdiff
path: root/lib/sg_pr2serr.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sg_pr2serr.c')
-rw-r--r--lib/sg_pr2serr.c2026
1 files changed, 2026 insertions, 0 deletions
diff --git a/lib/sg_pr2serr.c b/lib/sg_pr2serr.c
new file mode 100644
index 00000000..ef533967
--- /dev/null
+++ b/lib/sg_pr2serr.c
@@ -0,0 +1,2026 @@
+/*
+ * Copyright (c) 2022 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.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "sg_pr2serr.h"
+#include "sg_json_builder.h"
+
+/*
+ * Some users of sg_pr2serr may not need fixed and descriptor sense decoded
+ * for JSON output. If the following define is commented out the effective
+ * compile size of this file is reduced by 800 lines plus dependencies on
+ * other large components of the sg3_utils library.
+ * Comment out the next line to remove dependency on sg_lib.h and its code.
+ */
+#define SG_PRSE_SENSE_DECODE 1
+
+#ifdef SG_PRSE_SENSE_DECODE
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_unaligned.h"
+#endif
+
+
+#define sgj_opts_ev "SG3_UTILS_JSON_OPTS"
+
+/*
+ * #define json_serialize_mode_multiline 0
+ * #define json_serialize_mode_single_line 1
+ * #define json_serialize_mode_packed 2
+ *
+ * #define json_serialize_opt_CRLF (1 << 1)
+ * #define json_serialize_opt_pack_brackets (1 << 2)
+ * #define json_serialize_opt_no_space_after_comma (1 << 3)
+ * #define json_serialize_opt_no_space_after_colon (1 << 4)
+ * #define json_serialize_opt_use_tabs (1 << 5)
+ */
+
+
+static const json_serialize_opts def_out_settings = {
+ json_serialize_mode_multiline, /* one of serialize_mode_* */
+ 0, /* serialize_opt_* OR-ed together */
+ 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
+pr2serr(const char * fmt, ...)
+{
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = vfprintf(stderr, fmt, args);
+ va_end(args);
+ return n;
+}
+
+#ifndef SG_PRSE_SENSE_DECODE
+
+/* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of
+ * functions. Returns 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). Linux kernel has a similar function called scnprintf(). Public
+ * declaration in sg_pr2serr.h header */
+int
+sg_scnpr(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
+pr2ws(const char * fmt, ...)
+{
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = vfprintf(stderr, fmt, args);
+ va_end(args);
+ return n;
+}
+
+#endif
+
+static bool
+sgj_parse_opts(sgj_state * jsp, const char * j_optarg)
+{
+ bool bad_arg = false;
+ bool prev_negate = false;
+ bool negate;
+ int k, c;
+
+ for (k = 0; j_optarg[k]; ++k) { /* step over leading whitespace */
+ if (! isspace(j_optarg[k]))
+ break;
+ }
+ for ( ; j_optarg[k]; ++k) {
+ c = j_optarg[k];
+ negate = false;
+ switch (c) {
+ case '=':
+ if (0 == k)
+ break; /* allow and ignore leading '=' */
+ bad_arg = true;
+ if (0 == jsp->first_bad_char)
+ jsp->first_bad_char = c;
+ break;
+ case '!':
+ case '~':
+ case '-': /* '-' is probably most practical negation symbol */
+ negate = true;
+ break;
+ case '0':
+ case '2':
+ jsp->pr_indent_size = 2;
+ break;
+ case '3':
+ jsp->pr_indent_size = 3;
+ break;
+ case '4':
+ jsp->pr_indent_size = 4;
+ break;
+ case '8':
+ jsp->pr_indent_size = 8;
+ break;
+ case 'e':
+ jsp->pr_exit_status = ! prev_negate;
+ break;
+ case 'g':
+ jsp->pr_format = 'g';
+ break;
+ 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 'n':
+ jsp->pr_name_ex = ! prev_negate;
+ break;
+ case 'o':
+ jsp->pr_out_hr = ! prev_negate;
+ break;
+ case 'p':
+ jsp->pr_pretty = ! prev_negate;
+ break;
+ case 's':
+ jsp->pr_string = ! prev_negate;
+ break;
+ case 'v':
+ ++jsp->verbose;
+ break;
+ case 'y':
+ jsp->pr_format = 'g';
+ break;
+ case '?':
+ bad_arg = true;
+ jsp->first_bad_char = '\0';
+ break;
+ default:
+ bad_arg = true;
+ if (0 == jsp->first_bad_char)
+ jsp->first_bad_char = c;
+ break;
+ }
+ prev_negate = negate ? ! prev_negate : false;
+ }
+ return ! bad_arg;
+}
+
+char *
+sg_json_usage(int char_if_not_j, char * b, int blen)
+{
+ int n = 0;
+ char short_opt = char_if_not_j ? char_if_not_j : 'j';
+
+ if ((NULL == b) || (blen < 1))
+ goto fini;
+ n += sg_scnpr(b + n, blen - n, "JSON option usage:\n");
+ n += sg_scnpr(b + n, blen - n,
+ " --json[-JO] | -%c[JO]\n\n", short_opt);
+ n += sg_scnpr(b + n, blen - n, " where JO is a string of one or more "
+ "of:\n");
+ n += sg_scnpr(b + n, blen - n,
+ " 0 | 2 tab pretty output to 2 spaces\n");
+ n += sg_scnpr(b + n, blen - n,
+ " 4 tab pretty output to 4 spaces\n");
+ n += sg_scnpr(b + n, blen - n,
+ " 8 tab pretty output to 8 spaces\n");
+ if (n >= (blen - 1))
+ goto fini;
+ n += sg_scnpr(b + n, blen - n,
+ " e show 'exit_status' field\n");
+ 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,
+ " n show 'name_extra' information fields\n");
+ n += sg_scnpr(b + n, blen - n,
+ " o non-JSON output placed in 'output' array in "
+ "lead-in\n");
+ if (n >= (blen - 1))
+ goto fini;
+ n += sg_scnpr(b + n, blen - n,
+ " p pretty print the JSON output\n");
+ n += sg_scnpr(b + n, blen - n,
+ " s show string output (usually fields named "
+ "'meaning')\n");
+ n += sg_scnpr(b + n, blen - n,
+ " v make JSON output more verbose\n");
+ n += sg_scnpr(b + n, blen - n,
+ " = ignored if first character, else it's an "
+ "error\n");
+ n += sg_scnpr(b + n, blen - n,
+ " - | ~ | ! toggle next letter setting\n");
+
+ n += sg_scnpr(b + n, blen - n, "\nIn the absence of the optional JO "
+ "argument, the following are set\non: 'elps' while the "
+ "others are set off, and tabs are set to 4.\nBefore "
+ "command line JO options are applied, the environment\n"
+ "variable: %s is applied (if present). Note that\nno "
+ "space is permitted between the short option ('-%c') "
+ "and its\nargument ('JO').\n", sgj_opts_ev, short_opt);
+fini:
+ return b;
+}
+
+char *
+sg_json_settings(sgj_state * jsp, char * b, int blen)
+{
+ snprintf(b, blen, "%d%se%sh%sk%sl%sn%so%sp%ss%sv", jsp->pr_indent_size,
+ jsp->pr_exit_status ? "" : "-", jsp->pr_hex ? "" : "-",
+ jsp->pr_packed ? "" : "-", jsp->pr_leadin ? "" : "-",
+ jsp->pr_name_ex ? "" : "-", jsp->pr_out_hr ? "" : "-",
+ jsp->pr_pretty ? "" : "-", jsp->pr_string ? "" : "-",
+ jsp->verbose ? "" : "-");
+ return b;
+}
+
+static void
+sgj_def_opts(sgj_state * jsp)
+{
+ jsp->pr_as_json = true;
+ jsp->pr_exit_status = true;
+ jsp->pr_hex = false;
+ jsp->pr_leadin = true;
+ jsp->pr_out_hr = false;
+ jsp->pr_name_ex = false;
+ jsp->pr_packed = false; /* 'k' control character, needs '-p' */
+ jsp->pr_pretty = true;
+ jsp->pr_string = true;
+ jsp->pr_format = 0;
+ jsp->first_bad_char = 0;
+ jsp->verbose = 0;
+ jsp->pr_indent_size = 4;
+}
+
+bool
+sgj_init_state(sgj_state * jsp, const char * j_optarg)
+{
+ const char * cp;
+
+ sgj_def_opts(jsp);
+ jsp->basep = NULL;
+ jsp->out_hrp = NULL;
+ jsp->userp = NULL;
+
+ cp = getenv(sgj_opts_ev);
+ if (cp) {
+ if (! sgj_parse_opts(jsp, cp)) {
+ pr2ws("error parsing %s environment variable, ignore\n",
+ sgj_opts_ev);
+ sgj_def_opts(jsp);
+ }
+ }
+ return j_optarg ? sgj_parse_opts(jsp, j_optarg) : true;
+}
+
+sgj_opaque_p
+sgj_start_r(const char * util_name, const char * ver_str, int argc,
+ char *argv[], sgj_state * jsp)
+{
+ int k;
+ json_value * jvp = json_object_new(0);
+ json_value * jv2p = NULL;
+ json_value * jap = NULL;
+
+ if (NULL == jvp)
+ return NULL;
+ if (NULL == jsp)
+ return jvp;
+
+ jsp->basep = jvp;
+ if (jsp->pr_leadin) {
+ jap = json_array_new(0);
+ if (NULL == jap) {
+ json_builder_free((json_value *)jvp);
+ return NULL;
+ }
+ /* assume rest of json_*_new() calls succeed */
+ json_array_push((json_value *)jap, json_integer_new(1));
+ json_array_push((json_value *)jap, json_integer_new(0));
+ json_object_push((json_value *)jvp, "json_format_version",
+ (json_value *)jap);
+ if (util_name) {
+ jap = json_array_new(0);
+ 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",
+ json_string_new(util_name));
+ if (ver_str)
+ json_object_push((json_value *)jv2p, "version_date",
+ json_string_new(ver_str));
+ else
+ json_object_push((json_value *)jv2p, "version_date",
+ json_string_new("0.0"));
+ json_object_push((json_value *)jv2p, "argv", jap);
+ }
+ if (jsp->verbose) {
+ const char * cp = getenv(sgj_opts_ev);
+ char b[32];
+
+ json_object_push((json_value *)jv2p, "environment_variable_name",
+ json_string_new(sgj_opts_ev));
+ json_object_push((json_value *)jv2p, "environment_variable_value",
+ json_string_new(cp ? cp : "no available"));
+ sg_json_settings(jsp, b, sizeof(b));
+ json_object_push((json_value *)jv2p, "json_options",
+ json_string_new(b));
+ }
+ } else {
+ if (jsp->pr_out_hr && util_name)
+ jv2p = json_object_push((json_value *)jvp, "utility_invoked",
+ json_object_new(0));
+ }
+ 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_js_str_out(jsp, bp, strlen(bp));
+ free(bp);
+ }
+ }
+ }
+ return jvp;
+}
+
+void
+sgj_js2file(sgj_state * jsp, sgj_opaque_p jop, int exit_status, FILE * fp)
+{
+ size_t len;
+ char * b;
+ json_value * jvp = (json_value *)(jop ? jop : jsp->basep);
+ json_serialize_opts out_settings;
+
+ if (NULL == jvp) {
+ fprintf(fp, "%s: all NULL pointers ??\n", __func__);
+ return;
+ }
+ if ((NULL == jop) && jsp->pr_exit_status) {
+ char d[80];
+
+#ifdef SG_PRSE_SENSE_DECODE
+ if (sg_exit2str(exit_status, jsp->verbose, sizeof(d), d)) {
+ if (0 == strlen(d))
+ strncpy(d, "no errors", sizeof(d) - 1);
+ } else
+ strncpy(d, "not available", sizeof(d) - 1);
+#else
+ if (0 == exit_status)
+ strncpy(d, "no errors", sizeof(d) - 1);
+ else
+ snprintf(d, sizeof(d), "exit_status=%d", exit_status);
+#endif
+ sgj_js_nv_istr(jsp, jop, "exit_status", exit_status, NULL, d);
+ }
+ memcpy(&out_settings, &def_out_settings, sizeof(out_settings));
+ 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 = jsp->pr_packed ? json_serialize_mode_packed :
+ json_serialize_mode_single_line;
+
+ len = json_measure_ex(jvp, out_settings);
+ if (len < 1)
+ return;
+ if (jsp->verbose > 3)
+ fprintf(fp, "%s: serialization length: %zu bytes\n", __func__, len);
+ b = (char *)calloc(len, 1);
+ if (NULL == b) {
+ if (jsp->verbose > 3)
+ 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");
+ fprintf(fp, "%s\n", b);
+}
+
+void
+sgj_finish(sgj_state * jsp)
+{
+ if (jsp && jsp->basep) {
+ json_builder_free((json_value *)jsp->basep);
+ jsp->basep = NULL;
+ jsp->out_hrp = NULL;
+ jsp->userp = NULL;
+ }
+}
+
+void
+sgj_free_unattached(sgj_opaque_p jop)
+{
+ if (jop)
+ json_builder_free((json_value *)jop);
+}
+
+void
+sgj_pr_hr(sgj_state * jsp, const char * fmt, ...)
+{
+ va_list args;
+
+ if (jsp->pr_as_json && jsp->pr_out_hr) {
+ size_t len;
+ char b[256];
+
+ va_start(args, fmt);
+ len = vsnprintf(b, sizeof(b), fmt, args);
+ if ((len > 0) && (len < sizeof(b))) {
+ const char * cp = b;
+
+ /* remove up to two trailing linefeeds */
+ if (b[len - 1] == '\n') {
+ --len;
+ if (b[len - 1] == '\n')
+ --len;
+ b[len] = '\0';
+ }
+ /* remove leading linefeed, if present */
+ if ((len > 0) && ('\n' == b[0]))
+ ++cp;
+ json_array_push((json_value *)jsp->out_hrp, json_string_new(cp));
+ }
+ va_end(args);
+ } else if (jsp->pr_as_json) {
+ va_start(args, fmt);
+ va_end(args);
+ } else {
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+ }
+}
+
+/* jop will 'own' returned value (if non-NULL) */
+sgj_opaque_p
+sgj_named_subobject_r(sgj_state * jsp, sgj_opaque_p jop, const char * name)
+{
+ sgj_opaque_p resp = NULL;
+
+ if (jsp && jsp->pr_as_json && name)
+ resp = json_object_push((json_value *)(jop ? jop : jsp->basep), name,
+ json_object_new(0));
+ return resp;
+}
+
+sgj_opaque_p
+sgj_snake_named_subobject_r(sgj_state * jsp, sgj_opaque_p jop,
+ const char * conv2sname)
+{
+ if (jsp && jsp->pr_as_json && conv2sname) {
+ int olen = strlen(conv2sname);
+ char * sname = (char *)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_named_subarray_r(sgj_state * jsp, sgj_opaque_p jop, const char * name)
+{
+ sgj_opaque_p resp = NULL;
+
+ if (jsp && jsp->pr_as_json && name)
+ resp = json_object_push((json_value *)(jop ? jop : jsp->basep), name,
+ json_array_new(0));
+ return resp;
+}
+
+sgj_opaque_p
+sgj_snake_named_subarray_r(sgj_state * jsp, sgj_opaque_p jop,
+ const char * conv2sname)
+{
+ if (jsp && jsp->pr_as_json && conv2sname) {
+ int olen = strlen(conv2sname);
+ char * sname = (char *)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_r(sgj_state * jsp)
+{
+ return (jsp && jsp->pr_as_json) ? json_object_new(0) : NULL;
+}
+
+/* Newly created array is un-attached to jsp->basep tree */
+sgj_opaque_p
+sgj_new_unattached_array_r(sgj_state * jsp)
+{
+ return (jsp && jsp->pr_as_json) ? json_array_new(0) : NULL;
+}
+
+sgj_opaque_p
+sgj_js_nv_s(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ const char * value)
+{
+ if (jsp && jsp->pr_as_json && value) {
+ if (name)
+ return json_object_push((json_value *)(jop ? jop : jsp->basep),
+ name, json_string_new(value));
+ else
+ return json_array_push((json_value *)(jop ? jop : jsp->basep),
+ json_string_new(value));
+ } else
+ return NULL;
+}
+
+sgj_opaque_p
+sgj_js_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(k, value));
+ else
+ return json_array_push((json_value *)(jop ? jop : jsp->basep),
+ json_string_new_length(k, value));
+ } else
+ return NULL;
+}
+
+sgj_opaque_p
+sgj_js_nv_i(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ int64_t value)
+{
+ if (jsp && jsp->pr_as_json) {
+ if (name)
+ return json_object_push((json_value *)(jop ? jop : jsp->basep),
+ name, json_integer_new(value));
+ else
+ return json_array_push((json_value *)(jop ? jop : jsp->basep),
+ json_integer_new(value));
+ }
+ else
+ return NULL;
+}
+
+sgj_opaque_p
+sgj_js_nv_b(sgj_state * jsp, sgj_opaque_p jop, const char * name, bool value)
+{
+ if (jsp && jsp->pr_as_json) {
+ if (name)
+ return json_object_push((json_value *)(jop ? jop : jsp->basep),
+ name, json_boolean_new(value));
+ else
+ return json_array_push((json_value *)(jop ? jop : jsp->basep),
+ json_boolean_new(value));
+ } else
+ return NULL;
+}
+
+/* jop will 'own' ua_jop (if returned value is non-NULL) */
+sgj_opaque_p
+sgj_js_nv_o(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ sgj_opaque_p ua_jop)
+{
+ if (jsp && jsp->pr_as_json && ua_jop) {
+ if (name)
+ return json_object_push((json_value *)(jop ? jop : jsp->basep),
+ name, (json_value *)ua_jop);
+ else
+ return json_array_push((json_value *)(jop ? jop : jsp->basep),
+ (json_value *)ua_jop);
+ } else
+ return NULL;
+}
+
+void
+sgj_js_nv_ihex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ uint64_t value)
+{
+ if ((NULL == jsp) || (NULL == name) || (! jsp->pr_as_json))
+ return;
+ else if (jsp->pr_hex) {
+ sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
+ char b[64];
+
+ if (NULL == jo2p)
+ return;
+ sgj_js_nv_i(jsp, jo2p, "i", (int64_t)value);
+ snprintf(b, sizeof(b), "%" PRIx64, value);
+ sgj_js_nv_s(jsp, jo2p, "hex", b);
+ } else
+ sgj_js_nv_i(jsp, jop, name, (int64_t)value);
+}
+
+static const char * sc_mn_s = "meaning";
+
+void
+sgj_js_nv_istr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ int64_t val_i, const char * str_name, const char * val_s)
+{
+ if ((NULL == jsp) || (! jsp->pr_as_json))
+ return;
+ else if (val_s && jsp->pr_string) {
+ sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
+
+ if (NULL == jo2p)
+ return;
+ sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
+ sgj_js_nv_s(jsp, jo2p, str_name ? str_name : sc_mn_s, val_s);
+ } else
+ sgj_js_nv_i(jsp, jop, name, val_i);
+}
+
+void
+sgj_js_nv_ihexstr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ int64_t val_i, const char * str_name, const char * val_s)
+{
+ bool as_str;
+
+ if ((NULL == jsp) || (! jsp->pr_as_json))
+ return;
+ as_str = jsp->pr_string && val_s;
+ if ((! jsp->pr_hex) && (! as_str))
+ sgj_js_nv_i(jsp, jop, name, val_i);
+ else {
+ char b[64];
+ sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
+
+ if (NULL == jo2p)
+ return;
+ sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
+ if (jsp->pr_hex) {
+ snprintf(b, sizeof(b), "%" PRIx64, val_i);
+ sgj_js_nv_s(jsp, jo2p, "hex", b);
+ }
+ if (as_str)
+ sgj_js_nv_s(jsp, jo2p, str_name ? str_name : sc_mn_s, val_s);
+ }
+}
+
+static const char * sc_nex_s = "name_extra";
+
+void
+sgj_js_nv_ihex_nex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ int64_t val_i, bool hex_as_well, const char * nex_s)
+{
+ bool as_hex, as_nex;
+
+ if ((NULL == jsp) || (! jsp->pr_as_json))
+ return;
+ as_hex = jsp->pr_hex && hex_as_well;
+ as_nex = jsp->pr_name_ex && nex_s;
+ if (! (as_hex || as_nex))
+ sgj_js_nv_i(jsp, jop, name, val_i);
+ else {
+ char b[64];
+ sgj_opaque_p jo2p =
+ sgj_named_subobject_r(jsp, jop, name);
+
+ if (NULL == jo2p)
+ return;
+ sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
+ if (as_hex) {
+ snprintf(b, sizeof(b), "%" PRIx64, val_i);
+ sgj_js_nv_s(jsp, jo2p, "hex", b);
+ }
+ if (as_nex)
+ sgj_js_nv_s(jsp, jo2p, sc_nex_s, nex_s);
+ }
+}
+
+#ifndef SG_PRSE_SENSE_DECODE
+static void
+h2str(const uint8_t * byte_arr, int num_bytes, char * bp, int blen)
+{
+ int j, k, n;
+
+ for (k = 0, n = 0; (k < num_bytes) && (n < blen); ) {
+ j = sg_scnpr(bp + n, blen - n, "%02x ", byte_arr[k]);
+ if (j < 2)
+ break;
+ n += j;
+ ++k;
+ if ((0 == (k % 8)) && (k < num_bytes) && (n < blen)) {
+ bp[n++] = ' ';
+ }
+ }
+ j = strlen(bp);
+ if ((j > 0) && (' ' == bp[j - 1]))
+ bp[j - 1] = '\0'; /* chop off trailing space */
+}
+#endif
+
+/* Add hex byte strings irrespective of jsp->pr_hex setting. */
+void
+sgj_js_nv_hex_bytes(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ const uint8_t * byte_arr, int num_bytes)
+{
+ int blen = num_bytes * 4;
+ char * bp;
+
+ if ((NULL == jsp) || (! jsp->pr_as_json))
+ return;
+ bp = (char *)calloc(blen + 4, 1);
+ if (bp) {
+#ifdef SG_PRSE_SENSE_DECODE
+ hex2str(byte_arr, num_bytes, NULL, 2, blen, bp);
+#else
+ h2str(byte_arr, num_bytes, bp, blen);
+#endif
+ sgj_js_nv_s(jsp, jop, name, bp);
+ free(bp);
+ }
+}
+
+void
+sgj_js_nv_ihexstr_nex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
+ int64_t val_i, bool hex_as_well, const char * str_name,
+ const char * val_s, const char * nex_s)
+{
+ bool as_hex = jsp->pr_hex && hex_as_well;
+ bool as_str = jsp->pr_string && val_s;
+ bool as_nex = jsp->pr_name_ex && nex_s;
+ const char * sname = str_name ? str_name : sc_mn_s;
+
+ if ((NULL == jsp) || (! jsp->pr_as_json))
+ return;
+ if (! (as_hex || as_nex || as_str))
+ sgj_js_nv_i(jsp, jop, name, val_i);
+ else {
+ char b[64];
+ sgj_opaque_p jo2p =
+ sgj_named_subobject_r(jsp, jop, name);
+
+ if (NULL == jo2p)
+ return;
+ sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
+ if (as_nex) {
+ if (as_hex) {
+ snprintf(b, sizeof(b), "%" PRIx64, val_i);
+ sgj_js_nv_s(jsp, jo2p, "hex", b);
+ }
+ if (as_str) {
+ sgj_js_nv_s(jsp, jo2p, sname, val_s);
+ }
+ sgj_js_nv_s(jsp, jo2p, sc_nex_s, nex_s);
+ } else if (as_hex) {
+ snprintf(b, sizeof(b), "%" PRIx64, val_i);
+ sgj_js_nv_s(jsp, jo2p, "hex", b);
+ if (as_str)
+ sgj_js_nv_s(jsp, jo2p, sname, val_s);
+ } else if (as_str)
+ sgj_js_nv_s(jsp, jo2p, sname, val_s);
+ }
+}
+
+/* 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->out_hrp JSON
+ * array (if conditions met). */
+void
+sgj_js_str_out(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->out_hrp) || (! jsp->pr_as_json) ||
+ (! jsp->pr_out_hr))
+ return;
+ for (k = 0; k < slen; ++k, ++cur_sp) {
+ c = *cur_sp;
+ if ('\0' == c)
+ break;
+ else if ('\n' == c) {
+ n = cur_sp - prev_sp;
+ /* when name is NULL, add to array (jsp->out_hrp) */
+ sgj_js_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_js_nv_s_len(jsp, jsp->out_hrp, NULL, prev_sp, n);
+ }
+}
+
+char *
+sgj_convert_to_snake_name(const char * in_name, char * sname,
+ int max_sname_len)
+{
+ sgj_name_to_snake(in_name, sname, max_sname_len);
+ return sname;
+}
+
+bool
+sgj_is_snake_name(const char * in_name)
+{
+ size_t k;
+ size_t ln = strlen(in_name);
+ char c;
+
+ for (k = 0; k < ln; ++k) {
+ c = in_name[k];
+ if (((c >= '0') && (c <= '9')) ||
+ ((c >= 'a') && (c <= 'z')) ||
+ (c == '_'))
+ continue;
+ else
+ return false;
+ }
+ return true;
+}
+
+/* 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"). Parentheses and the characters between them are removed.
+ * Returns number of characters placed in 'out' excluding the trailing
+ * NULL */
+static int
+sgj_name_to_snake(const char * in, char * out, int maxlen_out)
+{
+ bool prev_underscore = false;
+ bool within_paren = false;
+ int c, k, j, inlen;
+
+ if (maxlen_out < 2) {
+ if (maxlen_out == 1)
+ out[0] = '\0';
+ return 0;
+ }
+ inlen = strlen(in);
+ for (k = 0, j = 0; (k < inlen) && (j < maxlen_out); ++k) {
+ c = in[k];
+ if (within_paren) {
+ if (')' == c)
+ within_paren = false;
+ continue;
+ }
+ if (isalnum(c)) {
+ out[j++] = isupper(c) ? tolower(c) : c;
+ prev_underscore = false;
+ } else if ('(' == c)
+ within_paren = true;
+ else if ((j > 0) && (! prev_underscore)) {
+ out[j++] = '_';
+ prev_underscore = true;
+ }
+ /* else we are skipping character 'c' */
+ }
+ if (j == maxlen_out)
+ out[--j] = '\0';
+ /* trim of trailing underscores (might have been spaces) */
+ for (k = j - 1; k >= 0; --k) {
+ if (out[k] != '_')
+ break;
+ }
+ if (k < 0)
+ k = 0;
+ else
+ ++k;
+ out[k] = '\0';
+ return k;
+}
+
+static int
+sgj_jtype_to_s(char * b, int blen_max, json_value * jvp)
+{
+ json_type jtype = jvp ? jvp->type : json_none;
+
+ switch (jtype) {
+ case json_string:
+ return sg_scnpr(b, blen_max, "%s", jvp->u.string.ptr);
+ case json_integer:
+ return sg_scnpr(b, blen_max, "%" PRIi64, jvp->u.integer);
+ case json_boolean:
+ return sg_scnpr(b, blen_max, "%s", jvp->u.boolean ? "true" : "false");
+ case json_none:
+ default:
+ if ((blen_max > 0) && ('\0' != b[0]))
+ b[0] = '\0';
+ break;
+ }
+ return 0;
+}
+
+static int
+sgj_haj_helper(char * b, int blen_max, const char * name,
+ enum sgj_separator_t sep, bool use_jvp,
+ json_value * jvp, int64_t val_instead)
+{
+ int n = 0;
+
+ if (name) {
+ n += sg_scnpr(b + n, blen_max - n, "%s", name);
+ switch (sep) {
+ case SGJ_SEP_NONE:
+ break;
+ case SGJ_SEP_SPACE_1:
+ n += sg_scnpr(b + n, blen_max - n, " ");
+ break;
+ case SGJ_SEP_SPACE_2:
+ n += sg_scnpr(b + n, blen_max - n, " ");
+ break;
+ case SGJ_SEP_SPACE_3:
+ n += sg_scnpr(b + n, blen_max - n, " ");
+ break;
+ case SGJ_SEP_SPACE_4:
+ n += sg_scnpr(b + n, blen_max - n, " ");
+ break;
+ case SGJ_SEP_EQUAL_NO_SPACE:
+ n += sg_scnpr(b + n, blen_max - n, "=");
+ break;
+ case SGJ_SEP_EQUAL_1_SPACE:
+ n += sg_scnpr(b + n, blen_max - n, "= ");
+ break;
+ case SGJ_SEP_COLON_NO_SPACE:
+ n += sg_scnpr(b + n, blen_max - n, ":");
+ break;
+ case SGJ_SEP_COLON_1_SPACE:
+ n += sg_scnpr(b + n, blen_max - n, ": ");
+ break;
+ default:
+ break;
+ }
+ }
+ if (use_jvp)
+ n += sgj_jtype_to_s(b + n, blen_max - n, jvp);
+ else
+ n += sg_scnpr(b + n, blen_max - n, "%" PRIi64, val_instead);
+ return n;
+}
+
+static void
+sgj_haj_xx(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
+ const char * name, enum sgj_separator_t sep, json_value * jvp,
+ bool hex_as_well, const char * val_s, const char * nex_s)
+{
+ bool eaten = false;
+ bool as_json = (jsp && jsp->pr_as_json);
+ bool done;
+ int n;
+ json_type jtype = jvp ? jvp->type : json_none;
+ char b[256];
+ char jname[96];
+ static const int blen = sizeof(b);
+
+ if (leadin_sp > 128)
+ leadin_sp = 128;
+ for (n = 0; n < leadin_sp; ++n)
+ b[n] = ' ';
+ b[n] = '\0';
+ if (NULL == name) {
+ if ((! as_json) || (jsp && jsp->pr_out_hr)) {
+ n += sgj_jtype_to_s(b + n, blen - n, jvp);
+ printf("%s\n", b);
+ }
+ if (NULL == jop) {
+ if (as_json && jsp->pr_out_hr) {
+ eaten = true;
+ json_array_push((json_value *)jsp->out_hrp,
+ jvp ? jvp : json_null_new());
+ }
+ } else { /* assume jop points to named array */
+ if (as_json) {
+ eaten = true;
+ json_array_push((json_value *)jop,
+ jvp ? jvp : json_null_new());
+ }
+ }
+ goto fini;
+ }
+ if (as_json) {
+ int k;
+
+ if (NULL == jop)
+ jop = jsp->basep;
+ k = sgj_name_to_snake(name, jname, sizeof(jname));
+ if (k > 0) {
+ done = false;
+ if (nex_s && (strlen(nex_s) > 0)) {
+ switch (jtype) {
+ case json_string:
+ break;
+ case json_integer:
+ sgj_js_nv_ihexstr_nex(jsp, jop, jname, jvp->u.integer,
+ hex_as_well, sc_mn_s, val_s, nex_s);
+ done = true;
+ break;
+ case json_boolean:
+ sgj_js_nv_ihexstr_nex(jsp, jop, jname, jvp->u.boolean,
+ false, sc_mn_s, val_s, nex_s);
+ done = true;
+ break;
+ case json_none:
+ default:
+ break;
+ }
+ } else {
+ switch (jtype) {
+ case json_string:
+ break;
+ case json_integer:
+ if (hex_as_well) {
+ sgj_js_nv_ihexstr(jsp, jop, jname, jvp->u.integer,
+ sc_mn_s, val_s);
+ done = true;
+ }
+ break;
+ case json_none:
+ default:
+ break;
+ }
+ }
+ if (! done) {
+ eaten = true;
+ json_object_push((json_value *)jop, jname,
+ jvp ? jvp : json_null_new());
+ }
+ }
+ }
+ if (jvp && ((as_json && jsp->pr_out_hr) || (! as_json)))
+ n += sgj_haj_helper(b + n, blen - n, name, sep, true, jvp, 0);
+
+ if (as_json && jsp->pr_out_hr)
+ json_array_push((json_value *)jsp->out_hrp, json_string_new(b));
+ if (! as_json)
+ printf("%s\n", b);
+fini:
+ if (jvp && (! eaten))
+ json_builder_free((json_value *)jvp);
+}
+
+void
+sgj_haj_vs(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
+ const char * name, enum sgj_separator_t sep, const char * value)
+{
+ json_value * jvp;
+
+ /* make json_value even if jsp->pr_as_json is false */
+ jvp = value ? json_string_new(value) : NULL;
+ sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, false, NULL, NULL);
+}
+
+void
+sgj_haj_vi(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
+ const char * name, enum sgj_separator_t sep, int64_t value,
+ bool hex_as_well)
+{
+ json_value * jvp;
+
+ jvp = json_integer_new(value);
+ sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, NULL, NULL);
+}
+
+void
+sgj_haj_vistr(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
+ const char * name, enum sgj_separator_t sep, int64_t value,
+ bool hex_as_well, const char * val_s)
+{
+ json_value * jvp;
+
+ jvp = json_integer_new(value);
+ sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, val_s,
+ NULL);
+}
+
+void
+sgj_haj_vi_nex(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
+ const char * name, enum sgj_separator_t sep,
+ int64_t value, bool hex_as_well, const char * nex_s)
+{
+ json_value * jvp;
+
+ jvp = json_integer_new(value);
+ sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, NULL, nex_s);
+}
+
+void
+sgj_haj_vistr_nex(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
+ const char * name, enum sgj_separator_t sep,
+ int64_t value, bool hex_as_well,
+ const char * val_s, const char * nex_s)
+{
+ json_value * jvp;
+
+ jvp = json_integer_new(value);
+ sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, val_s,
+ nex_s);
+}
+
+void
+sgj_haj_vb(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
+ const char * name, enum sgj_separator_t sep, bool value)
+{
+ json_value * jvp;
+
+ jvp = json_boolean_new(value);
+ sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, false, NULL, NULL);
+}
+
+sgj_opaque_p
+sgj_haj_subo_r(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
+ const char * name, enum sgj_separator_t sep, int64_t value,
+ bool hex_as_well)
+{
+ bool as_json = (jsp && jsp->pr_as_json);
+ int n = 0;
+ sgj_opaque_p jo2p;
+ char b[256];
+ static const int blen = sizeof(b);
+
+ if (NULL == name)
+ return NULL;
+ for (n = 0; n < leadin_sp; ++n)
+ b[n] = ' ';
+ b[n] = '\0';
+ if ((! as_json) || (jsp && jsp->pr_out_hr))
+ n += sgj_haj_helper(b + n, blen - n, name, sep, false, NULL, value);
+
+ if (as_json && jsp->pr_out_hr)
+ json_array_push((json_value *)jsp->out_hrp, json_string_new(b));
+ if (! as_json)
+ printf("%s\n", b);
+
+ if (as_json) {
+ sgj_name_to_snake(name, b, blen);
+ jo2p = sgj_named_subobject_r(jsp, jop, b);
+ if (jo2p) {
+ sgj_js_nv_i(jsp, jo2p, "i", value);
+ if (hex_as_well && jsp->pr_hex) {
+ snprintf(b, blen, "%" PRIx64, value);
+ sgj_js_nv_s(jsp, jo2p, "hex", b);
+ }
+ }
+ return jo2p;
+ }
+ return NULL;
+}
+
+#ifdef SG_PRSE_SENSE_DECODE
+
+static const char * dtsp = "descriptor too short";
+static const char * sksvp = "sense-key specific valid";
+static const char * ddep = "designation_descriptor_error";
+static const char * naa_exp = "Network Address Authority";
+static const char * aoi_exp = "IEEE-Administered Organizational Identifier";
+
+bool
+sgj_js_designation_descriptor(sgj_state * jsp, sgj_opaque_p jop,
+ const uint8_t * ddp, int dd_len)
+{
+ int p_id, piv, c_set, assoc, desig_type, d_id, naa;
+ int n, aoi, vsi, dlen;
+ uint64_t ull;
+ const uint8_t * ip;
+ char e[80];
+ char b[256];
+ const char * cp;
+ const char * naa_sp;
+ sgj_opaque_p jo2p;
+ static const int blen = sizeof(b);
+ static const int elen = sizeof(e);
+
+ if (dd_len < 4) {
+ sgj_js_nv_s(jsp, jop, ddep, "too short");
+ return false;
+ }
+ dlen = ddp[3];
+ if (dlen > (dd_len - 4)) {
+ snprintf(e, elen, "too long: says it is %d bytes, but given %d "
+ "bytes\n", dlen, dd_len - 4);
+ sgj_js_nv_s(jsp, jop, ddep, e);
+ return false;
+ }
+ 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);
+ cp = sg_get_desig_assoc_str(assoc);
+ if (assoc == 3)
+ cp = "Reserved [0x3]"; /* should not happen */
+ sgj_js_nv_ihexstr(jsp, jop, "association", assoc, NULL, cp);
+ cp = sg_get_desig_type_str(desig_type);
+ if (NULL == cp)
+ cp = "unknown";
+ sgj_js_nv_ihexstr(jsp, jop, "designator_type", desig_type,
+ NULL, cp);
+ cp = sg_get_desig_code_set_str(c_set);
+ if (NULL == cp)
+ cp = "unknown";
+ sgj_js_nv_ihexstr(jsp, jop, "code_set", desig_type,
+ NULL, cp);
+ sgj_js_nv_ihex_nex(jsp, jop, "piv", piv, false,
+ "Protocol Identifier Valid");
+ sg_get_trans_proto_str(p_id, elen, e);
+ sgj_js_nv_ihexstr(jsp, jop, "protocol_identifier", p_id, NULL, e);
+ switch (desig_type) {
+ case 0: /* vendor specific */
+ sgj_js_nv_hex_bytes(jsp, jop, "vendor_specific_hexbytes", ip, dlen);
+ break;
+ case 1: /* T10 vendor identification */
+ n = (dlen < 8) ? dlen : 8;
+ snprintf(b, blen, "%.*s", n, ip);
+ sgj_js_nv_s(jsp, jop, "t10_vendor_identification", b);
+ b[0] = '\0';
+ if (dlen > 8)
+ snprintf(b, blen, "%.*s", dlen - 8, ip + 8);
+ sgj_js_nv_s(jsp, jop, "vendor_specific_identifier", b);
+ break;
+ case 2: /* EUI-64 based */
+ sgj_js_nv_i(jsp, jop, "eui_64_based_designator_length", dlen);
+ ull = sg_get_unaligned_be64(ip);
+ switch (dlen) {
+ case 8:
+ sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ull);
+ break;
+ case 12:
+ sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ull);
+ sgj_js_nv_ihex(jsp, jop, "directory_id",
+ sg_get_unaligned_be32(ip + 8));
+ break;
+ case 16:
+ sgj_js_nv_ihex(jsp, jop, "identifier_extension", ull);
+ sgj_js_nv_ihex(jsp, jop, "ieee_identifier",
+ sg_get_unaligned_be64(ip + 8));
+ break;
+ default:
+ sgj_js_nv_s(jsp, jop, "eui_64", "decoding failed");
+ break;
+ }
+ break;
+ case 3: /* NAA <n> */
+ if (jsp->pr_hex)
+ sgj_js_nv_hex_bytes(jsp, jop, "full_naa_hexbytes", ip, dlen);
+ naa = (ip[0] >> 4) & 0xff;
+ switch (naa) {
+ case 2:
+ naa_sp = "IEEE Extended";
+ sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
+ naa_exp);
+ d_id = (((ip[0] & 0xf) << 8) | ip[1]);
+ sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_a", d_id);
+ aoi = sg_get_unaligned_be24(ip + 2);
+ sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
+ vsi = sg_get_unaligned_be24(ip + 5);
+ sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_b", vsi);
+ break;
+ case 3:
+ naa_sp = "Locally Assigned";
+ sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
+ naa_exp);
+ ull = sg_get_unaligned_be64(ip + 0) & 0xfffffffffffffffULL;
+ sgj_js_nv_ihex(jsp, jop, "locally_administered_value", ull);
+ break;
+ case 5:
+ naa_sp = "IEEE Registered";
+ sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
+ naa_exp);
+ aoi = (sg_get_unaligned_be32(ip + 0) >> 4) & 0xffffff;
+ sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
+ ull = sg_get_unaligned_be48(ip + 2) & 0xfffffffffULL;
+ sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier", ull);
+ break;
+ case 6:
+ naa_sp = "IEEE Registered Extended";
+ sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
+ naa_exp);
+ aoi = (sg_get_unaligned_be32(ip + 0) >> 4) & 0xffffff;
+ sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
+ ull = sg_get_unaligned_be48(ip + 2) & 0xfffffffffULL;
+ sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier", ull);
+ ull = sg_get_unaligned_be64(ip + 8);
+ sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_extension",
+ ull);
+ break;
+ default:
+ snprintf(b, blen, "unknown NAA value=0x%x", naa);
+ sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, true, NULL, b,
+ naa_exp);
+ sgj_js_nv_hex_bytes(jsp, jop, "full_naa_hexbytes", ip, dlen);
+ break;
+ }
+ break;
+ case 4: /* Relative target port */
+ if (jsp->pr_hex)
+ sgj_js_nv_hex_bytes(jsp, jop, "relative_target_port_hexbytes",
+ ip, dlen);
+ sgj_js_nv_ihex(jsp, jop, "relative_target_port_identifier",
+ sg_get_unaligned_be16(ip + 2));
+ break;
+ case 5: /* (primary) Target port group */
+ if (jsp->pr_hex)
+ sgj_js_nv_hex_bytes(jsp, jop, "target_port_group_hexbytes",
+ ip, dlen);
+ sgj_js_nv_ihex(jsp, jop, "target_port_group",
+ sg_get_unaligned_be16(ip + 2));
+ break;
+ case 6: /* Logical unit group */
+ if (jsp->pr_hex)
+ sgj_js_nv_hex_bytes(jsp, jop, "logical_unit_group_hexbytes",
+ ip, dlen);
+ sgj_js_nv_ihex(jsp, jop, "logical_unit_group",
+ sg_get_unaligned_be16(ip + 2));
+ break;
+ case 7: /* MD5 logical unit identifier */
+ sgj_js_nv_hex_bytes(jsp, jop, "md5_logical_unit_hexbytes",
+ ip, dlen);
+ break;
+ case 8: /* SCSI name string */
+ if (jsp->pr_hex)
+ sgj_js_nv_hex_bytes(jsp, jop, "scsi_name_string_hexbytes",
+ ip, dlen);
+ snprintf(b, blen, "%.*s", dlen, ip);
+ sgj_js_nv_s(jsp, jop, "scsi_name_string", b);
+ break;
+ case 9: /* Protocol specific port identifier */
+ if (jsp->pr_hex)
+ sgj_js_nv_hex_bytes(jsp, jop,
+ "protocol_specific_port_identifier_hexbytes",
+ ip, dlen);
+ if (TPROTO_UAS == p_id) {
+ jo2p = sgj_named_subobject_r(jsp, jop,
+ "usb_target_port_identifier");
+ sgj_js_nv_ihex(jsp, jo2p, "device_address", 0x7f & ip[0]);
+ sgj_js_nv_ihex(jsp, jo2p, "interface_number", ip[2]);
+ } else if (TPROTO_SOP == p_id) {
+ jo2p = sgj_named_subobject_r(jsp, jop, "pci_express_routing_id");
+ sgj_js_nv_ihex(jsp, jo2p, "routing_id",
+ sg_get_unaligned_be16(ip + 0));
+ } else
+ sgj_js_nv_s(jsp, jop, "protocol_specific_port_identifier",
+ "decoding failure");
+
+ break;
+ case 0xa: /* UUID identifier */
+ if (jsp->pr_hex)
+ sgj_js_nv_hex_bytes(jsp, jop, "uuid_hexbytes", ip, dlen);
+ sg_t10_uuid_desig2str(ip, dlen, c_set, false, true, NULL, blen, b);
+ n = strlen(b);
+ if ((n > 0) && ('\n' == b[n - 1]))
+ b[n - 1] = '\0';
+ sgj_js_nv_s(jsp, jop, "uuid", b);
+ break;
+ default: /* reserved */
+ sgj_js_nv_hex_bytes(jsp, jop, "reserved_designator_hexbytes",
+ ip, dlen);
+ break;
+ }
+ return true;
+}
+
+static void
+sgj_progress_indication(sgj_state * jsp, sgj_opaque_p jop,
+ uint16_t prog_indic, bool is_another)
+{
+ uint32_t progress, pr, rem;
+ sgj_opaque_p jo2p;
+ char b[64];
+
+ if (is_another)
+ jo2p = sgj_named_subobject_r(jsp, jop, "another_progress_indication");
+ else
+ jo2p = sgj_named_subobject_r(jsp, jop, "progress_indication");
+ if (NULL == jo2p)
+ return;
+ progress = prog_indic;
+ sgj_js_nv_i(jsp, jo2p, "i", progress);
+ snprintf(b, sizeof(b), "%x", progress);
+ sgj_js_nv_s(jsp, jo2p, "hex", b);
+ progress *= 100;
+ pr = progress / 65536;
+ rem = (progress % 65536) / 656;
+ snprintf(b, sizeof(b), "%d.02%d%%\n", pr, rem);
+ sgj_js_nv_s(jsp, jo2p, "percentage", b);
+}
+
+static bool
+sgj_decode_sks(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * dp, int dlen,
+ int sense_key)
+{
+ switch (sense_key) {
+ case SPC_SK_ILLEGAL_REQUEST:
+ if (dlen < 3) {
+ sgj_js_nv_s(jsp, jop, "illegal_request_sks", dtsp);
+ return false;
+ }
+ sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
+ sksvp);
+ sgj_js_nv_ihex_nex(jsp, jop, "c_d", !! (dp[0] & 0x40), false,
+ "c: cdb; d: data-out");
+ sgj_js_nv_ihex_nex(jsp, jop, "bpv", !! (dp[0] & 0x8), false,
+ "bit pointer (index) valid");
+ sgj_js_nv_i(jsp, jop, "bit_pointer", dp[0] & 0x7);
+ sgj_js_nv_ihex(jsp, jop, "field_pointer",
+ sg_get_unaligned_be16(dp + 1));
+ break;
+ case SPC_SK_HARDWARE_ERROR:
+ case SPC_SK_MEDIUM_ERROR:
+ case SPC_SK_RECOVERED_ERROR:
+ if (dlen < 3) {
+ sgj_js_nv_s(jsp, jop, "actual_retry_count_sks", dtsp);
+ return false;
+ }
+ sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
+ sksvp);
+ sgj_js_nv_ihex(jsp, jop, "actual_retry_count",
+ sg_get_unaligned_be16(dp + 1));
+ break;
+ case SPC_SK_NO_SENSE:
+ case SPC_SK_NOT_READY:
+ if (dlen < 7) {
+ sgj_js_nv_s(jsp, jop, "progress_indication_sks", dtsp);
+ return false;
+ }
+ sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
+ sksvp);
+ sgj_progress_indication(jsp, jop, sg_get_unaligned_be16(dp + 1),
+ false);
+ break;
+ case SPC_SK_COPY_ABORTED:
+ if (dlen < 7) {
+ sgj_js_nv_s(jsp, jop, "segment_indication_sks", dtsp);
+ return false;
+ }
+ sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
+ sksvp);
+ sgj_js_nv_ihex_nex(jsp, jop, "sd", !! (dp[0] & 0x20), false,
+ "field pointer relative to: 1->segment "
+ "descriptor, 0->parameter list");
+ sgj_js_nv_ihex_nex(jsp, jop, "bpv", !! (dp[0] & 0x8), false,
+ "bit pointer (index) valid");
+ sgj_js_nv_i(jsp, jop, "bit_pointer", dp[0] & 0x7);
+ sgj_js_nv_ihex(jsp, jop, "field_pointer",
+ sg_get_unaligned_be16(dp + 1));
+ break;
+ case SPC_SK_UNIT_ATTENTION:
+ if (dlen < 7) {
+ sgj_js_nv_s(jsp, jop, "segment_indication_sks", dtsp);
+ return false;
+ }
+ sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
+ sksvp);
+ sgj_js_nv_i(jsp, jop, "overflow", !! (dp[0] & 0x80));
+ break;
+ default:
+ sgj_js_nv_ihex(jsp, jop, "unexpected_sense_key", sense_key);
+ return false;
+ }
+ return true;
+}
+
+#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 sg_scnpr(b, blen, "active/optimized");
+ case TPGS_STATE_NONOPTIMIZED:
+ return sg_scnpr(b, blen, "active/non optimized");
+ case TPGS_STATE_STANDBY:
+ return sg_scnpr(b, blen, "standby");
+ case TPGS_STATE_UNAVAILABLE:
+ return sg_scnpr(b, blen, "unavailable");
+ case TPGS_STATE_OFFLINE:
+ return sg_scnpr(b, blen, "offline");
+ case TPGS_STATE_TRANSITIONING:
+ return sg_scnpr(b, blen, "transitioning between states");
+ default:
+ return sg_scnpr(b, blen, "unknown: 0x%x", st);
+ }
+}
+
+static bool
+sgj_uds_referral_descriptor(sgj_state * jsp, sgj_opaque_p jop,
+ const uint8_t * dp, int alen)
+{
+ int dlen = alen - 2;
+ int k, j, g, f, aas;
+ uint64_t ull;
+ const uint8_t * tp;
+ sgj_opaque_p jap, jo2p, ja2p, jo3p;
+ char c[40];
+
+ sgj_js_nv_ihex_nex(jsp, jop, "not_all_r", (dp[2] & 0x1), false,
+ "Not all referrals");
+ dp += 4;
+ jap = sgj_named_subarray_r(jsp, jop,
+ "user_data_segment_referral_descriptor_list");
+ for (k = 0, f = 1; (k + 4) < dlen; k += g, dp += g, ++f) {
+ int ntpgd = dp[3];
+
+ jo2p = sgj_new_unattached_object_r(jsp);
+ g = (ntpgd * 4) + 20;
+ sgj_js_nv_ihex(jsp, jo2p, "number_of_target_port_group_descriptors",
+ ntpgd);
+ if ((k + g) > dlen) {
+ sgj_js_nv_i(jsp, jo2p, "truncated_descriptor_dlen", dlen);
+ sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
+ return false;
+ }
+ ull = sg_get_unaligned_be64(dp + 4);
+ sgj_js_nv_ihex(jsp, jo2p, "first_user_date_sgment_lba", ull);
+ ull = sg_get_unaligned_be64(dp + 12);
+ sgj_js_nv_ihex(jsp, jo2p, "last_user_date_sgment_lba", ull);
+ ja2p = sgj_named_subarray_r(jsp, jo2p,
+ "target_port_group_descriptor_list");
+ for (j = 0; j < ntpgd; ++j) {
+ jo3p = sgj_new_unattached_object_r(jsp);
+ tp = dp + 20 + (j * 4);
+ aas = tp[0] & 0xf;
+ decode_tpgs_state(aas, c, sizeof(c));
+ sgj_js_nv_ihexstr(jsp, jo3p, "asymmetric_access_state", aas,
+ NULL, c);
+ sgj_js_nv_ihex(jsp, jo3p, "target_port_group",
+ sg_get_unaligned_be16(tp + 2));
+ sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
+ }
+ sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
+ }
+ return true;
+}
+
+/* Copy of static array in sg_lib.c */
+static const char * dd_usage_reason_str_arr[] = {
+ "Unknown",
+ "resend this and further commands to:",
+ "resend this command to:",
+ "new subsidiary lu added to this administrative lu:",
+ "administrative lu associated with a preferred binding:",
+ };
+
+static bool
+sgj_js_sense_descriptors(sgj_state * jsp, sgj_opaque_p jop,
+ const struct sg_scsi_sense_hdr * sshp,
+ const uint8_t * sbp, int sb_len)
+{
+ bool processed = true;
+ int add_sb_len, desc_len, k, dt, sense_key, n, sds;
+ uint16_t sct_sc;
+ uint64_t ull;
+ const uint8_t * descp;
+ const char * cp;
+ sgj_opaque_p jap, jo2p, jo3p;
+ char b[80];
+ static const int blen = sizeof(b);
+ static const char * parsing = "parsing_error";
+#if 0
+ static const char * eccp = "Extended copy command";
+ static const char * ddp = "destination device";
+#endif
+
+ 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_named_subarray_r(jsp, jop, "sense_data_descriptor_list");
+
+ for (descp = sbp, k = 0; (k < add_sb_len);
+ k += desc_len, descp += desc_len) {
+ int add_d_len = (k < (add_sb_len - 1)) ? descp[1] : -1;
+
+ jo2p = sgj_new_unattached_object_r(jsp);
+ if ((k + add_d_len + 2) > add_sb_len)
+ add_d_len = add_sb_len - k - 2;
+ desc_len = add_d_len + 2;
+ processed = true;
+ dt = descp[0];
+ switch (dt) {
+ case 0:
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt,
+ NULL, "Information");
+ if (add_d_len >= 10) {
+ int valid = !! (0x80 & descp[2]);
+ sgj_js_nv_ihexstr(jsp, jo2p, "valid", valid, NULL,
+ valid ? "as per T10" : "Vendor specific");
+ sgj_js_nv_ihex(jsp, jo2p, "information",
+ sg_get_unaligned_be64(descp + 4));
+ } else {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ }
+ break;
+ case 1:
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt,
+ NULL, "Command specific");
+ if (add_d_len >= 10) {
+ sgj_js_nv_ihex(jsp, jo2p, "command_specific_information",
+ sg_get_unaligned_be64(descp + 4));
+ } else {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ }
+ break;
+ case 2: /* Sense Key Specific */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Sense key specific");
+ processed = sgj_decode_sks(jsp, jo2p, descp + 4, desc_len - 4,
+ sense_key);
+ break;
+ case 3:
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Field replaceable unit code");
+ if (add_d_len >= 2)
+ sgj_js_nv_ihex(jsp, jo2p, "field_replaceable_unit_code",
+ descp[3]);
+ else {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ }
+ break;
+ case 4:
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Stream commands");
+ if (add_d_len >= 2) {
+ sgj_js_nv_i(jsp, jo2p, "filemark", !! (descp[3] & 0x80));
+ sgj_js_nv_ihex_nex(jsp, jo2p, "eom", !! (descp[3] & 0x40),
+ false, "End Of Medium");
+ sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (descp[3] & 0x20),
+ false, "Incorrect Length Indicator");
+ } else {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ }
+ break;
+ case 5:
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Block commands");
+ if (add_d_len >= 2)
+ sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (descp[3] & 0x20),
+ false, "Incorrect Length Indicator");
+ else {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ }
+ break;
+ case 6:
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "OSD object identification");
+ sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
+ processed = false;
+ break;
+ case 7:
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "OSD response integrity check value");
+ sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
+ break;
+ case 8:
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "OSD attribute identification");
+ sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
+ processed = false;
+ break;
+ case 9: /* this is defined in SAT (SAT-2) */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "ATA status return");
+ if (add_d_len >= 12) {
+ sgj_js_nv_i(jsp, jo2p, "extend", !! (descp[2] & 1));
+ sgj_js_nv_ihex(jsp, jo2p, "error", descp[3]);
+ sgj_js_nv_ihex(jsp, jo2p, "count",
+ sg_get_unaligned_be16(descp + 4));
+ ull = ((uint64_t)descp[10] << 40) |
+ ((uint64_t)descp[8] << 32) |
+ (descp[6] << 24) |
+ (descp[11] << 16) |
+ (descp[9] << 8) |
+ descp[7];
+ sgj_js_nv_ihex(jsp, jo2p, "lba", ull);
+ sgj_js_nv_ihex(jsp, jo2p, "device", descp[12]);
+ sgj_js_nv_ihex(jsp, jo2p, "status", descp[13]);
+ } else {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ }
+ break;
+ case 0xa:
+ /* Added in SPC-4 rev 17, became 'Another ...' in rev 34 */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Another progress indication");
+ if (add_d_len < 6) {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ break;
+ }
+ sgj_js_nv_ihex(jsp, jo2p, "another_sense_key", descp[2]);
+ sgj_js_nv_ihex(jsp, jo2p, "another_additional_sense_code",
+ descp[3]);
+ sgj_js_nv_ihex(jsp, jo2p,
+ "another_additional_sense_code_qualifier",
+ descp[4]);
+ sgj_progress_indication(jsp, jo2p,
+ sg_get_unaligned_be16(descp + 6), true);
+ break;
+ case 0xb: /* Added in SPC-4 rev 23, defined in SBC-3 rev 22 */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "User data segment referral");
+ if (add_d_len < 2) {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ break;
+ }
+ if (! sgj_uds_referral_descriptor(jsp, jo2p, descp, add_d_len)) {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ }
+ break;
+ case 0xc: /* Added in SPC-4 rev 28 */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Forwarded sense data");
+ if (add_d_len < 2) {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ break;
+ }
+ sgj_js_nv_ihex_nex(jsp, jo2p, "fsdt", !! (0x80 & descp[2]),
+ false, "Forwarded Sense Data Truncated");
+ sds = (0x7 & descp[2]);
+ if (sds < 1)
+ snprintf(b, blen, "%s [%d]", "Unknown", sds);
+ else if (sds > 9)
+ snprintf(b, blen, "%s [%d]", "Reserved", sds);
+ else {
+ n = 0;
+ n += sg_scnpr(b + n, blen - n, "EXTENDED COPY command copy %s",
+ (sds == 1) ? "source" : "destination");
+ if (sds > 1)
+ n += sg_scnpr(b + n, blen - n, " %d", sds - 1);
+ }
+ sgj_js_nv_ihexstr(jsp, jo2p, "sense_data_source",
+ (0x7 & descp[2]), NULL, b);
+ jo3p = sgj_named_subobject_r(jsp, jo2p, "forwarded_sense_data");
+ sgj_js_sense(jsp, jo3p, descp + 4, desc_len - 4);
+ break;
+ case 0xd: /* Added in SBC-3 rev 36d */
+ /* this descriptor combines descriptors 0, 1, 2 and 3 */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Direct-access block device");
+ if (add_d_len < 28) {
+ sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
+ processed = false;
+ break;
+ }
+ sgj_js_nv_i(jsp, jo2p, "valid", (0x80 & descp[2]));
+ sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (0x20 & descp[2]),
+ false, "Incorrect Length Indicator");
+ processed = sgj_decode_sks(jsp, jo2p, descp + 4, desc_len - 4,
+ sense_key);
+ sgj_js_nv_ihex(jsp, jo2p, "field_replaceable_unit_code",
+ descp[7]);
+ sgj_js_nv_ihex(jsp, jo2p, "information",
+ sg_get_unaligned_be64(descp + 8));
+ sgj_js_nv_ihex(jsp, jo2p, "command_specific_information",
+ sg_get_unaligned_be64(descp + 16));
+ break;
+ case 0xe: /* Added in SPC-5 rev 6 (for Bind/Unbind) */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Device designation");
+ n = descp[3];
+ cp = (n < (int)SG_ARRAY_SIZE(dd_usage_reason_str_arr)) ?
+ dd_usage_reason_str_arr[n] : "Unknown (reserved)";
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_usage_reason",
+ n, NULL, cp);
+ jo3p = sgj_named_subobject_r(jsp, jo2p,
+ "device_designation_descriptor");
+ sgj_js_designation_descriptor(jsp, jo3p, descp + 4, desc_len - 4);
+ break;
+ case 0xf: /* Added in SPC-5 rev 10 (for Write buffer) */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "Microcode activation");
+ if (add_d_len < 6) {
+ sgj_js_nv_s(jsp, jop, parsing, dtsp);
+ processed = false;
+ break;
+ }
+ sgj_js_nv_ihex(jsp, jo2p, "microcode_activation_time",
+ sg_get_unaligned_be16(descp + 6));
+ break;
+ case 0xde: /* NVME Status Field; vendor (sg3_utils) specific */
+ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
+ "NVME status (sg3_utils)");
+ if (add_d_len < 6) {
+ sgj_js_nv_s(jsp, jop, parsing, dtsp);
+ processed = false;
+ break;
+ }
+ sgj_js_nv_ihex_nex(jsp, jo2p, "dnr", !! (0x80 & descp[5]),
+ false, "Do not retry");
+ sgj_js_nv_ihex_nex(jsp, jo2p, "m", !! (0x40 & descp[5]),
+ false, "More");
+ sct_sc = sg_get_unaligned_be16(descp + 6);
+ sgj_js_nv_ihexstr_nex
+ (jsp, jo2p, "sct_sc", sct_sc, true, NULL,
+ sg_get_nvme_cmd_status_str(sct_sc, blen, b),
+ "Status Code Type (upper 8 bits) and Status Code");
+ break;
+ default:
+ if (dt >= 0x80)
+ sgj_js_nv_ihex(jsp, jo2p, "vendor_specific_descriptor_type",
+ dt);
+ else
+ sgj_js_nv_ihex(jsp, jo2p, "unknown_descriptor_type", dt);
+ sgj_js_nv_hex_bytes(jsp, jo2p, "descriptor_hexbytes",
+ descp, desc_len);
+ processed = false;
+ break;
+ }
+ sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
+ }
+ return processed;
+}
+
+#define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d /* corresponding ASC is 0 */
+
+/* Fetch sense information */
+bool
+sgj_js_sense(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * sbp,
+ int sb_len)
+{
+ bool descriptor_format = false;
+ bool sdat_ovfl = false;
+ bool ret = true;
+ bool valid_info_fld;
+ int len, n;
+ uint32_t info;
+ uint8_t resp_code;
+ const char * ebp = NULL;
+ char ebuff[64];
+ char b[256];
+ struct sg_scsi_sense_hdr ssh;
+ static int blen = sizeof(b);
+ static int elen = sizeof(ebuff);
+
+ if ((NULL == sbp) || (sb_len < 1)) {
+ snprintf(ebuff, elen, "sense buffer empty\n");
+ ebp = ebuff;
+ ret = false;
+ goto fini;
+ }
+ resp_code = 0x7f & sbp[0];
+ valid_info_fld = !!(sbp[0] & 0x80);
+ len = sb_len;
+ if (! sg_scsi_normalize_sense(sbp, sb_len, &ssh)) {
+ ebp = "unable to normalize sense buffer";
+ ret = false;
+ goto fini;
+ }
+ /* We have been able to normalize the sense buffer */
+ switch (resp_code) {
+ case 0x70: /* fixed, current */
+ ebp = "Fixed format, current";
+ len = (sb_len > 7) ? (sbp[7] + 8) : sb_len;
+ len = (len > sb_len) ? sb_len : len;
+ sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false;
+ break;
+ case 0x71: /* fixed, deferred */
+ /* error related to a previous command */
+ ebp = "Fixed format, <<<deferred>>>";
+ len = (sb_len > 7) ? (sbp[7] + 8) : sb_len;
+ len = (len > sb_len) ? sb_len : len;
+ sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false;
+ break;
+ case 0x72: /* descriptor, current */
+ descriptor_format = true;
+ ebp = "Descriptor format, current";
+ sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false;
+ break;
+ case 0x73: /* descriptor, deferred */
+ descriptor_format = true;
+ ebp = "Descriptor format, <<<deferred>>>";
+ sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false;
+ break;
+ default:
+ sg_scnpr(ebuff, elen, "Unknown code: 0x%x", resp_code);
+ ebp = ebuff;
+ break;
+ }
+ sgj_js_nv_ihexstr(jsp, jop, "response_code", resp_code, NULL, ebp);
+ sgj_js_nv_b(jsp, jop, "descriptor_format", descriptor_format);
+ sgj_js_nv_ihex_nex(jsp, jop, "sdat_ovfl", sdat_ovfl, false,
+ "Sense data overflow");
+ sgj_js_nv_ihexstr(jsp, jop, "sense_key", ssh.sense_key, NULL,
+ sg_lib_sense_key_desc[ssh.sense_key]);
+ sgj_js_nv_ihex(jsp, jop, "additional_sense_code", ssh.asc);
+ sgj_js_nv_ihex(jsp, jop, "additional_sense_code_qualifier", ssh.ascq);
+ sgj_js_nv_s(jsp, jop, "additional_sense_str",
+ sg_get_additional_sense_str(ssh.asc, ssh.ascq, false,
+ blen, b));
+ if (descriptor_format) {
+ if (len > 8) {
+ ret = sgj_js_sense_descriptors(jsp, jop, &ssh, sbp + 8, len - 8);
+ if (ret == false) {
+ ebp = "unable to decode sense descriptor";
+ goto fini;
+ }
+ }
+ } else if ((len > 12) && (0 == ssh.asc) &&
+ (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
+ /* SAT ATA PASS-THROUGH fixed format */
+ sgj_js_nv_ihex(jsp, jop, "error", sbp[3]);
+ sgj_js_nv_ihex(jsp, jop, "status", sbp[4]);
+ sgj_js_nv_ihex(jsp, jop, "device", sbp[5]);
+ sgj_js_nv_i(jsp, jop, "extend", !! (0x80 & sbp[8]));
+ sgj_js_nv_i(jsp, jop, "count_upper_nonzero", !! (0x40 & sbp[8]));
+ sgj_js_nv_i(jsp, jop, "lba_upper_nonzero", !! (0x20 & sbp[8]));
+ sgj_js_nv_i(jsp, jop, "log_index", (0xf & sbp[8]));
+ sgj_js_nv_i(jsp, jop, "lba", sg_get_unaligned_le24(sbp + 9));
+ } else if (len > 2) { /* fixed format */
+ sgj_js_nv_i(jsp, jop, "valid", valid_info_fld);
+ sgj_js_nv_i(jsp, jop, "filemark", !! (sbp[2] & 0x80));
+ sgj_js_nv_ihex_nex(jsp, jop, "eom", !! (sbp[2] & 0x40),
+ false, "End Of Medium");
+ sgj_js_nv_ihex_nex(jsp, jop, "ili", !! (sbp[2] & 0x20),
+ false, "Incorrect Length Indicator");
+ info = sg_get_unaligned_be32(sbp + 3);
+ sgj_js_nv_ihex(jsp, jop, "information", info);
+ sgj_js_nv_ihex(jsp, jop, "additional_sense_length", sbp[7]);
+ if (sb_len > 11) {
+ info = sg_get_unaligned_be32(sbp + 8);
+ sgj_js_nv_ihex(jsp, jop, "command_specific_information", info);
+ }
+ if (sb_len > 14)
+ sgj_js_nv_ihex(jsp, jop, "field_replaceable_unit_code", sbp[14]);
+ if (sb_len > 17)
+ sgj_decode_sks(jsp, jop, sbp + 15, sb_len - 15, ssh.sense_key);
+ n = sbp[7];
+ n = (sb_len > n) ? n : sb_len;
+ sgj_js_nv_ihex(jsp, jop, "number_of_bytes_beyond_18",
+ (n > 18) ? n - 18 : 0);
+ } else {
+ snprintf(ebuff, sizeof(ebuff), "sb_len=%d too short", sb_len);
+ ebp = ebuff;
+ ret = false;
+ }
+fini:
+ if ((! ret) && ebp)
+ sgj_js_nv_s(jsp, jop, "sense_decode_error", ebp);
+ return ret;
+}
+
+#endif