aboutsummaryrefslogtreecommitdiff
path: root/include/sg_pr2serr.h
blob: bebf990052bc254993257a1bae8d8ab5c6835b14 (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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#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 that can be called multiple times. 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 state information at the end of this structure, the earlier
 * fields are initialized from the command line argument given to the
 * --json= option. 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 environment
     * variable or command line argument to --json option, in that order. */
    bool pr_as_json;            /* = false (def: is human readable output) */
    bool pr_exit_status;        /* 'e' (def: true) */
    bool pr_hex;                /* 'h' (def: false) */
    bool pr_leadin;             /* 'l' (def: true) */
    bool pr_name_ex;        /* 'n' name_extra (information) (def: false) */
    bool pr_out_hr;             /* 'o' (def: false) */
    bool pr_packed;             /* 'k' (def: false) only when !pr_pretty */
    bool pr_pretty;             /* 'p' (def: true) */
    bool pr_string;             /* 's' (def: true) */
    char pr_format;             /*  (def: '\0') */
    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;

/* This function tries to convert the in_name C string to the "snake_case"
 * convention so the output sname only contains lower case ASCII letters,
 * numerals and "_" as a separator. Any leading or trailing underscores
 * are removed as are repeated underscores (e.g. "_Snake __ case" becomes
 * "snake_case"). Parentheses and the characters between them are removed.
 * Returns sname (i.e. the pointer to the output buffer).
 * Note: strlen(in_name) should be <= max_sname_len . */
char * sgj_convert_to_snake_name(const char * in_name, char * sname,
                                 int max_sname_len);

/* There are many variants of JSON supporting functions below and some
 * abbreviations are used to shorten their function names:
 *    sgj_  - prefix of all the functions related to (non-)JSON output
 *    hr    - human readable form (as it was before JSON)
 *    js    - JSON only output
 *    haj   - human readable and JSON output, hr goes in 'output' array
 *    pr    - has printf() like variadic arguments
 *    _r    - suffix indicating the return value should/must be used
 *    nv    - adds a name-value JSON field (or several)
 *    o     - value is the provided JSON object (or array)
 *    i     - value is a JSON integer object (int64_t or uint64_t)
 *    b     - value is a JSON boolean object
 *    s     - value is a JSON string object
 *    str   - same as s
 *    hex   - value is hexadecimal in a JSON string object
 *    _nex  - extra 'name_extra' JSON string object about name
 *    new   - object that needs sgj_free_unattached() if not attached
 *
 *    */

/* 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_r(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_js_nv_o(), for example. */
sgj_opaque_p sgj_new_unattached_object_r(sgj_state * jsp);
sgj_opaque_p sgj_new_unattached_array_r(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_named_subobject_r(sgj_state * jsp, sgj_opaque_p jop,
                                   const char * name);
sgj_opaque_p sgj_snake_named_subobject_r(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_named_subarray_r(sgj_state * jsp, sgj_opaque_p jop,
                                  const char * name);
sgj_opaque_p sgj_snake_named_subarray_r(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. Note that the jsp->pr_string
 * setting is ignored by this function. If successful returns a * a pointer
 * newly formed JSON string. */
sgj_opaque_p sgj_js_nv_s(sgj_state * jsp, sgj_opaque_p jop,
                         const char * name, const char * value);
sgj_opaque_p sgj_js_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_js_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_js_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_r() or similar. */
sgj_opaque_p sgj_js_nv_o(sgj_state * jsp, sgj_opaque_p jop,
                         const char * name, sgj_opaque_p ua_jop);

/* 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_js_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_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);

/* Similar to sgj_js_nv_istr(). The hex output is conditional jsp->pr_hex . */
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);

/* 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_name_ex 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 nex_s rendered
 * as a JSON string. If jsp->pr_hex and 'hex_as_well' are true, then a
 * sub-object named 'hex' with a value rendered as a hex string equal to
 * val_i. If jsp->pr_name_ex is false and either jsp->pr_hex or hex_as_well are
 * false then there are no sub-objects and the 'val_i' is rendered as a JSON
 * integer. */
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);

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);

/* Add named field whose value is a (large) JSON string made up of num_bytes
 * ASCII hexadecimal bytes (each two hex digits separated by a space) starting
 * at byte_arr. The heap is used for intermediate storage so num_bytes can
 * be arbitrarily large. */
void sgj_js_nv_hex_bytes(sgj_state * jsp, sgj_opaque_p jop, const char * name,
                         const uint8_t * byte_arr, int num_bytes);

/* The '_haj_' 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_haj_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_haj_vs()'s description with 'JSON string object'
 * replaced by 'JSON integer object'. */
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);
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);

/* The '_nex' refers to a "name_extra" (information) sub-object (a JSON
 * string) which explains a bit more about the 'name' entry. This is useful
 * when T10 specifies the name as an abbreviation (e.g. SYSV). Whether this
 * sub-object is shown in the JSON output is controlled by the 'n' control
 * character. */
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);
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);

/* Similar to above '_haj_' calls but a named sub-object is always formed
 * containing a JSON integer object named "i" whose value is 'value'. The
 * returned pointer is to that sub-object. */
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);

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

/* 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_js_str_out(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 detected, else it returns true. */
bool sgj_js_sense(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * sbp,
                  int sb_len);

bool sgj_js_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_js2file(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_r() or sgj_new_unattached_array_r() 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