diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt index 2b8180c52..01bc2a63c 100644 --- a/libsmartcols/docs/libsmartcols-sections.txt +++ b/libsmartcols/docs/libsmartcols-sections.txt @@ -93,6 +93,7 @@ scols_table_colors_wanted scols_table_enable_ascii scols_table_enable_colors scols_table_enable_export +scols_table_enable_json scols_table_enable_maxout scols_table_enable_noheadings scols_table_enable_raw @@ -106,6 +107,7 @@ scols_table_get_stream scols_table_is_ascii scols_table_is_empty scols_table_is_export +scols_table_is_json scols_table_is_maxout scols_table_is_noheadings scols_table_is_raw @@ -121,6 +123,7 @@ scols_table_remove_line scols_table_remove_lines scols_table_set_column_separator scols_table_set_line_separator +scols_table_set_name scols_table_set_stream scols_table_set_symbols scols_sort_table diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in index e61256022..d2a88c9cd 100644 --- a/libsmartcols/src/libsmartcols.h.in +++ b/libsmartcols/src/libsmartcols.h.in @@ -172,8 +172,10 @@ extern struct libscols_line *scols_copy_line(struct libscols_line *ln); /* table */ extern int scols_table_colors_wanted(struct libscols_table *tb); +extern int scols_table_set_name(struct libscols_table *tb, const char *name); extern int scols_table_is_raw(struct libscols_table *tb); extern int scols_table_is_ascii(struct libscols_table *tb); +extern int scols_table_is_json(struct libscols_table *tb); extern int scols_table_is_noheadings(struct libscols_table *tb); extern int scols_table_is_empty(struct libscols_table *tb); extern int scols_table_is_export(struct libscols_table *tb); @@ -183,6 +185,7 @@ extern int scols_table_is_tree(struct libscols_table *tb); extern int scols_table_enable_colors(struct libscols_table *tb, int enable); extern int scols_table_enable_raw(struct libscols_table *tb, int enable); extern int scols_table_enable_ascii(struct libscols_table *tb, int enable); +extern int scols_table_enable_json(struct libscols_table *tb, int enable); extern int scols_table_enable_noheadings(struct libscols_table *tb, int enable); extern int scols_table_enable_export(struct libscols_table *tb, int enable); extern int scols_table_enable_maxout(struct libscols_table *tb, int enable); diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym index 2629213c1..0ef3322b3 100644 --- a/libsmartcols/src/libsmartcols.sym +++ b/libsmartcols/src/libsmartcols.sym @@ -1,7 +1,7 @@ /* * symbols since util-linux 2.25 * - * Copyright (C) 2014 Karel Zak + * Copyright (C) 2014-2015 Karel Zak */ SMARTCOLS_2.25 { global: @@ -112,3 +112,11 @@ global: local: *; }; + + +SMARTCOLS_2.27 { +global: + scols_table_enable_json; + scols_table_is_json; + scols_table_set_name; +} SMARTCOLS_2.25; diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 511b182a2..c4fe725d0 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -122,7 +122,8 @@ struct libscols_line { enum { SCOLS_FMT_HUMAN = 0, /* default, human readable */ SCOLS_FMT_RAW, /* space separated */ - SCOLS_FMT_EXPORT /* COLNAME="data" ... */ + SCOLS_FMT_EXPORT, /* COLNAME="data" ... */ + SCOLS_FMT_JSON /* http://en.wikipedia.org/wiki/JSON */ }; /* @@ -130,6 +131,7 @@ enum { */ struct libscols_table { int refcount; + char *name; /* optional table table */ size_t ncols; /* number of columns */ size_t ntreecols; /* number of columns with SCOLS_FL_TREE */ size_t nlines; /* number of lines */ @@ -144,6 +146,8 @@ struct libscols_table { struct list_head tb_lines; struct libscols_symbols *symbols; + int indent; /* indention counter */ + int indent_last_sep;/* last printed has been line separator */ int format; /* SCOLS_FMT_* */ /* flags */ @@ -171,4 +175,13 @@ struct libscols_table { (itr)->p->next : (itr)->p->prev; \ } while(0) + +static inline int scols_iter_is_last(struct libscols_iter *itr) +{ + if (!itr || !itr->head || !itr->p) + return 0; + + return itr->p == itr->head; +} + #endif /* _LIBSMARTCOLS_PRIVATE_H */ diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c index 45d6a309c..a42ae514d 100644 --- a/libsmartcols/src/table.c +++ b/libsmartcols/src/table.c @@ -89,10 +89,38 @@ void scols_unref_table(struct libscols_table *tb) scols_unref_symbols(tb->symbols); free(tb->linesep); free(tb->colsep); + free(tb->name); free(tb); } } + +/** + * scols_table_set_name: + * @tb: a pointer to a struct libscols_table instance + * @name: a name + * + * The table name is used for example for JSON top level object name. + * + * Returns: 0, a negative number in case of an error. + */ +int scols_table_set_name(struct libscols_table *tb, const char *name) +{ + char *p = NULL; + + if (!tb) + return -EINVAL; + + if (name) { + p = strdup(name); + if (!p) + return -ENOMEM; + } + free(tb->name); + tb->name = p; + return 0; +} + /** * scols_table_add_column: * @tb: a pointer to a struct libscols_table instance @@ -659,6 +687,7 @@ int scols_table_set_symbols(struct libscols_table *tb, return 0; } + /** * scols_table_enable_colors: * @tb: table @@ -677,13 +706,14 @@ int scols_table_enable_colors(struct libscols_table *tb, int enable) tb->colors_wanted = enable; return 0; } + /** * scols_table_enable_raw: * @tb: table * @enable: 1 or 0 * * Enable/disable raw output format. The parsable output formats - * (export and raw) are mutually exclusive. + * (export, raw, JSON, ...) are mutually exclusive. * * Returns: 0 on success, negative number in case of an error. */ @@ -700,6 +730,29 @@ int scols_table_enable_raw(struct libscols_table *tb, int enable) return 0; } +/** + * scols_table_enable_json: + * @tb: table + * @enable: 1 or 0 + * + * Enable/disable JSON output format. The parsable output formats + * (export, raw, JSON, ...) are mutually exclusive. + * + * Returns: 0 on success, negative number in case of an error. + */ +int scols_table_enable_json(struct libscols_table *tb, int enable) +{ + if (!tb) + return -EINVAL; + + DBG(TAB, ul_debugobj(tb, "json: %s", enable ? "ENABLE" : "DISABLE")); + if (enable) + tb->format = SCOLS_FMT_JSON; + else if (tb->format == SCOLS_FMT_JSON) + tb->format = 0; + return 0; +} + /** * scols_table_enable_export: * @tb: table @@ -851,6 +904,17 @@ int scols_table_is_raw(struct libscols_table *tb) return tb && tb->format == SCOLS_FMT_RAW; } +/** + * scols_table_is_json: + * @tb: table + * + * Returns: 1 if JSON output format is enabled. + */ +int scols_table_is_json(struct libscols_table *tb) +{ + return tb && tb->format == SCOLS_FMT_JSON; +} + /** * scols_table_is_maxout diff --git a/libsmartcols/src/table_print.c b/libsmartcols/src/table_print.c index fbca28a1f..37d75c962 100644 --- a/libsmartcols/src/table_print.c +++ b/libsmartcols/src/table_print.c @@ -269,21 +269,33 @@ static int print_data(struct libscols_table *tb, if (!data) data = ""; - /* raw mode */ - if (scols_table_is_raw(tb)) { + switch (tb->format) { + case SCOLS_FMT_RAW: fputs_nonblank(data, tb->out); if (!is_last_column(tb, cl)) fputs(colsep(tb), tb->out); return 0; - } - /* NAME=value mode */ - if (scols_table_is_export(tb)) { + case SCOLS_FMT_EXPORT: fprintf(tb->out, "%s=", scols_cell_get_data(&cl->header)); fputs_quoted(data, tb->out); if (!is_last_column(tb, cl)) fputs(colsep(tb), tb->out); return 0; + + case SCOLS_FMT_JSON: + fputs_quoted(scols_cell_get_data(&cl->header), tb->out); + fputs(": ", tb->out); + if (!data || !*data) + fputs("null", tb->out); + else + fputs_quoted(data, tb->out); + if (!is_last_column(tb, cl)) + fputs(", ", tb->out); + return 0; + + case SCOLS_FMT_HUMAN: + break; /* continue below */ } if (tb->colors_wanted) { @@ -384,7 +396,7 @@ static int cell_to_buffer(struct libscols_table *tb, /* * Tree stuff */ - if (ln->parent) { + if (ln->parent && !scols_table_is_json(tb)) { rc = line_ascii_art_to_buffer(tb, ln->parent, buf); if (!rc && list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch)) @@ -400,6 +412,95 @@ static int cell_to_buffer(struct libscols_table *tb, return rc; } +static void fput_indent(struct libscols_table *tb) +{ + int i; + + for (i = 0; i <= tb->indent; i++) + fputs(" ", tb->out); +} + +static void fput_table_open(struct libscols_table *tb) +{ + tb->indent = 0; + + if (scols_table_is_json(tb)) { + fputc('{', tb->out); + fputs(linesep(tb), tb->out); + + fput_indent(tb); + fputs_quoted(tb->name, tb->out); + fputs(": [", tb->out); + fputs(linesep(tb), tb->out); + + tb->indent++; + tb->indent_last_sep = 1; + } +} + +static void fput_table_close(struct libscols_table *tb) +{ + tb->indent--; + + if (scols_table_is_json(tb)) { + fput_indent(tb); + fputc(']', tb->out); + tb->indent--; + fputs(linesep(tb), tb->out); + fputc('}', tb->out); + fputs(linesep(tb), tb->out); + tb->indent_last_sep = 1; + } +} + +static void fput_children_open(struct libscols_table *tb) +{ + if (scols_table_is_json(tb)) { + fputc(',', tb->out); + fputs(linesep(tb), tb->out); + fput_indent(tb); + fputs("\"children\": [", tb->out); + } + /* between parent and child is separator */ + fputs(linesep(tb), tb->out); + tb->indent_last_sep = 1; + tb->indent++; +} + +static void fput_children_close(struct libscols_table *tb) +{ + tb->indent--; + + if (scols_table_is_json(tb)) { + fput_indent(tb); + fputc(']', tb->out); + fputs(linesep(tb), tb->out); + tb->indent_last_sep = 1; + } +} + +static void fput_line_open(struct libscols_table *tb) +{ + if (scols_table_is_json(tb)) { + fput_indent(tb); + fputc('{', tb->out); + tb->indent_last_sep = 0; + } + tb->indent++; +} + +static void fput_line_close(struct libscols_table *tb, int last) +{ + tb->indent--; + if (scols_table_is_json(tb)) { + if (tb->indent_last_sep) + fput_indent(tb); + fputs(last ? "}" : "},", tb->out); + } + fputs(linesep(tb), tb->out); + tb->indent_last_sep = 1; +} + /* * Prints data. Data can be printed in more formats (raw, NAME=xxx pairs), and * control and non-printable characters can be encoded in the \x?? encoding. @@ -425,8 +526,6 @@ static int print_line(struct libscols_table *tb, buf); } - if (rc == 0) - fputs(linesep(tb), tb->out); return 0; } @@ -460,63 +559,82 @@ static int print_header(struct libscols_table *tb, struct libscols_buffer *buf) static int print_table(struct libscols_table *tb, struct libscols_buffer *buf) { - int rc; + int rc = 0; struct libscols_line *ln; struct libscols_iter itr; assert(tb); - rc = print_header(tb, buf); - scols_reset_iter(&itr, SCOLS_ITER_FORWARD); - while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) + while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) { + fput_line_open(tb); rc = print_line(tb, ln, buf); + fput_line_close(tb, scols_iter_is_last(&itr)); + } return rc; } static int print_tree_line(struct libscols_table *tb, struct libscols_line *ln, - struct libscols_buffer *buf) + struct libscols_buffer *buf, + int last) { int rc; struct list_head *p; + fput_line_open(tb); + rc = print_line(tb, ln, buf); if (rc) - return rc; - if (list_empty(&ln->ln_branch)) + goto done; + + if (list_empty(&ln->ln_branch)) { + fput_line_close(tb, last); return 0; + } + + fput_children_open(tb); /* print all children */ list_for_each(p, &ln->ln_branch) { struct libscols_line *chld = list_entry(p, struct libscols_line, ln_children); - rc = print_tree_line(tb, chld, buf); + + rc = print_tree_line(tb, chld, buf, p->next == &ln->ln_branch); if (rc) - break; + goto done; } + fput_children_close(tb); + + if (scols_table_is_json(tb)) + fput_line_close(tb, last); +done: return rc; } static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf) { - int rc; - struct libscols_line *ln; + int rc = 0; + struct libscols_line *ln, *last = NULL; struct libscols_iter itr; assert(tb); DBG(TAB, ul_debugobj(tb, "printing tree")); - rc = print_header(tb, buf); + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + + while (scols_table_next_line(tb, &itr, &ln) == 0) + if (!last || !ln->parent) + last = ln; scols_reset_iter(&itr, SCOLS_ITER_FORWARD); while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) { if (ln->parent) continue; - rc = print_tree_line(tb, ln, buf); + rc = print_tree_line(tb, ln, buf, ln == last); } return rc; @@ -852,11 +970,20 @@ int scols_print_table(struct libscols_table *tb) goto done; } + fput_table_open(tb); + + if (!scols_table_is_json(tb)) { + rc = print_header(tb, buf); + if (rc) + goto done; + } + if (scols_table_is_tree(tb)) rc = print_tree(tb, buf); else rc = print_table(tb, buf); + fput_table_close(tb); done: free_buffer(buf); return rc;