• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <string.h>
16 #include <ctype.h>
17 
18 #include "sg_pr2serr.h"
19 #include "sg_json_builder.h"
20 
21 /*
22  * Some users of sg_pr2serr may not need fixed and descriptor sense decoded
23  * for JSON output. If the following define is commented out the effective
24  * compile size of this file is reduced by 800 lines plus dependencies on
25  * other large components of the sg3_utils library.
26  * Comment out the next line to remove dependency on sg_lib.h and its code.
27  */
28 #define SG_PRSE_SENSE_DECODE 1
29 
30 #ifdef SG_PRSE_SENSE_DECODE
31 #include "sg_lib.h"
32 #include "sg_lib_data.h"
33 #include "sg_unaligned.h"
34 #endif
35 
36 
37 #define sgj_opts_ev "SG3_UTILS_JSON_OPTS"
38 
39 /*
40  * #define json_serialize_mode_multiline     0
41  * #define json_serialize_mode_single_line   1
42  * #define json_serialize_mode_packed        2
43  *
44  * #define json_serialize_opt_CRLF                    (1 << 1)
45  * #define json_serialize_opt_pack_brackets           (1 << 2)
46  * #define json_serialize_opt_no_space_after_comma    (1 << 3)
47  * #define json_serialize_opt_no_space_after_colon    (1 << 4)
48  * #define json_serialize_opt_use_tabs                (1 << 5)
49  */
50 
51 
52 static const json_serialize_opts def_out_settings = {
53     json_serialize_mode_multiline,      /* one of serialize_mode_* */
54     0,                                  /* serialize_opt_* OR-ed together */
55     4                                   /* indent size */
56 };
57 
58 static int sgj_name_to_snake(const char * in, char * out, int maxlen_out);
59 
60 
61 /* Users of the sg_pr2serr.h header need this function definition */
62 int
pr2serr(const char * fmt,...)63 pr2serr(const char * fmt, ...)
64 {
65     va_list args;
66     int n;
67 
68     va_start(args, fmt);
69     n = vfprintf(stderr, fmt, args);
70     va_end(args);
71     return n;
72 }
73 
74 #ifndef SG_PRSE_SENSE_DECODE
75 
76 /* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of
77  * functions. Returns number of chars placed in cp excluding the
78  * trailing null char. So for cp_max_len > 0 the return value is always
79  * < cp_max_len; for cp_max_len <= 1 the return value is 0 and no chars are
80  * written to cp. Note this means that when cp_max_len = 1, this function
81  * assumes that cp[0] is the null character and does nothing (and returns
82  * 0). Linux kernel has a similar function called  scnprintf(). Public
83  * declaration in sg_pr2serr.h header  */
84 int
sg_scnpr(char * cp,int cp_max_len,const char * fmt,...)85 sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...)
86 {
87     va_list args;
88     int n;
89 
90     if (cp_max_len < 2)
91         return 0;
92     va_start(args, fmt);
93     n = vsnprintf(cp, cp_max_len, fmt, args);
94     va_end(args);
95     return (n < cp_max_len) ? n : (cp_max_len - 1);
96 }
97 
98 int
pr2ws(const char * fmt,...)99 pr2ws(const char * fmt, ...)
100 {
101     va_list args;
102     int n;
103 
104     va_start(args, fmt);
105     n = vfprintf(stderr, fmt, args);
106     va_end(args);
107     return n;
108 }
109 
110 #endif
111 
112 static bool
sgj_parse_opts(sgj_state * jsp,const char * j_optarg)113 sgj_parse_opts(sgj_state * jsp, const char * j_optarg)
114 {
115     bool bad_arg = false;
116     bool prev_negate = false;
117     bool negate;
118     int k, c;
119 
120     for (k = 0; j_optarg[k]; ++k) {     /* step over leading whitespace */
121         if (! isspace(j_optarg[k]))
122             break;
123     }
124     for ( ; j_optarg[k]; ++k) {
125         c = j_optarg[k];
126         negate = false;
127         switch (c) {
128         case '=':
129             if (0 == k)
130                 break;  /* allow and ignore leading '=' */
131             bad_arg = true;
132             if (0 == jsp->first_bad_char)
133                 jsp->first_bad_char = c;
134             break;
135         case '!':
136         case '~':
137         case '-':       /* '-' is probably most practical negation symbol */
138             negate = true;
139             break;
140         case '0':
141         case '2':
142             jsp->pr_indent_size = 2;
143             break;
144         case '3':
145             jsp->pr_indent_size = 3;
146             break;
147         case '4':
148             jsp->pr_indent_size = 4;
149             break;
150         case '8':
151             jsp->pr_indent_size = 8;
152             break;
153         case 'e':
154             jsp->pr_exit_status = ! prev_negate;
155             break;
156         case 'g':
157             jsp->pr_format = 'g';
158             break;
159         case 'h':
160             jsp->pr_hex = ! prev_negate;
161             break;
162         case 'k':
163             jsp->pr_packed = ! prev_negate;
164             break;
165         case 'l':
166             jsp->pr_leadin = ! prev_negate;
167             break;
168         case 'n':
169             jsp->pr_name_ex = ! prev_negate;
170             break;
171         case 'o':
172             jsp->pr_out_hr = ! prev_negate;
173             break;
174         case 'p':
175             jsp->pr_pretty = ! prev_negate;
176             break;
177         case 's':
178             jsp->pr_string = ! prev_negate;
179             break;
180         case 'v':
181             ++jsp->verbose;
182             break;
183         case 'y':
184             jsp->pr_format = 'g';
185             break;
186         case '?':
187             bad_arg = true;
188             jsp->first_bad_char = '\0';
189             break;
190         default:
191             bad_arg = true;
192             if (0 == jsp->first_bad_char)
193                 jsp->first_bad_char = c;
194             break;
195         }
196         prev_negate = negate ? ! prev_negate : false;
197     }
198     return ! bad_arg;
199 }
200 
201 char *
sg_json_usage(int char_if_not_j,char * b,int blen)202 sg_json_usage(int char_if_not_j, char * b, int blen)
203 {
204     int n = 0;
205     char short_opt = char_if_not_j ? char_if_not_j : 'j';
206 
207     if ((NULL == b) || (blen < 1))
208         goto fini;
209     n +=  sg_scnpr(b + n, blen - n, "JSON option usage:\n");
210     n +=  sg_scnpr(b + n, blen - n,
211                    "     --json[-JO] | -%c[JO]\n\n", short_opt);
212     n +=  sg_scnpr(b + n, blen - n, "  where JO is a string of one or more "
213                    "of:\n");
214     n +=  sg_scnpr(b + n, blen - n,
215                    "      0 | 2    tab pretty output to 2 spaces\n");
216     n +=  sg_scnpr(b + n, blen - n,
217                    "      4    tab pretty output to 4 spaces\n");
218     n +=  sg_scnpr(b + n, blen - n,
219                    "      8    tab pretty output to 8 spaces\n");
220     if (n >= (blen - 1))
221         goto fini;
222     n +=  sg_scnpr(b + n, blen - n,
223                    "      e    show 'exit_status' field\n");
224     n +=  sg_scnpr(b + n, blen - n,
225                    "      h    show 'hex' fields\n");
226     n +=  sg_scnpr(b + n, blen - n,
227                    "      k    packed, only non-pretty printed output\n");
228     n +=  sg_scnpr(b + n, blen - n,
229                    "      l    show lead-in fields (invocation "
230                    "information)\n");
231     n +=  sg_scnpr(b + n, blen - n,
232                    "      n    show 'name_extra' information fields\n");
233     n +=  sg_scnpr(b + n, blen - n,
234                    "      o    non-JSON output placed in 'output' array in "
235                    "lead-in\n");
236     if (n >= (blen - 1))
237         goto fini;
238     n +=  sg_scnpr(b + n, blen - n,
239                    "      p    pretty print the JSON output\n");
240     n +=  sg_scnpr(b + n, blen - n,
241                    "      s    show string output (usually fields named "
242                    "'meaning')\n");
243     n +=  sg_scnpr(b + n, blen - n,
244                    "      v    make JSON output more verbose\n");
245     n +=  sg_scnpr(b + n, blen - n,
246                    "      =    ignored if first character, else it's an "
247                    "error\n");
248     n +=  sg_scnpr(b + n, blen - n,
249                    "      - | ~ | !    toggle next letter setting\n");
250 
251     n +=  sg_scnpr(b + n, blen - n, "\nIn the absence of the optional JO "
252                    "argument, the following are set\non: 'elps' while the "
253                    "others are set off, and tabs are set to 4.\nBefore "
254                    "command line JO options are applied, the environment\n"
255                    "variable: %s is applied (if present). Note that\nno "
256                    "space is permitted between the short option ('-%c') "
257                    "and its\nargument ('JO').\n", sgj_opts_ev, short_opt);
258 fini:
259     return b;
260 }
261 
262 char *
sg_json_settings(sgj_state * jsp,char * b,int blen)263 sg_json_settings(sgj_state * jsp, char * b, int blen)
264 {
265     snprintf(b, blen, "%d%se%sh%sk%sl%sn%so%sp%ss%sv", jsp->pr_indent_size,
266              jsp->pr_exit_status ? "" : "-", jsp->pr_hex ? "" : "-",
267              jsp->pr_packed ? "" : "-", jsp->pr_leadin ? "" : "-",
268              jsp->pr_name_ex ? "" : "-", jsp->pr_out_hr ? "" : "-",
269              jsp->pr_pretty ? "" : "-", jsp->pr_string ? "" : "-",
270              jsp->verbose ? "" : "-");
271     return b;
272 }
273 
274 static void
sgj_def_opts(sgj_state * jsp)275 sgj_def_opts(sgj_state * jsp)
276 {
277     jsp->pr_as_json = true;
278     jsp->pr_exit_status = true;
279     jsp->pr_hex = false;
280     jsp->pr_leadin = true;
281     jsp->pr_out_hr = false;
282     jsp->pr_name_ex = false;
283     jsp->pr_packed = false;     /* 'k' control character, needs '-p' */
284     jsp->pr_pretty = true;
285     jsp->pr_string = true;
286     jsp->pr_format = 0;
287     jsp->first_bad_char = 0;
288     jsp->verbose = 0;
289     jsp->pr_indent_size = 4;
290 }
291 
292 bool
sgj_init_state(sgj_state * jsp,const char * j_optarg)293 sgj_init_state(sgj_state * jsp, const char * j_optarg)
294 {
295     const char * cp;
296 
297     sgj_def_opts(jsp);
298     jsp->basep = NULL;
299     jsp->out_hrp = NULL;
300     jsp->userp = NULL;
301 
302     cp = getenv(sgj_opts_ev);
303     if (cp) {
304         if (! sgj_parse_opts(jsp, cp)) {
305             pr2ws("error parsing %s environment variable, ignore\n",
306                   sgj_opts_ev);
307             sgj_def_opts(jsp);
308         }
309     }
310     return j_optarg ? sgj_parse_opts(jsp, j_optarg) : true;
311 }
312 
313 sgj_opaque_p
sgj_start_r(const char * util_name,const char * ver_str,int argc,char * argv[],sgj_state * jsp)314 sgj_start_r(const char * util_name, const char * ver_str, int argc,
315             char *argv[], sgj_state * jsp)
316 {
317     int k;
318     json_value * jvp = json_object_new(0);
319     json_value * jv2p = NULL;
320     json_value * jap = NULL;
321 
322     if (NULL == jvp)
323         return NULL;
324     if (NULL == jsp)
325         return jvp;
326 
327     jsp->basep = jvp;
328     if (jsp->pr_leadin) {
329         jap = json_array_new(0);
330         if  (NULL == jap) {
331             json_builder_free((json_value *)jvp);
332             return NULL;
333         }
334         /* assume rest of json_*_new() calls succeed */
335         json_array_push((json_value *)jap, json_integer_new(1));
336         json_array_push((json_value *)jap, json_integer_new(0));
337         json_object_push((json_value *)jvp, "json_format_version",
338                          (json_value *)jap);
339         if (util_name) {
340             jap = json_array_new(0);
341             if (argv) {
342                 for (k = 0; k < argc; ++k)
343                     json_array_push((json_value *)jap,
344                                     json_string_new(argv[k]));
345             }
346             jv2p = json_object_push((json_value *)jvp, "utility_invoked",
347                                     json_object_new(0));
348             json_object_push((json_value *)jv2p, "name",
349                              json_string_new(util_name));
350             if (ver_str)
351                 json_object_push((json_value *)jv2p, "version_date",
352                                  json_string_new(ver_str));
353             else
354                 json_object_push((json_value *)jv2p, "version_date",
355                                  json_string_new("0.0"));
356             json_object_push((json_value *)jv2p, "argv", jap);
357         }
358         if (jsp->verbose) {
359             const char * cp = getenv(sgj_opts_ev);
360             char b[32];
361 
362             json_object_push((json_value *)jv2p, "environment_variable_name",
363                              json_string_new(sgj_opts_ev));
364             json_object_push((json_value *)jv2p, "environment_variable_value",
365                              json_string_new(cp ? cp : "no available"));
366             sg_json_settings(jsp, b, sizeof(b));
367             json_object_push((json_value *)jv2p, "json_options",
368                              json_string_new(b));
369         }
370     } else {
371         if (jsp->pr_out_hr && util_name)
372             jv2p = json_object_push((json_value *)jvp, "utility_invoked",
373                                     json_object_new(0));
374     }
375     if (jsp->pr_out_hr && jv2p) {
376         jsp->out_hrp = json_object_push((json_value *)jv2p, "output",
377                                         json_array_new(0));
378         if (jsp->pr_leadin && (jsp->verbose > 3)) {
379             char * bp = (char *)calloc(4096, 1);
380 
381             if (bp) {
382                 sg_json_usage(0, bp, 4096);
383                 sgj_js_str_out(jsp, bp, strlen(bp));
384                 free(bp);
385             }
386         }
387     }
388     return jvp;
389 }
390 
391 void
sgj_js2file(sgj_state * jsp,sgj_opaque_p jop,int exit_status,FILE * fp)392 sgj_js2file(sgj_state * jsp, sgj_opaque_p jop, int exit_status, FILE * fp)
393 {
394     size_t len;
395     char * b;
396     json_value * jvp = (json_value *)(jop ? jop : jsp->basep);
397     json_serialize_opts out_settings;
398 
399     if (NULL == jvp) {
400         fprintf(fp, "%s: all NULL pointers ??\n", __func__);
401         return;
402     }
403     if ((NULL == jop) && jsp->pr_exit_status) {
404         char d[80];
405 
406 #ifdef SG_PRSE_SENSE_DECODE
407         if (sg_exit2str(exit_status, jsp->verbose, sizeof(d), d)) {
408             if (0 == strlen(d))
409                 strncpy(d, "no errors", sizeof(d) - 1);
410         } else
411             strncpy(d, "not available", sizeof(d) - 1);
412 #else
413         if (0 == exit_status)
414             strncpy(d, "no errors", sizeof(d) - 1);
415         else
416             snprintf(d, sizeof(d), "exit_status=%d", exit_status);
417 #endif
418         sgj_js_nv_istr(jsp, jop, "exit_status", exit_status, NULL, d);
419     }
420     memcpy(&out_settings, &def_out_settings, sizeof(out_settings));
421     if (jsp->pr_indent_size != def_out_settings.indent_size)
422         out_settings.indent_size = jsp->pr_indent_size;
423     if (! jsp->pr_pretty)
424         out_settings.mode = jsp->pr_packed ? json_serialize_mode_packed :
425                                 json_serialize_mode_single_line;
426 
427     len = json_measure_ex(jvp, out_settings);
428     if (len < 1)
429         return;
430     if (jsp->verbose > 3)
431         fprintf(fp, "%s: serialization length: %zu bytes\n", __func__, len);
432     b = (char *)calloc(len, 1);
433     if (NULL == b) {
434         if (jsp->verbose > 3)
435             pr2serr("%s: unable to get %zu bytes on heap\n", __func__, len);
436         return;
437     }
438 
439     json_serialize_ex(b, jvp, out_settings);
440     if (jsp->verbose > 3)
441         fprintf(fp, "json serialized:\n");
442     fprintf(fp, "%s\n", b);
443 }
444 
445 void
sgj_finish(sgj_state * jsp)446 sgj_finish(sgj_state * jsp)
447 {
448     if (jsp && jsp->basep) {
449         json_builder_free((json_value *)jsp->basep);
450         jsp->basep = NULL;
451         jsp->out_hrp = NULL;
452         jsp->userp = NULL;
453     }
454 }
455 
456 void
sgj_free_unattached(sgj_opaque_p jop)457 sgj_free_unattached(sgj_opaque_p jop)
458 {
459     if (jop)
460         json_builder_free((json_value *)jop);
461 }
462 
463 void
sgj_pr_hr(sgj_state * jsp,const char * fmt,...)464 sgj_pr_hr(sgj_state * jsp, const char * fmt, ...)
465 {
466     va_list args;
467 
468     if (jsp->pr_as_json && jsp->pr_out_hr) {
469         size_t len;
470         char b[256];
471 
472         va_start(args, fmt);
473         len = vsnprintf(b, sizeof(b), fmt, args);
474         if ((len > 0) && (len < sizeof(b))) {
475             const char * cp = b;
476 
477             /* remove up to two trailing linefeeds */
478             if (b[len - 1] == '\n') {
479                 --len;
480                 if (b[len - 1] == '\n')
481                     --len;
482                 b[len] = '\0';
483             }
484             /* remove leading linefeed, if present */
485             if ((len > 0) && ('\n' == b[0]))
486                 ++cp;
487             json_array_push((json_value *)jsp->out_hrp, json_string_new(cp));
488         }
489         va_end(args);
490     } else if (jsp->pr_as_json) {
491         va_start(args, fmt);
492         va_end(args);
493     } else {
494         va_start(args, fmt);
495         vfprintf(stdout, fmt, args);
496         va_end(args);
497     }
498 }
499 
500 /* jop will 'own' returned value (if non-NULL) */
501 sgj_opaque_p
sgj_named_subobject_r(sgj_state * jsp,sgj_opaque_p jop,const char * name)502 sgj_named_subobject_r(sgj_state * jsp, sgj_opaque_p jop, const char * name)
503 {
504     sgj_opaque_p resp = NULL;
505 
506     if (jsp && jsp->pr_as_json && name)
507         resp = json_object_push((json_value *)(jop ? jop : jsp->basep), name,
508                                 json_object_new(0));
509     return resp;
510 }
511 
512 sgj_opaque_p
sgj_snake_named_subobject_r(sgj_state * jsp,sgj_opaque_p jop,const char * conv2sname)513 sgj_snake_named_subobject_r(sgj_state * jsp, sgj_opaque_p jop,
514                             const char * conv2sname)
515 {
516     if (jsp && jsp->pr_as_json && conv2sname) {
517         int olen = strlen(conv2sname);
518         char * sname = (char *)malloc(olen + 8);
519         int nlen = sgj_name_to_snake(conv2sname, sname, olen + 8);
520 
521         if (nlen > 0)
522             return json_object_push((json_value *)(jop ? jop : jsp->basep),
523                                     sname, json_object_new(0));
524     }
525     return NULL;
526 }
527 
528 /* jop will 'own' returned value (if non-NULL) */
529 sgj_opaque_p
sgj_named_subarray_r(sgj_state * jsp,sgj_opaque_p jop,const char * name)530 sgj_named_subarray_r(sgj_state * jsp, sgj_opaque_p jop, const char * name)
531 {
532     sgj_opaque_p resp = NULL;
533 
534     if (jsp && jsp->pr_as_json && name)
535         resp = json_object_push((json_value *)(jop ? jop : jsp->basep), name,
536                                 json_array_new(0));
537     return resp;
538 }
539 
540 sgj_opaque_p
sgj_snake_named_subarray_r(sgj_state * jsp,sgj_opaque_p jop,const char * conv2sname)541 sgj_snake_named_subarray_r(sgj_state * jsp, sgj_opaque_p jop,
542                            const char * conv2sname)
543 {
544     if (jsp && jsp->pr_as_json && conv2sname) {
545         int olen = strlen(conv2sname);
546         char * sname = (char *)malloc(olen + 8);
547         int nlen = sgj_name_to_snake(conv2sname, sname, olen + 8);
548 
549         if (nlen > 0)
550             return json_object_push((json_value *)(jop ? jop : jsp->basep),
551                                     sname, json_array_new(0));
552     }
553     return NULL;
554 }
555 
556 /* Newly created object is un-attached to jsp->basep tree */
557 sgj_opaque_p
sgj_new_unattached_object_r(sgj_state * jsp)558 sgj_new_unattached_object_r(sgj_state * jsp)
559 {
560     return (jsp && jsp->pr_as_json) ? json_object_new(0) : NULL;
561 }
562 
563 /* Newly created array is un-attached to jsp->basep tree */
564 sgj_opaque_p
sgj_new_unattached_array_r(sgj_state * jsp)565 sgj_new_unattached_array_r(sgj_state * jsp)
566 {
567     return (jsp && jsp->pr_as_json) ? json_array_new(0) : NULL;
568 }
569 
570 sgj_opaque_p
sgj_js_nv_s(sgj_state * jsp,sgj_opaque_p jop,const char * name,const char * value)571 sgj_js_nv_s(sgj_state * jsp, sgj_opaque_p jop, const char * name,
572             const char * value)
573 {
574     if (jsp && jsp->pr_as_json && value) {
575         if (name)
576             return json_object_push((json_value *)(jop ? jop : jsp->basep),
577                                     name, json_string_new(value));
578         else
579             return json_array_push((json_value *)(jop ? jop : jsp->basep),
580                                    json_string_new(value));
581     } else
582         return NULL;
583 }
584 
585 sgj_opaque_p
sgj_js_nv_s_len(sgj_state * jsp,sgj_opaque_p jop,const char * name,const char * value,int slen)586 sgj_js_nv_s_len(sgj_state * jsp, sgj_opaque_p jop, const char * name,
587                 const char * value, int slen)
588 {
589     int k;
590 
591     if (jsp && jsp->pr_as_json && value && (slen >= 0)) {
592         for (k = 0; k < slen; ++k) {    /* don't want '\0' in value string */
593             if (0 == value[k])
594                 break;
595         }
596         if (name)
597             return json_object_push((json_value *)(jop ? jop : jsp->basep),
598                                     name, json_string_new_length(k, value));
599         else
600             return json_array_push((json_value *)(jop ? jop : jsp->basep),
601                                    json_string_new_length(k, value));
602     } else
603         return NULL;
604 }
605 
606 sgj_opaque_p
sgj_js_nv_i(sgj_state * jsp,sgj_opaque_p jop,const char * name,int64_t value)607 sgj_js_nv_i(sgj_state * jsp, sgj_opaque_p jop, const char * name,
608             int64_t value)
609 {
610     if (jsp && jsp->pr_as_json) {
611         if (name)
612             return json_object_push((json_value *)(jop ? jop : jsp->basep),
613                                     name, json_integer_new(value));
614         else
615             return json_array_push((json_value *)(jop ? jop : jsp->basep),
616                                    json_integer_new(value));
617     }
618     else
619         return NULL;
620 }
621 
622 sgj_opaque_p
sgj_js_nv_b(sgj_state * jsp,sgj_opaque_p jop,const char * name,bool value)623 sgj_js_nv_b(sgj_state * jsp, sgj_opaque_p jop, const char * name, bool value)
624 {
625     if (jsp && jsp->pr_as_json) {
626         if (name)
627             return json_object_push((json_value *)(jop ? jop : jsp->basep),
628                                     name, json_boolean_new(value));
629         else
630             return json_array_push((json_value *)(jop ? jop : jsp->basep),
631                                    json_boolean_new(value));
632     } else
633         return NULL;
634 }
635 
636 /* jop will 'own' ua_jop (if returned value is non-NULL) */
637 sgj_opaque_p
sgj_js_nv_o(sgj_state * jsp,sgj_opaque_p jop,const char * name,sgj_opaque_p ua_jop)638 sgj_js_nv_o(sgj_state * jsp, sgj_opaque_p jop, const char * name,
639             sgj_opaque_p ua_jop)
640 {
641     if (jsp && jsp->pr_as_json && ua_jop) {
642         if (name)
643             return json_object_push((json_value *)(jop ? jop : jsp->basep),
644                                     name, (json_value *)ua_jop);
645         else
646             return json_array_push((json_value *)(jop ? jop : jsp->basep),
647                                    (json_value *)ua_jop);
648     } else
649         return NULL;
650 }
651 
652 void
sgj_js_nv_ihex(sgj_state * jsp,sgj_opaque_p jop,const char * name,uint64_t value)653 sgj_js_nv_ihex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
654                uint64_t value)
655 {
656     if ((NULL == jsp) || (NULL == name) || (! jsp->pr_as_json))
657         return;
658     else if (jsp->pr_hex)  {
659         sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
660         char b[64];
661 
662         if (NULL == jo2p)
663             return;
664         sgj_js_nv_i(jsp, jo2p, "i", (int64_t)value);
665         snprintf(b, sizeof(b), "%" PRIx64, value);
666         sgj_js_nv_s(jsp, jo2p, "hex", b);
667     } else
668         sgj_js_nv_i(jsp, jop, name, (int64_t)value);
669 }
670 
671 static const char * sc_mn_s = "meaning";
672 
673 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)674 sgj_js_nv_istr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
675                int64_t val_i, const char * str_name, const char * val_s)
676 {
677     if ((NULL == jsp) || (! jsp->pr_as_json))
678         return;
679     else if (val_s && jsp->pr_string) {
680         sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
681 
682         if (NULL == jo2p)
683             return;
684         sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
685         sgj_js_nv_s(jsp, jo2p, str_name ? str_name : sc_mn_s, val_s);
686     } else
687         sgj_js_nv_i(jsp, jop, name, val_i);
688 }
689 
690 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)691 sgj_js_nv_ihexstr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
692                   int64_t val_i, const char * str_name, const char * val_s)
693 {
694     bool as_str;
695 
696     if ((NULL == jsp) || (! jsp->pr_as_json))
697         return;
698     as_str = jsp->pr_string && val_s;
699     if ((! jsp->pr_hex) && (! as_str))
700         sgj_js_nv_i(jsp, jop, name, val_i);
701     else {
702         char b[64];
703         sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop, name);
704 
705         if (NULL == jo2p)
706             return;
707         sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
708         if (jsp->pr_hex) {
709             snprintf(b, sizeof(b), "%" PRIx64, val_i);
710             sgj_js_nv_s(jsp, jo2p, "hex", b);
711         }
712         if (as_str)
713             sgj_js_nv_s(jsp, jo2p, str_name ? str_name : sc_mn_s, val_s);
714     }
715 }
716 
717 static const char * sc_nex_s = "name_extra";
718 
719 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)720 sgj_js_nv_ihex_nex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
721                    int64_t val_i, bool hex_as_well, const char * nex_s)
722 {
723     bool as_hex, as_nex;
724 
725     if ((NULL == jsp) || (! jsp->pr_as_json))
726         return;
727     as_hex = jsp->pr_hex && hex_as_well;
728     as_nex = jsp->pr_name_ex && nex_s;
729     if (! (as_hex || as_nex))
730         sgj_js_nv_i(jsp, jop, name, val_i);
731     else {
732         char b[64];
733         sgj_opaque_p jo2p =
734                  sgj_named_subobject_r(jsp, jop, name);
735 
736         if (NULL == jo2p)
737             return;
738         sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
739         if (as_hex) {
740             snprintf(b, sizeof(b), "%" PRIx64, val_i);
741             sgj_js_nv_s(jsp, jo2p, "hex", b);
742         }
743         if (as_nex)
744             sgj_js_nv_s(jsp, jo2p, sc_nex_s, nex_s);
745     }
746 }
747 
748 #ifndef SG_PRSE_SENSE_DECODE
749 static void
h2str(const uint8_t * byte_arr,int num_bytes,char * bp,int blen)750 h2str(const uint8_t * byte_arr, int num_bytes, char * bp, int blen)
751 {
752     int j, k, n;
753 
754     for (k = 0, n = 0; (k < num_bytes) && (n < blen); ) {
755         j = sg_scnpr(bp + n, blen - n, "%02x ", byte_arr[k]);
756         if (j < 2)
757             break;
758         n += j;
759         ++k;
760         if ((0 == (k % 8)) && (k < num_bytes) && (n < blen)) {
761             bp[n++] = ' ';
762         }
763     }
764     j = strlen(bp);
765     if ((j > 0) && (' ' == bp[j - 1]))
766         bp[j - 1] = '\0';    /* chop off trailing space */
767 }
768 #endif
769 
770 /* Add hex byte strings irrespective of jsp->pr_hex setting. */
771 void
sgj_js_nv_hex_bytes(sgj_state * jsp,sgj_opaque_p jop,const char * name,const uint8_t * byte_arr,int num_bytes)772 sgj_js_nv_hex_bytes(sgj_state * jsp, sgj_opaque_p jop, const char * name,
773                     const uint8_t * byte_arr, int num_bytes)
774 {
775     int blen = num_bytes * 4;
776     char * bp;
777 
778     if ((NULL == jsp) || (! jsp->pr_as_json))
779         return;
780     bp = (char *)calloc(blen + 4, 1);
781     if (bp) {
782 #ifdef SG_PRSE_SENSE_DECODE
783         hex2str(byte_arr, num_bytes, NULL, 2, blen, bp);
784 #else
785         h2str(byte_arr, num_bytes, bp, blen);
786 #endif
787         sgj_js_nv_s(jsp, jop, name, bp);
788         free(bp);
789     }
790 }
791 
792 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)793 sgj_js_nv_ihexstr_nex(sgj_state * jsp, sgj_opaque_p jop, const char * name,
794                       int64_t val_i, bool hex_as_well, const char * str_name,
795                       const char * val_s, const char * nex_s)
796 {
797     bool as_hex = jsp->pr_hex && hex_as_well;
798     bool as_str = jsp->pr_string && val_s;
799     bool as_nex = jsp->pr_name_ex && nex_s;
800     const char * sname =  str_name ? str_name : sc_mn_s;
801 
802     if ((NULL == jsp) || (! jsp->pr_as_json))
803         return;
804     if (! (as_hex || as_nex || as_str))
805         sgj_js_nv_i(jsp, jop, name, val_i);
806     else {
807         char b[64];
808         sgj_opaque_p jo2p =
809                  sgj_named_subobject_r(jsp, jop, name);
810 
811         if (NULL == jo2p)
812             return;
813         sgj_js_nv_i(jsp, jo2p, "i", (int64_t)val_i);
814         if (as_nex) {
815             if (as_hex) {
816                 snprintf(b, sizeof(b), "%" PRIx64, val_i);
817                 sgj_js_nv_s(jsp, jo2p, "hex", b);
818             }
819             if (as_str) {
820                 sgj_js_nv_s(jsp, jo2p, sname, val_s);
821             }
822             sgj_js_nv_s(jsp, jo2p, sc_nex_s, nex_s);
823         } else if (as_hex) {
824             snprintf(b, sizeof(b), "%" PRIx64, val_i);
825             sgj_js_nv_s(jsp, jo2p, "hex", b);
826             if (as_str)
827                 sgj_js_nv_s(jsp, jo2p, sname, val_s);
828         } else if (as_str)
829             sgj_js_nv_s(jsp, jo2p, sname, val_s);
830     }
831 }
832 
833 /* Treat '\n' in sp as line breaks. Consumes characters from sp until either
834  * a '\0' is found or slen is exhausted. Add each line to jsp->out_hrp JSON
835  * array (if conditions met). */
836 void
sgj_js_str_out(sgj_state * jsp,const char * sp,int slen)837 sgj_js_str_out(sgj_state * jsp, const char * sp, int slen)
838 {
839     char c;
840     int k, n;
841     const char * prev_sp = sp;
842     const char * cur_sp = sp;
843 
844     if ((NULL == jsp) || (NULL == jsp->out_hrp) || (! jsp->pr_as_json) ||
845         (! jsp->pr_out_hr))
846         return;
847     for (k = 0; k < slen; ++k, ++cur_sp) {
848         c = *cur_sp;
849         if ('\0' == c)
850             break;
851         else if ('\n' == c) {
852             n = cur_sp - prev_sp;
853             /* when name is NULL, add to array (jsp->out_hrp) */
854             sgj_js_nv_s_len(jsp, jsp->out_hrp, NULL, prev_sp, n);
855             prev_sp = cur_sp + 1;
856         }
857     }
858     if (prev_sp < cur_sp) {
859         n = cur_sp - prev_sp;
860         sgj_js_nv_s_len(jsp, jsp->out_hrp, NULL, prev_sp, n);
861     }
862 }
863 
864 char *
sgj_convert_to_snake_name(const char * in_name,char * sname,int max_sname_len)865 sgj_convert_to_snake_name(const char * in_name, char * sname,
866                           int max_sname_len)
867 {
868     sgj_name_to_snake(in_name, sname, max_sname_len);
869     return sname;
870 }
871 
872 bool
sgj_is_snake_name(const char * in_name)873 sgj_is_snake_name(const char * in_name)
874 {
875     size_t k;
876     size_t ln = strlen(in_name);
877     char c;
878 
879     for (k = 0; k < ln; ++k) {
880         c = in_name[k];
881         if (((c >= '0') && (c <= '9')) ||
882             ((c >= 'a') && (c <= 'z')) ||
883             (c == '_'))
884             continue;
885         else
886             return false;
887     }
888     return true;
889 }
890 
891 /* This function tries to convert the 'in' C string to "snake_case"
892  * convention so the output 'out' only contains lower case ASCII letters,
893  * numerals and "_" as a separator. Any leading or trailing underscores
894  * are removed as are repeated underscores (e.g. "_Snake __ case" becomes
895  * "snake_case"). Parentheses and the characters between them are removed.
896  * Returns number of characters placed in 'out' excluding the trailing
897  * NULL */
898 static int
sgj_name_to_snake(const char * in,char * out,int maxlen_out)899 sgj_name_to_snake(const char * in, char * out, int maxlen_out)
900 {
901     bool prev_underscore = false;
902     bool within_paren = false;
903     int c, k, j, inlen;
904 
905     if (maxlen_out < 2) {
906         if (maxlen_out == 1)
907             out[0] = '\0';
908         return 0;
909     }
910     inlen = strlen(in);
911     for (k = 0, j = 0; (k < inlen) && (j < maxlen_out); ++k) {
912         c = in[k];
913         if (within_paren) {
914             if (')' == c)
915                 within_paren = false;
916             continue;
917         }
918         if (isalnum(c)) {
919             out[j++] = isupper(c) ? tolower(c) : c;
920             prev_underscore = false;
921         } else if ('(' == c)
922             within_paren = true;
923         else if ((j > 0) && (! prev_underscore)) {
924             out[j++] = '_';
925             prev_underscore = true;
926         }
927         /* else we are skipping character 'c' */
928     }
929     if (j == maxlen_out)
930         out[--j] = '\0';
931     /* trim of trailing underscores (might have been spaces) */
932     for (k = j - 1; k >= 0; --k) {
933         if (out[k] != '_')
934             break;
935     }
936     if (k < 0)
937         k = 0;
938     else
939         ++k;
940     out[k] = '\0';
941     return k;
942 }
943 
944 static int
sgj_jtype_to_s(char * b,int blen_max,json_value * jvp)945 sgj_jtype_to_s(char * b, int blen_max, json_value * jvp)
946 {
947     json_type jtype = jvp ? jvp->type : json_none;
948 
949     switch (jtype) {
950     case json_string:
951         return sg_scnpr(b, blen_max, "%s", jvp->u.string.ptr);
952     case json_integer:
953         return sg_scnpr(b, blen_max, "%" PRIi64, jvp->u.integer);
954     case json_boolean:
955         return sg_scnpr(b, blen_max, "%s", jvp->u.boolean ? "true" : "false");
956     case json_none:
957     default:
958         if ((blen_max > 0) && ('\0' != b[0]))
959             b[0] = '\0';
960         break;
961     }
962     return 0;
963 }
964 
965 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)966 sgj_haj_helper(char * b, int blen_max, const char * name,
967                enum sgj_separator_t sep, bool use_jvp,
968                json_value * jvp, int64_t val_instead)
969 {
970     int n = 0;
971 
972     if (name) {
973         n += sg_scnpr(b + n, blen_max - n, "%s", name);
974         switch (sep) {
975         case SGJ_SEP_NONE:
976             break;
977         case SGJ_SEP_SPACE_1:
978             n += sg_scnpr(b + n, blen_max - n, " ");
979             break;
980         case SGJ_SEP_SPACE_2:
981             n += sg_scnpr(b + n, blen_max - n, "  ");
982             break;
983         case SGJ_SEP_SPACE_3:
984             n += sg_scnpr(b + n, blen_max - n, "   ");
985             break;
986         case SGJ_SEP_SPACE_4:
987             n += sg_scnpr(b + n, blen_max - n, "    ");
988             break;
989         case SGJ_SEP_EQUAL_NO_SPACE:
990             n += sg_scnpr(b + n, blen_max - n, "=");
991             break;
992         case SGJ_SEP_EQUAL_1_SPACE:
993             n += sg_scnpr(b + n, blen_max - n, "= ");
994             break;
995         case SGJ_SEP_COLON_NO_SPACE:
996             n += sg_scnpr(b + n, blen_max - n, ":");
997             break;
998         case SGJ_SEP_COLON_1_SPACE:
999             n += sg_scnpr(b + n, blen_max - n, ": ");
1000             break;
1001         default:
1002             break;
1003         }
1004     }
1005     if (use_jvp)
1006         n += sgj_jtype_to_s(b + n, blen_max - n, jvp);
1007     else
1008         n += sg_scnpr(b + n, blen_max - n, "%" PRIi64, val_instead);
1009     return n;
1010 }
1011 
1012 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)1013 sgj_haj_xx(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1014            const char * name, enum sgj_separator_t sep, json_value * jvp,
1015            bool hex_as_well, const char * val_s, const char * nex_s)
1016 {
1017     bool eaten = false;
1018     bool as_json = (jsp && jsp->pr_as_json);
1019     bool done;
1020     int n;
1021     json_type jtype = jvp ? jvp->type : json_none;
1022     char b[256];
1023     char jname[96];
1024     static const int blen = sizeof(b);
1025 
1026     if (leadin_sp > 128)
1027         leadin_sp = 128;
1028     for (n = 0; n < leadin_sp; ++n)
1029         b[n] = ' ';
1030     b[n] = '\0';
1031     if (NULL == name) {
1032         if ((! as_json) || (jsp && jsp->pr_out_hr)) {
1033             n += sgj_jtype_to_s(b + n, blen - n, jvp);
1034             printf("%s\n", b);
1035         }
1036         if (NULL == jop) {
1037             if (as_json && jsp->pr_out_hr) {
1038                 eaten = true;
1039                 json_array_push((json_value *)jsp->out_hrp,
1040                                 jvp ? jvp : json_null_new());
1041             }
1042         } else {        /* assume jop points to named array */
1043             if (as_json) {
1044                 eaten = true;
1045                 json_array_push((json_value *)jop,
1046                                 jvp ? jvp : json_null_new());
1047             }
1048         }
1049         goto fini;
1050     }
1051     if (as_json) {
1052         int k;
1053 
1054         if (NULL == jop)
1055             jop = jsp->basep;
1056         k = sgj_name_to_snake(name, jname, sizeof(jname));
1057         if (k > 0) {
1058             done = false;
1059             if (nex_s && (strlen(nex_s) > 0)) {
1060                 switch (jtype) {
1061                 case json_string:
1062                     break;
1063                 case json_integer:
1064                     sgj_js_nv_ihexstr_nex(jsp, jop, jname, jvp->u.integer,
1065                                           hex_as_well, sc_mn_s, val_s, nex_s);
1066                     done = true;
1067                     break;
1068                 case json_boolean:
1069                     sgj_js_nv_ihexstr_nex(jsp, jop, jname, jvp->u.boolean,
1070                                           false, sc_mn_s, val_s, nex_s);
1071                     done = true;
1072                     break;
1073                 case json_none:
1074                 default:
1075                     break;
1076                 }
1077             } else {
1078                 switch (jtype) {
1079                 case json_string:
1080                     break;
1081                 case json_integer:
1082                     if (hex_as_well) {
1083                         sgj_js_nv_ihexstr(jsp, jop, jname, jvp->u.integer,
1084                                           sc_mn_s, val_s);
1085                         done = true;
1086                     }
1087                     break;
1088                 case json_none:
1089                 default:
1090                     break;
1091                 }
1092             }
1093             if (! done) {
1094                 eaten = true;
1095                 json_object_push((json_value *)jop, jname,
1096                                  jvp ? jvp : json_null_new());
1097             }
1098         }
1099     }
1100     if (jvp && ((as_json && jsp->pr_out_hr) || (! as_json)))
1101         n += sgj_haj_helper(b + n, blen - n, name, sep, true, jvp, 0);
1102 
1103     if (as_json && jsp->pr_out_hr)
1104         json_array_push((json_value *)jsp->out_hrp, json_string_new(b));
1105     if (! as_json)
1106         printf("%s\n", b);
1107 fini:
1108     if (jvp && (! eaten))
1109         json_builder_free((json_value *)jvp);
1110 }
1111 
1112 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)1113 sgj_haj_vs(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1114            const char * name, enum sgj_separator_t sep, const char * value)
1115 {
1116     json_value * jvp;
1117 
1118     /* make json_value even if jsp->pr_as_json is false */
1119     jvp = value ? json_string_new(value) : NULL;
1120     sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, false, NULL, NULL);
1121 }
1122 
1123 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)1124 sgj_haj_vi(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1125           const char * name, enum sgj_separator_t sep, int64_t value,
1126            bool hex_as_well)
1127 {
1128     json_value * jvp;
1129 
1130     jvp = json_integer_new(value);
1131     sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, NULL, NULL);
1132 }
1133 
1134 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)1135 sgj_haj_vistr(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1136               const char * name, enum sgj_separator_t sep, int64_t value,
1137               bool hex_as_well, const char * val_s)
1138 {
1139     json_value * jvp;
1140 
1141     jvp = json_integer_new(value);
1142     sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, val_s,
1143                  NULL);
1144 }
1145 
1146 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)1147 sgj_haj_vi_nex(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1148                const char * name, enum sgj_separator_t sep,
1149                int64_t value, bool hex_as_well, const char * nex_s)
1150 {
1151     json_value * jvp;
1152 
1153     jvp = json_integer_new(value);
1154     sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, NULL, nex_s);
1155 }
1156 
1157 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)1158 sgj_haj_vistr_nex(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1159                   const char * name, enum sgj_separator_t sep,
1160                   int64_t value, bool hex_as_well,
1161                   const char * val_s, const char * nex_s)
1162 {
1163     json_value * jvp;
1164 
1165     jvp = json_integer_new(value);
1166     sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, hex_as_well, val_s,
1167                nex_s);
1168 }
1169 
1170 void
sgj_haj_vb(sgj_state * jsp,sgj_opaque_p jop,int leadin_sp,const char * name,enum sgj_separator_t sep,bool value)1171 sgj_haj_vb(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1172            const char * name, enum sgj_separator_t sep, bool value)
1173 {
1174     json_value * jvp;
1175 
1176     jvp = json_boolean_new(value);
1177     sgj_haj_xx(jsp, jop, leadin_sp, name, sep, jvp, false, NULL, NULL);
1178 }
1179 
1180 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)1181 sgj_haj_subo_r(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
1182                const char * name, enum sgj_separator_t sep, int64_t value,
1183                bool hex_as_well)
1184 {
1185     bool as_json = (jsp && jsp->pr_as_json);
1186     int n = 0;
1187     sgj_opaque_p jo2p;
1188     char b[256];
1189     static const int blen = sizeof(b);
1190 
1191     if (NULL == name)
1192         return NULL;
1193     for (n = 0; n < leadin_sp; ++n)
1194         b[n] = ' ';
1195     b[n] = '\0';
1196     if ((! as_json) || (jsp && jsp->pr_out_hr))
1197         n += sgj_haj_helper(b + n, blen - n, name, sep, false, NULL, value);
1198 
1199     if (as_json && jsp->pr_out_hr)
1200         json_array_push((json_value *)jsp->out_hrp, json_string_new(b));
1201     if (! as_json)
1202         printf("%s\n", b);
1203 
1204     if (as_json) {
1205         sgj_name_to_snake(name, b, blen);
1206         jo2p = sgj_named_subobject_r(jsp, jop, b);
1207         if (jo2p) {
1208             sgj_js_nv_i(jsp, jo2p, "i", value);
1209             if (hex_as_well && jsp->pr_hex) {
1210                 snprintf(b, blen, "%" PRIx64, value);
1211                 sgj_js_nv_s(jsp, jo2p, "hex", b);
1212             }
1213         }
1214         return jo2p;
1215     }
1216     return NULL;
1217 }
1218 
1219 #ifdef SG_PRSE_SENSE_DECODE
1220 
1221 static const char * dtsp = "descriptor too short";
1222 static const char * sksvp = "sense-key specific valid";
1223 static const char * ddep = "designation_descriptor_error";
1224 static const char * naa_exp = "Network Address Authority";
1225 static const char * aoi_exp = "IEEE-Administered Organizational Identifier";
1226 
1227 bool
sgj_js_designation_descriptor(sgj_state * jsp,sgj_opaque_p jop,const uint8_t * ddp,int dd_len)1228 sgj_js_designation_descriptor(sgj_state * jsp, sgj_opaque_p jop,
1229                               const uint8_t * ddp, int dd_len)
1230 {
1231     int p_id, piv, c_set, assoc, desig_type, d_id, naa;
1232     int n, aoi, vsi, dlen;
1233     uint64_t ull;
1234     const uint8_t * ip;
1235     char e[80];
1236     char b[256];
1237     const char * cp;
1238     const char * naa_sp;
1239     sgj_opaque_p jo2p;
1240     static const int blen = sizeof(b);
1241     static const int elen = sizeof(e);
1242 
1243     if (dd_len < 4) {
1244         sgj_js_nv_s(jsp, jop, ddep, "too short");
1245         return false;
1246     }
1247     dlen = ddp[3];
1248     if (dlen > (dd_len - 4)) {
1249         snprintf(e, elen, "too long: says it is %d bytes, but given %d "
1250                  "bytes\n", dlen, dd_len - 4);
1251         sgj_js_nv_s(jsp, jop, ddep, e);
1252         return false;
1253     }
1254     ip = ddp + 4;
1255     p_id = ((ddp[0] >> 4) & 0xf);
1256     c_set = (ddp[0] & 0xf);
1257     piv = ((ddp[1] & 0x80) ? 1 : 0);
1258     assoc = ((ddp[1] >> 4) & 0x3);
1259     desig_type = (ddp[1] & 0xf);
1260     cp = sg_get_desig_assoc_str(assoc);
1261     if (assoc == 3)
1262         cp = "Reserved [0x3]";    /* should not happen */
1263     sgj_js_nv_ihexstr(jsp, jop, "association", assoc, NULL, cp);
1264     cp = sg_get_desig_type_str(desig_type);
1265     if (NULL == cp)
1266         cp = "unknown";
1267     sgj_js_nv_ihexstr(jsp, jop, "designator_type", desig_type,
1268                        NULL, cp);
1269     cp = sg_get_desig_code_set_str(c_set);
1270     if (NULL == cp)
1271         cp = "unknown";
1272     sgj_js_nv_ihexstr(jsp, jop, "code_set", desig_type,
1273                       NULL, cp);
1274     sgj_js_nv_ihex_nex(jsp, jop, "piv", piv, false,
1275                        "Protocol Identifier Valid");
1276     sg_get_trans_proto_str(p_id, elen, e);
1277     sgj_js_nv_ihexstr(jsp, jop, "protocol_identifier", p_id, NULL, e);
1278     switch (desig_type) {
1279     case 0: /* vendor specific */
1280         sgj_js_nv_hex_bytes(jsp, jop, "vendor_specific_hexbytes", ip, dlen);
1281         break;
1282     case 1: /* T10 vendor identification */
1283         n = (dlen < 8) ? dlen : 8;
1284         snprintf(b, blen, "%.*s", n, ip);
1285         sgj_js_nv_s(jsp, jop, "t10_vendor_identification", b);
1286         b[0] = '\0';
1287         if (dlen > 8)
1288             snprintf(b, blen, "%.*s", dlen - 8, ip + 8);
1289         sgj_js_nv_s(jsp, jop, "vendor_specific_identifier", b);
1290         break;
1291     case 2: /* EUI-64 based */
1292         sgj_js_nv_i(jsp, jop, "eui_64_based_designator_length", dlen);
1293         ull = sg_get_unaligned_be64(ip);
1294         switch (dlen) {
1295         case 8:
1296             sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ull);
1297             break;
1298         case 12:
1299             sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ull);
1300             sgj_js_nv_ihex(jsp, jop, "directory_id",
1301                             sg_get_unaligned_be32(ip + 8));
1302             break;
1303         case 16:
1304             sgj_js_nv_ihex(jsp, jop, "identifier_extension", ull);
1305             sgj_js_nv_ihex(jsp, jop, "ieee_identifier",
1306                             sg_get_unaligned_be64(ip + 8));
1307             break;
1308         default:
1309             sgj_js_nv_s(jsp, jop, "eui_64", "decoding failed");
1310             break;
1311         }
1312         break;
1313     case 3: /* NAA <n> */
1314         if (jsp->pr_hex)
1315             sgj_js_nv_hex_bytes(jsp, jop, "full_naa_hexbytes", ip, dlen);
1316         naa = (ip[0] >> 4) & 0xff;
1317         switch (naa) {
1318         case 2:
1319             naa_sp = "IEEE Extended";
1320             sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
1321                                   naa_exp);
1322             d_id = (((ip[0] & 0xf) << 8) | ip[1]);
1323             sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_a", d_id);
1324             aoi = sg_get_unaligned_be24(ip + 2);
1325             sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
1326             vsi = sg_get_unaligned_be24(ip + 5);
1327             sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_b", vsi);
1328             break;
1329         case 3:
1330             naa_sp = "Locally Assigned";
1331             sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
1332                                   naa_exp);
1333             ull = sg_get_unaligned_be64(ip + 0) & 0xfffffffffffffffULL;
1334             sgj_js_nv_ihex(jsp, jop, "locally_administered_value", ull);
1335             break;
1336         case 5:
1337             naa_sp = "IEEE Registered";
1338             sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
1339                                   naa_exp);
1340             aoi = (sg_get_unaligned_be32(ip + 0) >> 4) & 0xffffff;
1341             sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
1342             ull = sg_get_unaligned_be48(ip + 2) & 0xfffffffffULL;
1343             sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier", ull);
1344             break;
1345         case 6:
1346             naa_sp = "IEEE Registered Extended";
1347             sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp,
1348                                   naa_exp);
1349             aoi = (sg_get_unaligned_be32(ip + 0) >> 4) & 0xffffff;
1350             sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_exp);
1351             ull = sg_get_unaligned_be48(ip + 2) & 0xfffffffffULL;
1352             sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier", ull);
1353             ull = sg_get_unaligned_be64(ip + 8);
1354             sgj_js_nv_ihex(jsp, jop, "vendor_specific_identifier_extension",
1355                            ull);
1356             break;
1357         default:
1358             snprintf(b, blen, "unknown NAA value=0x%x", naa);
1359             sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, true, NULL, b,
1360                                   naa_exp);
1361             sgj_js_nv_hex_bytes(jsp, jop, "full_naa_hexbytes", ip, dlen);
1362             break;
1363         }
1364         break;
1365     case 4: /* Relative target port */
1366         if (jsp->pr_hex)
1367             sgj_js_nv_hex_bytes(jsp, jop, "relative_target_port_hexbytes",
1368                                 ip, dlen);
1369         sgj_js_nv_ihex(jsp, jop, "relative_target_port_identifier",
1370                        sg_get_unaligned_be16(ip + 2));
1371         break;
1372     case 5: /* (primary) Target port group */
1373         if (jsp->pr_hex)
1374             sgj_js_nv_hex_bytes(jsp, jop, "target_port_group_hexbytes",
1375                                 ip, dlen);
1376         sgj_js_nv_ihex(jsp, jop, "target_port_group",
1377                        sg_get_unaligned_be16(ip + 2));
1378         break;
1379     case 6: /* Logical unit group */
1380         if (jsp->pr_hex)
1381             sgj_js_nv_hex_bytes(jsp, jop, "logical_unit_group_hexbytes",
1382                                 ip, dlen);
1383         sgj_js_nv_ihex(jsp, jop, "logical_unit_group",
1384                        sg_get_unaligned_be16(ip + 2));
1385         break;
1386     case 7: /* MD5 logical unit identifier */
1387         sgj_js_nv_hex_bytes(jsp, jop, "md5_logical_unit_hexbytes",
1388                             ip, dlen);
1389         break;
1390     case 8: /* SCSI name string */
1391         if (jsp->pr_hex)
1392             sgj_js_nv_hex_bytes(jsp, jop, "scsi_name_string_hexbytes",
1393                                 ip, dlen);
1394         snprintf(b, blen, "%.*s", dlen, ip);
1395         sgj_js_nv_s(jsp, jop, "scsi_name_string", b);
1396         break;
1397     case 9: /* Protocol specific port identifier */
1398         if (jsp->pr_hex)
1399             sgj_js_nv_hex_bytes(jsp, jop,
1400                                 "protocol_specific_port_identifier_hexbytes",
1401                                 ip, dlen);
1402         if (TPROTO_UAS == p_id) {
1403             jo2p = sgj_named_subobject_r(jsp, jop,
1404                                         "usb_target_port_identifier");
1405             sgj_js_nv_ihex(jsp, jo2p, "device_address", 0x7f & ip[0]);
1406             sgj_js_nv_ihex(jsp, jo2p, "interface_number", ip[2]);
1407         } else if (TPROTO_SOP == p_id) {
1408             jo2p = sgj_named_subobject_r(jsp, jop, "pci_express_routing_id");
1409             sgj_js_nv_ihex(jsp, jo2p, "routing_id",
1410                            sg_get_unaligned_be16(ip + 0));
1411         } else
1412             sgj_js_nv_s(jsp, jop, "protocol_specific_port_identifier",
1413                         "decoding failure");
1414 
1415         break;
1416     case 0xa: /* UUID identifier */
1417         if (jsp->pr_hex)
1418             sgj_js_nv_hex_bytes(jsp, jop, "uuid_hexbytes", ip, dlen);
1419         sg_t10_uuid_desig2str(ip, dlen, c_set, false, true, NULL, blen, b);
1420         n = strlen(b);
1421         if ((n > 0) && ('\n' == b[n - 1]))
1422             b[n - 1] = '\0';
1423         sgj_js_nv_s(jsp, jop, "uuid", b);
1424         break;
1425     default: /* reserved */
1426         sgj_js_nv_hex_bytes(jsp, jop, "reserved_designator_hexbytes",
1427                             ip, dlen);
1428         break;
1429     }
1430     return true;
1431 }
1432 
1433 static void
sgj_progress_indication(sgj_state * jsp,sgj_opaque_p jop,uint16_t prog_indic,bool is_another)1434 sgj_progress_indication(sgj_state * jsp, sgj_opaque_p jop,
1435                         uint16_t prog_indic, bool is_another)
1436 {
1437     uint32_t progress, pr, rem;
1438     sgj_opaque_p jo2p;
1439     char b[64];
1440 
1441     if (is_another)
1442         jo2p = sgj_named_subobject_r(jsp, jop, "another_progress_indication");
1443     else
1444         jo2p = sgj_named_subobject_r(jsp, jop, "progress_indication");
1445     if (NULL == jo2p)
1446         return;
1447     progress = prog_indic;
1448     sgj_js_nv_i(jsp, jo2p, "i", progress);
1449     snprintf(b, sizeof(b), "%x", progress);
1450     sgj_js_nv_s(jsp, jo2p, "hex", b);
1451     progress *= 100;
1452     pr = progress / 65536;
1453     rem = (progress % 65536) / 656;
1454     snprintf(b, sizeof(b), "%d.02%d%%\n", pr, rem);
1455     sgj_js_nv_s(jsp, jo2p, "percentage", b);
1456 }
1457 
1458 static bool
sgj_decode_sks(sgj_state * jsp,sgj_opaque_p jop,const uint8_t * dp,int dlen,int sense_key)1459 sgj_decode_sks(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * dp, int dlen,
1460                int sense_key)
1461 {
1462     switch (sense_key) {
1463     case SPC_SK_ILLEGAL_REQUEST:
1464         if (dlen < 3) {
1465             sgj_js_nv_s(jsp, jop, "illegal_request_sks", dtsp);
1466             return false;
1467         }
1468         sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1469                            sksvp);
1470         sgj_js_nv_ihex_nex(jsp, jop, "c_d", !! (dp[0] & 0x40), false,
1471                            "c: cdb; d: data-out");
1472         sgj_js_nv_ihex_nex(jsp, jop, "bpv", !! (dp[0] & 0x8), false,
1473                            "bit pointer (index) valid");
1474         sgj_js_nv_i(jsp, jop, "bit_pointer", dp[0] & 0x7);
1475         sgj_js_nv_ihex(jsp, jop, "field_pointer",
1476                        sg_get_unaligned_be16(dp + 1));
1477         break;
1478     case SPC_SK_HARDWARE_ERROR:
1479     case SPC_SK_MEDIUM_ERROR:
1480     case SPC_SK_RECOVERED_ERROR:
1481         if (dlen < 3) {
1482             sgj_js_nv_s(jsp, jop, "actual_retry_count_sks", dtsp);
1483             return false;
1484         }
1485         sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1486                            sksvp);
1487         sgj_js_nv_ihex(jsp, jop, "actual_retry_count",
1488                        sg_get_unaligned_be16(dp + 1));
1489         break;
1490     case SPC_SK_NO_SENSE:
1491     case SPC_SK_NOT_READY:
1492         if (dlen < 7) {
1493             sgj_js_nv_s(jsp, jop, "progress_indication_sks", dtsp);
1494             return false;
1495         }
1496         sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1497                            sksvp);
1498         sgj_progress_indication(jsp, jop, sg_get_unaligned_be16(dp + 1),
1499                                 false);
1500         break;
1501     case SPC_SK_COPY_ABORTED:
1502         if (dlen < 7) {
1503             sgj_js_nv_s(jsp, jop, "segment_indication_sks", dtsp);
1504             return false;
1505         }
1506         sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1507                            sksvp);
1508         sgj_js_nv_ihex_nex(jsp, jop, "sd", !! (dp[0] & 0x20), false,
1509                            "field pointer relative to: 1->segment "
1510                            "descriptor, 0->parameter list");
1511         sgj_js_nv_ihex_nex(jsp, jop, "bpv", !! (dp[0] & 0x8), false,
1512                            "bit pointer (index) valid");
1513         sgj_js_nv_i(jsp, jop, "bit_pointer", dp[0] & 0x7);
1514         sgj_js_nv_ihex(jsp, jop, "field_pointer",
1515                        sg_get_unaligned_be16(dp + 1));
1516         break;
1517     case SPC_SK_UNIT_ATTENTION:
1518         if (dlen < 7) {
1519             sgj_js_nv_s(jsp, jop, "segment_indication_sks", dtsp);
1520             return false;
1521         }
1522         sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false,
1523                            sksvp);
1524         sgj_js_nv_i(jsp, jop, "overflow", !! (dp[0] & 0x80));
1525         break;
1526     default:
1527         sgj_js_nv_ihex(jsp, jop, "unexpected_sense_key", sense_key);
1528         return false;
1529     }
1530     return true;
1531 }
1532 
1533 #define TPGS_STATE_OPTIMIZED 0x0
1534 #define TPGS_STATE_NONOPTIMIZED 0x1
1535 #define TPGS_STATE_STANDBY 0x2
1536 #define TPGS_STATE_UNAVAILABLE 0x3
1537 #define TPGS_STATE_OFFLINE 0xe
1538 #define TPGS_STATE_TRANSITIONING 0xf
1539 
1540 static int
decode_tpgs_state(int st,char * b,int blen)1541 decode_tpgs_state(int st, char * b, int blen)
1542 {
1543     switch (st) {
1544     case TPGS_STATE_OPTIMIZED:
1545         return sg_scnpr(b, blen, "active/optimized");
1546     case TPGS_STATE_NONOPTIMIZED:
1547         return sg_scnpr(b, blen, "active/non optimized");
1548     case TPGS_STATE_STANDBY:
1549         return sg_scnpr(b, blen, "standby");
1550     case TPGS_STATE_UNAVAILABLE:
1551         return sg_scnpr(b, blen, "unavailable");
1552     case TPGS_STATE_OFFLINE:
1553         return sg_scnpr(b, blen, "offline");
1554     case TPGS_STATE_TRANSITIONING:
1555         return sg_scnpr(b, blen, "transitioning between states");
1556     default:
1557         return sg_scnpr(b, blen, "unknown: 0x%x", st);
1558     }
1559 }
1560 
1561 static bool
sgj_uds_referral_descriptor(sgj_state * jsp,sgj_opaque_p jop,const uint8_t * dp,int alen)1562 sgj_uds_referral_descriptor(sgj_state * jsp, sgj_opaque_p jop,
1563                             const uint8_t * dp, int alen)
1564 {
1565     int dlen = alen - 2;
1566     int k, j, g, f, aas;
1567     uint64_t ull;
1568     const uint8_t * tp;
1569     sgj_opaque_p jap, jo2p, ja2p, jo3p;
1570     char c[40];
1571 
1572     sgj_js_nv_ihex_nex(jsp, jop, "not_all_r", (dp[2] & 0x1), false,
1573                        "Not all referrals");
1574     dp += 4;
1575     jap = sgj_named_subarray_r(jsp, jop,
1576                                "user_data_segment_referral_descriptor_list");
1577     for (k = 0, f = 1; (k + 4) < dlen; k += g, dp += g, ++f) {
1578         int ntpgd = dp[3];
1579 
1580         jo2p = sgj_new_unattached_object_r(jsp);
1581         g = (ntpgd * 4) + 20;
1582         sgj_js_nv_ihex(jsp, jo2p, "number_of_target_port_group_descriptors",
1583                        ntpgd);
1584         if ((k + g) > dlen) {
1585             sgj_js_nv_i(jsp, jo2p, "truncated_descriptor_dlen", dlen);
1586             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1587             return false;
1588         }
1589         ull = sg_get_unaligned_be64(dp + 4);
1590         sgj_js_nv_ihex(jsp, jo2p, "first_user_date_sgment_lba", ull);
1591         ull = sg_get_unaligned_be64(dp + 12);
1592         sgj_js_nv_ihex(jsp, jo2p, "last_user_date_sgment_lba", ull);
1593         ja2p = sgj_named_subarray_r(jsp, jo2p,
1594                                     "target_port_group_descriptor_list");
1595         for (j = 0; j < ntpgd; ++j) {
1596             jo3p = sgj_new_unattached_object_r(jsp);
1597             tp = dp + 20 + (j * 4);
1598             aas = tp[0] & 0xf;
1599             decode_tpgs_state(aas, c, sizeof(c));
1600             sgj_js_nv_ihexstr(jsp, jo3p, "asymmetric_access_state", aas,
1601                               NULL, c);
1602             sgj_js_nv_ihex(jsp, jo3p, "target_port_group",
1603                            sg_get_unaligned_be16(tp + 2));
1604             sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
1605         }
1606         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1607     }
1608     return true;
1609 }
1610 
1611 /* Copy of static array in sg_lib.c */
1612 static const char * dd_usage_reason_str_arr[] = {
1613     "Unknown",
1614     "resend this and further commands to:",
1615     "resend this command to:",
1616     "new subsidiary lu added to this administrative lu:",
1617     "administrative lu associated with a preferred binding:",
1618    };
1619 
1620 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)1621 sgj_js_sense_descriptors(sgj_state * jsp, sgj_opaque_p jop,
1622                          const struct sg_scsi_sense_hdr * sshp,
1623                          const uint8_t * sbp, int sb_len)
1624 {
1625     bool processed = true;
1626     int add_sb_len, desc_len, k, dt, sense_key, n, sds;
1627     uint16_t sct_sc;
1628     uint64_t ull;
1629     const uint8_t * descp;
1630     const char * cp;
1631     sgj_opaque_p jap, jo2p, jo3p;
1632     char b[80];
1633     static const int blen = sizeof(b);
1634     static const char * parsing = "parsing_error";
1635 #if 0
1636     static const char * eccp = "Extended copy command";
1637     static const char * ddp = "destination device";
1638 #endif
1639 
1640     add_sb_len = sshp->additional_length;
1641     add_sb_len = (add_sb_len < sb_len) ? add_sb_len : sb_len;
1642     sense_key = sshp->sense_key;
1643     jap = sgj_named_subarray_r(jsp, jop, "sense_data_descriptor_list");
1644 
1645     for (descp = sbp, k = 0; (k < add_sb_len);
1646          k += desc_len, descp += desc_len) {
1647         int add_d_len = (k < (add_sb_len - 1)) ? descp[1] : -1;
1648 
1649         jo2p = sgj_new_unattached_object_r(jsp);
1650         if ((k + add_d_len + 2) > add_sb_len)
1651             add_d_len = add_sb_len - k - 2;
1652         desc_len = add_d_len + 2;
1653         processed = true;
1654         dt = descp[0];
1655         switch (dt) {
1656         case 0:
1657             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt,
1658                               NULL, "Information");
1659             if (add_d_len >= 10) {
1660                 int valid = !! (0x80 & descp[2]);
1661                 sgj_js_nv_ihexstr(jsp, jo2p, "valid", valid, NULL,
1662                                   valid ? "as per T10" : "Vendor specific");
1663                 sgj_js_nv_ihex(jsp, jo2p, "information",
1664                                sg_get_unaligned_be64(descp + 4));
1665             } else {
1666                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1667                 processed = false;
1668             }
1669             break;
1670         case 1:
1671             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt,
1672                               NULL, "Command specific");
1673             if (add_d_len >= 10) {
1674                 sgj_js_nv_ihex(jsp, jo2p, "command_specific_information",
1675                                sg_get_unaligned_be64(descp + 4));
1676             } else {
1677                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1678                 processed = false;
1679             }
1680             break;
1681         case 2:         /* Sense Key Specific */
1682             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1683                               "Sense key specific");
1684             processed = sgj_decode_sks(jsp, jo2p, descp + 4, desc_len - 4,
1685                                        sense_key);
1686             break;
1687         case 3:
1688             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1689                               "Field replaceable unit code");
1690             if (add_d_len >= 2)
1691                 sgj_js_nv_ihex(jsp, jo2p, "field_replaceable_unit_code",
1692                                descp[3]);
1693             else {
1694                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1695                 processed = false;
1696             }
1697             break;
1698         case 4:
1699             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1700                               "Stream commands");
1701             if (add_d_len >= 2) {
1702                 sgj_js_nv_i(jsp, jo2p, "filemark", !! (descp[3] & 0x80));
1703                 sgj_js_nv_ihex_nex(jsp, jo2p, "eom", !! (descp[3] & 0x40),
1704                                    false, "End Of Medium");
1705                 sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (descp[3] & 0x20),
1706                                    false, "Incorrect Length Indicator");
1707             } else {
1708                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1709                 processed = false;
1710             }
1711             break;
1712         case 5:
1713             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1714                               "Block commands");
1715             if (add_d_len >= 2)
1716                 sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (descp[3] & 0x20),
1717                                    false, "Incorrect Length Indicator");
1718             else {
1719                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1720                 processed = false;
1721             }
1722             break;
1723         case 6:
1724             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1725                               "OSD object identification");
1726             sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
1727             processed = false;
1728             break;
1729         case 7:
1730             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1731                               "OSD response integrity check value");
1732             sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
1733             break;
1734         case 8:
1735             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1736                               "OSD attribute identification");
1737             sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported");
1738             processed = false;
1739             break;
1740         case 9:         /* this is defined in SAT (SAT-2) */
1741             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1742                               "ATA status return");
1743             if (add_d_len >= 12) {
1744                 sgj_js_nv_i(jsp, jo2p, "extend", !! (descp[2] & 1));
1745                 sgj_js_nv_ihex(jsp, jo2p, "error", descp[3]);
1746                 sgj_js_nv_ihex(jsp, jo2p, "count",
1747                                sg_get_unaligned_be16(descp + 4));
1748                 ull = ((uint64_t)descp[10] << 40) |
1749                        ((uint64_t)descp[8] << 32) |
1750                        (descp[6] << 24) |
1751                        (descp[11] << 16) |
1752                        (descp[9] << 8) |
1753                        descp[7];
1754                 sgj_js_nv_ihex(jsp, jo2p, "lba", ull);
1755                 sgj_js_nv_ihex(jsp, jo2p, "device", descp[12]);
1756                 sgj_js_nv_ihex(jsp, jo2p, "status", descp[13]);
1757             } else {
1758                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1759                 processed = false;
1760             }
1761             break;
1762         case 0xa:
1763            /* Added in SPC-4 rev 17, became 'Another ...' in rev 34 */
1764             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1765                               "Another progress indication");
1766             if (add_d_len < 6) {
1767                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1768                 processed = false;
1769                 break;
1770             }
1771             sgj_js_nv_ihex(jsp, jo2p, "another_sense_key", descp[2]);
1772             sgj_js_nv_ihex(jsp, jo2p, "another_additional_sense_code",
1773                            descp[3]);
1774             sgj_js_nv_ihex(jsp, jo2p,
1775                            "another_additional_sense_code_qualifier",
1776                            descp[4]);
1777             sgj_progress_indication(jsp, jo2p,
1778                                     sg_get_unaligned_be16(descp + 6), true);
1779             break;
1780         case 0xb:       /* Added in SPC-4 rev 23, defined in SBC-3 rev 22 */
1781             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1782                               "User data segment referral");
1783             if (add_d_len < 2) {
1784                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1785                 processed = false;
1786                 break;
1787             }
1788             if (! sgj_uds_referral_descriptor(jsp, jo2p, descp, add_d_len)) {
1789                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1790                 processed = false;
1791             }
1792             break;
1793         case 0xc:       /* Added in SPC-4 rev 28 */
1794             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1795                               "Forwarded sense data");
1796             if (add_d_len < 2) {
1797                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1798                 processed = false;
1799                 break;
1800             }
1801             sgj_js_nv_ihex_nex(jsp, jo2p, "fsdt", !! (0x80 & descp[2]),
1802                                false, "Forwarded Sense Data Truncated");
1803             sds = (0x7 & descp[2]);
1804             if (sds < 1)
1805                 snprintf(b, blen, "%s [%d]", "Unknown", sds);
1806             else if (sds > 9)
1807                 snprintf(b, blen, "%s [%d]", "Reserved", sds);
1808             else {
1809                 n = 0;
1810                 n += sg_scnpr(b + n, blen - n, "EXTENDED COPY command copy %s",
1811                               (sds == 1) ? "source" : "destination");
1812                 if (sds > 1)
1813                     n += sg_scnpr(b + n, blen - n, " %d", sds - 1);
1814             }
1815             sgj_js_nv_ihexstr(jsp, jo2p, "sense_data_source",
1816                               (0x7 & descp[2]), NULL, b);
1817             jo3p = sgj_named_subobject_r(jsp, jo2p, "forwarded_sense_data");
1818             sgj_js_sense(jsp, jo3p, descp + 4, desc_len - 4);
1819             break;
1820         case 0xd:       /* Added in SBC-3 rev 36d */
1821             /* this descriptor combines descriptors 0, 1, 2 and 3 */
1822             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1823                               "Direct-access block device");
1824             if (add_d_len < 28) {
1825                 sgj_js_nv_s(jsp, jo2p, parsing, dtsp);
1826                 processed = false;
1827                 break;
1828             }
1829             sgj_js_nv_i(jsp, jo2p, "valid", (0x80 & descp[2]));
1830             sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (0x20 & descp[2]),
1831                                false, "Incorrect Length Indicator");
1832             processed = sgj_decode_sks(jsp, jo2p, descp + 4, desc_len - 4,
1833                                        sense_key);
1834             sgj_js_nv_ihex(jsp, jo2p, "field_replaceable_unit_code",
1835                            descp[7]);
1836             sgj_js_nv_ihex(jsp, jo2p, "information",
1837                            sg_get_unaligned_be64(descp + 8));
1838             sgj_js_nv_ihex(jsp, jo2p, "command_specific_information",
1839                            sg_get_unaligned_be64(descp + 16));
1840             break;
1841         case 0xe:       /* Added in SPC-5 rev 6 (for Bind/Unbind) */
1842             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1843                               "Device designation");
1844             n = descp[3];
1845             cp = (n < (int)SG_ARRAY_SIZE(dd_usage_reason_str_arr)) ?
1846                   dd_usage_reason_str_arr[n] : "Unknown (reserved)";
1847             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_usage_reason",
1848                               n, NULL, cp);
1849             jo3p = sgj_named_subobject_r(jsp, jo2p,
1850                                          "device_designation_descriptor");
1851             sgj_js_designation_descriptor(jsp, jo3p, descp + 4, desc_len - 4);
1852             break;
1853         case 0xf:       /* Added in SPC-5 rev 10 (for Write buffer) */
1854             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1855                               "Microcode activation");
1856             if (add_d_len < 6) {
1857                 sgj_js_nv_s(jsp, jop, parsing, dtsp);
1858                 processed = false;
1859                 break;
1860             }
1861             sgj_js_nv_ihex(jsp, jo2p, "microcode_activation_time",
1862                            sg_get_unaligned_be16(descp + 6));
1863             break;
1864         case 0xde:       /* NVME Status Field; vendor (sg3_utils) specific */
1865             sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL,
1866                               "NVME status (sg3_utils)");
1867             if (add_d_len < 6) {
1868                 sgj_js_nv_s(jsp, jop, parsing, dtsp);
1869                 processed = false;
1870                 break;
1871             }
1872             sgj_js_nv_ihex_nex(jsp, jo2p, "dnr", !! (0x80 & descp[5]),
1873                                false, "Do not retry");
1874             sgj_js_nv_ihex_nex(jsp, jo2p, "m", !! (0x40 & descp[5]),
1875                                false, "More");
1876             sct_sc = sg_get_unaligned_be16(descp + 6);
1877             sgj_js_nv_ihexstr_nex
1878                 (jsp, jo2p, "sct_sc", sct_sc, true, NULL,
1879                  sg_get_nvme_cmd_status_str(sct_sc, blen, b),
1880                  "Status Code Type (upper 8 bits) and Status Code");
1881             break;
1882         default:
1883             if (dt >= 0x80)
1884                 sgj_js_nv_ihex(jsp, jo2p, "vendor_specific_descriptor_type",
1885                                dt);
1886             else
1887                 sgj_js_nv_ihex(jsp, jo2p, "unknown_descriptor_type", dt);
1888             sgj_js_nv_hex_bytes(jsp, jo2p, "descriptor_hexbytes",
1889                                 descp, desc_len);
1890             processed = false;
1891             break;
1892         }
1893         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1894     }
1895     return processed;
1896 }
1897 
1898 #define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d  /* corresponding ASC is 0 */
1899 
1900 /* Fetch sense information */
1901 bool
sgj_js_sense(sgj_state * jsp,sgj_opaque_p jop,const uint8_t * sbp,int sb_len)1902 sgj_js_sense(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * sbp,
1903              int sb_len)
1904 {
1905     bool descriptor_format = false;
1906     bool sdat_ovfl = false;
1907     bool ret = true;
1908     bool valid_info_fld;
1909     int len, n;
1910     uint32_t info;
1911     uint8_t resp_code;
1912     const char * ebp = NULL;
1913     char ebuff[64];
1914     char b[256];
1915     struct sg_scsi_sense_hdr ssh;
1916     static int blen = sizeof(b);
1917     static int elen = sizeof(ebuff);
1918 
1919     if ((NULL == sbp) || (sb_len < 1)) {
1920         snprintf(ebuff, elen, "sense buffer empty\n");
1921         ebp = ebuff;
1922         ret = false;
1923         goto fini;
1924     }
1925     resp_code = 0x7f & sbp[0];
1926     valid_info_fld = !!(sbp[0] & 0x80);
1927     len = sb_len;
1928     if (! sg_scsi_normalize_sense(sbp, sb_len, &ssh)) {
1929         ebp = "unable to normalize sense buffer";
1930         ret = false;
1931         goto fini;
1932     }
1933     /* We have been able to normalize the sense buffer */
1934     switch (resp_code) {
1935     case 0x70:      /* fixed, current */
1936         ebp = "Fixed format, current";
1937         len = (sb_len > 7) ? (sbp[7] + 8) : sb_len;
1938         len = (len > sb_len) ? sb_len : len;
1939         sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false;
1940         break;
1941     case 0x71:      /* fixed, deferred */
1942         /* error related to a previous command */
1943         ebp = "Fixed format, <<<deferred>>>";
1944         len = (sb_len > 7) ? (sbp[7] + 8) : sb_len;
1945         len = (len > sb_len) ? sb_len : len;
1946         sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false;
1947         break;
1948     case 0x72:      /* descriptor, current */
1949         descriptor_format = true;
1950         ebp = "Descriptor format, current";
1951         sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false;
1952         break;
1953     case 0x73:      /* descriptor, deferred */
1954         descriptor_format = true;
1955         ebp = "Descriptor format, <<<deferred>>>";
1956         sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false;
1957         break;
1958     default:
1959         sg_scnpr(ebuff, elen, "Unknown code: 0x%x", resp_code);
1960         ebp = ebuff;
1961         break;
1962     }
1963     sgj_js_nv_ihexstr(jsp, jop, "response_code", resp_code, NULL, ebp);
1964     sgj_js_nv_b(jsp, jop, "descriptor_format", descriptor_format);
1965     sgj_js_nv_ihex_nex(jsp, jop, "sdat_ovfl", sdat_ovfl, false,
1966                        "Sense data overflow");
1967     sgj_js_nv_ihexstr(jsp, jop, "sense_key", ssh.sense_key, NULL,
1968                       sg_lib_sense_key_desc[ssh.sense_key]);
1969     sgj_js_nv_ihex(jsp, jop, "additional_sense_code", ssh.asc);
1970     sgj_js_nv_ihex(jsp, jop, "additional_sense_code_qualifier", ssh.ascq);
1971     sgj_js_nv_s(jsp, jop, "additional_sense_str",
1972                 sg_get_additional_sense_str(ssh.asc, ssh.ascq, false,
1973                                              blen, b));
1974     if (descriptor_format) {
1975         if (len > 8) {
1976             ret = sgj_js_sense_descriptors(jsp, jop, &ssh, sbp + 8, len - 8);
1977             if (ret == false) {
1978                 ebp = "unable to decode sense descriptor";
1979                 goto fini;
1980             }
1981         }
1982     } else if ((len > 12) && (0 == ssh.asc) &&
1983                (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) {
1984         /* SAT ATA PASS-THROUGH fixed format */
1985         sgj_js_nv_ihex(jsp, jop, "error", sbp[3]);
1986         sgj_js_nv_ihex(jsp, jop, "status", sbp[4]);
1987         sgj_js_nv_ihex(jsp, jop, "device", sbp[5]);
1988         sgj_js_nv_i(jsp, jop, "extend", !! (0x80 & sbp[8]));
1989         sgj_js_nv_i(jsp, jop, "count_upper_nonzero", !! (0x40 & sbp[8]));
1990         sgj_js_nv_i(jsp, jop, "lba_upper_nonzero", !! (0x20 & sbp[8]));
1991         sgj_js_nv_i(jsp, jop, "log_index", (0xf & sbp[8]));
1992         sgj_js_nv_i(jsp, jop, "lba", sg_get_unaligned_le24(sbp + 9));
1993     } else if (len > 2) {   /* fixed format */
1994         sgj_js_nv_i(jsp, jop, "valid", valid_info_fld);
1995         sgj_js_nv_i(jsp, jop, "filemark", !! (sbp[2] & 0x80));
1996         sgj_js_nv_ihex_nex(jsp, jop, "eom", !! (sbp[2] & 0x40),
1997                            false, "End Of Medium");
1998         sgj_js_nv_ihex_nex(jsp, jop, "ili", !! (sbp[2] & 0x20),
1999                            false, "Incorrect Length Indicator");
2000         info = sg_get_unaligned_be32(sbp + 3);
2001         sgj_js_nv_ihex(jsp, jop, "information", info);
2002         sgj_js_nv_ihex(jsp, jop, "additional_sense_length", sbp[7]);
2003         if (sb_len > 11) {
2004             info = sg_get_unaligned_be32(sbp + 8);
2005             sgj_js_nv_ihex(jsp, jop, "command_specific_information", info);
2006         }
2007         if (sb_len > 14)
2008             sgj_js_nv_ihex(jsp, jop, "field_replaceable_unit_code", sbp[14]);
2009         if (sb_len > 17)
2010             sgj_decode_sks(jsp, jop, sbp + 15, sb_len - 15, ssh.sense_key);
2011         n =  sbp[7];
2012         n = (sb_len > n) ? n : sb_len;
2013         sgj_js_nv_ihex(jsp, jop, "number_of_bytes_beyond_18",
2014                        (n > 18) ? n - 18 : 0);
2015     } else {
2016         snprintf(ebuff, sizeof(ebuff), "sb_len=%d too short", sb_len);
2017         ebp = ebuff;
2018         ret = false;
2019     }
2020 fini:
2021     if ((! ret) && ebp)
2022         sgj_js_nv_s(jsp, jop, "sense_decode_error", ebp);
2023     return ret;
2024 }
2025 
2026 #endif
2027