]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libsmartcols: support JSON Lines format output
authorWanBingjiang <wanbingjiang@webray.com.cn>
Thu, 5 Feb 2026 09:02:39 +0000 (17:02 +0800)
committerWanBingjiang <wanbingjiang@webray.com.cn>
Mon, 23 Feb 2026 08:13:14 +0000 (16:13 +0800)
include/jsonwrt.h
lib/jsonwrt.c
libfdisk/src/script.c
libsmartcols/src/filter.c
libsmartcols/src/print-api.c
libsmartcols/src/print.c
libsmartcols/src/smartcolsP.h
libsmartcols/src/table.c
sys-utils/dmesg.c

index 31e180abe526eee32f4b812dd7d11513f14af71f..74b43e2139ff58629cee729fe9a7b00e4fd73534 100644 (file)
@@ -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);
index 365d845ce91582e212e38ccb76bb6d9ece9cca5b..692592975917280408f919c8e9bbc32a853d0d44 100644 (file)
@@ -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;
 }
 
index 62ca5eeb2a2e983507ccbc61cd1ca4a09f9f7f12..c7164fe0d64eac166eaa65c64e6649453d078872 100644 (file)
@@ -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");
index 4923cd8025ed92ca67307af95916170bbe3fbf04..70bc57cdcd6e72cb12c2a841a7a44b1daa506994 100644 (file)
@@ -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);
index 52b26643558017d1e60923efa14b5425318cf494..15841b2bc6e9f81f0e5e2dfd8f5ef352535f2e73 100644 (file)
@@ -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);
index c1b81c50fdf289f6d3d1ac8e2262c2594ac2790d..488bb86d68211b365b042e6bba2804b1e82ff2e2 100644 (file)
@@ -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:
index 1e9dcec01a7b74d35ec990ebe4144b96507352a2..07df40d8ab9bb4d1dfb370e1a64ad99d6c149f15 100644 (file)
@@ -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 */
index aa7cdf3c6cef5d92fcc470f7765f20fac7b375b9..13360ce93de7cf3f557bab09fdaed57ef7f8494a 100644 (file)
@@ -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;
 }
index 854786554ac27deb8447fec3fd72670a87808f21..fd1dd0cfb9818becea9d5340441918164e3ec08e 100644 (file)
@@ -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");
                }