From fc6853caa0f3e4f8f10404e58f2dbf9f0df88bd4 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Thu, 31 May 2018 11:44:35 +0200
Subject: [PATCH 153/173] libsmartcols: backport upstream version
v2.32-158-gc0bdff999
Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1561350
Signed-off-by: Karel Zak <kzak@redhat.com>
---
Makefile.am | 1 +
configure.ac | 3 -
libsmartcols/Makemodule.am | 3 +-
libsmartcols/docs/Makefile.am | 4 +-
libsmartcols/docs/libsmartcols-docs.xml | 28 +-
libsmartcols/docs/libsmartcols-sections.txt | 47 +-
libsmartcols/samples/Makemodule.am | 37 +
libsmartcols/samples/continuous.c | 138 +++
libsmartcols/samples/fromfile.c | 344 +++++++
libsmartcols/samples/maxout.c | 56 ++
libsmartcols/samples/title.c | 135 +++
libsmartcols/{src/test.c => samples/tree.c} | 77 +-
libsmartcols/samples/wrap.c | 111 +++
libsmartcols/src/Makemodule.am | 41 +-
libsmartcols/src/cell.c | 111 ++-
libsmartcols/src/column.c | 351 +++++--
libsmartcols/src/iter.c | 2 +-
libsmartcols/src/libsmartcols.h.in | 156 +++-
libsmartcols/src/libsmartcols.sym | 72 ++
libsmartcols/src/line.c | 173 ++--
libsmartcols/src/smartcolsP.h | 70 +-
libsmartcols/src/symbols.c | 114 ++-
libsmartcols/src/table.c | 851 +++++++++++++----
libsmartcols/src/table_print.c | 1342 ++++++++++++++++++++++-----
24 files changed, 3530 insertions(+), 737 deletions(-)
create mode 100644 libsmartcols/samples/Makemodule.am
create mode 100644 libsmartcols/samples/continuous.c
create mode 100644 libsmartcols/samples/fromfile.c
create mode 100644 libsmartcols/samples/maxout.c
create mode 100644 libsmartcols/samples/title.c
rename libsmartcols/{src/test.c => samples/tree.c} (68%)
create mode 100644 libsmartcols/samples/wrap.c
diff --git a/Makefile.am b/Makefile.am
index 67464e4b2..7d5fa10e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -118,6 +118,7 @@ edit_cmd = sed \
-e 's|@VERSION[@]|$(VERSION)|g' \
-e 's|@LIBUUID_VERSION[@]|$(LIBUUID_VERSION)|g' \
-e 's|@LIBMOUNT_VERSION[@]|$(LIBMOUNT_VERSION)|g' \
+ -e 's|@LIBSMARTCOLS_VERSION[@]|$(LIBSMARTCOLS_VERSION)|g' \
-e 's|@LIBBLKID_VERSION[@]|$(LIBBLKID_VERSION)|g'
CLEANFILES += $(PATHFILES)
diff --git a/configure.ac b/configure.ac
index 8cf317dc0..d561e01d0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -133,9 +133,6 @@ AC_SUBST([BSD_WARN_CFLAGS])
dnl libtool-2
LT_INIT
-dnl check supported linker flags
-AX_CHECK_VSCRIPT
-
m4_ifndef([PKG_PROG_PKG_CONFIG],
[m4_fatal([Could not locate the pkg-config autoconf
macros. These are usually located in /usr/share/aclocal/pkg.m4.
diff --git a/libsmartcols/Makemodule.am b/libsmartcols/Makemodule.am
index 0089712f1..012848b2b 100644
--- a/libsmartcols/Makemodule.am
+++ b/libsmartcols/Makemodule.am
@@ -1,13 +1,14 @@
if BUILD_LIBSMARTCOLS
include libsmartcols/src/Makemodule.am
+include libsmartcols/samples/Makemodule.am
if ENABLE_GTK_DOC
# Docs uses separate Makefiles
SUBDIRS += libsmartcols/docs
endif
-# noinst for RHEL7: pkgconfig_DATA += libsmartcols/smartcols.pc
+pkgconfig_DATA += libsmartcols/smartcols.pc
PATHFILES += libsmartcols/smartcols.pc
EXTRA_DIST += libsmartcols/COPYING
diff --git a/libsmartcols/docs/Makefile.am b/libsmartcols/docs/Makefile.am
index c5aa2237c..e8a7600e9 100644
--- a/libsmartcols/docs/Makefile.am
+++ b/libsmartcols/docs/Makefile.am
@@ -32,7 +32,7 @@ SCAN_OPTIONS=
# Extra options to supply to gtkdoc-mkdb.
# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
-MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space mnt
+MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space scols
# Extra options to supply to gtkdoc-mktmpl
# e.g. MKTMPL_OPTIONS=--only-section-tmpl
@@ -67,7 +67,7 @@ HTML_IMAGES=
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
content_files = $(builddir)/version.xml
-# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# SGML files where gtk-doc abbreviations (#GtkWidget) are expanded
# These files must be listed here *and* in content_files
# e.g. expand_content_files=running.sgml
expand_content_files=
diff --git a/libsmartcols/docs/libsmartcols-docs.xml b/libsmartcols/docs/libsmartcols-docs.xml
index 4976ba701..02ee1ffe1 100644
--- a/libsmartcols/docs/libsmartcols-docs.xml
+++ b/libsmartcols/docs/libsmartcols-docs.xml
@@ -9,12 +9,12 @@
<title>libsmartcols Reference Manual</title>
<releaseinfo>for libsmartcols version &version;</releaseinfo>
<copyright>
- <year>2014</year>
+ <year>2014-2018</year>
<holder>Karel Zak <kzak@redhat.com></holder>
</copyright>
</bookinfo>
- <part id="gtk">
+ <part id="overview">
<title>libsmartcols Overview</title>
<partintro>
<para>
@@ -22,7 +22,7 @@ The libsmartcols library is used for smart adaptive formatting of tabular data.
</para>
<para>
The library is part of the util-linux package since version 2.25 and is
-available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+available from https://www.kernel.org/pub/linux/utils/util-linux/.
</para>
</partintro>
</part>
@@ -45,8 +45,28 @@ available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
<xi:include href="xml/version-utils.xml"/>
<xi:include href="xml/init.xml"/>
</part>
- <index id="api-index-full">
+ <index id="api-index">
<title>API Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
+ <index role="2.27">
+ <title>Index of new symbols in 2.27</title>
+ <xi:include href="xml/api-index-2.27.xml"><xi:fallback /></xi:include>
+ </index>
+ <index role="2.28">
+ <title>Index of new symbols in 2.28</title>
+ <xi:include href="xml/api-index-2.28.xml"><xi:fallback /></xi:include>
+ </index>
+ <index role="2.29">
+ <title>Index of new symbols in 2.29</title>
+ <xi:include href="xml/api-index-2.29.xml"><xi:fallback /></xi:include>
+ </index>
+ <index role="2.30">
+ <title>Index of new symbols in 2.30</title>
+ <xi:include href="xml/api-index-2.30.xml"><xi:fallback /></xi:include>
+ </index>
+ <index role="2.31">
+ <title>Index of new symbols in 2.31</title>
+ <xi:include href="xml/api-index-2.31.xml"><xi:fallback /></xi:include>
+ </index>
</book>
diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt
index 2b8180c52..79786b544 100644
--- a/libsmartcols/docs/libsmartcols-sections.txt
+++ b/libsmartcols/docs/libsmartcols-sections.txt
@@ -2,12 +2,15 @@
<FILE>cell</FILE>
libscols_cell
scols_cell_copy_content
+scols_cell_get_alignment
scols_cell_get_color
scols_cell_get_data
+scols_cell_get_flags
scols_cell_get_userdata
scols_cell_refer_data
scols_cell_set_color
scols_cell_set_data
+scols_cell_set_flags
scols_cell_set_userdata
scols_cmpstr_cells
scols_reset_cell
@@ -19,20 +22,32 @@ libscols_column
scols_column_get_color
scols_column_get_flags
scols_column_get_header
+scols_column_get_json_type
+scols_column_get_safechars
+scols_column_get_table
scols_column_get_whint
+scols_column_get_width
+scols_column_is_customwrap
+scols_column_is_hidden
scols_column_is_noextremes
scols_column_is_right
scols_column_is_strict_width
scols_column_is_tree
scols_column_is_trunc
+scols_column_is_wrap
scols_column_set_cmpfunc
scols_column_set_color
scols_column_set_flags
+scols_column_set_json_type
+scols_column_set_safechars
scols_column_set_whint
+scols_column_set_wrapfunc
scols_copy_column
scols_new_column
scols_ref_column
scols_unref_column
+scols_wrapnl_chunksize
+scols_wrapnl_nextchunk
</SECTION>
<SECTION>
@@ -58,10 +73,13 @@ scols_line_get_ncells
scols_line_get_parent
scols_line_get_userdata
scols_line_has_children
+scols_line_is_ancestor
scols_line_next_child
+scols_line_refer_column_data
scols_line_refer_data
scols_line_remove_child
scols_line_set_color
+scols_line_set_column_data
scols_line_set_data
scols_line_set_userdata
scols_new_line
@@ -78,6 +96,8 @@ scols_ref_symbols
scols_symbols_set_branch
scols_symbols_set_right
scols_symbols_set_vertical
+scols_symbols_set_title_padding
+scols_symbols_set_cell_padding
scols_unref_symbols
</SECTION>
@@ -87,29 +107,48 @@ libscols_table
scols_copy_table
scols_new_table
scols_ref_table
+scols_sort_table
+scols_sort_table_by_tree
scols_table_add_column
scols_table_add_line
scols_table_colors_wanted
scols_table_enable_ascii
scols_table_enable_colors
+scols_table_enable_noencoding
scols_table_enable_export
+scols_table_enable_header_repeat
+scols_table_enable_json
scols_table_enable_maxout
scols_table_enable_noheadings
+scols_table_enable_nolinesep
+scols_table_enable_nowrap
scols_table_enable_raw
scols_table_get_column
scols_table_get_column_separator
scols_table_get_line
scols_table_get_line_separator
+scols_table_get_name
scols_table_get_ncols
scols_table_get_nlines
scols_table_get_stream
+scols_table_get_symbols
+scols_table_get_termforce
+scols_table_get_termheight
+scols_table_get_termwidth
+scols_table_get_title
scols_table_is_ascii
scols_table_is_empty
scols_table_is_export
+scols_table_is_header_repeat
+scols_table_is_json
scols_table_is_maxout
scols_table_is_noheadings
+scols_table_is_noencoding
+scols_table_is_nolinesep
+scols_table_is_nowrap
scols_table_is_raw
scols_table_is_tree
+scols_table_move_column
scols_table_new_column
scols_table_new_line
scols_table_next_column
@@ -120,10 +159,14 @@ scols_table_remove_columns
scols_table_remove_line
scols_table_remove_lines
scols_table_set_column_separator
+scols_table_set_default_symbols
scols_table_set_line_separator
+scols_table_set_name
scols_table_set_stream
scols_table_set_symbols
-scols_sort_table
+scols_table_set_termforce
+scols_table_set_termheight
+scols_table_set_termwidth
scols_unref_table
</SECTION>
@@ -131,6 +174,8 @@ scols_unref_table
<FILE>table_print</FILE>
scols_print_table
scols_print_table_to_string
+scols_table_print_range
+scols_table_print_range_to_string
</SECTION>
<SECTION>
diff --git a/libsmartcols/samples/Makemodule.am b/libsmartcols/samples/Makemodule.am
new file mode 100644
index 000000000..0e0208f04
--- /dev/null
+++ b/libsmartcols/samples/Makemodule.am
@@ -0,0 +1,37 @@
+
+check_PROGRAMS += \
+ sample-scols-title \
+ sample-scols-wrap \
+ sample-scols-continuous \
+ sample-scols-fromfile \
+ sample-scols-maxout
+
+sample_scols_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \
+ -I$(ul_libsmartcols_incdir)
+sample_scols_ldadd = libsmartcols.la $(LDADD)
+
+check_PROGRAMS += sample-scols-tree
+sample_scols_tree_SOURCES = libsmartcols/samples/tree.c
+sample_scols_tree_LDADD = $(sample_scols_ldadd) libcommon.la
+sample_scols_tree_CFLAGS = $(sample_scols_cflags)
+
+sample_scols_title_SOURCES = libsmartcols/samples/title.c
+sample_scols_title_LDADD = $(sample_scols_ldadd) libcommon.la
+sample_scols_title_CFLAGS = $(sample_scols_cflags)
+
+sample_scols_wrap_SOURCES = libsmartcols/samples/wrap.c
+sample_scols_wrap_LDADD = $(sample_scols_ldadd)
+sample_scols_wrap_CFLAGS = $(sample_scols_cflags)
+
+sample_scols_continuous_SOURCES = libsmartcols/samples/continuous.c
+sample_scols_continuous_LDADD = $(sample_scols_ldadd) libcommon.la
+sample_scols_continuous_CFLAGS = $(sample_scols_cflags)
+
+sample_scols_maxout_SOURCES = libsmartcols/samples/maxout.c
+sample_scols_maxout_LDADD = $(sample_scols_ldadd)
+sample_scols_maxout_CFLAGS = $(sample_scols_cflags)
+
+sample_scols_fromfile_SOURCES = libsmartcols/samples/fromfile.c
+sample_scols_fromfile_LDADD = $(sample_scols_ldadd) libcommon.la
+sample_scols_fromfile_CFLAGS = $(sample_scols_cflags)
+
diff --git a/libsmartcols/samples/continuous.c b/libsmartcols/samples/continuous.c
new file mode 100644
index 000000000..8f9d13e6b
--- /dev/null
+++ b/libsmartcols/samples/continuous.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "xalloc.h"
+
+#include "libsmartcols.h"
+
+#define TIME_PERIOD 3.0 /* seconds */
+
+enum { COL_NUM, COL_DATA, COL_TIME };
+
+static double time_diff(struct timeval *a, struct timeval *b)
+{
+ return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6;
+}
+
+/* add columns to the @tb */
+static void setup_columns(struct libscols_table *tb)
+{
+ scols_table_enable_maxout(tb, 1);
+ if (!scols_table_new_column(tb, "#NUM", 0.1, SCOLS_FL_RIGHT))
+ goto fail;
+ if (!scols_table_new_column(tb, "DATA", 0.7, 0))
+ goto fail;
+ if (!scols_table_new_column(tb, "TIME", 0.2, 0))
+ goto fail;
+ return;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output columns");
+}
+
+static struct libscols_line *add_line(struct libscols_table *tb, size_t i)
+{
+ char *p;
+ struct libscols_line *ln = scols_table_new_line(tb, NULL);
+
+ if (!ln)
+ err(EXIT_FAILURE, "failed to create output line");
+
+ xasprintf(&p, "%zu", i);
+ if (scols_line_refer_data(ln, COL_NUM, p))
+ goto fail;
+
+ xasprintf(&p, "data-%02zu-%02zu-%02zu-end", i + 1, i + 2, i + 3);
+ if (scols_line_refer_data(ln, COL_DATA, p))
+ goto fail;
+
+ return ln;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output line");
+}
+
+int main(int argc, char *argv[])
+{
+ struct libscols_table *tb;
+ size_t i;
+ struct timeval last;
+
+ scols_init_debug(0);
+
+ tb = scols_new_table();
+ if (!tb)
+ err(EXIT_FAILURE, "failed to create output table");
+
+ setup_columns(tb);
+ gettimeofday(&last, NULL);
+
+ for (i = 0; i < 10; i++) {
+ struct libscols_line *line;
+ struct timeval now;
+ int done = 0;
+ char *timecell = xmalloc( sizeof(stringify_value(UINT_MAX)) );
+
+ line = add_line(tb, i);
+
+ /* Make a reference from cell data to the buffer, then we can
+ * update cell data without any interaction with libsmartcols
+ */
+ scols_line_refer_data(line, COL_TIME, timecell);
+
+ do {
+ double diff;
+
+ gettimeofday(&now, NULL);
+ diff = time_diff(&now, &last);
+
+ if (now.tv_sec == last.tv_sec + (long) TIME_PERIOD)
+ done = 1;
+ else
+ usleep(100000);
+
+ /* update "TIME" cell data */
+ sprintf(timecell, "%f [%3d%%]", diff,
+ done ? 100 : (int)(diff / (TIME_PERIOD / 100.0)));
+
+ /* Note that libsmartcols don't print \n for last line
+ * in the table, but if you print a line somewhere in
+ * the midle of the table you need
+ *
+ * scols_table_enable_nolinesep(tb, !done);
+ *
+ * to disable line breaks. In this example it's
+ * unnecessary as we print the latest line only.
+ */
+
+ /* print the line */
+ scols_table_print_range(tb, line, NULL);
+
+ if (!done) {
+ /* terminal is waiting for \n, fflush() to force output */
+ fflush(scols_table_get_stream(tb));
+ /* move to the begin of the line */
+ fputc('\r', scols_table_get_stream(tb));
+ } else
+ fputc('\n', scols_table_get_stream(tb));
+ } while (!done);
+
+ last = now;
+ }
+
+ scols_unref_table(tb);
+ return EXIT_SUCCESS;
+}
diff --git a/libsmartcols/samples/fromfile.c b/libsmartcols/samples/fromfile.c
new file mode 100644
index 000000000..c1ab728fd
--- /dev/null
+++ b/libsmartcols/samples/fromfile.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2016 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <getopt.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "xalloc.h"
+#include "optutils.h"
+
+#include "libsmartcols.h"
+
+struct column_flag {
+ const char *name;
+ int mask;
+};
+
+static const struct column_flag flags[] = {
+ { "trunc", SCOLS_FL_TRUNC },
+ { "tree", SCOLS_FL_TREE },
+ { "right", SCOLS_FL_RIGHT },
+ { "strictwidth",SCOLS_FL_STRICTWIDTH },
+ { "noextremes", SCOLS_FL_NOEXTREMES },
+ { "hidden", SCOLS_FL_HIDDEN },
+ { "wrap", SCOLS_FL_WRAP },
+ { "wrapnl", SCOLS_FL_WRAP },
+ { "none", 0 }
+};
+
+static long name_to_flag(const char *name, size_t namesz)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(flags); i++) {
+ const char *cn = flags[i].name;
+
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return flags[i].mask;
+ }
+ warnx("unknown flag: %s", name);
+ return -1;
+}
+
+static int parse_column_flags(char *str)
+{
+ unsigned long num_flags = 0;
+
+ if (string_to_bitmask(str, &num_flags, name_to_flag))
+ err(EXIT_FAILURE, "failed to parse column flags");
+
+ return num_flags;
+}
+
+static struct libscols_column *parse_column(FILE *f)
+{
+ size_t len = 0;
+ char *line = NULL;
+ int nlines = 0;
+
+ struct libscols_column *cl = NULL;
+
+ while (getline(&line, &len, f) != -1) {
+
+ char *p = strrchr(line, '\n');
+ if (p)
+ *p = '\0';
+
+ switch (nlines) {
+ case 0: /* NAME */
+ {
+ struct libscols_cell *hr;
+
+ cl = scols_new_column();
+ if (!cl)
+ goto fail;
+ hr = scols_column_get_header(cl);
+ if (!hr || scols_cell_set_data(hr, line))
+ goto fail;
+ break;
+ }
+ case 1: /* WIDTH-HINT */
+ {
+ double whint = strtod_or_err(line, "failed to parse column whint");
+ if (scols_column_set_whint(cl, whint))
+ goto fail;
+ break;
+ }
+ case 2: /* FLAGS */
+ {
+ int num_flags = parse_column_flags(line);
+ if (scols_column_set_flags(cl, num_flags))
+ goto fail;
+ if (strcmp(line, "wrapnl") == 0) {
+ scols_column_set_wrapfunc(cl,
+ scols_wrapnl_chunksize,
+ scols_wrapnl_nextchunk,
+ NULL);
+ scols_column_set_safechars(cl, "\n");
+ }
+ break;
+ }
+ case 3: /* COLOR */
+ if (scols_column_set_color(cl, line))
+ goto fail;
+ break;
+ default:
+ break;
+ }
+
+ nlines++;
+ }
+
+ free(line);
+ return cl;
+fail:
+ free(line);
+ scols_unref_column(cl);
+ return NULL;
+}
+
+static int parse_column_data(FILE *f, struct libscols_table *tb, int col)
+{
+ size_t len = 0, nlines = 0;
+ int i;
+ char *str = NULL;
+
+ while ((i = getline(&str, &len, f)) != -1) {
+
+ struct libscols_line *ln;
+ char *p = strrchr(str, '\n');
+ if (p)
+ *p = '\0';
+
+ while ((p = strrchr(str, '\\')) && *(p + 1) == 'n') {
+ *p = '\n';
+ memmove(p + 1, p + 2, i - (p + 2 - str));
+ }
+
+ ln = scols_table_get_line(tb, nlines++);
+ if (!ln)
+ break;
+
+ scols_line_set_data(ln, col, str);
+ }
+
+ free(str);
+ return 0;
+
+}
+
+static struct libscols_line *get_line_with_id(struct libscols_table *tb,
+ int col_id, const char *id)
+{
+ struct libscols_line *ln;
+ struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
+
+ while (scols_table_next_line(tb, itr, &ln) == 0) {
+ struct libscols_cell *ce = scols_line_get_cell(ln, col_id);
+ const char *data = ce ? scols_cell_get_data(ce) : NULL;
+
+ if (data && strcmp(data, id) == 0)
+ break;
+ }
+
+ scols_free_iter(itr);
+ return ln;
+}
+
+static void compose_tree(struct libscols_table *tb, int parent_col, int id_col)
+{
+ struct libscols_line *ln;
+ struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
+
+ while (scols_table_next_line(tb, itr, &ln) == 0) {
+ struct libscols_line *parent = NULL;
+ struct libscols_cell *ce = scols_line_get_cell(ln, parent_col);
+ const char *data = ce ? scols_cell_get_data(ce) : NULL;
+
+ if (data)
+ parent = get_line_with_id(tb, id_col, data);
+ if (parent)
+ scols_line_add_child(parent, ln);
+ }
+
+ scols_free_iter(itr);
+}
+
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fprintf(out,
+ "\n %s [options] <column-data-file> ...\n\n", program_invocation_short_name);
+
+ fputs(" -m, --maxout fill all terminal width\n", out);
+ fputs(" -c, --column <file> column definition\n", out);
+ fputs(" -n, --nlines <num> number of lines\n", out);
+ fputs(" -J, --json JSON output format\n", out);
+ fputs(" -r, --raw RAW output format\n", out);
+ fputs(" -E, --export use key=\"value\" output format\n", out);
+ fputs(" -C, --colsep <str> set columns separator\n", out);
+ fputs(" -w, --width <num> hardcode terminal width\n", out);
+ fputs(" -p, --tree-parent-column <n> parent column\n", out);
+ fputs(" -i, --tree-id-column <n> id column\n", out);
+ fputs(" -h, --help this help\n", out);
+ fputs("\n", out);
+
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ struct libscols_table *tb;
+ int c, n, nlines = 0;
+ int parent_col = -1, id_col = -1;
+
+ static const struct option longopts[] = {
+ { "maxout", 0, NULL, 'm' },
+ { "column", 1, NULL, 'c' },
+ { "nlines", 1, NULL, 'n' },
+ { "width", 1, NULL, 'w' },
+ { "tree-parent-column", 1, NULL, 'p' },
+ { "tree-id-column", 1, NULL, 'i' },
+ { "json", 0, NULL, 'J' },
+ { "raw", 0, NULL, 'r' },
+ { "export", 0, NULL, 'E' },
+ { "colsep", 1, NULL, 'C' },
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
+ { 'E', 'J', 'r' },
+ { 0 }
+ };
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */
+ scols_init_debug(0);
+
+ tb = scols_new_table();
+ if (!tb)
+ err(EXIT_FAILURE, "failed to create output table");
+
+ while((c = getopt_long(argc, argv, "hCc:Ei:Jmn:p:rw:", longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch(c) {
+ case 'c': /* add column from file */
+ {
+ struct libscols_column *cl;
+ FILE *f = fopen(optarg, "r");
+
+ if (!f)
+ err(EXIT_FAILURE, "%s: open failed", optarg);
+ cl = parse_column(f);
+ if (cl && scols_table_add_column(tb, cl))
+ err(EXIT_FAILURE, "%s: failed to add column", optarg);
+ scols_unref_column(cl);
+ fclose(f);
+ break;
+ }
+ case 'p':
+ parent_col = strtou32_or_err(optarg, "failed to parse tree PARENT column");
+ break;
+ case 'i':
+ id_col = strtou32_or_err(optarg, "failed to parse tree ID column");
+ break;
+ case 'J':
+ scols_table_enable_json(tb, 1);
+ scols_table_set_name(tb, "testtable");
+ break;
+ case 'm':
+ scols_table_enable_maxout(tb, TRUE);
+ break;
+ case 'r':
+ scols_table_enable_raw(tb, TRUE);
+ break;
+ case 'E':
+ scols_table_enable_export(tb, TRUE);
+ break;
+ case 'C':
+ scols_table_set_column_separator(tb, optarg);
+ break;
+ case 'n':
+ nlines = strtou32_or_err(optarg, "failed to parse number of lines");
+ break;
+ case 'w':
+ scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS);
+ scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width"));
+ break;
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (nlines <= 0)
+ errx(EXIT_FAILURE, "--nlines not set");
+
+ for (n = 0; n < nlines; n++) {
+ struct libscols_line *ln = scols_new_line();
+
+ if (!ln || scols_table_add_line(tb, ln))
+ err(EXIT_FAILURE, "failed to add a new line");
+
+ scols_unref_line(ln);
+ }
+
+ n = 0;
+
+ while (optind < argc) {
+ FILE *f = fopen(argv[optind], "r");
+
+ if (!f)
+ err(EXIT_FAILURE, "%s: open failed", argv[optind]);
+
+ parse_column_data(f, tb, n);
+ optind++;
+ n++;
+ }
+
+ if (scols_table_is_tree(tb) && parent_col >= 0 && id_col >= 0)
+ compose_tree(tb, parent_col, id_col);
+
+ scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
+
+ scols_print_table(tb);
+ scols_unref_table(tb);
+ return EXIT_SUCCESS;
+}
diff --git a/libsmartcols/samples/maxout.c b/libsmartcols/samples/maxout.c
new file mode 100644
index 000000000..07a05e13f
--- /dev/null
+++ b/libsmartcols/samples/maxout.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <getopt.h>
+
+#include "c.h"
+#include "nls.h"
+#include "libsmartcols.h"
+
+enum { COL_LEFT, COL_FOO, COL_RIGHT };
+
+int main(int argc, char *argv[])
+{
+ struct libscols_table *tb;
+ int rc = -1, nlines = 3;
+
+ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */
+
+ scols_init_debug(0);
+
+ tb = scols_new_table();
+ if (!tb)
+ err(EXIT_FAILURE, "failed to create output table");
+
+ scols_table_enable_maxout(tb, TRUE);
+ if (!scols_table_new_column(tb, "LEFT", 0, 0))
+ goto done;
+ if (!scols_table_new_column(tb, "FOO", 0, 0))
+ goto done;
+ if (!scols_table_new_column(tb, "RIGHT", 0, SCOLS_FL_RIGHT))
+ goto done;
+
+ while (nlines--) {
+ struct libscols_line *ln = scols_table_new_line(tb, NULL);
+
+ scols_line_set_data(ln, COL_LEFT, "A");
+ scols_line_set_data(ln, COL_FOO, "B");
+ scols_line_set_data(ln, COL_RIGHT, "C");
+ }
+
+ scols_print_table(tb);
+ rc = 0;
+done:
+ scols_unref_table(tb);
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/libsmartcols/samples/title.c b/libsmartcols/samples/title.c
new file mode 100644
index 000000000..131400da4
--- /dev/null
+++ b/libsmartcols/samples/title.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <getopt.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "xalloc.h"
+
+#include "libsmartcols.h"
+
+
+enum { COL_NAME, COL_DATA };
+
+/* add columns to the @tb */
+static void setup_columns(struct libscols_table *tb)
+{
+ if (!scols_table_new_column(tb, "NAME", 0, 0))
+ goto fail;
+ if (!scols_table_new_column(tb, "DATA", 0, 0))
+ goto fail;
+ return;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output columns");
+}
+
+static void add_line(struct libscols_table *tb, const char *name, const char *data)
+{
+ struct libscols_line *ln = scols_table_new_line(tb, NULL);
+ if (!ln)
+ err(EXIT_FAILURE, "failed to create output line");
+
+ if (scols_line_set_data(ln, COL_NAME, name))
+ goto fail;
+ if (scols_line_set_data(ln, COL_DATA, data))
+ goto fail;
+ return;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output line");
+}
+
+int main(int argc, char *argv[])
+{
+ struct libscols_table *tb;
+ struct libscols_symbols *sy;
+ struct libscols_cell *title;
+ int c;
+
+ static const struct option longopts[] = {
+ { "maxout", 0, NULL, 'm' },
+ { "width", 1, NULL, 'w' },
+ { "help", 1, NULL, 'h' },
+
+ { NULL, 0, NULL, 0 },
+ };
+
+ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */
+
+ scols_init_debug(0);
+
+ tb = scols_new_table();
+ if (!tb)
+ err(EXIT_FAILURE, "failed to create output table");
+
+ while((c = getopt_long(argc, argv, "hmw:", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'h':
+ printf("%s [--help | --maxout | --width <num>]\n", program_invocation_short_name);
+ break;
+ case 'm':
+ scols_table_enable_maxout(tb, TRUE);
+ break;
+ case 'w':
+ scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS);
+ scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width"));
+ break;
+ }
+ }
+
+ scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
+ setup_columns(tb);
+ add_line(tb, "foo", "bla bla bla");
+ add_line(tb, "bar", "alb alb alb");
+
+ title = scols_table_get_title(tb);
+
+ /* right */
+ scols_cell_set_data(title, "This is right title");
+ scols_cell_set_color(title, "red");
+ scols_cell_set_flags(title, SCOLS_CELL_FL_RIGHT);
+ scols_print_table(tb);
+
+ /* left without padding */
+ scols_cell_set_data(title, "This is left title (without padding)");
+ scols_cell_set_color(title, "yellow");
+ scols_cell_set_flags(title, SCOLS_CELL_FL_LEFT);
+ scols_print_table(tb);
+
+ /* center */
+ sy = scols_new_symbols();
+ if (!sy)
+ err_oom();
+ scols_table_set_symbols(tb, sy);
+ scols_unref_symbols(sy);
+
+ scols_symbols_set_title_padding(sy, "=");
+ scols_cell_set_data(title, "This is center title (with padding)");
+ scols_cell_set_color(title, "green");
+ scols_cell_set_flags(title, SCOLS_CELL_FL_CENTER);
+ scols_print_table(tb);
+
+ /* left with padding */
+ scols_symbols_set_title_padding(sy, "-");
+ scols_cell_set_data(title, "This is left title (with padding)");
+ scols_cell_set_color(title, "blue");
+ scols_cell_set_flags(title, SCOLS_CELL_FL_LEFT);
+ scols_print_table(tb);
+
+
+ scols_unref_table(tb);
+ return EXIT_SUCCESS;
+}
diff --git a/libsmartcols/src/test.c b/libsmartcols/samples/tree.c
similarity index 68%
rename from libsmartcols/src/test.c
rename to libsmartcols/samples/tree.c
index dd87fd38b..0cdb99420 100644
--- a/libsmartcols/src/test.c
+++ b/libsmartcols/samples/tree.c
@@ -39,7 +39,7 @@ static void setup_columns(struct libscols_table *tb, int notree)
return;
fail:
scols_unref_table(tb);
- err(EXIT_FAILURE, "faild to create output columns");
+ err(EXIT_FAILURE, "failed to create output columns");
}
/* add a new line to @tb, the content is based on @st */
@@ -104,7 +104,7 @@ fail:
return -1;
}
-/* read all entrines from directory addressed by @fd */
+/* read all entries from directory addressed by @fd */
static int add_children(struct libscols_table *tb,
struct libscols_line *ln,
int fd)
@@ -142,12 +142,15 @@ static void add_lines(struct libscols_table *tb, const char *dirname)
static void __attribute__((__noreturn__)) usage(FILE *out)
{
fprintf(out, " %s [options] [<dir> ...]\n\n", program_invocation_short_name);
- fputs(" -c, --csv display a csv-like output\n", out);
- fputs(" -i, --ascii use ascii characters only\n", out);
- fputs(" -l, --list use list format output\n", out);
- fputs(" -n, --noheadings don't print headings\n", out);
- fputs(" -p, --pairs use key=\"value\" output format\n", out);
- fputs(" -r, --raw use raw output format\n", out);
+ fputs(" -c, --csv display a csv-like output\n", out);
+ fputs(" -i, --ascii use ascii characters only\n", out);
+ fputs(" -l, --list use list format output\n", out);
+ fputs(" -n, --noheadings don't print headings\n", out);
+ fputs(" -p, --pairs use key=\"value\" output format\n", out);
+ fputs(" -J, --json use JSON output format\n", out);
+ fputs(" -r, --raw use raw output format\n", out);
+ fputs(" -S, --range-start <n> first line to print\n", out);
+ fputs(" -E, --range-end <n> last line to print\n", out);
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
@@ -155,17 +158,20 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
int main(int argc, char *argv[])
{
struct libscols_table *tb;
- int c, notree = 0;
+ int c, notree = 0, nstart = -1, nend = -1;
+
static const struct option longopts[] = {
- { "ascii", 0, 0, 'i' },
- { "csv", 0, 0, 'c' },
- { "list", 0, 0, 'l' },
- { "noheadings", 0, 0, 'n' },
- { "pairs", 0, 0, 'p' },
- { "raw", 0, 0, 'r' },
-
- { NULL, 0, 0, 0 },
+ { "ascii", 0, NULL, 'i' },
+ { "csv", 0, NULL, 'c' },
+ { "list", 0, NULL, 'l' },
+ { "noheadings", 0, NULL, 'n' },
+ { "pairs", 0, NULL, 'p' },
+ { "json", 0, NULL, 'J' },
+ { "raw", 0, NULL, 'r' },
+ { "range-start",1, NULL, 'S' },
+ { "range-end", 1, NULL, 'E' },
+ { NULL, 0, NULL, 0 },
};
setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */
@@ -174,9 +180,9 @@ int main(int argc, char *argv[])
tb = scols_new_table();
if (!tb)
- err(EXIT_FAILURE, "faild to create output table");
+ err(EXIT_FAILURE, "failed to create output table");
- while((c = getopt_long(argc, argv, "cilnpr", longopts, NULL)) != -1) {
+ while((c = getopt_long(argc, argv, "ciJlnprS:E:", longopts, NULL)) != -1) {
switch(c) {
case 'c':
scols_table_set_column_separator(tb, ",");
@@ -186,6 +192,10 @@ int main(int argc, char *argv[])
case 'i':
scols_table_enable_ascii(tb, 1);
break;
+ case 'J':
+ scols_table_set_name(tb, "scolstest");
+ scols_table_enable_json(tb, 1);
+ break;
case 'l':
notree = 1;
break;
@@ -200,19 +210,40 @@ int main(int argc, char *argv[])
scols_table_enable_raw(tb, 1);
notree = 1;
break;
+ case 'S':
+ nstart = strtos32_or_err(optarg, "failed to parse range start") - 1;
+ break;
+ case 'E':
+ nend = strtos32_or_err(optarg, "failed to parse range end") - 1;
+ break;
default:
usage(stderr);
}
}
- scols_table_enable_colors(tb, 1);
+ scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
setup_columns(tb, notree);
- while (optind < argc)
+ if (optind == argc)
+ add_lines(tb, ".");
+ else while (optind < argc)
add_lines(tb, argv[optind++]);
- scols_print_table(tb);
- scols_unref_table(tb);
+ if (nstart >= 0 || nend >= 0) {
+ /* print subset */
+ struct libscols_line *start = NULL, *end = NULL;
+
+ if (nstart >= 0)
+ start = scols_table_get_line(tb, nstart);
+ if (nend >= 0)
+ end = scols_table_get_line(tb, nend);
+ if (start || end)
+ scols_table_print_range(tb, start, end);
+ } else
+ /* print all table */
+ scols_print_table(tb);
+
+ scols_unref_table(tb);
return EXIT_SUCCESS;
}
diff --git a/libsmartcols/samples/wrap.c b/libsmartcols/samples/wrap.c
new file mode 100644
index 000000000..795bef714
--- /dev/null
+++ b/libsmartcols/samples/wrap.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <getopt.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "xalloc.h"
+
+#include "libsmartcols.h"
+
+
+enum { COL_NAME, COL_DESC, COL_FOO, COL_LIKE, COL_TEXT };
+
+/* add columns to the @tb */
+static void setup_columns(struct libscols_table *tb)
+{
+ if (!scols_table_new_column(tb, "NAME", 0, SCOLS_FL_TREE))
+ goto fail;
+ if (!scols_table_new_column(tb, "DESC", 0, 0))
+ goto fail;
+ if (!scols_table_new_column(tb, "FOO", 0, SCOLS_FL_WRAP))
+ goto fail;
+ if (!scols_table_new_column(tb, "LIKE", 0, SCOLS_FL_RIGHT))
+ goto fail;
+ if (!scols_table_new_column(tb, "TEXT", 0, SCOLS_FL_WRAP))
+ goto fail;
+ return;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output columns");
+}
+
+static char *gen_text(const char *prefix, const char *sub_prefix, char *buf, size_t sz)
+{
+ int x = snprintf(buf, sz, "%s-%s-", prefix, sub_prefix);
+
+ for ( ; (size_t)x < sz - 1; x++)
+ buf[x] = *prefix;
+
+ buf[x++] = 'x';
+ buf[x] = '\0';
+ return buf;
+}
+
+static struct libscols_line * add_line( struct libscols_table *tb,
+ struct libscols_line *parent,
+ const char *prefix)
+{
+ char buf[BUFSIZ];
+ struct libscols_line *ln = scols_table_new_line(tb, parent);
+ if (!ln)
+ err(EXIT_FAILURE, "failed to create output line");
+
+ if (scols_line_set_data(ln, COL_NAME, gen_text(prefix, "N", buf, 15)))
+ goto fail;
+ if (scols_line_set_data(ln, COL_DESC, gen_text(prefix, "D", buf, 10)))
+ goto fail;
+ if (scols_line_set_data(ln, COL_FOO, gen_text(prefix, "U", buf, 55)))
+ goto fail;
+ if (scols_line_set_data(ln, COL_LIKE, "1"))
+ goto fail;
+ if (scols_line_set_data(ln, COL_TEXT, gen_text(prefix, "T", buf, 50)))
+ goto fail;
+ return ln;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output line");
+}
+
+int main(int argc, char *argv[])
+{
+ struct libscols_table *tb;
+ struct libscols_line *ln, *xln;
+
+ setlocale(LC_ALL, ""); /* just to have enable UTF8 chars */
+
+ scols_init_debug(0);
+
+ tb = scols_new_table();
+ if (!tb)
+ err(EXIT_FAILURE, "failed to create output table");
+
+ scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
+ setup_columns(tb);
+
+ ln = add_line(tb, NULL, "A");
+ add_line(tb, ln, "aa");
+ add_line(tb, ln, "ab");
+
+ ln = add_line(tb, NULL, "B");
+ xln = add_line(tb, ln, "ba");
+ add_line(tb, xln, "baa");
+ add_line(tb, xln, "bab");
+ add_line(tb, ln, "bb");
+
+ scols_print_table(tb);
+ scols_unref_table(tb);
+ return EXIT_SUCCESS;
+}
diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am
index bfe8c75c1..952d5e58f 100644
--- a/libsmartcols/src/Makemodule.am
+++ b/libsmartcols/src/Makemodule.am
@@ -1,10 +1,10 @@
-## smartcols.h is generated, so it's stored in builddir! (no distribute in RHEL7)
-#smartcolsincdir = $(includedir)/libsmartcols
-#nodist_smartcolsinc_HEADERS = $(top_builddir)/libsmartcols/src/libsmartcols.h
+# smartcols.h is generated, so it's stored in builddir!
+smartcolsincdir = $(includedir)/libsmartcols
+nodist_smartcolsinc_HEADERS = libsmartcols/src/libsmartcols.h
-noinst_LTLIBRARIES += libsmartcols.la
+usrlib_exec_LTLIBRARIES += libsmartcols.la
libsmartcols_la_SOURCES= \
include/list.h \
\
@@ -17,49 +17,32 @@ libsmartcols_la_SOURCES= \
libsmartcols/src/table.c \
libsmartcols/src/table_print.c \
libsmartcols/src/version.c \
- libsmartcols/src/init.c \
- $(nodist_smartcolsinc_HEADERS)
+ libsmartcols/src/init.c
-nodist_libsmartcols_la_SOURCES = libsmartcols/src/smartcolsP.h
-
-libsmartcols_la_LIBADD = libcommon.la
+libsmartcols_la_LIBADD = $(LDADD) libcommon.la
libsmartcols_la_CFLAGS = \
+ $(AM_CFLAGS) \
$(SOLIB_CFLAGS) \
-I$(ul_libsmartcols_incdir) \
-I$(top_srcdir)/libsmartcols/src
-libsmartcols_la_DEPENDENCIES = \
- libcommon.la \
- libsmartcols/src/libsmartcols.sym \
- libsmartcols/src/libsmartcols.h.in
+EXTRA_libsmartcols_la_DEPENDENCIES = \
+ libsmartcols/src/libsmartcols.sym
libsmartcols_la_LDFLAGS = \
$(SOLIB_LDFLAGS) \
-Wl,--version-script=$(top_srcdir)/libsmartcols/src/libsmartcols.sym \
-version-info $(LIBSMARTCOLS_VERSION_INFO)
-EXTRA_DIST += \
- libsmartcols/src/libsmartcols.sym \
- libsmartcols/src/libsmartcols.h.in
-
-
-if BUILD_LIBSMARTCOLS_TESTS
-check_PROGRAMS += test_smartcols
-
-libsmartcols_tests_cflags = $(libsmartcols_la_CFLAGS)
-libsmartcols_tests_ldadd = libsmartcols.la libcommon.la
-
-test_smartcols_SOURCES = libsmartcols/src/test.c
-test_smartcols_CFLAGS = $(libsmartcols_tests_cflags)
-test_smartcols_LDADD = $(libsmartcols_tests_ldadd)
-endif # BUILD_LIBSMARTCOLS_TESTS
+EXTRA_DIST += \
+ libsmartcols/src/libsmartcols.sym
# move lib from $(usrlib_execdir) to $(libdir) if needed
install-exec-hook-libsmartcols:
if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libsmartcols.so"; then \
- mkdir -p $(DESTDIR)$(libdir); \
+ $(MKDIR_P) $(DESTDIR)$(libdir); \
mv $(DESTDIR)$(usrlib_execdir)/libsmartcols.so.* $(DESTDIR)$(libdir); \
so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libsmartcols.so); \
so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
diff --git a/libsmartcols/src/cell.c b/libsmartcols/src/cell.c
index ea41f698f..0717a2d09 100644
--- a/libsmartcols/src/cell.c
+++ b/libsmartcols/src/cell.c
@@ -11,7 +11,7 @@
/**
* SECTION: cell
* @title: Cell
- * @short_description: cell API
+ * @short_description: container for your data
*
* An API to access and modify per-cell data and information. Note that cell is
* always part of the line. If you destroy (un-reference) a line than it
@@ -41,8 +41,6 @@
*/
int scols_reset_cell(struct libscols_cell *ce)
{
- assert(ce);
-
if (!ce)
return -EINVAL;
@@ -56,34 +54,21 @@ int scols_reset_cell(struct libscols_cell *ce)
/**
* scols_cell_set_data:
* @ce: a pointer to a struct libscols_cell instance
- * @str: data (used for scols_printtable())
+ * @data: data (used for scols_print_table())
*
- * Stores a copy of the @str in @ce.
+ * Stores a copy of the @str in @ce, the old data are deallocated by free().
*
* Returns: 0, a negative value in case of an error.
*/
-int scols_cell_set_data(struct libscols_cell *ce, const char *str)
+int scols_cell_set_data(struct libscols_cell *ce, const char *data)
{
- char *p = NULL;
-
- assert(ce);
-
- if (!ce)
- return -EINVAL;
- if (str) {
- p = strdup(str);
- if (!p)
- return -ENOMEM;
- }
- free(ce->data);
- ce->data = p;
- return 0;
+ return strdup_to_struct_member(ce, data, data);
}
/**
* scols_cell_refer_data:
* @ce: a pointer to a struct libscols_cell instance
- * @str: data (used for scols_printtable())
+ * @data: data (used for scols_print_table())
*
* Adds a reference to @str to @ce. The pointer is deallocated by
* scols_reset_cell() or scols_unref_line(). This function is mostly designed
@@ -92,14 +77,12 @@ int scols_cell_set_data(struct libscols_cell *ce, const char *str)
*
* Returns: 0, a negative value in case of an error.
*/
-int scols_cell_refer_data(struct libscols_cell *ce, char *str)
+int scols_cell_refer_data(struct libscols_cell *ce, char *data)
{
- assert(ce);
-
if (!ce)
return -EINVAL;
free(ce->data);
- ce->data = str;
+ ce->data = data;
return 0;
}
@@ -111,7 +94,6 @@ int scols_cell_refer_data(struct libscols_cell *ce, char *str)
*/
const char *scols_cell_get_data(const struct libscols_cell *ce)
{
- assert(ce);
return ce ? ce->data : NULL;
}
@@ -124,8 +106,6 @@ const char *scols_cell_get_data(const struct libscols_cell *ce)
*/
int scols_cell_set_userdata(struct libscols_cell *ce, void *data)
{
- assert(ce);
-
if (!ce)
return -EINVAL;
ce->userdata = data;
@@ -140,7 +120,7 @@ int scols_cell_set_userdata(struct libscols_cell *ce, void *data)
*/
void *scols_cell_get_userdata(struct libscols_cell *ce)
{
- return ce ? ce->userdata : NULL;
+ return ce->userdata;
}
/**
@@ -178,7 +158,7 @@ int scols_cmpstr_cells(struct libscols_cell *a,
/**
* scols_cell_set_color:
* @ce: a pointer to a struct libscols_cell instance
- * @color: ESC sequence
+ * @color: color name or ESC sequence
*
* Set the color of @ce to @color.
*
@@ -186,32 +166,70 @@ int scols_cmpstr_cells(struct libscols_cell *a,
*/
int scols_cell_set_color(struct libscols_cell *ce, const char *color)
{
- char *p = NULL;
+ if (color && isalpha(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
+ }
+ return strdup_to_struct_member(ce, color, color);
+}
- assert(ce);
+/**
+ * scols_cell_get_color:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Returns: the current color of @ce.
+ */
+const char *scols_cell_get_color(const struct libscols_cell *ce)
+{
+ return ce->color;
+}
+/**
+ * scols_cell_set_flags:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @flags: SCOLS_CELL_FL_* flags
+ *
+ * Note that cells in the table are always aligned by column flags. The cell
+ * flags are used for table title only (now).
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_set_flags(struct libscols_cell *ce, int flags)
+{
if (!ce)
return -EINVAL;
- if (color) {
- p = strdup(color);
- if (!p)
- return -ENOMEM;
- }
- free(ce->color);
- ce->color = p;
+ ce->flags = flags;
return 0;
}
/**
- * scols_cell_get_color:
+ * scols_cell_get_flags:
* @ce: a pointer to a struct libscols_cell instance
*
- * Returns: the current color of @ce.
+ * Returns: the current flags
*/
-const char *scols_cell_get_color(const struct libscols_cell *ce)
+int scols_cell_get_flags(const struct libscols_cell *ce)
{
- assert(ce);
- return ce ? ce->color : NULL;
+ return ce->flags;
+}
+
+/**
+ * scols_cell_get_alignment:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Since: 2.30
+ *
+ * Returns: SCOLS_CELL_FL_{RIGHT,CELNTER,LEFT}
+ */
+int scols_cell_get_alignment(const struct libscols_cell *ce)
+{
+ if (ce->flags & SCOLS_CELL_FL_RIGHT)
+ return SCOLS_CELL_FL_RIGHT;
+ else if (ce->flags & SCOLS_CELL_FL_CENTER)
+ return SCOLS_CELL_FL_CENTER;
+
+ return SCOLS_CELL_FL_LEFT; /* default */
}
/**
@@ -228,15 +246,12 @@ int scols_cell_copy_content(struct libscols_cell *dest,
{
int rc;
- assert(dest);
- assert(src);
-
rc = scols_cell_set_data(dest, scols_cell_get_data(src));
if (!rc)
rc = scols_cell_set_color(dest, scols_cell_get_color(src));
if (!rc)
dest->userdata = src->userdata;
- DBG(CELL, ul_debugobj((void *) src, "copy into %p", dest));
+ DBG(CELL, ul_debugobj(src, "copy"));
return rc;
}
diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c
index d1d10a6d0..e9d6dc404 100644
--- a/libsmartcols/src/column.c
+++ b/libsmartcols/src/column.c
@@ -11,7 +11,7 @@
/**
* SECTION: column
* @title: Column
- * @short_description: column API
+ * @short_description: defines output columns formats, headers, etc.
*
* An API to access and modify per-column data and information.
*/
@@ -22,6 +22,8 @@
#include <string.h>
#include <ctype.h>
+#include "mbsalign.h"
+
#include "smartcolsP.h"
/**
@@ -29,7 +31,7 @@
*
* Allocates space for a new column.
*
- * Returns: a pointer to a new struct libscols_cell instance, NULL in case of an ENOMEM error.
+ * Returns: a pointer to a new struct libscols_column instance, NULL in case of an ENOMEM error.
*/
struct libscols_column *scols_new_column(void)
{
@@ -70,6 +72,8 @@ void scols_unref_column(struct libscols_column *cl)
list_del(&cl->cl_columns);
scols_reset_cell(&cl->header);
free(cl->color);
+ free(cl->safechars);
+ free(cl->pending_data_buf);
free(cl);
}
}
@@ -86,14 +90,13 @@ struct libscols_column *scols_copy_column(const struct libscols_column *cl)
{
struct libscols_column *ret;
- assert (cl);
if (!cl)
return NULL;
ret = scols_new_column();
if (!ret)
return NULL;
- DBG(COL, ul_debugobj((void *) cl, "copy to %p", ret));
+ DBG(COL, ul_debugobj(cl, "copy"));
if (scols_column_set_color(ret, cl->color))
goto err;
@@ -119,14 +122,12 @@ err:
* @cl: a pointer to a struct libscols_column instance
* @whint: a width hint
*
- * Sets the width hint of column @cl to @whint.
+ * Sets the width hint of column @cl to @whint. See scols_table_new_column().
*
* Returns: 0, a negative value in case of an error.
*/
int scols_column_set_whint(struct libscols_column *cl, double whint)
{
- assert(cl);
-
if (!cl)
return -EINVAL;
@@ -140,10 +141,9 @@ int scols_column_set_whint(struct libscols_column *cl, double whint)
*
* Returns: The width hint of column @cl, a negative value in case of an error.
*/
-double scols_column_get_whint(struct libscols_column *cl)
+double scols_column_get_whint(const struct libscols_column *cl)
{
- assert(cl);
- return cl ? cl->width_hint : -EINVAL;
+ return cl->width_hint;
}
/**
@@ -157,25 +157,78 @@ double scols_column_get_whint(struct libscols_column *cl)
*/
int scols_column_set_flags(struct libscols_column *cl, int flags)
{
- assert(cl);
-
if (!cl)
return -EINVAL;
+ if (cl->table) {
+ if (!(cl->flags & SCOLS_FL_TREE) && (flags & SCOLS_FL_TREE))
+ cl->table->ntreecols++;
+ else if ((cl->flags & SCOLS_FL_TREE) && !(flags & SCOLS_FL_TREE))
+ cl->table->ntreecols--;
+ }
+
cl->flags = flags;
return 0;
}
+/**
+ * scols_column_set_json_type:
+ * @cl: a pointer to a struct libscols_column instance
+ * @type: SCOLS_JSON_* type
+ *
+ * Sets the type used for JSON formatting, the default is SCOLS_JSON_STRING.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.33
+ */
+int scols_column_set_json_type(struct libscols_column *cl, int type)
+{
+ if (!cl)
+ return -EINVAL;
+
+ cl->json_type = type;
+ return 0;
+
+}
+
+/**
+ * scols_column_get_json_type:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Note that SCOLS_JSON_BOOLEAN interprets NULL, empty strings, '0', 'N' and
+ * 'n' as "false"; and everything else as "true".
+ *
+ * Returns: JSON type used for formatting or a negative value in case of an error.
+ *
+ * Since: 2.33
+ */
+int scols_column_get_json_type(const struct libscols_column *cl)
+{
+ return cl ? cl->json_type : -EINVAL;
+}
+
+
+/**
+ * scols_column_get_table:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: pointer to the table where columns is used
+ */
+struct libscols_table *scols_column_get_table(const struct libscols_column *cl)
+{
+ return cl->table;
+}
+
/**
* scols_column_get_flags:
* @cl: a pointer to a struct libscols_column instance
*
* Returns: The flag mask of @cl, a negative value in case of an error.
*/
-int scols_column_get_flags(struct libscols_column *cl)
+int scols_column_get_flags(const struct libscols_column *cl)
{
- assert(cl);
- return cl ? cl->flags : -EINVAL;
+ return cl->flags;
}
/**
@@ -187,14 +240,13 @@ int scols_column_get_flags(struct libscols_column *cl)
*/
struct libscols_cell *scols_column_get_header(struct libscols_column *cl)
{
- assert(cl);
- return cl ? &cl->header : NULL;
+ return &cl->header;
}
/**
* scols_column_set_color:
* @cl: a pointer to a struct libscols_column instance
- * @color: ESC sequence
+ * @color: color name or ESC sequence
*
* The default color for data cells and column header.
*
@@ -208,20 +260,12 @@ struct libscols_cell *scols_column_get_header(struct libscols_column *cl)
*/
int scols_column_set_color(struct libscols_column *cl, const char *color)
{
- char *p = NULL;
-
- assert(cl);
- if (!cl)
- return -EINVAL;
- if (color) {
- p = strdup(color);
- if (!p)
- return -ENOMEM;
+ if (color && isalpha(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
}
-
- free(cl->color);
- cl->color = p;
- return 0;
+ return strdup_to_struct_member(cl, color, color);
}
/**
@@ -230,12 +274,79 @@ int scols_column_set_color(struct libscols_column *cl, const char *color)
*
* Returns: The current color setting of the column @cl.
*/
-const char *scols_column_get_color(struct libscols_column *cl)
+const char *scols_column_get_color(const struct libscols_column *cl)
+{
+ return cl->color;
+}
+
+/**
+ * scols_wrapnl_nextchunk:
+ * @cl: a pointer to a struct libscols_column instance
+ * @data: string
+ * @userdata: callback private data
+ *
+ * This is built-in function for scols_column_set_wrapfunc(). This function
+ * terminates the current chunk by \0 and returns pointer to the begin of
+ * the next chunk. The chunks are based on \n.
+ *
+ * For example for data "AAA\nBBB\nCCC" the next chunk is "BBB".
+ *
+ * Returns: next chunk
+ *
+ * Since: 2.29
+ */
+char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unused)),
+ char *data,
+ void *userdata __attribute__((unused)))
{
- assert(cl);
- return cl ? cl->color : NULL;
+ char *p = data ? strchr(data, '\n') : NULL;
+
+ if (p) {
+ *p = '\0';
+ return p + 1;
+ }
+ return NULL;
}
+/**
+ * scols_wrapnl_chunksize:
+ * @cl: a pointer to a struct libscols_column instance
+ * @data: string
+ * @userdata: callback private data
+ *
+ * Analyzes @data and returns size of the largest chunk. The chunks are based
+ * on \n. For example for data "AAA\nBBB\nCCCC" the largest chunk size is 4.
+ *
+ * Note that the size has to be based on number of terminal cells rather than
+ * bytes to support multu-byte output.
+ *
+ * Returns: size of the largest chunk.
+ *
+ * Since: 2.29
+ */
+size_t scols_wrapnl_chunksize(const struct libscols_column *cl __attribute__((unused)),
+ const char *data,
+ void *userdata __attribute__((unused)))
+{
+ size_t sum = 0;
+
+ while (data && *data) {
+ const char *p;
+ size_t sz;
+
+ p = strchr(data, '\n');
+ if (p) {
+ sz = mbs_safe_nwidth(data, p - data, NULL);
+ p++;
+ } else
+ sz = mbs_safe_width(data);
+
+ sum = max(sum, sz);
+ data = p;
+ }
+
+ return sum;
+}
/**
* scols_column_set_cmpfunc:
@@ -251,7 +362,6 @@ int scols_column_set_cmpfunc(struct libscols_column *cl,
void *),
void *data)
{
- assert(cl);
if (!cl)
return -EINVAL;
@@ -261,19 +371,114 @@ int scols_column_set_cmpfunc(struct libscols_column *cl,
}
/**
- * scols_column_is_trunc:
+ * scols_column_set_wrapfunc:
* @cl: a pointer to a struct libscols_column instance
+ * @wrap_chunksize: function to return size of the largest chink of data
+ * @wrap_nextchunk: function to return next zero terminated data
+ * @userdata: optional stuff for callbacks
*
- * Gets the value of @cl's flag trunc.
+ * Extends SCOLS_FL_WRAP and allows to set custom wrap function. The default
+ * is to wrap by column size, but you can create functions to wrap for example
+ * after \n or after words, etc.
+ *
+ * Returns: 0, a negative value in case of an error.
*
- * Returns: trunc flag value, negative value in case of an error.
+ * Since: 2.29
*/
-int scols_column_is_trunc(struct libscols_column *cl)
+int scols_column_set_wrapfunc(struct libscols_column *cl,
+ size_t (*wrap_chunksize)(const struct libscols_column *,
+ const char *,
+ void *),
+ char * (*wrap_nextchunk)(const struct libscols_column *,
+ char *,
+ void *),
+ void *userdata)
{
- assert(cl);
if (!cl)
return -EINVAL;
- return cl->flags & SCOLS_FL_TRUNC;
+
+ cl->wrap_nextchunk = wrap_nextchunk;
+ cl->wrap_chunksize = wrap_chunksize;
+ cl->wrapfunc_data = userdata;
+ return 0;
+}
+
+/**
+ * scols_column_set_safechars:
+ * @cl: a pointer to a struct libscols_column instance
+ * @safe: safe characters (e.g. "\n\t")
+ *
+ * Use for bytes you don't want to encode on output. This is for example
+ * necessary if you want to use custom wrap function based on \n, in this case
+ * you have to set "\n" as a safe char.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_column_set_safechars(struct libscols_column *cl, const char *safe)
+{
+ return strdup_to_struct_member(cl, safechars, safe);
+}
+
+/**
+ * scols_column_get_safechars:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: safe chars
+ *
+ * Since: 2.29
+ */
+const char *scols_column_get_safechars(const struct libscols_column *cl)
+{
+ return cl->safechars;
+}
+
+/**
+ * scols_column_get_width:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Important note: the column width is unknown until library starts printing
+ * (width is calculated before printing). The function is usable for example in
+ * nextchunk() callback specified by scols_column_set_wrapfunc().
+ *
+ * See also scols_column_get_whint(), it returns wanted size (!= final size).
+ *
+ * Returns: column width
+ *
+ * Since: 2.29
+ */
+size_t scols_column_get_width(const struct libscols_column *cl)
+{
+ return cl->width;
+}
+
+/**
+ * scols_column_is_hidden:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag hidden.
+ *
+ * Returns: 0 or 1
+ *
+ * Since: 2.27
+ */
+int scols_column_is_hidden(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_HIDDEN ? 1 : 0;
+}
+
+/**
+ * scols_column_is_trunc:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag trunc.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_trunc(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_TRUNC ? 1 : 0;
}
/**
* scols_column_is_tree:
@@ -281,14 +486,11 @@ int scols_column_is_trunc(struct libscols_column *cl)
*
* Gets the value of @cl's flag tree.
*
- * Returns: tree flag value, negative value in case of an error.
+ * Returns: 0 or 1
*/
-int scols_column_is_tree(struct libscols_column *cl)
+int scols_column_is_tree(const struct libscols_column *cl)
{
- assert(cl);
- if (!cl)
- return -EINVAL;
- return cl->flags & SCOLS_FL_TREE;
+ return cl->flags & SCOLS_FL_TREE ? 1 : 0;
}
/**
* scols_column_is_right:
@@ -296,14 +498,11 @@ int scols_column_is_tree(struct libscols_column *cl)
*
* Gets the value of @cl's flag right.
*
- * Returns: right flag value, negative value in case of an error.
+ * Returns: 0 or 1
*/
-int scols_column_is_right(struct libscols_column *cl)
+int scols_column_is_right(const struct libscols_column *cl)
{
- assert(cl);
- if (!cl)
- return -EINVAL;
- return cl->flags & SCOLS_FL_RIGHT;
+ return cl->flags & SCOLS_FL_RIGHT ? 1 : 0;
}
/**
* scols_column_is_strict_width:
@@ -311,14 +510,11 @@ int scols_column_is_right(struct libscols_column *cl)
*
* Gets the value of @cl's flag strict_width.
*
- * Returns: strict_width flag value, negative value in case of an error.
+ * Returns: 0 or 1
*/
-int scols_column_is_strict_width(struct libscols_column *cl)
+int scols_column_is_strict_width(const struct libscols_column *cl)
{
- assert(cl);
- if (!cl)
- return -EINVAL;
- return cl->flags & SCOLS_FL_STRICTWIDTH;
+ return cl->flags & SCOLS_FL_STRICTWIDTH ? 1 : 0;
}
/**
* scols_column_is_noextremes:
@@ -326,12 +522,37 @@ int scols_column_is_strict_width(struct libscols_column *cl)
*
* Gets the value of @cl's flag no_extremes.
*
- * Returns: no_extremes flag value, negative value in case of an error.
+ * Returns: 0 or 1
*/
-int scols_column_is_noextremes(struct libscols_column *cl)
+int scols_column_is_noextremes(const struct libscols_column *cl)
{
- assert(cl);
- if (!cl)
- return -EINVAL;
- return cl->flags & SCOLS_FL_NOEXTREMES;
+ return cl->flags & SCOLS_FL_NOEXTREMES ? 1 : 0;
+}
+/**
+ * scols_column_is_wrap:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag wrap.
+ *
+ * Returns: 0 or 1
+ *
+ * Since: 2.28
+ */
+int scols_column_is_wrap(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_WRAP ? 1 : 0;
+}
+/**
+ * scols_column_is_customwrap:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: 0 or 1
+ *
+ * Since: 2.29
+ */
+int scols_column_is_customwrap(const struct libscols_column *cl)
+{
+ return (cl->flags & SCOLS_FL_WRAP)
+ && cl->wrap_chunksize
+ && cl->wrap_nextchunk ? 1 : 0;
}
diff --git a/libsmartcols/src/iter.c b/libsmartcols/src/iter.c
index 72c7865a8..91cc08009 100644
--- a/libsmartcols/src/iter.c
+++ b/libsmartcols/src/iter.c
@@ -68,7 +68,7 @@ void scols_reset_iter(struct libscols_iter *itr, int direction)
*
* Returns: SCOLS_INTER_{FOR,BACK}WARD
*/
-int scols_iter_get_direction(struct libscols_iter *itr)
+int scols_iter_get_direction(const struct libscols_iter *itr)
{
return itr->direction;
}
diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in
index e61256022..f8be0bc04 100644
--- a/libsmartcols/src/libsmartcols.h.in
+++ b/libsmartcols/src/libsmartcols.h.in
@@ -83,12 +83,33 @@ enum {
SCOLS_FL_RIGHT = (1 << 2), /* align to the right */
SCOLS_FL_STRICTWIDTH = (1 << 3), /* don't reduce width if column is empty */
SCOLS_FL_NOEXTREMES = (1 << 4), /* ignore extreme fields when count column width*/
+ SCOLS_FL_HIDDEN = (1 << 5), /* maintain data, but don't print */
+ SCOLS_FL_WRAP = (1 << 6) /* wrap long lines to multi-line cells */
+};
+
+/*
+ * Column JSON types
+ */
+enum {
+ SCOLS_JSON_STRING = 0, /* default */
+ SCOLS_JSON_NUMBER = 1,
+ SCOLS_JSON_BOOLEAN = 2
+};
+
+/*
+ * Cell flags, see scols_cell_set_flags() before use
+ */
+enum {
+ /* alignment evaluated in order: right,center,left */
+ SCOLS_CELL_FL_LEFT = 0,
+ SCOLS_CELL_FL_CENTER = (1 << 0),
+ SCOLS_CELL_FL_RIGHT = (1 << 1)
};
extern struct libscols_iter *scols_new_iter(int direction);
extern void scols_free_iter(struct libscols_iter *itr);
extern void scols_reset_iter(struct libscols_iter *itr, int direction);
-extern int scols_iter_get_direction(struct libscols_iter *itr);
+extern int scols_iter_get_direction(const struct libscols_iter *itr);
/* init.c */
extern void scols_init_debug(int mask);
@@ -101,50 +122,78 @@ extern int scols_get_library_version(const char **ver_string);
extern struct libscols_symbols *scols_new_symbols(void);
extern void scols_ref_symbols(struct libscols_symbols *sy);
extern void scols_unref_symbols(struct libscols_symbols *sy);
-extern struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sb);
-extern int scols_symbols_set_branch(struct libscols_symbols *sb, const char *str);
-extern int scols_symbols_set_vertical(struct libscols_symbols *sb, const char *str);
-extern int scols_symbols_set_right(struct libscols_symbols *sb, const char *str);
+extern struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sy);
+extern int scols_symbols_set_branch(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_vertical(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_right(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_title_padding(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_cell_padding(struct libscols_symbols *sy, const char *str);
/* cell.c */
extern int scols_reset_cell(struct libscols_cell *ce);
extern int scols_cell_copy_content(struct libscols_cell *dest,
const struct libscols_cell *src);
-extern int scols_cell_set_data(struct libscols_cell *ce, const char *str);
-extern int scols_cell_refer_data(struct libscols_cell *ce, char *str);
+extern int scols_cell_set_data(struct libscols_cell *ce, const char *data);
+extern int scols_cell_refer_data(struct libscols_cell *ce, char *data);
extern const char *scols_cell_get_data(const struct libscols_cell *ce);
extern int scols_cell_set_color(struct libscols_cell *ce, const char *color);
extern const char *scols_cell_get_color(const struct libscols_cell *ce);
+extern int scols_cell_set_flags(struct libscols_cell *ce, int flags);
+extern int scols_cell_get_flags(const struct libscols_cell *ce);
+extern int scols_cell_get_alignment(const struct libscols_cell *ce);
+
extern void *scols_cell_get_userdata(struct libscols_cell *ce);
extern int scols_cell_set_userdata(struct libscols_cell *ce, void *data);
extern int scols_cmpstr_cells(struct libscols_cell *a,
struct libscols_cell *b, void *data);
/* column.c */
-extern int scols_column_is_tree(struct libscols_column *cl);
-extern int scols_column_is_trunc(struct libscols_column *cl);
-extern int scols_column_is_right(struct libscols_column *cl);
-extern int scols_column_is_strict_width(struct libscols_column *cl);
-extern int scols_column_is_noextremes(struct libscols_column *cl);
+extern int scols_column_is_tree(const struct libscols_column *cl);
+extern int scols_column_is_trunc(const struct libscols_column *cl);
+extern int scols_column_is_right(const struct libscols_column *cl);
+extern int scols_column_is_strict_width(const struct libscols_column *cl);
+extern int scols_column_is_hidden(const struct libscols_column *cl);
+extern int scols_column_is_noextremes(const struct libscols_column *cl);
+extern int scols_column_is_wrap(const struct libscols_column *cl);
+extern int scols_column_is_customwrap(const struct libscols_column *cl);
+
+extern size_t scols_column_get_width(const struct libscols_column *cl);
+
+extern int scols_column_set_safechars(struct libscols_column *cl, const char *safe);
+extern const char *scols_column_get_safechars(const struct libscols_column *cl);
+
+extern int scols_column_set_json_type(struct libscols_column *cl, int type);
+extern int scols_column_get_json_type(const struct libscols_column *cl);
extern int scols_column_set_flags(struct libscols_column *cl, int flags);
-extern int scols_column_get_flags(struct libscols_column *cl);
+extern int scols_column_get_flags(const struct libscols_column *cl);
extern struct libscols_column *scols_new_column(void);
extern void scols_ref_column(struct libscols_column *cl);
extern void scols_unref_column(struct libscols_column *cl);
extern struct libscols_column *scols_copy_column(const struct libscols_column *cl);
extern int scols_column_set_whint(struct libscols_column *cl, double whint);
-extern double scols_column_get_whint(struct libscols_column *cl);
+extern double scols_column_get_whint(const struct libscols_column *cl);
extern struct libscols_cell *scols_column_get_header(struct libscols_column *cl);
extern int scols_column_set_color(struct libscols_column *cl, const char *color);
-extern const char *scols_column_get_color(struct libscols_column *cl);
+extern const char *scols_column_get_color(const struct libscols_column *cl);
+extern struct libscols_table *scols_column_get_table(const struct libscols_column *cl);
extern int scols_column_set_cmpfunc(struct libscols_column *cl,
int (*cmp)(struct libscols_cell *a,
struct libscols_cell *b, void *),
void *data);
+extern int scols_column_set_wrapfunc(struct libscols_column *cl,
+ size_t (*wrap_chunksize)(const struct libscols_column *,
+ const char *, void *),
+ char * (*wrap_nextchunk)(const struct libscols_column *,
+ char *, void *),
+ void *userdata);
+
+extern char *scols_wrapnl_nextchunk(const struct libscols_column *cl, char *data, void *userdata);
+extern size_t scols_wrapnl_chunksize(const struct libscols_column *cl, const char *data, void *userdata);
+
/* line.c */
extern struct libscols_line *scols_new_line(void);
extern void scols_ref_line(struct libscols_line *ln);
@@ -156,36 +205,52 @@ extern void *scols_line_get_userdata(struct libscols_line *ln);
extern int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child);
extern int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child);
extern int scols_line_has_children(struct libscols_line *ln);
+extern int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent);
extern int scols_line_next_child(struct libscols_line *ln,
struct libscols_iter *itr, struct libscols_line **chld);
-extern struct libscols_line *scols_line_get_parent(struct libscols_line *ln);
+extern struct libscols_line *scols_line_get_parent(const struct libscols_line *ln);
extern int scols_line_set_color(struct libscols_line *ln, const char *color);
-extern const char *scols_line_get_color(struct libscols_line *ln);
-extern size_t scols_line_get_ncells(struct libscols_line *ln);
+extern const char *scols_line_get_color(const struct libscols_line *ln);
+extern size_t scols_line_get_ncells(const struct libscols_line *ln);
extern struct libscols_cell *scols_line_get_cell(struct libscols_line *ln, size_t n);
extern struct libscols_cell *scols_line_get_column_cell(
struct libscols_line *ln,
struct libscols_column *cl);
extern int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data);
extern int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data);
-extern struct libscols_line *scols_copy_line(struct libscols_line *ln);
+extern int scols_line_set_column_data(struct libscols_line *ln, struct libscols_column *cl, const char *data);
+extern int scols_line_refer_column_data(struct libscols_line *ln, struct libscols_column *cl, char *data);
+extern struct libscols_line *scols_copy_line(const struct libscols_line *ln);
/* table */
-extern int scols_table_colors_wanted(struct libscols_table *tb);
-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_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);
-extern int scols_table_is_maxout(struct libscols_table *tb);
-extern int scols_table_is_tree(struct libscols_table *tb);
+extern int scols_table_colors_wanted(const struct libscols_table *tb);
+extern int scols_table_set_name(struct libscols_table *tb, const char *name);
+extern const char *scols_table_get_name(const struct libscols_table *tb);
+extern struct libscols_cell *scols_table_get_title(struct libscols_table *tb);
+extern int scols_table_is_raw(const struct libscols_table *tb);
+extern int scols_table_is_ascii(const struct libscols_table *tb);
+extern int scols_table_is_json(const struct libscols_table *tb);
+extern int scols_table_is_noheadings(const struct libscols_table *tb);
+extern int scols_table_is_header_repeat(const struct libscols_table *tb);
+extern int scols_table_is_empty(const struct libscols_table *tb);
+extern int scols_table_is_export(const struct libscols_table *tb);
+extern int scols_table_is_maxout(const struct libscols_table *tb);
+extern int scols_table_is_nowrap(const struct libscols_table *tb);
+extern int scols_table_is_nolinesep(const struct libscols_table *tb);
+extern int scols_table_is_tree(const struct libscols_table *tb);
+extern int scols_table_is_noencoding(const 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_header_repeat(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);
+extern int scols_table_enable_nowrap(struct libscols_table *tb, int enable);
+extern int scols_table_enable_nolinesep(struct libscols_table *tb, int enable);
+extern int scols_table_enable_noencoding(struct libscols_table *tb, int enable);
extern int scols_table_set_column_separator(struct libscols_table *tb, const char *sep);
extern int scols_table_set_line_separator(struct libscols_table *tb, const char *sep);
@@ -196,12 +261,13 @@ extern void scols_unref_table(struct libscols_table *tb);
extern int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl);
extern int scols_table_remove_column(struct libscols_table *tb, struct libscols_column *cl);
extern int scols_table_remove_columns(struct libscols_table *tb);
+extern int scols_table_move_column(struct libscols_table *tb, struct libscols_column *pre, struct libscols_column *cl);
extern struct libscols_column *scols_table_new_column(struct libscols_table *tb, const char *name, double whint, int flags);
extern int scols_table_next_column(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_column **cl);
-extern char *scols_table_get_column_separator(struct libscols_table *tb);
-extern char *scols_table_get_line_separator(struct libscols_table *tb);
-extern int scols_table_get_ncols(struct libscols_table *tb);
-extern int scols_table_get_nlines(struct libscols_table *tb);
+extern const char *scols_table_get_column_separator(const struct libscols_table *tb);
+extern const char *scols_table_get_line_separator(const struct libscols_table *tb);
+extern size_t scols_table_get_ncols(const struct libscols_table *tb);
+extern size_t scols_table_get_nlines(const struct libscols_table *tb);
extern struct libscols_column *scols_table_get_column(struct libscols_table *tb, size_t n);
extern int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln);
extern int scols_table_remove_line(struct libscols_table *tb, struct libscols_line *ln);
@@ -211,17 +277,43 @@ extern struct libscols_line *scols_table_new_line(struct libscols_table *tb, str
extern struct libscols_line *scols_table_get_line(struct libscols_table *tb, size_t n);
extern struct libscols_table *scols_copy_table(struct libscols_table *tb);
extern int scols_table_set_symbols(struct libscols_table *tb, struct libscols_symbols *sy);
+extern int scols_table_set_default_symbols(struct libscols_table *tb);
+extern struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb);
extern int scols_table_set_stream(struct libscols_table *tb, FILE *stream);
-extern FILE *scols_table_get_stream(struct libscols_table *tb);
+extern FILE *scols_table_get_stream(const struct libscols_table *tb);
extern int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce);
extern int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl);
+extern int scols_sort_table_by_tree(struct libscols_table *tb);
+/*
+ *
+ */
+enum {
+ SCOLS_TERMFORCE_AUTO = 0,
+ SCOLS_TERMFORCE_NEVER,
+ SCOLS_TERMFORCE_ALWAYS
+};
+extern int scols_table_set_termforce(struct libscols_table *tb, int force);
+extern int scols_table_get_termforce(const struct libscols_table *tb);
+extern int scols_table_set_termwidth(struct libscols_table *tb, size_t width);
+extern size_t scols_table_get_termwidth(const struct libscols_table *tb);
+extern int scols_table_set_termheight(struct libscols_table *tb, size_t height);
+extern size_t scols_table_get_termheight(const struct libscols_table *tb);
+
/* table_print.c */
extern int scols_print_table(struct libscols_table *tb);
extern int scols_print_table_to_string(struct libscols_table *tb, char **data);
+extern int scols_table_print_range( struct libscols_table *tb,
+ struct libscols_line *start,
+ struct libscols_line *end);
+extern int scols_table_print_range_to_string( struct libscols_table *tb,
+ struct libscols_line *start,
+ struct libscols_line *end,
+ char **data);
+
#ifdef __cplusplus
}
#endif
diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym
index a4de524f4..331a55554 100644
--- a/libsmartcols/src/libsmartcols.sym
+++ b/libsmartcols/src/libsmartcols.sym
@@ -1,5 +1,7 @@
/*
* symbols since util-linux 2.25
+ *
+ * Copyright (C) 2014-2016 Karel Zak <kzak@redhat.com>
*/
SMARTCOLS_2.25 {
global:
@@ -110,3 +112,73 @@ global:
local:
*;
};
+
+SMARTCOLS_2.27 {
+global:
+ scols_column_is_hidden;
+ scols_table_enable_json;
+ scols_table_is_json;
+ scols_table_set_name;
+} SMARTCOLS_2.25;
+
+SMARTCOLS_2.28 {
+global:
+ scols_column_is_wrap;
+ scols_line_refer_column_data;
+ scols_line_set_column_data;
+ scols_symbols_set_title_padding;
+ scols_table_enable_nowrap;
+ scols_table_get_title;
+ scols_cell_get_flags;
+ scols_cell_set_flags;
+ scols_table_print_range;
+ scols_table_print_range_to_string;
+ scols_table_enable_nolinesep;
+} SMARTCOLS_2.27;
+
+SMARTCOLS_2.29 {
+global:
+ scols_column_get_safechars;
+ scols_column_get_table;
+ scols_column_get_width;
+ scols_column_is_customwrap;
+ scols_column_set_safechars;
+ scols_column_set_wrapfunc;
+ scols_symbols_set_cell_padding;
+ scols_table_get_name;
+ scols_table_get_symbols;
+ scols_table_get_termforce;
+ scols_table_get_termwidth;
+ scols_table_is_nolinesep;
+ scols_table_is_nowrap;
+ scols_table_set_default_symbols;
+ scols_table_set_termforce;
+ scols_table_set_termwidth;
+ scols_wrapnl_chunksize;
+ scols_wrapnl_nextchunk;
+} SMARTCOLS_2.28;
+
+
+SMARTCOLS_2.30 {
+global:
+ scols_cell_get_alignment;
+ scols_table_move_column;
+ scols_sort_table_by_tree;
+ scols_line_is_ancestor;
+} SMARTCOLS_2.29;
+
+
+SMARTCOLS_2.31 {
+ scols_table_set_termheight;
+ scols_table_get_termheight;
+ scols_table_is_header_repeat;
+ scols_table_enable_header_repeat;
+ scols_table_enable_noencoding;
+ scols_table_is_noencoding;
+} SMARTCOLS_2.30;
+
+
+SMARTCOLS_2.33 {
+ scols_column_set_json_type;
+ scols_column_get_json_type;
+} SMARTCOLS_2.31;
diff --git a/libsmartcols/src/line.c b/libsmartcols/src/line.c
index debfeab78..60be2c135 100644
--- a/libsmartcols/src/line.c
+++ b/libsmartcols/src/line.c
@@ -11,7 +11,7 @@
/**
* SECTION: line
* @title: Line
- * @short_description: line API
+ * @short_description: cells container, also keeps tree (parent->child) information
*
* An API to access and modify per-line data and information.
*/
@@ -71,7 +71,6 @@ void scols_ref_line(struct libscols_line *ln)
*/
void scols_unref_line(struct libscols_line *ln)
{
-
if (ln && --ln->refcount <= 0) {
DBG(CELL, ul_debugobj(ln, "dealloc"));
list_del(&ln->ln_lines);
@@ -122,8 +121,6 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
{
struct libscols_cell *ce;
- assert(ln);
-
if (!ln)
return -EINVAL;
if (ln->ncells == n)
@@ -149,6 +146,35 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
return 0;
}
+int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn)
+{
+ struct libscols_cell ce;
+
+ if (!ln || newn >= ln->ncells || oldn >= ln->ncells)
+ return -EINVAL;
+ if (oldn == newn)
+ return 0;
+
+ DBG(LINE, ul_debugobj(ln, "move cells[%zu] -> cells[%zu]", oldn, newn));
+
+ /* remember data from old position */
+ memcpy(&ce, &ln->cells[oldn], sizeof(struct libscols_cell));
+
+ /* remove old position (move data behind oldn to oldn) */
+ if (oldn + 1 < ln->ncells)
+ memmove(ln->cells + oldn, ln->cells + oldn + 1,
+ (ln->ncells - oldn - 1) * sizeof(struct libscols_cell));
+
+ /* create a space for new position */
+ if (newn + 1 < ln->ncells)
+ memmove(ln->cells + newn + 1, ln->cells + newn,
+ (ln->ncells - newn - 1) * sizeof(struct libscols_cell));
+
+ /* copy original data to new position */
+ memcpy(&ln->cells[newn], &ce, sizeof(struct libscols_cell));
+ return 0;
+}
+
/**
* scols_line_set_userdata:
* @ln: a pointer to a struct libscols_line instance
@@ -160,7 +186,6 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
*/
int scols_line_set_userdata(struct libscols_line *ln, void *data)
{
- assert(ln);
if (!ln)
return -EINVAL;
ln->userdata = data;
@@ -171,12 +196,11 @@ int scols_line_set_userdata(struct libscols_line *ln, void *data)
* scols_line_get_userdata:
* @ln: a pointer to a struct libscols_line instance
*
- * Returns: 0, a negative value in case of an error.
+ * Returns: user data
*/
void *scols_line_get_userdata(struct libscols_line *ln)
{
- assert(ln);
- return ln ? ln->userdata : NULL;
+ return ln->userdata;
}
/**
@@ -190,13 +214,10 @@ void *scols_line_get_userdata(struct libscols_line *ln)
*/
int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child)
{
- assert(ln);
- assert(child);
-
if (!ln || !child)
return -EINVAL;
- DBG(LINE, ul_debugobj(ln, "remove child %p", child));
+ DBG(LINE, ul_debugobj(ln, "remove child"));
list_del_init(&child->ln_children);
child->parent = NULL;
@@ -217,26 +238,22 @@ int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *chil
*/
int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child)
{
- assert(ln);
- assert(child);
-
if (!ln || !child)
return -EINVAL;
+ DBG(LINE, ul_debugobj(ln, "add child"));
+ scols_ref_line(child);
+ scols_ref_line(ln);
+
/* unref old<->parent */
if (child->parent)
scols_line_remove_child(child->parent, child);
- DBG(LINE, ul_debugobj(ln, "add child %p", child));
-
/* new reference from parent to child */
list_add_tail(&child->ln_children, &ln->ln_branch);
- scols_ref_line(child);
/* new reference from child to parent */
child->parent = ln;
- scols_ref_line(ln);
-
return 0;
}
@@ -246,9 +263,8 @@ int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child)
*
* Returns: a pointer to @ln's parent, NULL in case it has no parent or if there was an error.
*/
-struct libscols_line *scols_line_get_parent(struct libscols_line *ln)
+struct libscols_line *scols_line_get_parent(const struct libscols_line *ln)
{
- assert(ln);
return ln ? ln->parent : NULL;
}
@@ -260,7 +276,6 @@ struct libscols_line *scols_line_get_parent(struct libscols_line *ln)
*/
int scols_line_has_children(struct libscols_line *ln)
{
- assert(ln);
return ln ? !list_empty(&ln->ln_branch) : 0;
}
@@ -294,29 +309,44 @@ int scols_line_next_child(struct libscols_line *ln,
return rc;
}
+
+/**
+ * scols_line_is_ancestor:
+ * @ln: line
+ * @parent: potential parent
+ *
+ * The function is designed to detect circular dependencies between @ln and
+ * @parent. It checks if @ln is not any (grand) parent in the @parent's tree.
+ *
+ * Since: 2.30
+ *
+ * Returns: 0 or 1
+ */
+int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent)
+{
+ while (parent) {
+ if (parent == ln)
+ return 1;
+ parent = scols_line_get_parent(parent);
+ };
+ return 0;
+}
+
/**
* scols_line_set_color:
* @ln: a pointer to a struct libscols_line instance
- * @color: ESC sequence
+ * @color: color name or ESC sequence
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_set_color(struct libscols_line *ln, const char *color)
{
- char *p = NULL;
-
- assert(ln);
- if (!ln)
- return -EINVAL;
- if (color) {
- p = strdup(color);
- if (!p)
- return -ENOMEM;
+ if (color && isalnum(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
}
-
- free(ln->color);
- ln->color = p;
- return 0;
+ return strdup_to_struct_member(ln, color, color);
}
/**
@@ -325,22 +355,20 @@ int scols_line_set_color(struct libscols_line *ln, const char *color)
*
* Returns: @ln's color string, NULL in case of an error.
*/
-const char *scols_line_get_color(struct libscols_line *ln)
+const char *scols_line_get_color(const struct libscols_line *ln)
{
- assert(ln);
- return ln ? ln->color : NULL;
+ return ln->color;
}
/**
* scols_line_get_ncells:
* @ln: a pointer to a struct libscols_line instance
*
- * Returns: @ln's number of cells
+ * Returns: number of cells
*/
-size_t scols_line_get_ncells(struct libscols_line *ln)
+size_t scols_line_get_ncells(const struct libscols_line *ln)
{
- assert(ln);
- return ln ? ln->ncells : 0;
+ return ln->ncells;
}
/**
@@ -353,8 +381,6 @@ size_t scols_line_get_ncells(struct libscols_line *ln)
struct libscols_cell *scols_line_get_cell(struct libscols_line *ln,
size_t n)
{
- assert(ln);
-
if (!ln || n >= ln->ncells)
return NULL;
return &ln->cells[n];
@@ -373,15 +399,15 @@ struct libscols_cell *scols_line_get_column_cell(
struct libscols_line *ln,
struct libscols_column *cl)
{
- assert(ln);
- assert(cl);
+ if (!ln || !cl)
+ return NULL;
return scols_line_get_cell(ln, cl->seqnum);
}
/**
* scols_line_set_data:
- * @ln: a pointer to a struct libscols_cell instance
+ * @ln: a pointer to a struct libscols_line instance
* @n: number of the cell, whose data is to be set
* @data: actual data to set
*
@@ -396,9 +422,28 @@ int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data)
return scols_cell_set_data(ce, data);
}
+/**
+ * scols_line_set_column_data:
+ * @ln: a pointer to a struct libscols_line instance
+ * @cl: column, whose data is to be set
+ * @data: actual data to set
+ *
+ * The same as scols_line_set_data() but cell is referenced by column object.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.28
+ */
+int scols_line_set_column_data(struct libscols_line *ln,
+ struct libscols_column *cl,
+ const char *data)
+{
+ return scols_line_set_data(ln, cl->seqnum, data);
+}
+
/**
* scols_line_refer_data:
- * @ln: a pointer to a struct libscols_cell instance
+ * @ln: a pointer to a struct libscols_line instance
* @n: number of the cell which will refer to @data
* @data: actual data to refer to
*
@@ -413,18 +458,36 @@ int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data)
return scols_cell_refer_data(ce, data);
}
+/**
+ * scols_line_refer_column_data:
+ * @ln: a pointer to a struct libscols_line instance
+ * @cl: column, whose data is to be set
+ * @data: actual data to refer to
+ *
+ * The same as scols_line_refer_data() but cell is referenced by column object.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.28
+ */
+int scols_line_refer_column_data(struct libscols_line *ln,
+ struct libscols_column *cl,
+ char *data)
+{
+ return scols_line_refer_data(ln, cl->seqnum, data);
+}
+
/**
* scols_copy_line:
- * @ln: a pointer to a struct libscols_cell instance
+ * @ln: a pointer to a struct libscols_line instance
*
* Returns: A newly allocated copy of @ln, NULL in case of an error.
*/
-struct libscols_line *scols_copy_line(struct libscols_line *ln)
+struct libscols_line *scols_copy_line(const struct libscols_line *ln)
{
struct libscols_line *ret;
size_t i;
- assert (ln);
if (!ln)
return NULL;
@@ -440,7 +503,7 @@ struct libscols_line *scols_copy_line(struct libscols_line *ln)
ret->ncells = ln->ncells;
ret->seqnum = ln->seqnum;
- DBG(LINE, ul_debugobj(ln, "copy to %p", ret));
+ DBG(LINE, ul_debugobj(ln, "copy"));
for (i = 0; i < ret->ncells; ++i) {
if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i]))
@@ -452,5 +515,3 @@ err:
scols_unref_line(ret);
return NULL;
}
-
-
diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h
index cea4f3101..398e6f064 100644
--- a/libsmartcols/src/smartcolsP.h
+++ b/libsmartcols/src/smartcolsP.h
@@ -13,23 +13,18 @@
#include "c.h"
#include "list.h"
+#include "strutils.h"
#include "colors.h"
#include "debug.h"
-#include "libsmartcols.h"
-
-/* features */
-#define CONFIG_LIBSMARTCOLS_ASSERT
+#include <assert.h>
-#ifdef CONFIG_LIBSMARTCOLS_ASSERT
-# include <assert.h>
-#else
-# define assert(x)
-#endif
+#include "libsmartcols.h"
/*
* Debug
*/
+#define SCOLS_DEBUG_HELP (1 << 0)
#define SCOLS_DEBUG_INIT (1 << 1)
#define SCOLS_DEBUG_CELL (1 << 2)
#define SCOLS_DEBUG_LINE (1 << 3)
@@ -43,7 +38,7 @@ UL_DEBUG_DECLARE_MASK(libsmartcols);
#define ON_DBG(m, x) __UL_DBG_CALL(libsmartcols, SCOLS_DEBUG_, m, x)
#define DBG_FLUSH __UL_DBG_FLUSH(libsmartcols, SCOLS_DEBUG_)
-#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(libsmartcols)
+#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(libsmartcols)
#include "debugobj.h"
/*
@@ -63,6 +58,8 @@ struct libscols_symbols {
char *branch;
char *vert;
char *right;
+ char *title_padding;
+ char *cell_padding;
};
/*
@@ -72,8 +69,10 @@ struct libscols_cell {
char *data;
char *color;
void *userdata;
+ int flags;
};
+extern int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn);
/*
* Table column
@@ -86,19 +85,36 @@ struct libscols_column {
size_t width_min; /* minimal width (usually header width) */
size_t width_max; /* maximal width */
size_t width_avg; /* average width, used to detect extreme fields */
+ size_t width_treeart; /* size of the tree ascii art */
double width_hint; /* hint (N < 1 is in percent of termwidth) */
+ int json_type; /* SCOLS_JSON_* */
+
int flags;
int is_extreme;
char *color; /* default column color */
+ char *safechars; /* do not encode this bytes */
+
+ char *pending_data;
+ size_t pending_data_sz;
+ char *pending_data_buf;
int (*cmpfunc)(struct libscols_cell *,
struct libscols_cell *,
void *); /* cells comparison function */
void *cmpfunc_data;
+ size_t (*wrap_chunksize)(const struct libscols_column *,
+ const char *, void *);
+ char *(*wrap_nextchunk)(const struct libscols_column *,
+ char *, void *);
+ void *wrapfunc_data;
+
+
struct libscols_cell header;
struct list_head cl_columns;
+
+ struct libscols_table *table;
};
/*
@@ -124,7 +140,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 */
};
/*
@@ -132,11 +149,14 @@ enum {
*/
struct libscols_table {
int refcount;
+ char *name; /* optional table name (for JSON) */
size_t ncols; /* number of columns */
size_t ntreecols; /* number of columns with SCOLS_FL_TREE */
size_t nlines; /* number of lines */
- size_t termwidth; /* terminal width */
+ size_t termwidth; /* terminal width (number of columns) */
+ size_t termheight; /* terminal height (number of lines) */
size_t termreduce; /* extra blank space */
+ int termforce; /* SCOLS_TERMFORCE_* */
FILE *out; /* output stream */
char *colsep; /* column separator */
@@ -145,15 +165,28 @@ struct libscols_table {
struct list_head tb_columns;
struct list_head tb_lines;
struct libscols_symbols *symbols;
+ struct libscols_cell title; /* optional table title (for humans) */
+ int indent; /* indention counter */
+ int indent_last_sep;/* last printed has been line separator */
int format; /* SCOLS_FMT_* */
+ size_t termlines_used; /* printed line counter */
+ size_t header_next; /* where repeat header */
+
/* flags */
unsigned int ascii :1, /* don't use unicode */
colors_wanted :1, /* enable colors */
is_term :1, /* isatty() */
- maxout :1, /* maximalize output */
- no_headings :1; /* don't print header */
+ padding_debug :1, /* output visible padding chars */
+ maxout :1, /* maximize output */
+ header_repeat :1, /* print header after libscols_table->termheight */
+ header_printed :1, /* header already printed */
+ priv_symbols :1, /* default private symbols */
+ no_headings :1, /* don't print header */
+ no_encode :1, /* don't care about control and non-printable chars */
+ no_linesep :1, /* don't print line separator */
+ no_wrap :1; /* never wrap lines */
};
#define IS_ITER_FORWARD(_i) ((_i)->direction == SCOLS_ITER_FORWARD)
@@ -173,4 +206,13 @@ struct libscols_table {
(itr)->p->next : (itr)->p->prev; \
} while(0)
+
+static inline int scols_iter_is_last(const 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/symbols.c b/libsmartcols/src/symbols.c
index 2b8f81dc9..6ddf1869b 100644
--- a/libsmartcols/src/symbols.c
+++ b/libsmartcols/src/symbols.c
@@ -2,6 +2,7 @@
* symbols.c - routines for symbol handling
*
* Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
@@ -10,7 +11,7 @@
/**
* SECTION: symbols
* @title: Symbols
- * @short_description: symbols API
+ * @short_description: allows to overwrite default output chars (for ascii art)
*
* An API to access and modify data and information per symbol/symbol group.
*/
@@ -61,115 +62,112 @@ void scols_unref_symbols(struct libscols_symbols *sy)
free(sy->branch);
free(sy->vert);
free(sy->right);
+ free(sy->title_padding);
+ free(sy->cell_padding);
free(sy);
}
}
/**
* scols_symbols_set_branch:
- * @sb: a pointer to a struct libscols_symbols instance
+ * @sy: a pointer to a struct libscols_symbols instance
* @str: a string which will represent the branch part of a tree output
*
* Returns: 0, a negative value in case of an error.
*/
-int scols_symbols_set_branch(struct libscols_symbols *sb, const char *str)
+int scols_symbols_set_branch(struct libscols_symbols *sy, const char *str)
{
- char *p = NULL;
-
- assert(sb);
-
- if (!sb)
- return -EINVAL;
- if (str) {
- p = strdup(str);
- if (!p)
- return -ENOMEM;
- }
- free(sb->branch);
- sb->branch = p;
- return 0;
+ return strdup_to_struct_member(sy, branch, str);
}
/**
* scols_symbols_set_vertical:
- * @sb: a pointer to a struct libscols_symbols instance
+ * @sy: a pointer to a struct libscols_symbols instance
* @str: a string which will represent the vertical part of a tree output
*
* Returns: 0, a negative value in case of an error.
*/
-int scols_symbols_set_vertical(struct libscols_symbols *sb, const char *str)
+int scols_symbols_set_vertical(struct libscols_symbols *sy, const char *str)
{
- char *p = NULL;
-
- assert(sb);
-
- if (!sb)
- return -EINVAL;
- if (str) {
- p = strdup(str);
- if (!p)
- return -ENOMEM;
- }
- free(sb->vert);
- sb->vert = p;
- return 0;
+ return strdup_to_struct_member(sy, vert, str);
}
/**
* scols_symbols_set_right:
- * @sb: a pointer to a struct libscols_symbols instance
+ * @sy: a pointer to a struct libscols_symbols instance
* @str: a string which will represent the right part of a tree output
*
* Returns: 0, a negative value in case of an error.
*/
-int scols_symbols_set_right(struct libscols_symbols *sb, const char *str)
+int scols_symbols_set_right(struct libscols_symbols *sy, const char *str)
{
- char *p = NULL;
+ return strdup_to_struct_member(sy, right, str);
+}
- assert(sb);
+/**
+ * scols_symbols_set_title_padding:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the symbols which fill title output
+ *
+ * The current implementation uses only the first byte from the padding string.
+ * A multibyte chars are not supported yet.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.28
+ */
+int scols_symbols_set_title_padding(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, title_padding, str);
+}
- if (!sb)
- return -EINVAL;
- if (str) {
- p = strdup(str);
- if (!p)
- return -ENOMEM;
- }
- free(sb->right);
- sb->right = p;
- return 0;
+/**
+ * scols_symbols_set_cell_padding:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the symbols which fill cells
+ *
+ * The padding char has to take up just one cell on the terminal.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_symbols_set_cell_padding(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, cell_padding, str);
}
/**
* scols_copy_symbols:
- * @sb: a pointer to a struct libscols_symbols instance
+ * @sy: a pointer to a struct libscols_symbols instance
*
- * Returns: a newly allocated copy of the @sb symbol group or NULL in caes of an error.
+ * Returns: a newly allocated copy of the @sy symbol group or NULL in case of an error.
*/
-struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sb)
+struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sy)
{
struct libscols_symbols *ret;
int rc;
- assert(sb);
- if (!sb)
+ assert(sy);
+ if (!sy)
return NULL;
ret = scols_new_symbols();
if (!ret)
return NULL;
- rc = scols_symbols_set_branch(ret, sb->branch);
+ rc = scols_symbols_set_branch(ret, sy->branch);
+ if (!rc)
+ rc = scols_symbols_set_vertical(ret, sy->vert);
if (!rc)
- rc = scols_symbols_set_vertical(ret, sb->vert);
+ rc = scols_symbols_set_right(ret, sy->right);
if (!rc)
- rc = scols_symbols_set_right(ret, sb->right);
+ rc = scols_symbols_set_title_padding(ret, sy->title_padding);
+ if (!rc)
+ rc = scols_symbols_set_cell_padding(ret, sy->cell_padding);
if (!rc)
return ret;
scols_unref_symbols(ret);
return NULL;
-
}
-
-
diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c
index 8c404f858..979a09a39 100644
--- a/libsmartcols/src/table.c
+++ b/libsmartcols/src/table.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
* Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
@@ -11,7 +12,7 @@
/**
* SECTION: table
* @title: Table
- * @short_description: table data API
+ * @short_description: container for rows and columns
*
* Table data manipulation API.
*/
@@ -24,7 +25,7 @@
#include <ctype.h>
#include "nls.h"
-#include "widechar.h"
+#include "ttyutils.h"
#include "smartcolsP.h"
#ifdef HAVE_WIDECHAR
@@ -38,6 +39,20 @@
list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns)
+static void check_padding_debug(struct libscols_table *tb)
+{
+ const char *str;
+
+ assert(libsmartcols_debug_mask); /* debug has to be enabled! */
+
+ str = getenv("LIBSMARTCOLS_DEBUG_PADDING");
+ if (!str || (strcmp(str, "on") != 0 && strcmp(str, "1") != 0))
+ return;
+
+ DBG(INIT, ul_debugobj(tb, "padding debug: ENABLE"));
+ tb->padding_debug = 1;
+}
+
/**
* scols_new_table:
*
@@ -46,6 +61,7 @@
struct libscols_table *scols_new_table(void)
{
struct libscols_table *tb;
+ int c, l;
tb = calloc(1, sizeof(struct libscols_table));
if (!tb)
@@ -54,10 +70,16 @@ struct libscols_table *scols_new_table(void)
tb->refcount = 1;
tb->out = stdout;
+ get_terminal_dimension(&c, &l);
+ tb->termwidth = c > 0 ? c : 80;
+ tb->termheight = l > 0 ? l : 24;
+
INIT_LIST_HEAD(&tb->tb_lines);
INIT_LIST_HEAD(&tb->tb_columns);
DBG(TAB, ul_debugobj(tb, "alloc"));
+ ON_DBG(INIT, check_padding_debug(tb));
+
return tb;
}
@@ -87,44 +109,102 @@ void scols_unref_table(struct libscols_table *tb)
scols_table_remove_lines(tb);
scols_table_remove_columns(tb);
scols_unref_symbols(tb->symbols);
+ scols_reset_cell(&tb->title);
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.
+ *
+ * Since: 2.27
+ */
+int scols_table_set_name(struct libscols_table *tb, const char *name)
+{
+ return strdup_to_struct_member(tb, name, name);
+}
+
+/**
+ * scols_table_get_name:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Returns: The current name setting of the table @tb
+ *
+ * Since: 2.29
+ */
+const char *scols_table_get_name(const struct libscols_table *tb)
+{
+ return tb->name;
+}
+
+/**
+ * scols_table_get_title:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * The returned pointer is possible to modify by cell functions. Note that
+ * title output alignment on non-tty is hardcoded to 80 output chars. For the
+ * regular terminal it's based on terminal width.
+ *
+ * Returns: Title of the table, or NULL in case of blank title.
+ *
+ * Since: 2.28
+ */
+struct libscols_cell *scols_table_get_title(struct libscols_table *tb)
+{
+ return &tb->title;
+}
+
/**
* scols_table_add_column:
* @tb: a pointer to a struct libscols_table instance
* @cl: a pointer to a struct libscols_column instance
*
- * Adds @cl to @tb's column list.
+ * Adds @cl to @tb's column list. The column cannot be shared between more
+ * tables.
*
* Returns: 0, a negative number in case of an error.
*/
int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl)
{
- assert(tb);
- assert(cl);
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+ int rc = 0;
- if (!tb || !cl || !list_empty(&tb->tb_lines))
+ if (!tb || !cl || cl->table)
return -EINVAL;
if (cl->flags & SCOLS_FL_TREE)
tb->ntreecols++;
- DBG(TAB, ul_debugobj(tb, "add column %p", cl));
+ DBG(TAB, ul_debugobj(tb, "add column"));
list_add_tail(&cl->cl_columns, &tb->tb_columns);
cl->seqnum = tb->ncols++;
+ cl->table = tb;
scols_ref_column(cl);
- /* TODO:
- *
- * Currently it's possible to add/remove columns only if the table is
- * empty (see list_empty(tb->tb_lines) above). It would be nice to
- * enlarge/reduce lines cells[] always when we add/remove a new column.
+ if (list_empty(&tb->tb_lines))
+ return 0;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
+ /* Realloc line cell arrays
*/
- return 0;
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ rc = scols_line_alloc_cells(ln, tb->ncols);
+ if (rc)
+ break;
+ }
+
+ return rc;
}
/**
@@ -139,18 +219,16 @@ int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl
int scols_table_remove_column(struct libscols_table *tb,
struct libscols_column *cl)
{
- assert(tb);
- assert(cl);
-
if (!tb || !cl || !list_empty(&tb->tb_lines))
return -EINVAL;
if (cl->flags & SCOLS_FL_TREE)
tb->ntreecols--;
- DBG(TAB, ul_debugobj(tb, "remove column %p", cl));
+ DBG(TAB, ul_debugobj(tb, "remove column"));
list_del_init(&cl->cl_columns);
tb->ncols--;
+ cl->table = NULL;
scols_unref_column(cl);
return 0;
}
@@ -165,8 +243,6 @@ int scols_table_remove_column(struct libscols_table *tb,
*/
int scols_table_remove_columns(struct libscols_table *tb)
{
- assert(tb);
-
if (!tb || !list_empty(&tb->tb_lines))
return -EINVAL;
@@ -179,12 +255,64 @@ int scols_table_remove_columns(struct libscols_table *tb)
return 0;
}
+/**
+ * scols_table_move_column:
+ * @tb: table
+ * @pre: column before the column
+ * @cl: column to move
+ *
+ * Move the @cl behind @pre. If the @pre is NULL then the @col is the first
+ * column in the table.
+ *
+ * Since: 2.30
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_move_column(struct libscols_table *tb,
+ struct libscols_column *pre,
+ struct libscols_column *cl)
+{
+ struct list_head *head;
+ struct libscols_iter itr;
+ struct libscols_column *p;
+ struct libscols_line *ln;
+ size_t n = 0, oldseq;
+
+ if (!tb || !cl)
+ return -EINVAL;
+
+ if (pre && pre->seqnum + 1 == cl->seqnum)
+ return 0;
+ if (pre == NULL && cl->seqnum == 0)
+ return 0;
+
+ DBG(TAB, ul_debugobj(tb, "move column %zu behind %zu",
+ cl->seqnum, pre? pre->seqnum : 0));
+
+ list_del_init(&cl->cl_columns); /* remove from old position */
+
+ head = pre ? &pre->cl_columns : &tb->tb_columns;
+ list_add(&cl->cl_columns, head); /* add to the new place */
+
+ oldseq = cl->seqnum;
+
+ /* fix seq. numbers */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &p) == 0)
+ p->seqnum = n++;
+
+ /* move data in lines */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0)
+ scols_line_move_cells(ln, cl->seqnum, oldseq);
+ return 0;
+}
/**
* scols_table_new_column:
* @tb: table
* @name: column header
- * @whint: column width hint (absolute width: N > 1; relative width: N < 1)
+ * @whint: column width hint (absolute width: N > 1; relative width: 0 < N < 1)
* @flags: flags integer
*
* This is shortcut for
@@ -193,17 +321,36 @@ int scols_table_remove_columns(struct libscols_table *tb)
* scols_column_set_....(cl, ...);
* scols_table_add_column(tb, cl);
*
- * The column width is possible to define by three ways:
+ * The column width is possible to define by:
+ *
+ * @whint: 0 < N < 1 : relative width, percent of terminal width
+ *
+ * @whint: N >= 1 : absolute width, empty column will be truncated to
+ * the column header width if no specified STRICTWIDTH flag
*
- * @whint = 0..1 : relative width, percent of terminal width
+ * Note that if table has disabled "maxout" flag (disabled by default) than
+ * relative width is used as a hint only. It's possible that column will be
+ * narrow if the specified size is too large for column data.
*
- * @whint = 1..N : absolute width, empty colum will be truncated to
- * the column header width
*
- * @whint = 1..N
+ * If the width of all columns is greater than terminal width then library
+ * tries to reduce width of the individual columns. It's done in three stages:
*
- * The column is necessary to address by
- * sequential number. The first defined column has the colnum = 0. For example:
+ * #1 reduce columns with SCOLS_FL_TRUNC flag and with relative width if the
+ * width is greater than width defined by @whint (@whint * terminal_width)
+ *
+ * #2 reduce all columns with SCOLS_FL_TRUNC flag
+ *
+ * #3 reduce all columns with relative width
+ *
+ * The next stage is always used if the previous stage is unsuccessful. Note
+ * that SCOLS_FL_WRAP is interpreted as SCOLS_FL_TRUNC when calculate column
+ * width (if custom wrap function is not specified), but the final text is not
+ * truncated, but wrapped to multi-line cell.
+ *
+ *
+ * The column is necessary to address by sequential number. The first defined
+ * column has the colnum = 0. For example:
*
* scols_table_new_column(tab, "FOO", 0.5, 0); // colnum = 0
* scols_table_new_column(tab, "BAR", 0.5, 0); // colnum = 1
@@ -222,7 +369,6 @@ struct libscols_column *scols_table_new_column(struct libscols_table *tb,
struct libscols_column *cl;
struct libscols_cell *hr;
- assert (tb);
if (!tb)
return NULL;
@@ -282,29 +428,26 @@ int scols_table_next_column(struct libscols_table *tb,
return rc;
}
-
/**
* scols_table_get_ncols:
* @tb: table
*
- * Returns: the ncols table member, a negative number in case of an error.
+ * Returns: the ncols table member.
*/
-int scols_table_get_ncols(struct libscols_table *tb)
+size_t scols_table_get_ncols(const struct libscols_table *tb)
{
- assert(tb);
- return tb ? tb->ncols : -EINVAL;
+ return tb->ncols;
}
/**
* scols_table_get_nlines:
* @tb: table
*
- * Returns: the nlines table member, a negative number in case of an error.
+ * Returns: the nlines table member.
*/
-int scols_table_get_nlines(struct libscols_table *tb)
+size_t scols_table_get_nlines(const struct libscols_table *tb)
{
- assert(tb);
- return tb ? tb->nlines : -EINVAL;
+ return tb->nlines;
}
/**
@@ -335,10 +478,9 @@ int scols_table_set_stream(struct libscols_table *tb, FILE *stream)
*
* Returns: stream pointer, NULL in case of an error or an unset stream.
*/
-FILE *scols_table_get_stream(struct libscols_table *tb)
+FILE *scols_table_get_stream(const struct libscols_table *tb)
{
- assert(tb);
- return tb ? tb->out: NULL;
+ return tb->out;
}
/**
@@ -346,13 +488,20 @@ FILE *scols_table_get_stream(struct libscols_table *tb)
* @tb: table
* @reduce: width
*
- * Reduce the output width to @reduce.
+ * If necessary then libsmartcols use all terminal width, the @reduce setting
+ * provides extra space (for example for borders in ncurses applications).
+ *
+ * The @reduce must be smaller than terminal width, otherwise it's silently
+ * ignored. The reduction is not applied when STDOUT_FILENO is not terminal.
+ *
+ * Note that after output initialization (scols_table_print_* calls) the width
+ * will be reduced, this behavior affects subsequenced scols_table_get_termwidth()
+ * calls.
*
* Returns: 0, a negative value in case of an error.
*/
int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce)
{
- assert(tb);
if (!tb)
return -EINVAL;
@@ -374,7 +523,6 @@ struct libscols_column *scols_table_get_column(struct libscols_table *tb,
struct libscols_iter itr;
struct libscols_column *cl;
- assert(tb);
if (!tb)
return NULL;
if (n >= tb->ncols)
@@ -400,11 +548,7 @@ struct libscols_column *scols_table_get_column(struct libscols_table *tb,
*/
int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln)
{
-
- assert(tb);
- assert(ln);
-
- if (!tb || !ln)
+ if (!tb || !ln || tb->ncols == 0)
return -EINVAL;
if (tb->ncols > ln->ncells) {
@@ -413,7 +557,7 @@ int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln)
return rc;
}
- DBG(TAB, ul_debugobj(tb, "add line %p", ln));
+ DBG(TAB, ul_debugobj(tb, "add line"));
list_add_tail(&ln->ln_lines, &tb->tb_lines);
ln->seqnum = tb->nlines++;
scols_ref_line(ln);
@@ -433,13 +577,10 @@ int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln)
int scols_table_remove_line(struct libscols_table *tb,
struct libscols_line *ln)
{
- assert(tb);
- assert(ln);
-
if (!tb || !ln)
return -EINVAL;
- DBG(TAB, ul_debugobj(tb, "remove line %p", ln));
+ DBG(TAB, ul_debugobj(tb, "remove line"));
list_del_init(&ln->ln_lines);
tb->nlines--;
scols_unref_line(ln);
@@ -454,7 +595,6 @@ int scols_table_remove_line(struct libscols_table *tb,
*/
void scols_table_remove_lines(struct libscols_table *tb)
{
- assert(tb);
if (!tb)
return;
@@ -517,9 +657,6 @@ struct libscols_line *scols_table_new_line(struct libscols_table *tb,
{
struct libscols_line *ln;
- assert(tb);
- assert(tb->ncols);
-
if (!tb || !tb->ncols)
return NULL;
@@ -544,13 +681,7 @@ err:
* @tb: table
* @n: column number (0..N)
*
- * This is a shortcut for
- *
- * ln = scols_new_line();
- * scols_line_set_....(cl, ...);
- * scols_table_add_line(tb, ln);
- *
- * Returns: a newly allocate line
+ * Returns: a line or NULL
*/
struct libscols_line *scols_table_get_line(struct libscols_table *tb,
size_t n)
@@ -558,7 +689,6 @@ struct libscols_line *scols_table_get_line(struct libscols_table *tb,
struct libscols_iter itr;
struct libscols_line *ln;
- assert(tb);
if (!tb)
return NULL;
if (n >= tb->nlines)
@@ -588,14 +718,13 @@ struct libscols_table *scols_copy_table(struct libscols_table *tb)
struct libscols_column *cl;
struct libscols_iter itr;
- assert(tb);
if (!tb)
return NULL;
ret = scols_new_table();
if (!ret)
return NULL;
- DBG(TAB, ul_debugobj(tb, "copy into %p", ret));
+ DBG(TAB, ul_debugobj(tb, "copy"));
if (tb->symbols)
scols_table_set_symbols(ret, tb->symbols);
@@ -639,54 +768,141 @@ err:
return NULL;
}
+/**
+ * scols_table_set_default_symbols:
+ * @tb: table
+ *
+ * The library check the current environment to select ASCII or UTF8 symbols.
+ * This default behavior could be controlled by scols_table_enable_ascii().
+ *
+ * Use scols_table_set_symbols() to unset symbols or use your own setting.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_table_set_default_symbols(struct libscols_table *tb)
+{
+ struct libscols_symbols *sy;
+ int rc;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "setting default symbols"));
+
+ sy = scols_new_symbols();
+ if (!sy)
+ return -ENOMEM;
+
+#if defined(HAVE_WIDECHAR)
+ if (!scols_table_is_ascii(tb) &&
+ !strcmp(nl_langinfo(CODESET), "UTF-8")) {
+ scols_symbols_set_branch(sy, UTF_VR UTF_H);
+ scols_symbols_set_vertical(sy, UTF_V " ");
+ scols_symbols_set_right(sy, UTF_UR UTF_H);
+ } else
+#endif
+ {
+ scols_symbols_set_branch(sy, "|-");
+ scols_symbols_set_vertical(sy, "| ");
+ scols_symbols_set_right(sy, "`-");
+ }
+ scols_symbols_set_title_padding(sy, " ");
+ scols_symbols_set_cell_padding(sy, " ");
+
+ rc = scols_table_set_symbols(tb, sy);
+ scols_unref_symbols(sy);
+ return rc;
+}
+
+
/**
* scols_table_set_symbols:
* @tb: table
* @sy: symbols or NULL
*
* Add a reference to @sy from the table. The symbols are used by library to
- * draw tree output. If no symbols are specified then library checks the
- * current environment to select ASCII or UTF8 symbols. This default behavior
- * could be controlled by scols_table_enable_ascii().
+ * draw tree output. If no symbols are used for the table then library creates
+ * default temporary symbols to draw output by scols_table_set_default_symbols().
+ *
+ * If @sy is NULL then remove reference from the currently used symbols.
*
* Returns: 0, a negative value in case of an error.
*/
int scols_table_set_symbols(struct libscols_table *tb,
struct libscols_symbols *sy)
{
- assert(tb);
-
if (!tb)
return -EINVAL;
- DBG(TAB, ul_debugobj(tb, "setting alternative symbols %p", sy));
-
- if (tb->symbols) /* unref old */
+ /* remove old */
+ if (tb->symbols) {
+ DBG(TAB, ul_debugobj(tb, "remove symbols reference"));
scols_unref_symbols(tb->symbols);
+ tb->symbols = NULL;
+ }
+
+ /* set new */
if (sy) { /* ref user defined */
+ DBG(TAB, ul_debugobj(tb, "set symbols"));
tb->symbols = sy;
scols_ref_symbols(sy);
- } else { /* default symbols */
- tb->symbols = scols_new_symbols();
- if (!tb->symbols)
- return -ENOMEM;
-#if defined(HAVE_WIDECHAR)
- if (!scols_table_is_ascii(tb) &&
- !strcmp(nl_langinfo(CODESET), "UTF-8")) {
- scols_symbols_set_branch(tb->symbols, UTF_VR UTF_H);
- scols_symbols_set_vertical(tb->symbols, UTF_V " ");
- scols_symbols_set_right(tb->symbols, UTF_UR UTF_H);
- } else
-#endif
- {
- scols_symbols_set_branch(tb->symbols, "|-");
- scols_symbols_set_vertical(tb->symbols, "| ");
- scols_symbols_set_right(tb->symbols, "`-");
- }
}
+ return 0;
+}
+
+/**
+ * scols_table_get_symbols:
+ * @tb: table
+ *
+ * Returns: pointer to symbols table.
+ *
+ * Since: 2.29
+ */
+struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb)
+{
+ return tb->symbols;
+}
+
+/**
+ * scols_table_enable_nolinesep:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable line separator printing. This is useful if you want to
+ * re-printing the same line more than once (e.g. progress bar). Don't use it
+ * if you're not sure.
+ *
+ * Note that for the last line in the table the separator is disabled at all.
+ * The library differentiate between table terminator and line terminator
+ * (although for standard output \n byte is used in both cases).
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_nolinesep(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "nolinesep: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->no_linesep = enable ? 1 : 0;
return 0;
}
+
+/**
+ * scols_table_is_nolinesep:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Returns: 1 if line separator printing is disabled.
+ *
+ * Since: 2.29
+ */
+int scols_table_is_nolinesep(const struct libscols_table *tb)
+{
+ return tb->no_linesep;
+}
+
/**
* scols_table_enable_colors:
* @tb: table
@@ -698,7 +914,6 @@ int scols_table_set_symbols(struct libscols_table *tb,
*/
int scols_table_enable_colors(struct libscols_table *tb, int enable)
{
- assert(tb);
if (!tb)
return -EINVAL;
@@ -706,19 +921,19 @@ 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.
*/
int scols_table_enable_raw(struct libscols_table *tb, int enable)
{
- assert(tb);
if (!tb)
return -EINVAL;
@@ -730,6 +945,31 @@ 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.
+ *
+ * Since: 2.27
+ */
+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
@@ -742,7 +982,6 @@ int scols_table_enable_raw(struct libscols_table *tb, int enable)
*/
int scols_table_enable_export(struct libscols_table *tb, int enable)
{
- assert(tb);
if (!tb)
return -EINVAL;
@@ -771,7 +1010,6 @@ int scols_table_enable_export(struct libscols_table *tb, int enable)
*/
int scols_table_enable_ascii(struct libscols_table *tb, int enable)
{
- assert(tb);
if (!tb)
return -EINVAL;
@@ -791,7 +1029,6 @@ int scols_table_enable_ascii(struct libscols_table *tb, int enable)
*/
int scols_table_enable_noheadings(struct libscols_table *tb, int enable)
{
- assert(tb);
if (!tb)
return -EINVAL;
DBG(TAB, ul_debugobj(tb, "noheading: %s", enable ? "ENABLE" : "DISABLE"));
@@ -799,6 +1036,28 @@ int scols_table_enable_noheadings(struct libscols_table *tb, int enable)
return 0;
}
+/**
+ * scols_table_enable_header_repeat:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable header line repeat. The header line is printed only once by
+ * default. Note that the flag will be silently ignored and disabled if the
+ * output is not on terminal or output format is JSON, raw, etc.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.31
+ */
+int scols_table_enable_header_repeat(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "header-repeat: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->header_repeat = enable ? 1 : 0;
+ return 0;
+}
+
/**
* scols_table_enable_maxout:
* @tb: table
@@ -811,7 +1070,6 @@ int scols_table_enable_noheadings(struct libscols_table *tb, int enable)
*/
int scols_table_enable_maxout(struct libscols_table *tb, int enable)
{
- assert(tb);
if (!tb)
return -EINVAL;
DBG(TAB, ul_debugobj(tb, "maxout: %s", enable ? "ENABLE" : "DISABLE"));
@@ -819,28 +1077,92 @@ int scols_table_enable_maxout(struct libscols_table *tb, int enable)
return 0;
}
+/**
+ * scols_table_enable_nowrap:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Never continue on next line, remove last column(s) when too large, truncate last column.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.28
+ */
+int scols_table_enable_nowrap(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "nowrap: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->no_wrap = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_is_nowrap:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Returns: 1 if nowrap is enabled.
+ *
+ * Since: 2.29
+ */
+int scols_table_is_nowrap(const struct libscols_table *tb)
+{
+ return tb->no_wrap;
+}
+
+/**
+ * scols_table_enable_noencoding:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * The library encode non-printable and control chars by \xHEX by default.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.31
+ */
+int scols_table_enable_noencoding(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "encoding: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->no_encode = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_is_noencoding:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Returns: 1 if encoding is disabled.
+ *
+ * Since: 2.31
+ */
+int scols_table_is_noencoding(const struct libscols_table *tb)
+{
+ return tb->no_encode;
+}
+
/**
* scols_table_colors_wanted:
* @tb: table
*
* Returns: 1 if colors are enabled.
*/
-int scols_table_colors_wanted(struct libscols_table *tb)
+int scols_table_colors_wanted(const struct libscols_table *tb)
{
- assert(tb);
- return tb && tb->colors_wanted;
+ return tb->colors_wanted;
}
/**
* scols_table_is_empty:
* @tb: table
*
- * Returns: 1 if the table is empty.
+ * Returns: 1 if the table is empty.
*/
-int scols_table_is_empty(struct libscols_table *tb)
+int scols_table_is_empty(const struct libscols_table *tb)
{
- assert(tb);
- return !tb || !tb->nlines;
+ return !tb->nlines;
}
/**
@@ -849,10 +1171,9 @@ int scols_table_is_empty(struct libscols_table *tb)
*
* Returns: 1 if ASCII tree is enabled.
*/
-int scols_table_is_ascii(struct libscols_table *tb)
+int scols_table_is_ascii(const struct libscols_table *tb)
{
- assert(tb);
- return tb && tb->ascii;
+ return tb->ascii;
}
/**
@@ -861,10 +1182,22 @@ int scols_table_is_ascii(struct libscols_table *tb)
*
* Returns: 1 if header output is disabled.
*/
-int scols_table_is_noheadings(struct libscols_table *tb)
+int scols_table_is_noheadings(const struct libscols_table *tb)
{
- assert(tb);
- return tb && tb->no_headings;
+ return tb->no_headings;
+}
+
+/**
+ * scols_table_is_header_repeat
+ * @tb: table
+ *
+ * Returns: 1 if header repeat is enabled.
+ *
+ * Since: 2.31
+ */
+int scols_table_is_header_repeat(const struct libscols_table *tb)
+{
+ return tb->header_repeat;
}
/**
@@ -873,10 +1206,9 @@ int scols_table_is_noheadings(struct libscols_table *tb)
*
* Returns: 1 if export output format is enabled.
*/
-int scols_table_is_export(struct libscols_table *tb)
+int scols_table_is_export(const struct libscols_table *tb)
{
- assert(tb);
- return tb && tb->format == SCOLS_FMT_EXPORT;
+ return tb->format == SCOLS_FMT_EXPORT;
}
/**
@@ -885,23 +1217,33 @@ int scols_table_is_export(struct libscols_table *tb)
*
* Returns: 1 if raw output format is enabled.
*/
-int scols_table_is_raw(struct libscols_table *tb)
+int scols_table_is_raw(const struct libscols_table *tb)
{
- assert(tb);
- return tb && tb->format == SCOLS_FMT_RAW;
+ return tb->format == SCOLS_FMT_RAW;
}
+/**
+ * scols_table_is_json:
+ * @tb: table
+ *
+ * Returns: 1 if JSON output format is enabled.
+ *
+ * Since: 2.27
+ */
+int scols_table_is_json(const struct libscols_table *tb)
+{
+ return tb->format == SCOLS_FMT_JSON;
+}
/**
* scols_table_is_maxout
* @tb: table
*
- * Returns: 1 if output maximization is enabled, negative value in case of an error.
+ * Returns: 1 if output maximization is enabled or 0
*/
-int scols_table_is_maxout(struct libscols_table *tb)
+int scols_table_is_maxout(const struct libscols_table *tb)
{
- assert(tb);
- return tb && tb->maxout;
+ return tb->maxout;
}
/**
@@ -910,10 +1252,9 @@ int scols_table_is_maxout(struct libscols_table *tb)
*
* Returns: returns 1 tree-like output is expected.
*/
-int scols_table_is_tree(struct libscols_table *tb)
+int scols_table_is_tree(const struct libscols_table *tb)
{
- assert(tb);
- return tb && tb->ntreecols > 0;
+ return tb->ntreecols > 0;
}
/**
@@ -922,29 +1263,12 @@ int scols_table_is_tree(struct libscols_table *tb)
* @sep: separator
*
* Sets the column separator of @tb to @sep.
- * Please note that @sep should always take up a single cell in the output.
*
* Returns: 0, a negative value in case of an error.
*/
int scols_table_set_column_separator(struct libscols_table *tb, const char *sep)
{
- char *p = NULL;
-
- assert (tb);
-
- if (!tb)
- return -EINVAL;
-
- if (sep) {
- p = strdup(sep);
- if (!p)
- return -ENOMEM;
- }
-
- DBG(TAB, ul_debugobj(tb, "new columns separator: %s", sep));
- free(tb->colsep);
- tb->colsep = p;
- return 0;
+ return strdup_to_struct_member(tb, colsep, sep);
}
/**
@@ -958,23 +1282,7 @@ int scols_table_set_column_separator(struct libscols_table *tb, const char *sep)
*/
int scols_table_set_line_separator(struct libscols_table *tb, const char *sep)
{
- char *p = NULL;
-
- assert (tb);
-
- if (!tb)
- return -EINVAL;
-
- if (sep) {
- p = strdup(sep);
- if (!p)
- return -ENOMEM;
- }
-
- DBG(TAB, ul_debugobj(tb, "new lines separator: %s", sep));
- free(tb->linesep);
- tb->linesep = p;
- return 0;
+ return strdup_to_struct_member(tb, linesep, sep);
}
/**
@@ -983,12 +1291,8 @@ int scols_table_set_line_separator(struct libscols_table *tb, const char *sep)
*
* Returns: @tb column separator, NULL in case of an error
*/
-char *scols_table_get_column_separator(struct libscols_table *tb)
+const char *scols_table_get_column_separator(const struct libscols_table *tb)
{
- assert (tb);
-
- if (!tb)
- return NULL;
return tb->colsep;
}
@@ -998,17 +1302,12 @@ char *scols_table_get_column_separator(struct libscols_table *tb)
*
* Returns: @tb line separator, NULL in case of an error
*/
-char *scols_table_get_line_separator(struct libscols_table *tb)
+const char *scols_table_get_line_separator(const struct libscols_table *tb)
{
- assert (tb);
-
- if (!tb)
- return NULL;
return tb->linesep;
-
}
-
-static int cells_cmp_wrapper(struct list_head *a, struct list_head *b, void *data)
+/* for lines in the struct libscols_line->ln_lines list */
+static int cells_cmp_wrapper_lines(struct list_head *a, struct list_head *b, void *data)
{
struct libscols_column *cl = (struct libscols_column *) data;
struct libscols_line *ra, *rb;
@@ -1026,24 +1325,218 @@ static int cells_cmp_wrapper(struct list_head *a, struct list_head *b, void *dat
return cl->cmpfunc(ca, cb, cl->cmpfunc_data);
}
+/* for lines in the struct libscols_line->ln_children list */
+static int cells_cmp_wrapper_children(struct list_head *a, struct list_head *b, void *data)
+{
+ struct libscols_column *cl = (struct libscols_column *) data;
+ struct libscols_line *ra, *rb;
+ struct libscols_cell *ca, *cb;
+
+ assert(a);
+ assert(b);
+ assert(cl);
+
+ ra = list_entry(a, struct libscols_line, ln_children);
+ rb = list_entry(b, struct libscols_line, ln_children);
+ ca = scols_line_get_cell(ra, cl->seqnum);
+ cb = scols_line_get_cell(rb, cl->seqnum);
+
+ return cl->cmpfunc(ca, cb, cl->cmpfunc_data);
+}
+
+
+static int sort_line_children(struct libscols_line *ln, struct libscols_column *cl)
+{
+ struct list_head *p;
+
+ if (list_empty(&ln->ln_branch))
+ return 0;
+
+ list_for_each(p, &ln->ln_branch) {
+ struct libscols_line *chld =
+ list_entry(p, struct libscols_line, ln_children);
+ sort_line_children(chld, cl);
+ }
+
+ list_sort(&ln->ln_branch, cells_cmp_wrapper_children, cl);
+ return 0;
+}
+
/**
* scols_sort_table:
* @tb: table
* @cl: order by this column
*
- * Orders the table by the column. See also scols_column_set_cmpfunc().
+ * Orders the table by the column. See also scols_column_set_cmpfunc(). If the
+ * tree output is enabled then children in the tree are recursively sorted too.
*
* Returns: 0, a negative value in case of an error.
*/
int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl)
{
- assert(tb);
- assert(cl);
-
- if (!tb || !cl)
+ if (!tb || !cl || !cl->cmpfunc)
return -EINVAL;
DBG(TAB, ul_debugobj(tb, "sorting table"));
- list_sort(&tb->tb_lines, cells_cmp_wrapper, cl);
+ list_sort(&tb->tb_lines, cells_cmp_wrapper_lines, cl);
+
+ if (scols_table_is_tree(tb)) {
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0)
+ sort_line_children(ln, cl);
+ }
+
+ return 0;
+}
+
+static struct libscols_line *move_line_and_children(struct libscols_line *ln, struct libscols_line *pre)
+{
+ if (pre) {
+ list_del_init(&ln->ln_lines); /* remove from old position */
+ list_add(&ln->ln_lines, &pre->ln_lines); /* add to the new place (behind @pre) */
+ }
+ pre = ln;
+
+ if (!list_empty(&ln->ln_branch)) {
+ struct list_head *p;
+
+ list_for_each(p, &ln->ln_branch) {
+ struct libscols_line *chld =
+ list_entry(p, struct libscols_line, ln_children);
+ pre = move_line_and_children(chld, pre);
+ }
+ }
+
+ return pre;
+}
+
+/**
+ * scols_sort_table_by_tree:
+ * @tb: table
+ *
+ * Reorders lines in the table by parent->child relation. Note that order of
+ * the lines in the table is independent on the tree hierarchy.
+ *
+ * Since: 2.30
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_sort_table_by_tree(struct libscols_table *tb)
+{
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "sorting table by tree"));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (ln->parent)
+ continue;
+
+ move_line_and_children(ln, NULL);
+ }
+
+ return 0;
+}
+
+
+/**
+ * scols_table_set_termforce:
+ * @tb: table
+ * @force: SCOLS_TERMFORCE_{NEVER,ALWAYS,AUTO}
+ *
+ * Forces library to use stdout as terminal, non-terminal or use automatic
+ * detection (default).
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_table_set_termforce(struct libscols_table *tb, int force)
+{
+ if (!tb)
+ return -EINVAL;
+ tb->termforce = force;
+ return 0;
+}
+
+/**
+ * scols_table_get_termforce:
+ * @tb: table
+ *
+ * Returns: SCOLS_TERMFORCE_{NEVER,ALWAYS,AUTO} or a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_table_get_termforce(const struct libscols_table *tb)
+{
+ return tb->termforce;
+}
+
+/**
+ * scols_table_set_termwidth
+ * @tb: table
+ * @width: terminal width
+ *
+ * The library automatically detects terminal width or defaults to 80 chars if
+ * detections is unsuccessful. This function override this behaviour.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.29
+ */
+int scols_table_set_termwidth(struct libscols_table *tb, size_t width)
+{
+ DBG(TAB, ul_debugobj(tb, "set terminatl width: %zu", width));
+ tb->termwidth = width;
return 0;
}
+
+/**
+ * scols_table_get_termwidth
+ * @tb: table
+ *
+ * Returns: terminal width.
+ */
+size_t scols_table_get_termwidth(const struct libscols_table *tb)
+{
+ return tb->termwidth;
+}
+
+/**
+ * scols_table_set_termheight
+ * @tb: table
+ * @height: terminal height (number of lines)
+ *
+ * The library automatically detects terminal height or defaults to 24 lines if
+ * detections is unsuccessful. This function override this behaviour.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.31
+ */
+int scols_table_set_termheight(struct libscols_table *tb, size_t height)
+{
+ DBG(TAB, ul_debugobj(tb, "set terminatl height: %zu", height));
+ tb->termheight = height;
+ return 0;
+}
+
+/**
+ * scols_table_get_termheight
+ * @tb: table
+ *
+ * Returns: terminal height (number of lines).
+ *
+ * Since: 2.31
+ */
+size_t scols_table_get_termheight(const struct libscols_table *tb)
+{
+ return tb->termheight;
+}
diff --git a/libsmartcols/src/table_print.c b/libsmartcols/src/table_print.c
index c9f3d8f4b..10126fd79 100644
--- a/libsmartcols/src/table_print.c
+++ b/libsmartcols/src/table_print.c
@@ -2,6 +2,7 @@
* table.c - functions handling the data at the table level
*
* Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
@@ -10,7 +11,7 @@
/**
* SECTION: table_print
* @title: Table print
- * @short_description: table print API
+ * @short_description: output functions
*
* Table output API.
*/
@@ -21,13 +22,30 @@
#include <termios.h>
#include <ctype.h>
-#include "nls.h"
#include "mbsalign.h"
-#include "widechar.h"
-#include "ttyutils.h"
#include "carefulputc.h"
#include "smartcolsP.h"
+#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
+#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
+
+/* Fallback for symbols
+ *
+ * Note that by default library define all the symbols, but in case user does
+ * not define all symbols or if we extended the symbols struct then we need
+ * fallback to be more robust and backwardly compatible.
+ */
+#define titlepadding_symbol(tb) ((tb)->symbols->title_padding ? (tb)->symbols->title_padding : " ")
+#define branch_symbol(tb) ((tb)->symbols->branch ? (tb)->symbols->branch : "|-")
+#define vertical_symbol(tb) ((tb)->symbols->vert ? (tb)->symbols->vert : "| ")
+#define right_symbol(tb) ((tb)->symbols->right ? (tb)->symbols->right : "`-")
+
+#define cellpadding_symbol(tb) ((tb)->padding_debug ? "." : \
+ ((tb)->symbols->cell_padding ? (tb)->symbols->cell_padding: " "))
+
+#define want_repeat_header(tb) (!(tb)->header_repeat || (tb)->header_next <= (tb)->termlines_used)
+
+
/* This is private struct to work with output data */
struct libscols_buffer {
char *begin; /* begin of the buffer */
@@ -88,7 +106,6 @@ static int buffer_append_data(struct libscols_buffer *buf, const char *str)
if (maxsz <= sz)
return -EINVAL;
-
memcpy(buf->cur, str, sz + 1);
buf->cur += sz;
return 0;
@@ -100,7 +117,7 @@ static int buffer_set_data(struct libscols_buffer *buf, const char *str)
return rc ? rc : buffer_append_data(buf, str);
}
-/* save the current buffer possition to art_idx */
+/* save the current buffer position to art_idx */
static void buffer_set_art_index(struct libscols_buffer *buf)
{
if (buf) {
@@ -115,7 +132,10 @@ static char *buffer_get_data(struct libscols_buffer *buf)
}
/* encode data by mbs_safe_encode() to avoid control and non-printable chars */
-static char *buffer_get_safe_data(struct libscols_buffer *buf, size_t *cells)
+static char *buffer_get_safe_data(struct libscols_table *tb,
+ struct libscols_buffer *buf,
+ size_t *cells,
+ const char *safechars)
{
char *data = buffer_get_data(buf);
char *res = NULL;
@@ -129,7 +149,14 @@ static char *buffer_get_safe_data(struct libscols_buffer *buf, size_t *cells)
goto nothing;
}
- res = mbs_safe_encode_to_buffer(data, cells, buf->encdata);
+ if (tb->no_encode) {
+ *cells = mbs_safe_width(data);
+ strcpy(buf->encdata, data);
+ res = buf->encdata;
+ } else {
+ res = mbs_safe_encode_to_buffer(data, cells, buf->encdata, safechars);
+ }
+
if (!res || !*cells || *cells == (size_t) -1)
goto nothing;
return res;
@@ -151,11 +178,257 @@ static size_t buffer_get_safe_art_size(struct libscols_buffer *buf)
return bytes;
}
-#define is_last_column(_tb, _cl) \
- list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns)
+/* returns pointer to the end of used data */
+static int line_ascii_art_to_buffer(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_buffer *buf)
+{
+ const char *art;
+ int rc;
+
+ assert(ln);
+ assert(buf);
-#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
-#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
+ if (!ln->parent)
+ return 0;
+
+ rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
+ if (rc)
+ return rc;
+
+ if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
+ art = " ";
+ else
+ art = vertical_symbol(tb);
+
+ return buffer_append_data(buf, art);
+}
+
+static int is_last_column(struct libscols_column *cl)
+{
+ int rc = list_entry_is_last(&cl->cl_columns, &cl->table->tb_columns);
+ struct libscols_column *next;
+
+ if (rc)
+ return 1;
+
+ next = list_entry(cl->cl_columns.next, struct libscols_column, cl_columns);
+ if (next && scols_column_is_hidden(next) && is_last_column(next))
+ return 1;
+ return 0;
+}
+
+
+static int has_pending_data(struct libscols_table *tb)
+{
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
+ if (cl->pending_data)
+ return 1;
+ }
+ return 0;
+}
+
+/* print padding or ASCII-art instead of data of @cl */
+static void print_empty_cell(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ size_t bufsz)
+{
+ size_t len_pad = 0; /* in screen cells as opposed to bytes */
+
+ /* generate tree ASCII-art rather than padding */
+ if (ln && scols_column_is_tree(cl)) {
+ if (!ln->parent) {
+ /* only print symbols->vert if followed by child */
+ if (!list_empty(&ln->ln_branch)) {
+ fputs(vertical_symbol(tb), tb->out);
+ len_pad = mbs_safe_width(vertical_symbol(tb));
+ }
+ } else {
+ /* use the same draw function as though we were intending to draw an L-shape */
+ struct libscols_buffer *art = new_buffer(bufsz);
+ char *data;
+
+ if (art) {
+ /* whatever the rc, len_pad will be sensible */
+ line_ascii_art_to_buffer(tb, ln, art);
+ if (!list_empty(&ln->ln_branch) && has_pending_data(tb))
+ buffer_append_data(art, vertical_symbol(tb));
+ data = buffer_get_safe_data(tb, art, &len_pad, NULL);
+ if (data && len_pad)
+ fputs(data, tb->out);
+ free_buffer(art);
+ }
+ }
+ }
+
+ if (is_last_column(cl))
+ return;
+
+ /* fill rest of cell with space */
+ for(; len_pad < cl->width; ++len_pad)
+ fputs(cellpadding_symbol(tb), tb->out);
+
+ fputs(colsep(tb), tb->out);
+}
+
+
+static const char *get_cell_color(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ struct libscols_cell *ce) /* optional */
+{
+ const char *color = NULL;
+
+ if (tb && tb->colors_wanted) {
+ if (ce)
+ color = ce->color;
+ if (ln && !color)
+ color = ln->color;
+ if (!color)
+ color = cl->color;
+ }
+ return color;
+}
+
+/* Fill the start of a line with padding (or with tree ascii-art).
+ *
+ * This is necessary after a long non-truncated column, as this requires the
+ * next column to be printed on the next line. For example (see 'DDD'):
+ *
+ * aaa bbb ccc ddd eee
+ * AAA BBB CCCCCCC
+ * DDD EEE
+ * ^^^^^^^^^^^^
+ * new line padding
+ */
+static void print_newline_padding(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ size_t bufsz)
+{
+ size_t i;
+
+ assert(tb);
+ assert(cl);
+
+ fputs(linesep(tb), tb->out); /* line break */
+ tb->termlines_used++;
+
+ /* fill cells after line break */
+ for (i = 0; i <= (size_t) cl->seqnum; i++)
+ print_empty_cell(tb, scols_table_get_column(tb, i), ln, bufsz);
+}
+
+/*
+ * Pending data
+ *
+ * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
+ * printed as usually and output is truncated to match column width.
+ *
+ * The rest of the long text is printed on next extra line(s). The extra lines
+ * don't exist in the table (not represented by libscols_line). The data for
+ * the extra lines are stored in libscols_column->pending_data_buf and the
+ * function print_line() adds extra lines until the buffer is not empty in all
+ * columns.
+ */
+
+/* set data that will be printed by extra lines */
+static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz)
+{
+ char *p = NULL;
+
+ if (data && *data) {
+ DBG(COL, ul_debugobj(cl, "setting pending data"));
+ assert(sz);
+ p = strdup(data);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ free(cl->pending_data_buf);
+ cl->pending_data_buf = p;
+ cl->pending_data_sz = sz;
+ cl->pending_data = cl->pending_data_buf;
+ return 0;
+}
+
+/* the next extra line has been printed, move pending data cursor */
+static int step_pending_data(struct libscols_column *cl, size_t bytes)
+{
+ DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));
+
+ if (bytes >= cl->pending_data_sz)
+ return set_pending_data(cl, NULL, 0);
+
+ cl->pending_data += bytes;
+ cl->pending_data_sz -= bytes;
+ return 0;
+}
+
+/* print next pending data for the column @cl */
+static int print_pending_data(
+ struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ struct libscols_cell *ce)
+{
+ const char *color = get_cell_color(tb, cl, ln, ce);
+ size_t width = cl->width, bytes;
+ size_t len = width, i;
+ char *data;
+ char *nextchunk = NULL;
+
+ if (!cl->pending_data)
+ return 0;
+ if (!width)
+ return -EINVAL;
+
+ DBG(COL, ul_debugobj(cl, "printing pending data"));
+
+ data = strdup(cl->pending_data);
+ if (!data)
+ goto err;
+
+ if (scols_column_is_customwrap(cl)
+ && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
+ bytes = nextchunk - data;
+
+ len = mbs_safe_nwidth(data, bytes, NULL);
+ } else
+ bytes = mbs_truncate(data, &len);
+
+ if (bytes == (size_t) -1)
+ goto err;
+
+ if (bytes)
+ step_pending_data(cl, bytes);
+
+ if (color)
+ fputs(color, tb->out);
+ fputs(data, tb->out);
+ if (color)
+ fputs(UL_COLOR_RESET, tb->out);
+ free(data);
+
+ if (is_last_column(cl))
+ return 0;
+
+ for (i = len; i < width; i++)
+ fputs(cellpadding_symbol(tb), tb->out); /* padding */
+
+ fputs(colsep(tb), tb->out); /* columns separator */
+ return 0;
+err:
+ free(data);
+ return -errno;
+}
static int print_data(struct libscols_table *tb,
struct libscols_column *cl,
@@ -165,76 +438,120 @@ static int print_data(struct libscols_table *tb,
{
size_t len = 0, i, width, bytes;
const char *color = NULL;
- char *data;
+ char *data, *nextchunk;
+ int is_last;
assert(tb);
assert(cl);
- DBG(TAB, ul_debugobj(tb,
- " -> data, column=%p, line=%p, cell=%p, buff=%p",
- cl, ln, ce, buf));
-
data = buffer_get_data(buf);
if (!data)
data = "";
- /* raw mode */
- if (scols_table_is_raw(tb)) {
+ is_last = is_last_column(cl);
+
+ switch (tb->format) {
+ case SCOLS_FMT_RAW:
fputs_nonblank(data, tb->out);
- if (!is_last_column(tb, cl))
+ if (!is_last)
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))
+ if (!is_last)
fputs(colsep(tb), tb->out);
return 0;
- }
- if (tb->colors_wanted) {
- if (ce && !color)
- color = ce->color;
- if (ln && !color)
- color = ln->color;
- if (!color)
- color = cl->color;
+ case SCOLS_FMT_JSON:
+ fputs_quoted_json_lower(scols_cell_get_data(&cl->header), tb->out);
+ fputs(":", tb->out);
+ switch (cl->json_type) {
+ case SCOLS_JSON_STRING:
+ if (!*data)
+ fputs("null", tb->out);
+ else
+ fputs_quoted_json(data, tb->out);
+ break;
+ case SCOLS_JSON_NUMBER:
+ if (!*data)
+ fputs("null", tb->out);
+ else
+ fputs(data, tb->out);
+ break;
+ case SCOLS_JSON_BOOLEAN:
+ fputs(!*data ? "false" :
+ *data == '0' ? "false" :
+ *data == 'N' || *data == 'n' ? "false" : "true",
+ tb->out);
+ break;
+ }
+ if (!is_last)
+ fputs(", ", tb->out);
+ return 0;
+
+ case SCOLS_FMT_HUMAN:
+ break; /* continue below */
}
- /* encode, note that 'len' and 'width' are number of cells, not bytes */
- data = buffer_get_safe_data(buf, &len);
+ color = get_cell_color(tb, cl, ln, ce);
+
+ /* Encode. Note that 'len' and 'width' are number of cells, not bytes.
+ */
+ data = buffer_get_safe_data(tb, buf, &len, scols_column_get_safechars(cl));
if (!data)
data = "";
- width = cl->width;
bytes = strlen(data);
+ width = cl->width;
+
+ /* custom multi-line cell based */
+ if (*data && scols_column_is_customwrap(cl)
+ && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
+ set_pending_data(cl, nextchunk, bytes - (nextchunk - data));
+ bytes = nextchunk - data;
+ len = mbs_safe_nwidth(data, bytes, NULL);
+ }
- if (is_last_column(tb, cl) && len < width && !scols_table_is_maxout(tb))
+ if (is_last
+ && len < width
+ && !scols_table_is_maxout(tb)
+ && !scols_column_is_right(cl))
width = len;
/* truncate data */
if (len > width && scols_column_is_trunc(cl)) {
len = width;
bytes = mbs_truncate(data, &len); /* updates 'len' */
+ }
- if (!data || bytes == (size_t) -1) {
- bytes = len = 0;
- data = NULL;
- }
+ /* standard multi-line cell */
+ if (len > width && scols_column_is_wrap(cl)
+ && !scols_column_is_customwrap(cl)) {
+ set_pending_data(cl, data, bytes);
+
+ len = width;
+ bytes = mbs_truncate(data, &len);
+ if (bytes != (size_t) -1 && bytes > 0)
+ step_pending_data(cl, bytes);
+ }
+
+ if (bytes == (size_t) -1) {
+ bytes = len = 0;
+ data = NULL;
}
if (data) {
if (scols_column_is_right(cl)) {
- size_t xw = cl->width;
if (color)
fputs(color, tb->out);
- fprintf(tb->out, "%*s", (int) xw, data);
+ for (i = len; i < width; i++)
+ fputs(cellpadding_symbol(tb), tb->out);
+ fputs(data, tb->out);
if (color)
fputs(UL_COLOR_RESET, tb->out);
- if (len < xw)
- len = xw;
+ len = width;
+
} else if (color) {
char *p = data;
size_t art = buffer_get_safe_art_size(buf);
@@ -252,46 +569,17 @@ static int print_data(struct libscols_table *tb,
fputs(data, tb->out);
}
for (i = len; i < width; i++)
- fputs(" ", tb->out); /* padding */
-
- if (!is_last_column(tb, cl)) {
- if (len > width && !scols_column_is_trunc(cl)) {
- fputs(linesep(tb), tb->out);
- for (i = 0; i <= (size_t) cl->seqnum; i++) {
- struct libscols_column *x = scols_table_get_column(tb, i);
- fprintf(tb->out, "%*s ", -((int)x->width), " ");
- }
- } else
- fputs(colsep(tb), tb->out); /* columns separator */
- }
-
- return 0;
-}
-
-/* returns pointer to the end of used data */
-static int line_ascii_art_to_buffer(struct libscols_table *tb,
- struct libscols_line *ln,
- struct libscols_buffer *buf)
-{
- const char *art;
- int rc;
+ fputs(cellpadding_symbol(tb), tb->out); /* padding */
- assert(ln);
- assert(buf);
-
- if (!ln->parent)
+ if (is_last)
return 0;
- rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
- if (rc)
- return rc;
-
- if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
- art = " ";
+ if (len > width && !scols_column_is_trunc(cl))
+ print_newline_padding(tb, cl, ln, buf->bufsz); /* next column starts on next line */
else
- art = tb->symbols->vert;
+ fputs(colsep(tb), tb->out); /* columns separator */
- return buffer_append_data(buf, art);
+ return 0;
}
static int cell_to_buffer(struct libscols_table *tb,
@@ -322,13 +610,13 @@ 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))
- rc = buffer_append_data(buf, tb->symbols->right);
+ rc = buffer_append_data(buf, right_symbol(tb));
else if (!rc)
- rc = buffer_append_data(buf, tb->symbols->branch);
+ rc = buffer_append_data(buf, branch_symbol(tb));
if (!rc)
buffer_set_art_index(buf);
}
@@ -338,34 +626,252 @@ 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);
+ 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++;
+ tb->termlines_used++;
+}
+
+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, int last_in_table)
+{
+ tb->indent--;
+ if (scols_table_is_json(tb)) {
+ if (tb->indent_last_sep)
+ fput_indent(tb);
+ fputs(last ? "}" : "},", tb->out);
+ if (!tb->no_linesep)
+ fputs(linesep(tb), tb->out);
+
+ } else if (tb->no_linesep == 0 && last_in_table == 0) {
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
+
+ tb->indent_last_sep = 1;
+}
+
/*
- * Prints data, data maybe be printed in more formats (raw, NAME=xxx pairs) and
- * control and non-printable chars maybe encoded in \x?? hex encoding.
+ * 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.
*/
static int print_line(struct libscols_table *tb,
struct libscols_line *ln,
struct libscols_buffer *buf)
{
- int rc = 0;
+ int rc = 0, pending = 0;
struct libscols_column *cl;
struct libscols_iter itr;
assert(ln);
- DBG(TAB, ul_debugobj(tb, "printing line, line=%p, buff=%p", ln, buf));
+ DBG(TAB, ul_debugobj(tb, "printing line"));
+ /* regular line */
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
rc = cell_to_buffer(tb, ln, cl, buf);
- if (!rc)
+ if (rc == 0)
rc = print_data(tb, cl, ln,
scols_line_get_cell(ln, cl->seqnum),
buf);
+ if (rc == 0 && cl->pending_data)
+ pending = 1;
+ }
+
+ /* extra lines of the multi-line cells */
+ while (rc == 0 && pending) {
+ pending = 0;
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
+ if (cl->pending_data) {
+ rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum));
+ if (rc == 0 && cl->pending_data)
+ pending = 1;
+ } else
+ print_empty_cell(tb, cl, ln, buf->bufsz);
+ }
}
- if (rc == 0)
- fputs(linesep(tb), tb->out);
- return 0;
+ return 0;
+}
+
+static int print_title(struct libscols_table *tb)
+{
+ int rc, color = 0;
+ mbs_align_t align;
+ size_t width, len = 0, bufsz, titlesz;
+ char *title = NULL, *buf = NULL;
+
+ assert(tb);
+
+ if (!tb->title.data)
+ return 0;
+
+ DBG(TAB, ul_debugobj(tb, "printing title"));
+
+ /* encode data */
+ if (tb->no_encode) {
+ len = bufsz = strlen(tb->title.data) + 1;
+ buf = strdup(tb->title.data);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ } else {
+ bufsz = mbs_safe_encode_size(strlen(tb->title.data)) + 1;
+ if (bufsz == 1) {
+ DBG(TAB, ul_debugobj(tb, "title is empty string -- ignore"));
+ return 0;
+ }
+ buf = malloc(bufsz);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ if (!mbs_safe_encode_to_buffer(tb->title.data, &len, buf, NULL) ||
+ !len || len == (size_t) -1) {
+ rc = -EINVAL;
+ goto done;
+ }
+ }
+
+ /* truncate and align */
+ width = tb->is_term ? tb->termwidth : 80;
+ titlesz = width + bufsz;
+
+ title = malloc(titlesz);
+ if (!title) {
+ rc = -EINVAL;
+ goto done;
+ }
+
+ switch (scols_cell_get_alignment(&tb->title)) {
+ case SCOLS_CELL_FL_RIGHT:
+ align = MBS_ALIGN_RIGHT;
+ break;
+ case SCOLS_CELL_FL_CENTER:
+ align = MBS_ALIGN_CENTER;
+ break;
+ case SCOLS_CELL_FL_LEFT:
+ default:
+ align = MBS_ALIGN_LEFT;
+ /*
+ * Don't print extra blank chars after the title if on left
+ * (that's same as we use for the last column in the table).
+ */
+ if (len < width
+ && !scols_table_is_maxout(tb)
+ && isblank(*titlepadding_symbol(tb)))
+ width = len;
+ break;
+
+ }
+
+ /* copy from buf to title and align to width with title_padding */
+ rc = mbsalign_with_padding(buf, title, titlesz,
+ &width, align,
+ 0, (int) *titlepadding_symbol(tb));
+
+ if (rc == -1) {
+ rc = -EINVAL;
+ goto done;
+ }
+
+ if (tb->colors_wanted && tb->title.color)
+ color = 1;
+ if (color)
+ fputs(tb->title.color, tb->out);
+
+ fputs(title, tb->out);
+
+ if (color)
+ fputs(UL_COLOR_RESET, tb->out);
+
+ fputc('\n', tb->out);
+ rc = 0;
+done:
+ free(buf);
+ free(title);
+ DBG(TAB, ul_debugobj(tb, "printing title done [rc=%d]", rc));
+ return rc;
}
static int print_header(struct libscols_table *tb, struct libscols_buffer *buf)
@@ -376,86 +882,139 @@ static int print_header(struct libscols_table *tb, struct libscols_buffer *buf)
assert(tb);
- if (scols_table_is_noheadings(tb) ||
+ if ((tb->header_printed == 1 && tb->header_repeat == 0) ||
+ scols_table_is_noheadings(tb) ||
scols_table_is_export(tb) ||
+ scols_table_is_json(tb) ||
list_empty(&tb->tb_lines))
return 0;
DBG(TAB, ul_debugobj(tb, "printing header"));
- /* set width according to the size of data
- */
+ /* set the width according to the size of the data */
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
rc = buffer_set_data(buf, scols_cell_get_data(&cl->header));
if (!rc)
rc = print_data(tb, cl, NULL, &cl->header, buf);
}
- if (rc == 0)
+ if (rc == 0) {
fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
+
+ tb->header_printed = 1;
+ tb->header_next = tb->termlines_used + tb->termheight;
+ if (tb->header_repeat)
+ DBG(TAB, ul_debugobj(tb, "\tnext header: %zu [current=%zu]",
+ tb->header_next, tb->termlines_used));
return rc;
}
-static int print_table(struct libscols_table *tb, struct libscols_buffer *buf)
+
+static int print_range( struct libscols_table *tb,
+ struct libscols_buffer *buf,
+ struct libscols_iter *itr,
+ struct libscols_line *end)
{
- int rc;
+ int rc = 0;
struct libscols_line *ln;
- struct libscols_iter itr;
assert(tb);
+ DBG(TAB, ul_debugobj(tb, "printing range"));
- rc = print_header(tb, buf);
+ while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) {
- scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
- while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0)
+ int last = scols_iter_is_last(itr);
+
+ fput_line_open(tb);
rc = print_line(tb, ln, buf);
+ fput_line_close(tb, last, last);
+
+ if (end && ln == end)
+ break;
+
+ if (!last && want_repeat_header(tb))
+ print_header(tb, buf);
+ }
return rc;
+
+}
+
+static int print_table(struct libscols_table *tb, struct libscols_buffer *buf)
+{
+ struct libscols_iter itr;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ return print_range(tb, buf, &itr, NULL);
}
+
static int print_tree_line(struct libscols_table *tb,
struct libscols_line *ln,
- struct libscols_buffer *buf)
+ struct libscols_buffer *buf,
+ int last,
+ int last_in_table)
{
int rc;
- struct list_head *p;
+ /* print the line */
+ fput_line_open(tb);
rc = print_line(tb, ln, buf);
if (rc)
- return rc;
- if (list_empty(&ln->ln_branch))
- return 0;
+ goto done;
- /* 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);
- if (rc)
- break;
+ /* print children */
+ if (!list_empty(&ln->ln_branch)) {
+ struct list_head *p;
+
+ 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);
+ int last_child = p->next == &ln->ln_branch;
+
+ rc = print_tree_line(tb, chld, buf, last_child, last_in_table && last_child);
+ if (rc)
+ goto done;
+ }
+
+ fput_children_close(tb);
}
+ if (list_empty(&ln->ln_branch) || scols_table_is_json(tb))
+ fput_line_close(tb, last, last_in_table);
+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, ln == last);
}
return rc;
@@ -463,9 +1022,14 @@ static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
{
+ if (scols_column_is_hidden(cl)) {
+ DBG(COL, ul_debugobj(cl, "%s (hidden) ignored", cl->header.data));
+ return;
+ }
+
DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
"hint=%d, avg=%zu, max=%zu, min=%zu, "
- "extreme=%s",
+ "extreme=%s %s",
cl->header.data, cl->seqnum, cl->width,
cl->width_hint > 1 ? (int) cl->width_hint :
@@ -473,7 +1037,8 @@ static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
cl->width_avg,
cl->width_max,
cl->width_min,
- cl->is_extreme ? "yes" : "not"));
+ cl->is_extreme ? "yes" : "not",
+ cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
}
static void dbg_columns(struct libscols_table *tb)
@@ -486,14 +1051,15 @@ static void dbg_columns(struct libscols_table *tb)
dbg_column(tb, cl);
}
+
/*
* This function counts column width.
*
- * For the SCOLS_FL_NOEXTREMES columns is possible to call this function two
- * times. The first pass counts width and average width. If the column
- * contains too large fields (width greater than 2 * average) then the column
- * is marked as "extreme". In the second pass all extreme fields are ignored
- * and column width is counted from non-extreme fields only.
+ * For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
+ * two times. The first pass counts the width and average width. If the column
+ * contains fields that are too large (a width greater than 2 * average) then
+ * the column is marked as "extreme". In the second pass all extreme fields
+ * are ignored and the column width is counted from non-extreme fields only.
*/
static int count_column_width(struct libscols_table *tb,
struct libscols_column *cl,
@@ -508,6 +1074,19 @@ static int count_column_width(struct libscols_table *tb,
assert(cl);
cl->width = 0;
+ if (!cl->width_min) {
+ if (cl->width_hint < 1 && scols_table_is_maxout(tb) && tb->is_term) {
+ cl->width_min = (size_t) (cl->width_hint * tb->termwidth);
+ if (cl->width_min && !is_last_column(cl))
+ cl->width_min--;
+ }
+ if (scols_cell_get_data(&cl->header)) {
+ size_t len = mbs_safe_width(scols_cell_get_data(&cl->header));
+ cl->width_min = max(cl->width_min, len);
+ }
+ if (!cl->width_min)
+ cl->width_min = 1;
+ }
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (scols_table_next_line(tb, &itr, &ln) == 0) {
@@ -516,15 +1095,20 @@ static int count_column_width(struct libscols_table *tb,
rc = cell_to_buffer(tb, ln, cl, buf);
if (rc)
- return rc;
+ goto done;
data = buffer_get_data(buf);
- len = data ? mbs_safe_width(data) : 0;
+
+ if (!data)
+ len = 0;
+ else if (scols_column_is_customwrap(cl))
+ len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data);
+ else
+ len = mbs_safe_width(data);
if (len == (size_t) -1) /* ignore broken multibyte strings */
len = 0;
- if (len > cl->width_max)
- cl->width_max = len;
+ cl->width_max = max(len, cl->width_max);
if (cl->is_extreme && len > cl->width_avg * 2)
continue;
@@ -532,81 +1116,105 @@ static int count_column_width(struct libscols_table *tb,
sum += len;
count++;
}
- if (len > cl->width)
- cl->width = len;
+ cl->width = max(len, cl->width);
+ if (scols_column_is_tree(cl)) {
+ size_t treewidth = buffer_get_safe_art_size(buf);
+ cl->width_treeart = max(cl->width_treeart, treewidth);
+ }
}
if (count && cl->width_avg == 0) {
cl->width_avg = sum / count;
-
if (cl->width_max > cl->width_avg * 2)
cl->is_extreme = 1;
}
- /* check and set minimal column width */
- if (scols_cell_get_data(&cl->header))
- cl->width_min = mbs_safe_width(scols_cell_get_data(&cl->header));
-
/* enlarge to minimal width */
if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
cl->width = cl->width_min;
- /* use relative size for large columns */
+ /* use absolute size for large columns */
else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint
&& cl->width_min < (size_t) cl->width_hint)
cl->width = (size_t) cl->width_hint;
+done:
ON_DBG(COL, dbg_column(tb, cl));
return rc;
}
-
/*
- * This is core of the scols_* voodo...
+ * This is core of the scols_* voodoo...
*/
static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf)
{
struct libscols_column *cl;
struct libscols_iter itr;
- size_t width = 0; /* output width */
- int trunc_only, rc = 0;
+ size_t width = 0, width_min = 0; /* output width */
+ int stage, rc = 0;
int extremes = 0;
+ size_t colsepsz;
DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth));
+ colsepsz = mbs_safe_width(colsep(tb));
+
/* set basic columns width
*/
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ int is_last;
+
+ if (scols_column_is_hidden(cl))
+ continue;
rc = count_column_width(tb, cl, buf);
if (rc)
- return rc;
+ goto done;
+
+ is_last = is_last_column(cl);
- width += cl->width + (is_last_column(tb, cl) ? 0 : 1);
+ width += cl->width + (is_last ? 0 : colsepsz); /* separator for non-last column */
+ width_min += cl->width_min + (is_last ? 0 : colsepsz);
extremes += cl->is_extreme;
}
- if (!tb->is_term)
- return 0;
+ if (!tb->is_term) {
+ DBG(TAB, ul_debugobj(tb, " non-terminal output"));
+ goto done;
+ }
- /* reduce columns with extreme fields
- */
+ /* be paranoid */
+ if (width_min > tb->termwidth && scols_table_is_maxout(tb)) {
+ DBG(TAB, ul_debugobj(tb, " min width larger than terminal! [width=%zu, term=%zu]", width_min, tb->termwidth));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (width_min > tb->termwidth
+ && scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
+ width_min--;
+ cl->width_min--;
+ }
+ DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min));
+ }
+
+ /* reduce columns with extreme fields */
if (width > tb->termwidth && extremes) {
- DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
+ DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (scols_table_next_column(tb, &itr, &cl) == 0) {
size_t org_width;
- if (!cl->is_extreme)
+ if (!cl->is_extreme || scols_column_is_hidden(cl))
continue;
org_width = cl->width;
rc = count_column_width(tb, cl, buf);
if (rc)
- return rc;
+ goto done;
if (org_width > cl->width)
width -= org_width - cl->width;
@@ -617,17 +1225,17 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf
if (width < tb->termwidth) {
if (extremes) {
- DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
+ DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
/* enlarge the first extreme column */
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (scols_table_next_column(tb, &itr, &cl) == 0) {
size_t add;
- if (!cl->is_extreme)
+ if (!cl->is_extreme || scols_column_is_hidden(cl))
continue;
- /* this column is tooo large, ignore?
+ /* this column is too large, ignore?
if (cl->width_max - cl->width >
(tb->termwidth - width))
continue;
@@ -646,12 +1254,14 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf
}
if (width < tb->termwidth && scols_table_is_maxout(tb)) {
- DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
+ DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
- /* try enlarge all columns */
+ /* try enlarging all columns */
while (width < tb->termwidth) {
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
cl->width++;
width++;
if (width == tb->termwidth)
@@ -660,67 +1270,133 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf
}
} else if (width < tb->termwidth) {
/* enlarge the last column */
- struct libscols_column *cl = list_entry(
+ struct libscols_column *col = list_entry(
tb->tb_columns.prev, struct libscols_column, cl_columns);
- DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
+ DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
- if (!scols_column_is_right(cl) && tb->termwidth - width > 0) {
- cl->width += tb->termwidth - width;
+ if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
+ col->width += tb->termwidth - width;
width = tb->termwidth;
}
}
}
- /* bad, we have to reduce output width, this is done in two steps:
- * 1/ reduce columns with a relative width and with truncate flag
- * 2) reduce columns with a relative width without truncate flag
+ /* bad, we have to reduce output width, this is done in three stages:
+ *
+ * 1) trunc relative with trunc flag if the column width is greater than
+ * expected column width (it means "width_hint * terminal_width").
+ *
+ * 2) trunc all with trunc flag
+ *
+ * 3) trunc relative without trunc flag
+ *
+ * Note that SCOLS_FL_WRAP (if no custom wrap function is specified) is
+ * interpreted as SCOLS_FL_TRUNC.
*/
- trunc_only = 1;
- while (width > tb->termwidth) {
- size_t org = width;
+ for (stage = 1; width > tb->termwidth && stage <= 3; ) {
+ size_t org_width = width;
- DBG(TAB, ul_debugobj(tb, " reduce width (current=%zu, "
- "wanted=%zu, mode=%s)",
- width, tb->termwidth,
- trunc_only ? "trunc-only" : "all-relative"));
+ DBG(TAB, ul_debugobj(tb, " reduce width - #%d stage (current=%zu, wanted=%zu)",
+ stage, width, tb->termwidth));
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (scols_table_next_column(tb, &itr, &cl) == 0) {
+
+ int trunc_flag = 0;
+
+ DBG(TAB, ul_debugobj(cl, " checking %s (width=%zu, treeart=%zu)",
+ cl->header.data, cl->width, cl->width_treeart));
+ if (scols_column_is_hidden(cl))
+ continue;
if (width <= tb->termwidth)
break;
- if (cl->width_hint > 1 && !scols_column_is_trunc(cl))
- continue; /* never truncate columns with absolute sizes */
- if (scols_column_is_tree(cl))
- continue; /* never truncate the tree */
- if (trunc_only && !scols_column_is_trunc(cl))
- continue;
+
+ /* never truncate if already minimal width */
if (cl->width == cl->width_min)
continue;
- /* truncate column with relative sizes */
- if (cl->width_hint < 1 && cl->width > 0 && width > 0 &&
- cl->width > cl->width_hint * tb->termwidth) {
+ /* never truncate the tree */
+ if (scols_column_is_tree(cl) && width <= cl->width_treeart)
+ continue;
+
+ /* nothing to truncate */
+ if (cl->width == 0 || width == 0)
+ continue;
+
+ trunc_flag = scols_column_is_trunc(cl)
+ || (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl));
+
+ switch (stage) {
+ /* #1 stage - trunc relative with TRUNC flag */
+ case 1:
+ if (!trunc_flag) /* ignore: missing flag */
+ break;
+ if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
+ break;
+ if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) /* ignore: smaller than expected width */
+ break;
+
+ DBG(TAB, ul_debugobj(tb, " reducing (relative with flag)"));
cl->width--;
width--;
- }
- /* truncate column with absolute size */
- if (cl->width_hint > 1 && cl->width > 0 && width > 0 &&
- !trunc_only) {
+ break;
+
+ /* #2 stage - trunc all with TRUNC flag */
+ case 2:
+ if (!trunc_flag) /* ignore: missing flag */
+ break;
+
+ DBG(TAB, ul_debugobj(tb, " reducing (all with flag)"));
cl->width--;
width--;
+ break;
+
+ /* #3 stage - trunc relative without flag */
+ case 3:
+ if (cl->width_hint <= 0 || cl->width_hint >= 1) /* ignore: no relative */
+ break;
+
+ DBG(TAB, ul_debugobj(tb, " reducing (relative without flag)"));
+ cl->width--;
+ width--;
+ break;
}
+ /* hide zero width columns */
+ if (cl->width == 0)
+ cl->flags |= SCOLS_FL_HIDDEN;
}
- if (org == width) {
- if (trunc_only)
- trunc_only = 0;
- else
+
+ /* the current stage is without effect, go to the next */
+ if (org_width == width)
+ stage++;
+ }
+
+ /* ignore last column(s) or force last column to be truncated if
+ * nowrap mode enabled */
+ if (tb->no_wrap && width > tb->termwidth) {
+ scols_reset_iter(&itr, SCOLS_ITER_BACKWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+
+ if (scols_column_is_hidden(cl))
+ continue;
+ if (width <= tb->termwidth)
break;
+ if (width - cl->width < tb->termwidth) {
+ size_t r = width - tb->termwidth;
+
+ cl->flags |= SCOLS_FL_TRUNC;
+ cl->width -= r;
+ width -= r;
+ } else {
+ cl->flags |= SCOLS_FL_HIDDEN;
+ width -= cl->width + colsepsz;
+ }
}
}
-
- DBG(TAB, ul_debugobj(tb, " result: %zu", width));
+done:
+ DBG(TAB, ul_debugobj(tb, " final width: %zu (rc=%d)", width, rc));
ON_DBG(TAB, dbg_columns(tb));
return rc;
@@ -742,64 +1418,281 @@ static size_t strlen_line(struct libscols_line *ln)
return sz;
}
+static void cleanup_printing(struct libscols_table *tb, struct libscols_buffer *buf)
+{
+ if (!tb)
+ return;
+ free_buffer(buf);
-/**
- * scols_print_table:
- * @tb: table
- *
- * Prints the table to the output stream.
- *
- * Returns: 0, a negative value in case of an error.
- */
-int scols_print_table(struct libscols_table *tb)
+ if (tb->priv_symbols) {
+ scols_table_set_symbols(tb, NULL);
+ tb->priv_symbols = 0;
+ }
+}
+
+static int initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf)
{
- int rc = 0;
- size_t bufsz;
+ size_t bufsz, extra_bufsz = 0;
struct libscols_line *ln;
struct libscols_iter itr;
- struct libscols_buffer *buf;
+ int rc;
- assert(tb);
- if (!tb)
- return -1;
+ DBG(TAB, ul_debugobj(tb, "initialize printing"));
+ *buf = NULL;
- DBG(TAB, ul_debugobj(tb, "printing"));
- if (!tb->symbols)
- scols_table_set_symbols(tb, NULL); /* use default */
+ if (!tb->symbols) {
+ rc = scols_table_set_default_symbols(tb);
+ if (rc)
+ goto err;
+ tb->priv_symbols = 1;
+ } else
+ tb->priv_symbols = 0;
+
+ if (tb->format == SCOLS_FMT_HUMAN)
+ tb->is_term = tb->termforce == SCOLS_TERMFORCE_NEVER ? 0 :
+ tb->termforce == SCOLS_TERMFORCE_ALWAYS ? 1 :
+ isatty(STDOUT_FILENO);
+
+ if (tb->is_term) {
+ size_t width = (size_t) scols_table_get_termwidth(tb);
+
+ if (tb->termreduce > 0 && tb->termreduce < width) {
+ width -= tb->termreduce;
+ scols_table_set_termwidth(tb, width);
+ }
+ bufsz = width;
+ } else
+ bufsz = BUFSIZ;
- tb->is_term = isatty(STDOUT_FILENO) ? 1 : 0;
- tb->termwidth = tb->is_term ? get_terminal_width() : 0;
- if (tb->termwidth <= 0)
- tb->termwidth = 80;
- tb->termwidth -= tb->termreduce;
+ if (!tb->is_term || tb->format != SCOLS_FMT_HUMAN || scols_table_is_tree(tb))
+ tb->header_repeat = 0;
- bufsz = tb->termwidth;
+ /*
+ * Estimate extra space necessary for tree, JSON or another output
+ * decoration.
+ */
+ if (scols_table_is_tree(tb))
+ extra_bufsz += tb->nlines * strlen(vertical_symbol(tb));
+
+ switch (tb->format) {
+ case SCOLS_FMT_RAW:
+ extra_bufsz += tb->ncols; /* separator between columns */
+ break;
+ case SCOLS_FMT_JSON:
+ if (tb->format == SCOLS_FMT_JSON)
+ extra_bufsz += tb->nlines * 3; /* indention */
+ /* fallthrough */
+ case SCOLS_FMT_EXPORT:
+ {
+ struct libscols_column *cl;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (scols_column_is_hidden(cl))
+ continue;
+ extra_bufsz += strlen(scols_cell_get_data(&cl->header)); /* data */
+ extra_bufsz += 2; /* separators */
+ }
+ break;
+ }
+ case SCOLS_FMT_HUMAN:
+ break;
+ }
+ /*
+ * Enlarge buffer if necessary, the buffer should be large enough to
+ * store line data and tree ascii art (or another decoration).
+ */
scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
while (scols_table_next_line(tb, &itr, &ln) == 0) {
- size_t sz = strlen_line(ln);
+ size_t sz;
+
+ sz = strlen_line(ln) + extra_bufsz;
if (sz > bufsz)
bufsz = sz;
}
- buf = new_buffer(bufsz + 1); /* data + space for \0 */
- if (!buf)
- return -ENOMEM;
+ *buf = new_buffer(bufsz + 1); /* data + space for \0 */
+ if (!*buf) {
+ rc = -ENOMEM;
+ goto err;
+ }
- if (!(scols_table_is_raw(tb) || scols_table_is_export(tb))) {
- rc = recount_widths(tb, buf);
+ if (tb->format == SCOLS_FMT_HUMAN) {
+ rc = recount_widths(tb, *buf);
if (rc != 0)
+ goto err;
+ }
+
+ return 0;
+err:
+ cleanup_printing(tb, *buf);
+ return rc;
+}
+
+/**
+ * scola_table_print_range:
+ * @tb: table
+ * @start: first printed line or NULL to print from the begin of the table
+ * @end: last printed line or NULL to print all from start.
+ *
+ * If the start is the first line in the table than prints table header too.
+ * The header is printed only once. This does not work for trees.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_print_range( struct libscols_table *tb,
+ struct libscols_line *start,
+ struct libscols_line *end)
+{
+ struct libscols_buffer *buf = NULL;
+ struct libscols_iter itr;
+ int rc;
+
+ if (scols_table_is_tree(tb))
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing range from API"));
+
+ rc = initialize_printing(tb, &buf);
+ if (rc)
+ return rc;
+
+ if (start) {
+ itr.direction = SCOLS_ITER_FORWARD;
+ itr.head = &tb->tb_lines;
+ itr.p = &start->ln_lines;
+ } else
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
+ if (!start || itr.p == tb->tb_lines.next) {
+ rc = print_header(tb, buf);
+ if (rc)
goto done;
}
+ rc = print_range(tb, buf, &itr, end);
+done:
+ cleanup_printing(tb, buf);
+ return rc;
+}
+
+/**
+ * scols_table_print_range_to_string:
+ * @tb: table
+ * @start: first printed line or NULL to print from the beginning of the table
+ * @end: last printed line or NULL to print all from start.
+ * @data: pointer to the beginning of a memory area to print to
+ *
+ * The same as scols_table_print_range(), but prints to @data instead of
+ * stream.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+#ifdef HAVE_OPEN_MEMSTREAM
+int scols_table_print_range_to_string( struct libscols_table *tb,
+ struct libscols_line *start,
+ struct libscols_line *end,
+ char **data)
+{
+ FILE *stream, *old_stream;
+ size_t sz;
+ int rc;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing range to string"));
+
+ /* create a stream for output */
+ stream = open_memstream(data, &sz);
+ if (!stream)
+ return -ENOMEM;
+
+ old_stream = scols_table_get_stream(tb);
+ scols_table_set_stream(tb, stream);
+ rc = scols_table_print_range(tb, start, end);
+ fclose(stream);
+ scols_table_set_stream(tb, old_stream);
+
+ return rc;
+}
+#else
+int scols_table_print_range_to_string(
+ struct libscols_table *tb __attribute__((__unused__)),
+ struct libscols_line *start __attribute__((__unused__)),
+ struct libscols_line *end __attribute__((__unused__)),
+ char **data __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+#endif
+
+static int __scols_print_table(struct libscols_table *tb, int *is_empty)
+{
+ int rc = 0;
+ struct libscols_buffer *buf = NULL;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing"));
+ if (is_empty)
+ *is_empty = 0;
+
+ if (list_empty(&tb->tb_columns)) {
+ DBG(TAB, ul_debugobj(tb, "error -- no columns"));
+ return -EINVAL;
+ }
+ if (list_empty(&tb->tb_lines)) {
+ DBG(TAB, ul_debugobj(tb, "ignore -- no lines"));
+ if (is_empty)
+ *is_empty = 1;
+ return 0;
+ }
+
+ tb->header_printed = 0;
+ rc = initialize_printing(tb, &buf);
+ if (rc)
+ return rc;
+
+ fput_table_open(tb);
+
+ if (tb->format == SCOLS_FMT_HUMAN)
+ print_title(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);
+ cleanup_printing(tb, buf);
+ return rc;
+}
+
+/**
+ * scols_print_table:
+ * @tb: table
+ *
+ * Prints the table to the output stream and terminate by \n.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_print_table(struct libscols_table *tb)
+{
+ int empty = 0;
+ int rc = __scols_print_table(tb, &empty);
+
+ if (rc == 0 && !empty)
+ fputc('\n', tb->out);
return rc;
}
@@ -812,10 +1705,10 @@ done:
*
* Returns: 0, a negative value in case of an error.
*/
+#ifdef HAVE_OPEN_MEMSTREAM
int scols_print_table_to_string(struct libscols_table *tb, char **data)
{
-#ifdef HAVE_OPEN_MEMSTREAM
- FILE *stream;
+ FILE *stream, *old_stream;
size_t sz;
int rc;
@@ -829,13 +1722,20 @@ int scols_print_table_to_string(struct libscols_table *tb, char **data)
if (!stream)
return -ENOMEM;
+ old_stream = scols_table_get_stream(tb);
scols_table_set_stream(tb, stream);
- rc = scols_print_table(tb);
+ rc = __scols_print_table(tb, NULL);
fclose(stream);
+ scols_table_set_stream(tb, old_stream);
return rc;
+}
#else
+int scols_print_table_to_string(
+ struct libscols_table *tb __attribute__((__unused__)),
+ char **data __attribute__((__unused__)))
+{
return -ENOSYS;
-#endif
}
+#endif
--
2.14.4