aboutsummaryrefslogtreecommitdiff
path: root/include/sg_pr2serr.h
blob: 23cad944d704ac60b96ae6117f28ff861e9af006 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#ifndef SG_PR2SERR_H
#define SG_PR2SERR_H

/*
 * Copyright (c) 2004-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 <inttypes.h>
#include <stdio.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/* pr2serr and pr2ws are convenience functions that replace the somewhat
 * long-winded fprintf(stderr, ....). The second form (i.e. pr2ws() ) is for
 * internal library use and may place its output somewhere other than stderr;
 * it depends on the external variable sg_warnings_strm which can be set
 * with sg_set_warnings_strm(). By default it uses stderr. */

#if __USE_MINGW_ANSI_STDIO -0 == 1
#define __printf(a, b) __attribute__((__format__(gnu_printf, a, b)))
#elif defined(__GNUC__) || defined(__clang__)
#define __printf(a, b) __attribute__((__format__(printf, a, b)))
#else
#define __printf(a, b)
#endif

int pr2serr(const char * fmt, ...) __printf(1, 2);

int pr2ws(const char * fmt, ...) __printf(1, 2);

/* 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().  */
int sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...) __printf(3, 4);

/* JSON support functions and structures follow. The prefix "sgj_" is used
 * for sg3_utils JSON functions, types and values. */

enum sgj_separator_t {
    SGJ_SEP_NONE = 0,
    SGJ_SEP_SPACE_1,
    SGJ_SEP_SPACE_2,
    SGJ_SEP_SPACE_3,
    SGJ_SEP_SPACE_4,
    SGJ_SEP_EQUAL_NO_SPACE,
    SGJ_SEP_EQUAL_1_SPACE,
    SGJ_SEP_COLON_NO_SPACE,
    SGJ_SEP_COLON_1_SPACE,
};

typedef void * sgj_opaque_p;

/* Apart from the pointers at the end the other fields are initialized
 * from the argument given to --json= . If there is no argument then
 * they initialized as shown. */
typedef struct sgj_state_t {
    /* the following set by default, the SG3_UTILS_JSON_OPTS envirinment
     * variable or command line argument to --json option, in that order. */
    bool pr_as_json;            /* = false */
    bool pr_ane;        /* 'a' abbreviated name expansion (def: false) */
    bool pr_exit_status;        /* 'e' (def: true) */
    bool pr_hex;                /* 'h' (def: false) */
    bool pr_leadin;             /* 'l' (def: true) */
    bool pr_out_hr;             /* 'o' (def: false) */
    bool pr_packed;             /* 'k' (def: false) only when !pr_pretty */
    bool pr_pretty;             /* 'p' (def: true) */
    bool pr_string;             /* 's' (def: true) */
    char pr_format;             /*  (def: '\0') */
    int pr_indent_size;         /* digit (def: 4) */
    int verbose;                /* 'v' (def: 0) incremented each appearance */
    /* the following hold state information */
    int first_bad_char;         /* = '\0' */
    sgj_opaque_p basep;         /* base JSON object pointer */
    sgj_opaque_p out_hrp;       /* JSON array pointer when pr_out_hr set */
    sgj_opaque_p userp;         /* for temporary usage */
} sgj_state;

/* If jsp in non-NULL and jsp->pr_as_json is true then this call is ignored
 * unless jsp->pr_out_hrp is true. Otherwise this function prints to stdout
 * like printf(fmt, ...); note that no LF is added. In the jsp->pr_out_hrp is
 * true case, nothing is printed to stdout but instead is placed into a JSON
 * array (jsp->out_hrp) after some preprocessing. That preprocessing involves
 * removing a leading LF from 'fmt' (if present) and up to two trailing LF
 * characters. */
void sgj_pr_hr(sgj_state * jsp, const char * fmt, ...) __printf(2, 3);

/* Initializes the state object pointed to by jsp based on the argument
 * given to the right of --json= pointed to by j_optarg. If it is NULL
 * then state object gets its default values. Returns true if argument
 * to --json= is decoded properly, else returns false and places the
 * first "bad" character in jsp->first_bad_char . Note that no JSON
 * in-core tree needs to exist when this function is called. */
bool sgj_init_state(sgj_state * jsp, const char * j_optarg);

/* sgj_start() creates a JSON in-core tree and returns a pointer to it (or
 * NULL if the associated heap allocation fails). It should be paired with
 * sgj_finish() to clean up (i.e. remove all heap allocations) all the
 * elements (i.e. JSON objects and arrays) that have been placed in that
 * in-core tree. If jsp is NULL nothing further happens. Otherwise the pointer
 * to be returned is placed in jsp->basep. If jsp->pr_leadin is true and
 * util_name is non-NULL then a "utility_invoked" JSON object is made with
 * "name", and "version_date" object fields. If the jsp->pr_out_hr field is
 * true a named array called "output" is added to the "utility_invoked" object
 * (creating it in the case when jsp->pr_leadin is false) and a pointer to
 * that array object is placed in jsp->objectp . The returned pointer is not
 * usually needed but if it is NULL then a heap allocation has failed. */
sgj_opaque_p sgj_start(const char * util_name, const char * ver_str,
                       int argc, char *argv[], sgj_state * jsp);

/* These are low level functions returning a pointer to a newly created JSON
 * object or array. If jsp is NULL or jsp->pr_as_json is false nothing happens
 * and NULL is returned. Note that this JSON object is _not_ placed in the
 * in-core tree controlled by jsp (jsp->basep); it may be added later as the
 * fourth argument to sgj_add_nv_o(), for example. */
sgj_opaque_p sgj_new_unattached_object(sgj_state * jsp);
sgj_opaque_p sgj_new_unattached_array(sgj_state * jsp);

/* If jsp is NULL or jsp->pr_as_json is false nothing happens and NULL is
 * returned. Otherwise it creates a new named object (whose name is what
 * 'name' points to) at 'jop' with an empty object as its value; a pointer
 * to that empty object is returned. If 'jop' is NULL then jsp->basep is
 * used instead. The returned value should always be checked (for NULL)
 * and if not, used. */
sgj_opaque_p sgj_new_named_object(sgj_state * jsp, sgj_opaque_p jop,
                                  const char * name);
sgj_opaque_p sgj_new_snake_named_object(sgj_state * jsp, sgj_opaque_p jop,
                                        const char * conv2sname);

/* If jsp is NULL or jsp->pr_as_json is false nothing happens and NULL is
 * returned. Otherwise it creates a new named object (whose name is what
 * 'name' points to) at 'jop' with an empty array as its value; a pointer
 * to that empty array is returned.  If 'jop' is NULL then jsp->basep is
 * used instead. The returned value should always * be checked (for NULL)
 * and if not, used. */
sgj_opaque_p sgj_new_named_array(sgj_state * jsp, sgj_opaque_p jop,
                                 const char * name);
sgj_opaque_p sgj_new_snake_named_array(sgj_state * jsp, sgj_opaque_p jop,
                                       const char * conv2sname);

/* If either jsp or value is NULL or jsp->pr_as_json is false then nothing
 * happens and NULL is returned. The insertion point is at jop but if it is
 * NULL jsp->basep is used. If 'name' is non-NULL a new named JSON object is
 * added using 'name' and the associated value is a JSON string formed from
 * 'value'. If 'name' is NULL then 'jop' is assumed to be a JSON array and
 * a JSON string formed from 'value' is added. If successful returns a
 * a pointer newly formed JSON string. */
sgj_opaque_p sgj_add_nv_s(sgj_state * jsp, sgj_opaque_p jop,
                          const char * name, const char * value);
sgj_opaque_p sgj_add_nv_s_len(sgj_state * jsp, sgj_opaque_p jop,
                              const char * name,
                              const char * value, int slen);

/* If either jsp is NULL or jsp->pr_as_json is false then nothing happens and
 * NULL is returned. The insertion point is at jop but if it is NULL
 * jsp->basep is used. If 'name' is non-NULL a new named JSON object is
 * added using 'name' and the associated value is a JSON integer formed from
 * 'value'. If 'name' is NULL then 'jop' is assumed to be a JSON array and
 * a JSON integer formed from 'value' is added. If successful returns a
 * a pointer newly formed JSON integer. */
sgj_opaque_p sgj_add_nv_i(sgj_state * jsp, sgj_opaque_p jop,
                          const char * name, int64_t value);

/* If either jsp is NULL or jsp->pr_as_json is false then nothing happens and
 * NULL is returned. The insertion point is at jop but if it is NULL
 * jsp->basep is used. If 'name' is non-NULL a new named JSON object is
 * added using 'name' and the associated value is a JSON boolean formed from
 * 'value'. If 'name' is NULL then 'jop' is assumed to be a JSON array and
 * a JSON boolean formed from 'value' is added. If successful returns a
 * a pointer newly formed JSON boolean. */
sgj_opaque_p sgj_add_nv_b(sgj_state * jsp, sgj_opaque_p jop,
                          const char * name, bool value);

/* If jsp is NULL, jsp->pr_as_json is false or ua_jop is NULL nothing then
 * happens and NULL is returned. 'jop' is the insertion point but if it is
 * NULL jsp->basep is used instead. If 'name' is non-NULL a new named JSON
 * object is added using 'name' and the associated value is ua_jop. If 'name'
 * is NULL then 'jop' is assumed to be a JSON array and ua_jop is added to
 * it. If successful returns ua_jop . The "ua_" prefix stands for unattached.
 * That should be the case before invocation and it will be attached to jop
 * after a successful invocation. This means that ua_jop must have been
 * created by sgj_new_unattached_object() or similar. */
sgj_opaque_p sgj_add_nv_o(sgj_state * jsp, sgj_opaque_p jop,
                          const char * name, sgj_opaque_p ua_jop);

/* The '_hr_js_' refers to generating output both for human readable and/or
 * JSON with a single invocation. If jsp is non_NULL and jsp->pr_out_hr is
 * true then both JSON and human readable output is formed (and the latter is
 * placed in the jsp->out_hrp JSON array). The human readable form will have
 * leadin_sp spaces followed by 'name' then a separator, then 'value' with a
 * trailing LF. If 'name' is NULL then it and the separator are ignored. If
 * there is JSON output, then leadin_sp and sep are ignored. If 'jop' is NULL
 * then basep->basep is used. If 'name' is NULL then a JSON string object,
 * made from 'value' is added to the JSON array pointed to by 'jop'.
 * Otherwise a 'name'-d JSON object whose value is a JSON string object made
 * from 'value' is added at 'jop'. */
void sgj_pr_hr_js_vs(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
                     const char * name, enum sgj_separator_t sep,
                     const char * value);

/* Similar to sgj_pr_hr_js_vs()'s description with 'JSON string object'
 * replaced by 'JSON integer object'. */
void sgj_pr_hr_js_vi(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
                     const char * name, enum sgj_separator_t sep,
                     int64_t value);

/* Similar to sgj_pr_hr_js_vs()'s description with 'JSON string object'
 * replaced by 'JSON boolean object'. */
void sgj_pr_hr_js_vb(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp,
                     const char * name, enum sgj_separator_t sep, bool value);

/* This function only produces JSON output if jsp is non-NULL and
 * jsp->pr_as_json is true. It adds a named object at 'jop' (or jop->basep
 * if jop is NULL) along with a value. If jsp->pr_hex is true then that
 * value is two sub-objects, one named 'i' with a 'value' as a JSON integer,
 * the other one named 'hex' with 'value' rendered as hex in a JSON string.
 * If jsp->pr_hex is false then there are no sub-objects and the 'value' is
 * rendered as JSON integer. */
void sgj_add_nv_ihex(sgj_state * jsp, sgj_opaque_p jop,
                     const char * name, uint64_t value);

/* This function only produces JSON output if jsp is non-NULL and
 * jsp->pr_as_json is true. It adds a named object at 'jop' (or jop->basep
 * if jop is NULL) along with a value. If jsp->pr_string is true then that
 * value is two sub-objects, one named 'i' with a 'val_i' as a JSON integer,
 * the other one named str_name with val_s rendered as a JSON string. If
 * str_name is NULL then "meaning" will be used. If jsp->pr_string is false
 * then there are no sub-objects and the 'val_i' is rendered as a JSON
 * integer. */
void sgj_add_nv_istr(sgj_state * jsp, sgj_opaque_p jop,
                     const char * name, int64_t val_i,
                     const char * str_name, const char * val_s);

void sgj_add_nv_ihexstr(sgj_state * jsp, sgj_opaque_p jop,
                        const char * name, int64_t val_i,
                        const char * str_name, const char * val_s);

/* This function only produces JSON output if jsp is non-NULL and
 * jsp->pr_as_json is true. It adds a named object at 'jop' (or jop->basep
 * if jop is NULL) along with a value. If jsp->pr_ane is true then that
 * value is two sub-objects, one named 'i' with a 'val_i' as a JSON integer,
 * the other one named "abbreviated_name_expansion" with value ane_s rendered
 * as a JSON string. If jsp->pr_hex and 'want_hex' are true, then a
 * sub-object named 'hex' with a value rendered as a hex string equal to
 * val_i. If jsp->pr_ane is false and either jsp->pr_hex or want_hex are
 * false then there are no sub-objects and the 'val_i' is rendered as a JSON
 * integer. */
void sgj_add_nv_ihex_ane(sgj_state * jsp, sgj_opaque_p jop, const char * name,
                         int64_t val_i, bool want_hex, const char * ane_s);

void sgj_add_nv_ihexstr_ane(sgj_state * jsp, sgj_opaque_p jop,
                            const char * name, int64_t val_i, bool want_hex,
                            const char * str_name, const char * val_s,
                            const char * ane_s);

/* Breaks up the string pointed to by 'sp' into lines and adds them to the
 * jsp->out_hrp array. Treat '\n' in sp as line breaks. Consumes characters
 * from sp until either a '\0' is found or slen is exhausted. Add each line
 * to jsp->out_hrp JSON array (if conditions met). */
void sgj_pr_str_out_hr(sgj_state * jsp, const char * sp, int slen);

/* This function only produces JSON output if jsp is non-NULL and
 * jsp->pr_as_json is true. 'sbp' is assumed to point to sense data as
 * defined by T10 with a length of 'sb_len' bytes. Returns false if an
 * issue is detetected, else it returns true. */
bool sgj_get_sense(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * sbp,
                   int sb_len);

bool sgj_get_designation_descriptor(sgj_state * jsp, sgj_opaque_p jop,
                                    const uint8_t * ddp, int dd_len);

/* Nothing in the in-core JSON tree is actually printed to 'fp' (typically
 * stdout) until this call is made. If jsp is NULL, jsp->pr_as_json is false
 * or jsp->basep is NULL then this function does nothing. If jsp->exit_status
 * is true then a new JSON object named "exit_status" and the 'exit_status'
 * value rendered as a JSON integer is appended to jsp->basep. The in-core
 * JSON tree with jsp->basep as its root is streamed to 'fp'. */
void sgj_pr2file(sgj_state * jsp, sgj_opaque_p jop, int exit_status,
                 FILE * fp);

/* This function is only needed if the pointer returned from either
 * sgj_new_unattached_object() or sgj_new_unattached_array() has not been
 * attached into the in-core JSON tree whose root is jsp->basep . */
void sgj_free_unattached(sgj_opaque_p jop);

/* If jsp is NULL or jsp->basep is NULL then this function does nothing.
 * This function does bottom up, heap freeing of all the in-core JSON
 * objects and arrays attached to the root JSON object assumed to be
 * found at jsp->basep . After this call jsp->basep, jsp->out_hrp and
 * jsp->userp will all be set to NULL.  */
void sgj_finish(sgj_state * jsp);

char * sg_json_usage(int char_if_not_j, char * b, int blen);


#ifdef __cplusplus
}
#endif

#endif