libsmartcols: add JSON output format

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2015-06-04 15:47:21 +02:00
parent 1280109fdc
commit 2a6cfc1361
6 changed files with 242 additions and 24 deletions

View File

@ -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

View File

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

View File

@ -1,7 +1,7 @@
/*
* symbols since util-linux 2.25
*
* Copyright (C) 2014 Karel Zak <kzak@redhat.com>
* Copyright (C) 2014-2015 Karel Zak <kzak@redhat.com>
*/
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;

View File

@ -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 */

View File

@ -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

View File

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