From: WanBingjiang Date: Thu, 5 Feb 2026 09:02:39 +0000 (+0800) Subject: libsmartcols: support JSON Lines format output X-Git-Tag: v2.43-devel~50^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=08f4133918df90caa4ee4ebd118a8696a7849833;p=thirdparty%2Futil-linux.git libsmartcols: support JSON Lines format output --- diff --git a/include/jsonwrt.h b/include/jsonwrt.h index 31e180abe..74b43e213 100644 --- a/include/jsonwrt.h +++ b/include/jsonwrt.h @@ -11,14 +11,21 @@ enum { UL_JSON_VALUE }; +typedef enum { + UL_JSON_PRETTY, + UL_JSON_COMPACT, + UL_JSON_LINE, +} ul_json_format_t; + struct ul_jsonwrt { FILE *out; int indent; unsigned int after_close :1; + ul_json_format_t json_format; }; -void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent); +void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent, ul_json_format_t json_format); int ul_jsonwrt_is_ready(struct ul_jsonwrt *fmt); void ul_jsonwrt_indent(struct ul_jsonwrt *fmt); void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type); diff --git a/lib/jsonwrt.c b/lib/jsonwrt.c index 365d845ce..692592975 100644 --- a/lib/jsonwrt.c +++ b/lib/jsonwrt.c @@ -102,10 +102,11 @@ static void fputs_quoted_case_json(const char *data, FILE *out, int dir, size_t #define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1, 0) #define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1, 0) -void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent) +void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent, ul_json_format_t json_format) { fmt->out = out; fmt->indent = indent; + fmt->json_format = json_format; fmt->after_close = 0; } @@ -124,81 +125,92 @@ void ul_jsonwrt_indent(struct ul_jsonwrt *fmt) static void print_name(struct ul_jsonwrt *fmt, const char *name) { - if (name) { - if (fmt->after_close) - fputs(",\n", fmt->out); - ul_jsonwrt_indent(fmt); - fputs_quoted_json_lower(name, fmt->out); - } else { - if (fmt->after_close) - fputs(",", fmt->out); + if (fmt->after_close) { + if (fmt->json_format == UL_JSON_LINE) + fputs(fmt->indent == 0 ? "\n" : ",", fmt->out); else - ul_jsonwrt_indent(fmt); + fputs(name ? ",\n" : ",", fmt->out); } + if (fmt->json_format != UL_JSON_LINE && (name || !fmt->after_close)) + ul_jsonwrt_indent(fmt); + if (name) + fputs_quoted_json_lower(name, fmt->out); } void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) { + const char *s = ""; + print_name(fmt, name); switch (type) { case UL_JSON_OBJECT: - fputs(name ? ": {\n" : "{\n", fmt->out); + s = fmt->json_format == UL_JSON_LINE ? (name ? ":{" : "{") : (name ? ": {\n" : "{\n"); fmt->indent++; break; case UL_JSON_ARRAY: - fputs(name ? ": [\n" : "[\n", fmt->out); + s = fmt->json_format == UL_JSON_LINE ? (name ? ":[" : "[") : (name ? ": [\n" : "[\n"); fmt->indent++; break; case UL_JSON_VALUE: - fputs(name ? ": " : " ", fmt->out); + s = fmt->json_format == UL_JSON_LINE ? (name ? ":" : "") : (name ? ": " : " "); break; } + fputs(s, fmt->out); fmt->after_close = 0; } void ul_jsonwrt_empty(struct ul_jsonwrt *fmt, const char *name, int type) { + const char *s = ""; + print_name(fmt, name); switch (type) { case UL_JSON_OBJECT: - fputs(name ? ": {}" : "{}", fmt->out); + s = fmt->json_format == UL_JSON_LINE ? (name ? ":{}" : "{}") : (name ? ": {}" : "{}"); break; case UL_JSON_ARRAY: - fputs(name ? ": []" : "[]", fmt->out); + s = fmt->json_format == UL_JSON_LINE ? (name ? ":[]" : "[]") : (name ? ": []" : "[]"); break; case UL_JSON_VALUE: - fputs(name ? ": null" : "null", fmt->out); + s = fmt->json_format == UL_JSON_LINE ? (name ? ":null" : "null") : (name ? ": null" : "null"); break; } - + fputs(s, fmt->out); fmt->after_close = 1; } void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type) { + char endchr; + assert(fmt->indent > 0); switch (type) { case UL_JSON_OBJECT: fmt->indent--; - fputc('\n', fmt->out); - ul_jsonwrt_indent(fmt); - fputs("}", fmt->out); - if (fmt->indent == 0) - fputs("\n", fmt->out); + endchr = '}'; break; case UL_JSON_ARRAY: fmt->indent--; - fputc('\n', fmt->out); - ul_jsonwrt_indent(fmt); - fputs("]", fmt->out); + endchr = ']'; break; case UL_JSON_VALUE: - break; + fmt->after_close = 1; + return; + default: + fmt->after_close = 1; + return; } + if (fmt->json_format != UL_JSON_LINE) { + fputc('\n', fmt->out); + ul_jsonwrt_indent(fmt); + } + fputc(endchr, fmt->out); + if (fmt->json_format != UL_JSON_LINE && type == UL_JSON_OBJECT && fmt->indent == 0) + fputc('\n', fmt->out); fmt->after_close = 1; } diff --git a/libfdisk/src/script.c b/libfdisk/src/script.c index 62ca5eeb2..c7164fe0d 100644 --- a/libfdisk/src/script.c +++ b/libfdisk/src/script.c @@ -562,7 +562,7 @@ static int write_file_json(struct fdisk_script *dp, FILE *f) DBG(SCRIPT, ul_debugobj(dp, "writing json dump to file")); - ul_jsonwrt_init(&json, f, 0); + ul_jsonwrt_init(&json, f, 0, UL_JSON_PRETTY); ul_jsonwrt_root_open(&json); ul_jsonwrt_object_open(&json, "partitiontable"); diff --git a/libsmartcols/src/filter.c b/libsmartcols/src/filter.c index 4923cd802..70bc57cdc 100644 --- a/libsmartcols/src/filter.c +++ b/libsmartcols/src/filter.c @@ -229,7 +229,7 @@ int scols_dump_filter(struct libscols_filter *fltr, FILE *out) if (!fltr || !out) return -EINVAL; - ul_jsonwrt_init(&json, out, 0); + ul_jsonwrt_init(&json, out, 0, UL_JSON_PRETTY); ul_jsonwrt_root_open(&json); filter_dump_node(&json, fltr->root); diff --git a/libsmartcols/src/print-api.c b/libsmartcols/src/print-api.c index 52b266435..15841b2bc 100644 --- a/libsmartcols/src/print-api.c +++ b/libsmartcols/src/print-api.c @@ -117,11 +117,13 @@ static int do_print_table(struct libscols_table *tb, int *is_empty) if (list_empty(&tb->tb_lines)) { DBG(TAB, ul_debugobj(tb, "ignore -- no lines")); if (scols_table_is_json(tb)) { - ul_jsonwrt_init(&tb->json, tb->out, 0); - ul_jsonwrt_root_open(&tb->json); - ul_jsonwrt_array_open(&tb->json, tb->name ? tb->name : ""); - ul_jsonwrt_array_close(&tb->json); - ul_jsonwrt_root_close(&tb->json); + ul_jsonwrt_init(&tb->json, tb->out, 0, tb->json_format); + if (tb->json_format != UL_JSON_LINE) { + ul_jsonwrt_root_open(&tb->json); + ul_jsonwrt_array_open(&tb->json, tb->name ? tb->name : ""); + ul_jsonwrt_array_close(&tb->json); + ul_jsonwrt_root_close(&tb->json); + } } else if (is_empty) *is_empty = 1; return 0; @@ -132,7 +134,7 @@ static int do_print_table(struct libscols_table *tb, int *is_empty) if (rc) return rc; - if (scols_table_is_json(tb)) { + if (scols_table_is_json(tb) && tb->json_format != UL_JSON_LINE) { ul_jsonwrt_root_open(&tb->json); ul_jsonwrt_array_open(&tb->json, tb->name ? tb->name : ""); } @@ -150,8 +152,12 @@ static int do_print_table(struct libscols_table *tb, int *is_empty) rc = __scols_print_table(tb, &buf); if (scols_table_is_json(tb)) { - ul_jsonwrt_array_close(&tb->json); - ul_jsonwrt_root_close(&tb->json); + if (tb->json_format != UL_JSON_LINE) { + ul_jsonwrt_array_close(&tb->json); + ul_jsonwrt_root_close(&tb->json); + } else { + fputc('\n', tb->out); /* trailing newline after last object */ + } } done: __scols_cleanup_printing(tb, &buf); diff --git a/libsmartcols/src/print.c b/libsmartcols/src/print.c index c1b81c50f..488bb86d6 100644 --- a/libsmartcols/src/print.c +++ b/libsmartcols/src/print.c @@ -1287,7 +1287,7 @@ int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf extra_bufsz += tb->ncols; /* separator between columns */ break; case SCOLS_FMT_JSON: - ul_jsonwrt_init(&tb->json, tb->out, 0); + ul_jsonwrt_init(&tb->json, tb->out, 0, tb->json_format); extra_bufsz += tb->nlines * 3; /* indentation */ FALLTHROUGH; case SCOLS_FMT_EXPORT: diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 1e9dcec01..07df40d8a 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -270,6 +270,8 @@ struct libscols_table { struct libscols_line *cur_line; /* currently used line */ struct libscols_column *cur_column; /* currently used column */ + ul_json_format_t json_format; /* JSON output format */ + /* flags */ bool ascii , /* don't use unicode */ colors_wanted , /* enable colors */ diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c index aa7cdf3c6..13360ce93 100644 --- a/libsmartcols/src/table.c +++ b/libsmartcols/src/table.c @@ -60,6 +60,22 @@ static void check_padding_debug(struct libscols_table *tb) tb->padding_debug = 1; } +static ul_json_format_t get_json_format(void) +{ + const char *str; + + str = getenv("LIBSMARTCOLS_JSON"); + if (!str) + return UL_JSON_PRETTY; + else if (strcmp(str, "compact") == 0) + return UL_JSON_COMPACT; + else if (strcmp(str, "lines") == 0) + return UL_JSON_LINE; + + /* default to pretty */ + return UL_JSON_PRETTY; +} + /** * scols_new_table: * @@ -87,6 +103,7 @@ struct libscols_table *scols_new_table(void) DBG(TAB, ul_debugobj(tb, "alloc")); ON_DBG(INIT, check_padding_debug(tb)); + tb->json_format = get_json_format(); return tb; } diff --git a/sys-utils/dmesg.c b/sys-utils/dmesg.c index 854786554..fd1dd0cfb 100644 --- a/sys-utils/dmesg.c +++ b/sys-utils/dmesg.c @@ -1145,7 +1145,7 @@ static void print_record(struct dmesg_control *ctl, if (ctl->json) { if (!ul_jsonwrt_is_ready(&ctl->jfmt)) { - ul_jsonwrt_init(&ctl->jfmt, stdout, 0); + ul_jsonwrt_init(&ctl->jfmt, stdout, 0, UL_JSON_PRETTY); ul_jsonwrt_root_open(&ctl->jfmt); ul_jsonwrt_array_open(&ctl->jfmt, "dmesg"); }