summaryrefslogtreecommitdiffstats
path: root/libsmartcols
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 14:30:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 14:30:35 +0000
commit378c18e5f024ac5a8aef4cb40d7c9aa9633d144c (patch)
tree44dfb6ca500d32cabd450649b322a42e70a30683 /libsmartcols
parentInitial commit. (diff)
downloadutil-linux-upstream.tar.xz
util-linux-upstream.zip
Adding upstream version 2.38.1.upstream/2.38.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--libsmartcols/COPYING8
-rw-r--r--libsmartcols/Makemodule.am15
-rw-r--r--libsmartcols/docs/Makefile.am93
-rw-r--r--libsmartcols/docs/Makefile.in894
-rw-r--r--libsmartcols/docs/libsmartcols-docs.xml85
-rw-r--r--libsmartcols/docs/libsmartcols-sections.txt213
-rw-r--r--libsmartcols/docs/version.xml1
-rw-r--r--libsmartcols/docs/version.xml.in1
-rw-r--r--libsmartcols/meson.build53
-rw-r--r--libsmartcols/samples/Makemodule.am53
-rw-r--r--libsmartcols/samples/colors.c125
-rw-r--r--libsmartcols/samples/continuous.c140
-rw-r--r--libsmartcols/samples/fromfile.c347
-rw-r--r--libsmartcols/samples/grouping-overlay.c143
-rw-r--r--libsmartcols/samples/grouping-simple.c155
-rw-r--r--libsmartcols/samples/maxout.c61
-rw-r--r--libsmartcols/samples/title.c135
-rw-r--r--libsmartcols/samples/tree.c269
-rw-r--r--libsmartcols/samples/wrap.c111
-rw-r--r--libsmartcols/smartcols.pc.in10
-rw-r--r--libsmartcols/src/Makemodule.am62
-rw-r--r--libsmartcols/src/calculate.c454
-rw-r--r--libsmartcols/src/cell.c257
-rw-r--r--libsmartcols/src/column.c643
-rw-r--r--libsmartcols/src/grouping.c575
-rw-r--r--libsmartcols/src/init.c62
-rw-r--r--libsmartcols/src/iter.c74
-rw-r--r--libsmartcols/src/libsmartcols.h.in346
-rw-r--r--libsmartcols/src/libsmartcols.sym211
-rw-r--r--libsmartcols/src/line.c560
-rw-r--r--libsmartcols/src/print-api.c220
-rw-r--r--libsmartcols/src/print.c1243
-rw-r--r--libsmartcols/src/smartcolsP.h449
-rw-r--r--libsmartcols/src/symbols.c293
-rw-r--r--libsmartcols/src/table.c1754
-rw-r--r--libsmartcols/src/version.c62
-rw-r--r--libsmartcols/src/walk.c152
37 files changed, 10329 insertions, 0 deletions
diff --git a/libsmartcols/COPYING b/libsmartcols/COPYING
new file mode 100644
index 0000000..1c4252a
--- /dev/null
+++ b/libsmartcols/COPYING
@@ -0,0 +1,8 @@
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later
+version.
+
+The complete text of the license is available in the
+../Documentation/licenses/COPYING.LGPL-2.1-or-later file.
diff --git a/libsmartcols/Makemodule.am b/libsmartcols/Makemodule.am
new file mode 100644
index 0000000..012848b
--- /dev/null
+++ b/libsmartcols/Makemodule.am
@@ -0,0 +1,15 @@
+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
+
+pkgconfig_DATA += libsmartcols/smartcols.pc
+PATHFILES += libsmartcols/smartcols.pc
+EXTRA_DIST += libsmartcols/COPYING
+
+endif # BUILD_LIBSMARTCOLS
diff --git a/libsmartcols/docs/Makefile.am b/libsmartcols/docs/Makefile.am
new file mode 100644
index 0000000..e8a7600
--- /dev/null
+++ b/libsmartcols/docs/Makefile.am
@@ -0,0 +1,93 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.10 at least.
+AUTOMAKE_OPTIONS = 1.10
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=libsmartcols
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=../src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+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 scols
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_builddir)/libsmartcols/src/libsmartcols.h
+CFILE_GLOB=$(top_srcdir)/libsmartcols/src/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES=
+
+# Header files to ignore when scanning. Use base file name, no paths
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES=smartcolsP.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = $(builddir)/version.xml
+
+# 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=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS=
+GTKDOC_LIBS=
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/config/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES += version.xml
diff --git a/libsmartcols/docs/Makefile.in b/libsmartcols/docs/Makefile.in
new file mode 100644
index 0000000..06d601f
--- /dev/null
+++ b/libsmartcols/docs/Makefile.in
@@ -0,0 +1,894 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# WARNING: this is not gtk-doc.make file from gtk-doc project. This
+# file has been modified to match with util-linux requirements:
+#
+# * install files to $datadir
+# * don't maintain generated files in git repository
+# * don't distribute the final html files
+# * don't require --enable-gtk-doc for "make dist"
+# * support out-of-tree build ($srcdir != $builddir)
+#
+# -- kzak, Nov 2009
+#
+
+####################################
+# Everything below here is generic #
+####################################
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = libsmartcols/docs
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_vscript.m4 \
+ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/tls.m4 \
+ $(top_srcdir)/m4/ul.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = version.xml
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/version.xml.in \
+ $(top_srcdir)/config/gtk-doc.make
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ADJTIME_PATH = @ADJTIME_PATH@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASAN_LDFLAGS = @ASAN_LDFLAGS@
+ASCIIDOCTOR = @ASCIIDOCTOR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BSD_WARN_CFLAGS = @BSD_WARN_CFLAGS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTSETUP_CFLAGS = @CRYPTSETUP_CFLAGS@
+CRYPTSETUP_LIBS = @CRYPTSETUP_LIBS@
+CRYPTSETUP_LIBS_STATIC = @CRYPTSETUP_LIBS_STATIC@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DAEMON_CFLAGS = @DAEMON_CFLAGS@
+DAEMON_LDFLAGS = @DAEMON_LDFLAGS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+ECONF_CFLAGS = @ECONF_CFLAGS@
+ECONF_LIBS = @ECONF_LIBS@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FUZZING_ENGINE_LDFLAGS = @FUZZING_ENGINE_LDFLAGS@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBBLKID_DATE = @LIBBLKID_DATE@
+LIBBLKID_VERSION = @LIBBLKID_VERSION@
+LIBBLKID_VERSION_INFO = @LIBBLKID_VERSION_INFO@
+LIBFDISK_MAJOR_VERSION = @LIBFDISK_MAJOR_VERSION@
+LIBFDISK_MINOR_VERSION = @LIBFDISK_MINOR_VERSION@
+LIBFDISK_PATCH_VERSION = @LIBFDISK_PATCH_VERSION@
+LIBFDISK_PC_REQUIRES = @LIBFDISK_PC_REQUIRES@
+LIBFDISK_VERSION = @LIBFDISK_VERSION@
+LIBFDISK_VERSION_INFO = @LIBFDISK_VERSION_INFO@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBMOUNT_MAJOR_VERSION = @LIBMOUNT_MAJOR_VERSION@
+LIBMOUNT_MINOR_VERSION = @LIBMOUNT_MINOR_VERSION@
+LIBMOUNT_PATCH_VERSION = @LIBMOUNT_PATCH_VERSION@
+LIBMOUNT_VERSION = @LIBMOUNT_VERSION@
+LIBMOUNT_VERSION_INFO = @LIBMOUNT_VERSION_INFO@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSMARTCOLS_VERSION = @LIBSMARTCOLS_VERSION@
+LIBSMARTCOLS_VERSION_INFO = @LIBSMARTCOLS_VERSION_INFO@
+LIBTOOL = @LIBTOOL@
+LIBUSER_CFLAGS = @LIBUSER_CFLAGS@
+LIBUSER_LIBS = @LIBUSER_LIBS@
+LIBUUID_VERSION = @LIBUUID_VERSION@
+LIBUUID_VERSION_INFO = @LIBUUID_VERSION_INFO@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAGIC_LIBS = @MAGIC_LIBS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MATH_LIBS = @MATH_LIBS@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NCURSES5_CONFIG = @NCURSES5_CONFIG@
+NCURSES6_CONFIG = @NCURSES6_CONFIG@
+NCURSESW5_CONFIG = @NCURSESW5_CONFIG@
+NCURSESW6_CONFIG = @NCURSESW6_CONFIG@
+NCURSESW_CFLAGS = @NCURSESW_CFLAGS@
+NCURSESW_LIBS = @NCURSESW_LIBS@
+NCURSES_CFLAGS = @NCURSES_CFLAGS@
+NCURSES_LIBS = @NCURSES_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NO_UNUSED_WARN_CFLAGS = @NO_UNUSED_WARN_CFLAGS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PO4A = @PO4A@
+POSUB = @POSUB@
+PYTHON = @PYTHON@
+PYTHON_CFLAGS = @PYTHON_CFLAGS@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_LIBS = @PYTHON_LIBS@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+PYTHON_WARN_CFLAGS = @PYTHON_WARN_CFLAGS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+READLINE_LIBS_STATIC = @READLINE_LIBS_STATIC@
+REALTIME_LIBS = @REALTIME_LIBS@
+RTAS_LIBS = @RTAS_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SELINUX_LIBS_STATIC = @SELINUX_LIBS_STATIC@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SOLIB_CFLAGS = @SOLIB_CFLAGS@
+SOLIB_LDFLAGS = @SOLIB_LDFLAGS@
+STRIP = @STRIP@
+SUID_CFLAGS = @SUID_CFLAGS@
+SUID_LDFLAGS = @SUID_LDFLAGS@
+SYSCONFSTATICDIR = @SYSCONFSTATICDIR@
+SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
+SYSTEMD_DAEMON_CFLAGS = @SYSTEMD_DAEMON_CFLAGS@
+SYSTEMD_DAEMON_LIBS = @SYSTEMD_DAEMON_LIBS@
+SYSTEMD_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@
+SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+TINFOW_CFLAGS = @TINFOW_CFLAGS@
+TINFOW_LIBS = @TINFOW_LIBS@
+TINFO_CFLAGS = @TINFO_CFLAGS@
+TINFO_LIBS = @TINFO_LIBS@
+TINFO_LIBS_STATIC = @TINFO_LIBS_STATIC@
+UBSAN_LDFLAGS = @UBSAN_LDFLAGS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+VSCRIPT_LDFLAGS = @VSCRIPT_LDFLAGS@
+WARN_CFLAGS = @WARN_CFLAGS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+XSLTPROC = @XSLTPROC@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bashcompletiondir = @bashcompletiondir@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+sysconfstaticdir = @sysconfstaticdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+usrbin_execdir = @usrbin_execdir@
+usrlib_execdir = @usrlib_execdir@
+usrsbin_execdir = @usrsbin_execdir@
+vendordir = @vendordir@
+
+# We require automake 1.10 at least.
+AUTOMAKE_OPTIONS = 1.10
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE = libsmartcols
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR = ../src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS =
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+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 scols
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS =
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS =
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS =
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB = $(top_builddir)/libsmartcols/src/libsmartcols.h
+CFILE_GLOB = $(top_srcdir)/libsmartcols/src/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES =
+
+# Header files to ignore when scanning. Use base file name, no paths
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES = smartcolsP.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES =
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = $(builddir)/version.xml
+
+# 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 =
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS =
+GTKDOC_LIBS =
+@GTK_DOC_USE_LIBTOOL_FALSE@GTKDOC_CC = $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+@GTK_DOC_USE_LIBTOOL_TRUE@GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+@GTK_DOC_USE_LIBTOOL_FALSE@GTKDOC_LD = $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+@GTK_DOC_USE_LIBTOOL_TRUE@GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+@GTK_DOC_USE_LIBTOOL_FALSE@GTKDOC_RUN =
+@GTK_DOC_USE_LIBTOOL_TRUE@GTKDOC_RUN = $(LIBTOOL) --mode=execute
+
+# We set GPATH here; this gives us semantics for GNU make
+# which are more like other make's VPATH, when it comes to
+# whether a source that is a target of one rule is then
+# searched for in VPATH/GPATH.
+#
+GPATH = $(srcdir)
+TARGET_DIR = $(docdir)/$(DOC_MODULE)
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES = version.xml
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST = $(content_files) $(HTML_IMAGES) $(DOC_MAIN_SGML_FILE) \
+ $(DOC_MODULE)-sections.txt version.xml.in
+# $(DOC_MODULE)-overrides.txt
+DOC_STAMPS = scan-build.stamp sgml-build.stamp html-build.stamp \
+ $(srcdir)/setup.stamp $(srcdir)/sgml.stamp \
+ $(srcdir)/html.stamp
+
+SCANOBJ_FILES = \
+ $(DOC_MODULE).args \
+ $(DOC_MODULE).hierarchy \
+ $(DOC_MODULE).interfaces \
+ $(DOC_MODULE).prerequisites \
+ $(DOC_MODULE).signals \
+ $(DOC_MODULE).types # util-linux: we don't use types
+
+REPORT_FILES = \
+ $(DOC_MODULE)-undocumented.txt \
+ $(DOC_MODULE)-undeclared.txt \
+ $(DOC_MODULE)-unused.txt
+
+CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/config/gtk-doc.make $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign libsmartcols/docs/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign libsmartcols/docs/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/config/gtk-doc.make $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+version.xml: $(top_builddir)/config.status $(srcdir)/version.xml.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile all-local
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-local
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-local
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am all-local check check-am clean clean-generic \
+ clean-libtool clean-local cscopelist-am ctags-am distclean \
+ distclean-generic distclean-libtool distclean-local distdir \
+ dvi dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-data-local install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am uninstall-local
+
+.PRECIOUS: Makefile
+
+
+@ENABLE_GTK_DOC_TRUE@all-local: html-build.stamp
+@ENABLE_GTK_DOC_FALSE@all-local:
+
+docs: html-build.stamp
+
+$(REPORT_FILES): sgml-build.stamp
+
+#### setup ####
+
+setup-build.stamp:
+ -@if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \
+ echo 'gtk-doc: Preparing build'; \
+ files=`echo $(EXTRA_DIST) $(expand_content_files) $(srcdir)/$(DOC_MODULE).types`; \
+ if test "x$$files" != "x" ; then \
+ for file in $$files ; do \
+ test -f $(abs_srcdir)/$$file && \
+ cp -p $(abs_srcdir)/$$file $(abs_builddir)/; \
+ done \
+ fi \
+ fi
+ @touch setup-build.stamp
+
+setup.stamp: setup-build.stamp
+ @true
+
+#### scan ####
+
+scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB) $(srcdir)/$(DOC_MODULE)-*.txt $(content_files)
+
+ @test -f $(DOC_MODULE)-sections.txt || \
+ cp $(srcdir)/$(DOC_MODULE)-sections.txt $(builddir)
+
+ $(AM_V_GEN)gtkdoc-scan --module=$(DOC_MODULE) \
+ --source-dir=$(srcdir)/$(DOC_SOURCE_DIR) \
+ --source-dir=$(builddir)/$(DOC_SOURCE_DIR) \
+ --ignore-decorators="__ul_attribute__\(.*\)" \
+ --ignore-headers="$(IGNORE_HFILES)" \
+ --output-dir=$(builddir) \
+ $(SCAN_OPTIONS) $(EXTRA_HFILES)
+
+ @ if grep -l '^..*$$' $(srcdir)/$(DOC_MODULE).types > /dev/null 2>&1 ; then \
+ CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" \
+ CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) \
+ $(LDFLAGS)" gtkdoc-scangobj $(SCANGOBJ_OPTIONS) \
+ --module=$(DOC_MODULE) --output-dir=$(builddir) ; \
+ else \
+ for i in $(SCANOBJ_FILES) ; do \
+ test -f $$i || touch $$i ; \
+ done \
+ fi
+ @ touch scan-build.stamp
+
+$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp
+ @true
+
+#### templates ####
+#
+#tmpl-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(srcdir)/$(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt
+# @echo 'gtk-doc: Rebuilding template files'
+# test -z $(builddir)/tmpl || $(MKDIR_P) $(builddir)/tmpl
+# gtkdoc-mktmpl --module=$(DOC_MODULE) \
+# $(MKTMPL_OPTIONS)
+# touch tmpl-build.stamp
+#
+#tmpl.stamp: tmpl-build.stamp
+# @true
+#
+#tmpl/*.sgml:
+# @true
+#
+
+#### xml ####
+
+sgml-build.stamp: setup.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-decl.txt $(DOC_MODULE)-sections.txt $(expand_content_files)
+ $(AM_V_GEN)gtkdoc-mkdb --module=$(DOC_MODULE) \
+ --source-dir=$(srcdir)/$(DOC_SOURCE_DIR) \
+ --source-dir=$(builddir)/$(DOC_SOURCE_DIR) \
+ --output-format=xml \
+ --ignore-files="$(IGNORE_HFILES)" \
+ --expand-content-files="$(expand_content_files)" \
+ --main-sgml-file=$(srcdir)/$(DOC_MAIN_SGML_FILE) \
+ $(MKDB_OPTIONS)
+ @touch sgml-build.stamp
+
+sgml.stamp: sgml-build.stamp
+ @true
+
+#### html ####
+
+html-build.stamp: sgml.stamp $(srcdir)/$(DOC_MAIN_SGML_FILE) $(content_files)
+ @rm -rf $(builddir)/html
+ @$(MKDIR_P) $(builddir)/html
+ $(AM_V_GEN)cd $(builddir)/html && \
+ gtkdoc-mkhtml --path="$(abs_builddir):$(abs_builddir)/xml:$(abs_srcdir)" \
+ $(MKHTML_OPTIONS) \
+ $(DOC_MODULE) \
+ $(abs_srcdir)/$(DOC_MAIN_SGML_FILE)
+
+ @test "x$(HTML_IMAGES)" = "x" || \
+ ( cd $(srcdir) && cp $(HTML_IMAGES) $(abs_builddir)/html )
+
+ $(AM_V_GEN)gtkdoc-fixxref --module-dir=html \
+ --module=$(DOC_MODULE) \
+ --html-dir=$(HTML_DIR) \
+ $(FIXXREF_OPTIONS)
+ @touch html-build.stamp
+
+##############
+
+clean-local:
+ rm -f *~ *.bak
+ rm -rf .libs
+
+distclean-local:
+ rm -rf xml html $(REPORT_FILES) *.stamp \
+ $(DOC_MODULE)-overrides.txt \
+ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt
+ test $(abs_builddir) == $(abs_srcdir) || \
+ rm -f $(DOC_MODULE)-*.txt $(DOC_MODULE)-*.xml *.xml.in
+
+install-data-local:
+ installfiles=`echo $(builddir)/html/*`; \
+ if test "$$installfiles" = '$(builddir)/html/*'; \
+ then echo '-- Nothing to install' ; \
+ else \
+ if test -n "$(DOC_MODULE_VERSION)"; then \
+ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \
+ else \
+ installdir="$(DESTDIR)$(TARGET_DIR)"; \
+ fi; \
+ $(mkinstalldirs) $${installdir} ; \
+ for i in $$installfiles; do \
+ echo '-- Installing '$$i ; \
+ $(INSTALL_DATA) $$i $${installdir}; \
+ done; \
+ if test -n "$(DOC_MODULE_VERSION)"; then \
+ mv -f $${installdir}/$(DOC_MODULE).devhelp2 \
+ $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \
+ mv -f $${installdir}/$(DOC_MODULE).devhelp \
+ $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp; \
+ fi; \
+ ! which gtkdoc-rebase >/dev/null 2>&1 || \
+ gtkdoc-rebase --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir} ; \
+ fi
+
+uninstall-local:
+ if test -n "$(DOC_MODULE_VERSION)"; then \
+ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \
+ else \
+ installdir="$(DESTDIR)$(TARGET_DIR)"; \
+ fi; \
+ rm -rf $${installdir}
+
+#
+# Require gtk-doc when making dist
+#
+@ENABLE_GTK_DOC_TRUE@dist-check-gtkdoc:
+@ENABLE_GTK_DOC_FALSE@dist-check-gtkdoc:
+@ENABLE_GTK_DOC_FALSE@ @echo "*** gtk-doc must be installed and enabled in order to make dist"
+@ENABLE_GTK_DOC_FALSE@ @false
+
+#dist-hook: dist-check-gtkdoc dist-hook-local sgml.stamp html-build.stamp
+# mkdir $(distdir)/tmpl
+# mkdir $(distdir)/xml
+# mkdir $(distdir)/html
+# -cp $(srcdir)/tmpl/*.sgml $(distdir)/tmpl
+# -cp $(srcdir)/xml/*.xml $(distdir)/xml
+# cp $(srcdir)/html/* $(distdir)/html
+# -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/
+# -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/
+# cd $(distdir) && rm -f $(DISTCLEANFILES)
+# ! which gtkdoc-rebase >/dev/null 2>&1 || \
+# gtkdoc-rebase --online --relative --html-dir=$(distdir)/html
+#
+#.PHONY : dist-hook-local docs
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libsmartcols/docs/libsmartcols-docs.xml b/libsmartcols/docs/libsmartcols-docs.xml
new file mode 100644
index 0000000..9ded584
--- /dev/null
+++ b/libsmartcols/docs/libsmartcols-docs.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+ <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>libsmartcols Reference Manual</title>
+ <releaseinfo>for libsmartcols version &version;</releaseinfo>
+ <copyright>
+ <year>2014-2022</year>
+ <holder>Karel Zak &lt;kzak@redhat.com&gt;</holder>
+ </copyright>
+ </bookinfo>
+
+ <part id="overview">
+ <title>libsmartcols Overview</title>
+ <partintro>
+ <para>
+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 https://www.kernel.org/pub/linux/utils/util-linux/.
+ </para>
+ </partintro>
+ </part>
+
+ <part>
+ <title>Data manipulation</title>
+ <xi:include href="xml/table.xml"/>
+ <xi:include href="xml/column.xml"/>
+ <xi:include href="xml/line.xml"/>
+ <xi:include href="xml/cell.xml"/>
+ <xi:include href="xml/symbols.xml"/>
+ <xi:include href="xml/grouping.xml"/>
+ </part>
+ <part>
+ <title>Printing</title>
+ <xi:include href="xml/table_print.xml"/>
+ </part>
+ <part>
+ <title>Misc</title>
+ <xi:include href="xml/iter.xml"/>
+ <xi:include href="xml/version-utils.xml"/>
+ <xi:include href="xml/init.xml"/>
+ </part>
+ <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>
+ <index role="2.33">
+ <title>Index of new symbols in 2.33</title>
+ <xi:include href="xml/api-index-2.33.xml"><xi:fallback /></xi:include>
+ </index>
+ <index role="2.34">
+ <title>Index of new symbols in 2.34</title>
+ <xi:include href="xml/api-index-2.34.xml"><xi:fallback /></xi:include>
+ </index>
+ <index role="2.35">
+ <title>Index of new symbols in 2.35</title>
+ <xi:include href="xml/api-index-2.35.xml"><xi:fallback /></xi:include>
+ </index>
+</book>
diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt
new file mode 100644
index 0000000..aa7bb15
--- /dev/null
+++ b/libsmartcols/docs/libsmartcols-sections.txt
@@ -0,0 +1,213 @@
+<SECTION>
+<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
+</SECTION>
+
+<SECTION>
+<FILE>column</FILE>
+libscols_column
+scols_column_get_color
+scols_column_get_flags
+scols_column_get_header
+scols_column_get_json_type
+scols_column_get_name
+scols_column_get_name_as_shellvar
+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_name
+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>
+<FILE>iter</FILE>
+libscols_iter
+scols_free_iter
+scols_iter_get_direction
+scols_new_iter
+scols_reset_iter
+</SECTION>
+
+<SECTION>
+<FILE>line</FILE>
+libscols_line
+scols_copy_line
+scols_line_add_child
+scols_line_alloc_cells
+scols_line_free_cells
+scols_line_get_cell
+scols_line_get_color
+scols_line_get_column_cell
+scols_line_get_column_data
+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
+scols_ref_line
+scols_unref_line
+</SECTION>
+
+<SECTION>
+<FILE>grouping</FILE>
+scols_line_link_group
+scols_table_group_lines
+</SECTION>
+
+<SECTION>
+<FILE>symbols</FILE>
+libscols_symbols
+scols_copy_symbols
+scols_new_symbols
+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_symbols_set_group_vertical
+scols_symbols_set_group_horizontal
+scols_symbols_set_group_first_member
+scols_symbols_set_group_last_member
+scols_symbols_set_group_middle_member
+scols_symbols_set_group_last_child
+scols_symbols_set_group_middle_child
+scols_unref_symbols
+</SECTION>
+
+<SECTION>
+<FILE>table</FILE>
+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_export
+scols_table_enable_header_repeat
+scols_table_enable_json
+scols_table_enable_maxout
+scols_table_enable_minout
+scols_table_enable_noencoding
+scols_table_enable_noheadings
+scols_table_enable_nolinesep
+scols_table_enable_nowrap
+scols_table_enable_raw
+scols_table_enable_shellvar
+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_minout
+scols_table_is_noencoding
+scols_table_is_noheadings
+scols_table_is_nolinesep
+scols_table_is_nowrap
+scols_table_is_raw
+scols_table_is_shellvar
+scols_table_is_tree
+scols_table_move_column
+scols_table_new_column
+scols_table_new_line
+scols_table_next_column
+scols_table_next_line
+scols_table_reduce_termwidth
+scols_table_remove_column
+scols_table_remove_columns
+scols_table_remove_line
+scols_table_remove_lines
+scols_table_set_column_separator
+scols_table_set_columns_iter
+scols_table_set_default_symbols
+scols_table_set_line_separator
+scols_table_set_name
+scols_table_set_stream
+scols_table_set_symbols
+scols_table_set_termforce
+scols_table_set_termheight
+scols_table_set_termwidth
+scols_unref_table
+</SECTION>
+
+<SECTION>
+<FILE>table_print</FILE>
+scols_print_table
+scols_print_table_to_string
+scols_table_print_range
+scols_table_print_range_to_string
+</SECTION>
+
+<SECTION>
+<FILE>version-utils</FILE>
+scols_get_library_version
+scols_parse_version_string
+LIBSMARTCOLS_VERSION
+</SECTION>
+
+<SECTION>
+<FILE>init</FILE>
+scols_init_debug
+</SECTION>
diff --git a/libsmartcols/docs/version.xml b/libsmartcols/docs/version.xml
new file mode 100644
index 0000000..d8c7561
--- /dev/null
+++ b/libsmartcols/docs/version.xml
@@ -0,0 +1 @@
+2.38.1
diff --git a/libsmartcols/docs/version.xml.in b/libsmartcols/docs/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/libsmartcols/docs/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/libsmartcols/meson.build b/libsmartcols/meson.build
new file mode 100644
index 0000000..11c4f08
--- /dev/null
+++ b/libsmartcols/meson.build
@@ -0,0 +1,53 @@
+dir_libsmartcols = include_directories('.', 'src')
+
+defs = configuration_data()
+defs.set('LIBSMARTCOLS_VERSION', pc_version)
+
+libsmartcols_h = configure_file(
+ input : 'src/libsmartcols.h.in',
+ output : 'libsmartcols.h',
+ configuration : defs,
+ install : build_libsmartcols,
+ install_dir : join_paths(get_option('includedir'), 'libsmartcols'),
+)
+
+lib_smartcols_sources = '''
+ src/smartcolsP.h
+ src/iter.c
+ src/symbols.c
+ src/cell.c
+ src/column.c
+ src/line.c
+ src/table.c
+ src/print.c
+ src/print-api.c
+ src/version.c
+ src/calculate.c
+ src/grouping.c
+ src/walk.c
+ src/init.c
+'''.split()
+
+libsmartcols_sym = 'src/libsmartcols.sym'
+libsmartcols_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libsmartcols_sym)
+
+lib_smartcols = both_libraries(
+ 'smartcols',
+ list_h,
+ lib_smartcols_sources,
+ include_directories : [dir_include, dir_libsmartcols],
+ link_depends : libsmartcols_sym,
+ version : libsmartcols_version,
+ link_args : ['-Wl,--version-script=@0@'.format(libsmartcols_sym_path)],
+ link_with : lib_common,
+ dependencies : build_libsmartcols ? [] : disabler(),
+ install : build_libsmartcols)
+
+lib_smartcols_static = lib_smartcols.get_static_lib()
+
+if build_libsmartcols
+ pkgconfig.generate(lib_smartcols,
+ description : 'table or tree library',
+ subdirs : 'libsmartcols',
+ version : pc_version)
+endif
diff --git a/libsmartcols/samples/Makemodule.am b/libsmartcols/samples/Makemodule.am
new file mode 100644
index 0000000..c0130b9
--- /dev/null
+++ b/libsmartcols/samples/Makemodule.am
@@ -0,0 +1,53 @@
+
+check_PROGRAMS += \
+ sample-scols-colors \
+ sample-scols-title \
+ sample-scols-wrap \
+ sample-scols-continuous \
+ sample-scols-fromfile \
+ sample-scols-grouping-simple \
+ sample-scols-grouping-overlay \
+ sample-scols-maxout
+
+sample_scols_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \
+ -I$(ul_libsmartcols_incdir)
+sample_scols_ldadd = libsmartcols.la $(LDADD)
+
+if HAVE_OPENAT
+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)
+endif
+
+sample_scols_colors_SOURCES = libsmartcols/samples/colors.c
+sample_scols_colors_LDADD = $(sample_scols_ldadd) libcommon.la
+sample_scols_colors_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)
+
+sample_scols_grouping_simple_SOURCES = libsmartcols/samples/grouping-simple.c
+sample_scols_grouping_simple_LDADD = $(sample_scols_ldadd) libcommon.la
+sample_scols_grouping_simple_CFLAGS = $(sample_scols_cflags)
+
+sample_scols_grouping_overlay_SOURCES = libsmartcols/samples/grouping-overlay.c
+sample_scols_grouping_overlay_LDADD = $(sample_scols_ldadd) libcommon.la
+sample_scols_grouping_overlay_CFLAGS = $(sample_scols_cflags)
diff --git a/libsmartcols/samples/colors.c b/libsmartcols/samples/colors.c
new file mode 100644
index 0000000..0e9ab66
--- /dev/null
+++ b/libsmartcols/samples/colors.c
@@ -0,0 +1,125 @@
+/*
+ * 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_FOO, COL_BAR };
+
+/* 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, "BAR", 0, 0))
+ goto fail;
+ if (!scols_table_new_column(tb, "FOO", 0, 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, 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_FOO, data))
+ goto fail;
+ if (scols_line_set_data(ln, COL_BAR, data))
+ 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_column *cl;
+ struct libscols_line *ln;
+ struct libscols_cell *ce;
+ 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, "AAA", "bla bla bla");
+ add_line(tb, "BB", "b");
+ add_line(tb, "CCCC", "fooo");
+ add_line(tb, "D", "baaar");
+ add_line(tb, "EE", "eee");
+
+ cl = scols_table_get_column(tb, 1);
+ scols_column_set_color(cl, "red"); /* red column */
+
+ cl = scols_table_get_column(tb, 2);
+ scols_column_set_color(cl, "reverse"); /* reverse column */
+
+ ln = scols_table_get_line(tb, 0);
+ scols_line_set_color(ln, "\033[37;41m"); /* line with red bg */
+ ce = scols_line_get_cell(ln, 0);
+ scols_cell_set_color(ce, "\033[37;45m"); /* cell with purple bg */
+
+ ln = scols_table_get_line(tb, 3);
+ scols_line_set_color(ln, "\033[37;41m"); /* line with red bg */
+ ce = scols_line_get_cell(ln, 2);
+ scols_cell_set_color(ce, "\033[37;44m"); /* cell with blue bg */
+
+ scols_print_table(tb);
+ scols_unref_table(tb);
+ return EXIT_SUCCESS;
+}
diff --git a/libsmartcols/samples/continuous.c b/libsmartcols/samples/continuous.c
new file mode 100644
index 0000000..7db3f84
--- /dev/null
+++ b/libsmartcols/samples/continuous.c
@@ -0,0 +1,140 @@
+/*
+ * 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;
+ const size_t timecellsz = sizeof(stringify_value(UINT_MAX));
+ 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( timecellsz );
+
+ 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
+ */
+ if (scols_line_refer_data(line, COL_TIME, timecell) != 0)
+ err(EXIT_FAILURE, "failed to add data to table");
+
+ do {
+ double diff;
+
+ gettimeofday(&now, NULL);
+ diff = time_diff(&now, &last);
+
+ if (now.tv_sec == last.tv_sec + (long) TIME_PERIOD)
+ done = 1;
+ else
+ xusleep(100000);
+
+ /* update "TIME" cell data */
+ snprintf(timecell, timecellsz, "%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 0000000..0fdc929
--- /dev/null
+++ b/libsmartcols/samples/fromfile.c
@@ -0,0 +1,347 @@
+/*
+ * 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 */
+ cl = scols_new_column();
+ if (!cl)
+ goto fail;
+ if (scols_column_set_name(cl, line) != 0)
+ 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;
+
+ if (*str && scols_line_set_data(ln, col, str) != 0)
+ err(EXIT_FAILURE, "failed to add output data");
+ }
+
+ 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(" -M, --minout minimize tailing padding\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' },
+ { "minout", 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' },
+ { 'M', 'm' },
+ { 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:JMmn: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 'M':
+ scols_table_enable_minout(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/grouping-overlay.c b/libsmartcols/samples/grouping-overlay.c
new file mode 100644
index 0000000..d55c57d
--- /dev/null
+++ b/libsmartcols/samples/grouping-overlay.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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)
+{
+ struct libscols_column *cl;
+
+ if (!scols_table_new_column(tb, "NAME", 0, SCOLS_FL_TREE))
+ goto fail;
+ cl = scols_table_new_column(tb, "DATA", 0, SCOLS_FL_WRAP);
+ if (!cl)
+ goto fail;
+ scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize,
+ scols_wrapnl_nextchunk, NULL);
+ scols_column_set_safechars(cl, "\n");
+ return;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output columns");
+}
+
+static struct libscols_line *add_line(struct libscols_table *tb, struct libscols_line *parent, const char *name, const char *data)
+{
+ 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, name))
+ goto fail;
+ if (scols_line_set_data(ln, COL_DATA, data))
+ 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; /* any line */
+ struct libscols_line *g1, *g2, *g3; /* groups */
+ struct libscols_line *p1; /* parents */
+ 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, NULL, "Alone", "bla bla bla");
+
+ p1 = add_line(tb, NULL, "A", "bla bla\nbla");
+ add_line(tb, p1, "A:B", "bla bla\nbla");
+ add_line(tb, p1, "A:C", "bla bla\nbla");
+
+ g1 = add_line(tb, NULL, "B", "bla bla\nbla");
+
+ g2 = add_line(tb, NULL, "C", "bla bla\nbla");
+ ln = add_line(tb, NULL, "D", "bla bla\nbla");
+ scols_table_group_lines(tb, g2, ln, 0);
+
+ ln = add_line(tb, NULL, "G2:A", "alb alb\nalb");
+ scols_line_link_group(ln, g2, 0);
+
+ ln = add_line(tb, NULL, "E", "bla bla\nbla");
+ scols_table_group_lines(tb, g1, ln, 0);
+
+
+ ln = add_line(tb, NULL, "G1:A", "alb alb alb");
+ scols_line_link_group(ln, g1, 0);
+
+ add_line(tb, NULL, "G", "bla bla bla");
+
+ g3 = ln = add_line(tb, NULL, "G1:B", "alb alb\nalb");
+ scols_line_link_group(ln, g1, 0);
+
+ ln = add_line(tb, NULL, "F", "bla bla bla");
+ scols_table_group_lines(tb, g3, ln, 0);
+
+ ln = add_line(tb, NULL, "G3:A", "alb alb alb");
+ scols_line_link_group(ln, g3, 0);
+
+ add_line(tb, NULL, "foo", "bla bla bla");
+ add_line(tb, NULL, "bar", "bla bla bla");
+
+ scols_print_table(tb);
+
+ scols_unref_table(tb);
+ return EXIT_SUCCESS;
+}
diff --git a/libsmartcols/samples/grouping-simple.c b/libsmartcols/samples/grouping-simple.c
new file mode 100644
index 0000000..6b9cbcf
--- /dev/null
+++ b/libsmartcols/samples/grouping-simple.c
@@ -0,0 +1,155 @@
+/*
+ * 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)
+{
+ struct libscols_column *cl;
+
+ if (!scols_table_new_column(tb, "NAME", 0, SCOLS_FL_TREE))
+ goto fail;
+ cl = scols_table_new_column(tb, "DATA", 0, SCOLS_FL_WRAP);
+ if (!cl)
+ goto fail;
+
+ scols_column_set_wrapfunc(cl, scols_wrapnl_chunksize,
+ scols_wrapnl_nextchunk,
+ NULL);
+ scols_column_set_safechars(cl, "\n");
+
+ return;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output columns");
+}
+
+static struct libscols_line *add_line(struct libscols_table *tb, struct libscols_line *parent, const char *name, const char *data)
+{
+ 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, name))
+ goto fail;
+ if (scols_line_set_data(ln, COL_DATA, data))
+ 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; /* any line */
+ struct libscols_line *g1; /* groups */
+ struct libscols_line *p1, *p2; /* parents */
+ 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, NULL, "Alone", "bla bla bla");
+
+ p1 = add_line(tb, NULL, "A", "bla bla bla");
+ add_line(tb, p1, "A:B", "bla bla bla");
+ add_line(tb, p1, "A:C", "bla bla bla");
+
+ g1 = add_line(tb, NULL, "B", "bla bla bla");
+ add_line(tb, NULL, "C", "bla\nfoo");
+ p1 = add_line(tb, NULL, "D", "bla bla\nbar");
+
+ p2 = add_line(tb, p1, "D:A", "bla bla bla");
+
+ ln = add_line(tb, p2, "D:A:A", "bla\nbla\nbla");
+ scols_table_group_lines(tb, g1, ln, 0);
+
+ add_line(tb, p1, "D:B", "bla bla bla");
+ add_line(tb, p1, "D:C", "bla\nother bla");
+ add_line(tb, p1, "D:D", "bla bla bla");
+
+ ln = add_line(tb, NULL, "E", "bla bla bla");
+ scols_table_group_lines(tb, g1, ln, 0);
+
+ p1 = ln;
+ add_line(tb, p1, "E:A", "bla bla bla");
+ add_line(tb, p1, "E:B", "bla bla bla");
+ add_line(tb, p1, "E:C", "bla bla bla");
+
+ add_line(tb, NULL, "F", "bla bla bla");
+
+ ln = add_line(tb, NULL, "G1:A", "alb alb alb");
+ scols_line_link_group(ln, g1, 0);
+
+ p1 = ln;
+ add_line(tb, p1, "G1:A:A", "bla\nbla bla");
+ add_line(tb, p1, "G1:A:B", "bla bla bla");
+ add_line(tb, p1, "G1:A:C", "bla bla bla");
+
+ add_line(tb, NULL, "G", "bla bla bla");
+
+ ln = add_line(tb, NULL, "G1:B", "alb alb\nalb");
+ scols_line_link_group(ln, g1, 0);
+
+ add_line(tb, NULL, "foo", "bla bla bla");
+ add_line(tb, NULL, "bar", "bla bla bla");
+
+ 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 0000000..263d4de
--- /dev/null
+++ b/libsmartcols/samples/maxout.c
@@ -0,0 +1,61 @@
+/*
+ * 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);
+ int rc;
+
+ rc = scols_line_set_data(ln, COL_LEFT, "A");
+ if (!rc)
+ rc = scols_line_set_data(ln, COL_FOO, "B");
+ if (!rc)
+ rc = scols_line_set_data(ln, COL_RIGHT, "C");
+ if (rc)
+ err(EXIT_FAILURE, "failed to set line data");
+ }
+
+ 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 0000000..131400d
--- /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/samples/tree.c b/libsmartcols/samples/tree.c
new file mode 100644
index 0000000..e40ea9a
--- /dev/null
+++ b/libsmartcols/samples/tree.c
@@ -0,0 +1,269 @@
+/*
+ * 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 "libsmartcols.h"
+
+static int add_children(struct libscols_table *tb,
+ struct libscols_line *ln, int fd);
+
+
+enum { COL_MODE, COL_SIZE, COL_NAME };
+
+struct libscols_column *sort_column;
+
+/* add columns to the @tb */
+static void setup_columns(struct libscols_table *tb, int notree)
+{
+ if (!scols_table_new_column(tb, "MODE", 0.3, 0))
+ goto fail;
+ if (!scols_table_new_column(tb, "SIZE", 5, SCOLS_FL_RIGHT))
+ goto fail;
+
+ sort_column = scols_table_new_column(tb, "NAME", 0.5,
+ (notree ? 0 : SCOLS_FL_TREE) | SCOLS_FL_NOEXTREMES);
+ if (!sort_column)
+ goto fail;
+ scols_column_set_cmpfunc(sort_column, scols_cmpstr_cells, NULL);
+
+ return;
+fail:
+ scols_unref_table(tb);
+ err(EXIT_FAILURE, "failed to create output columns");
+}
+
+/* add a new line to @tb, the content is based on @st */
+static int add_line_from_stat(struct libscols_table *tb,
+ struct libscols_line *parent,
+ int parent_fd,
+ struct stat *st,
+ const char *name)
+{
+ struct libscols_line *ln;
+ char modbuf[11], *p;
+ mode_t mode = st->st_mode;
+ int rc = 0;
+
+ ln = scols_table_new_line(tb, parent);
+ if (!ln)
+ err(EXIT_FAILURE, "failed to create output line");
+
+ /* MODE; local buffer, use scols_line_set_data() that calls strdup() */
+ xstrmode(mode, modbuf);
+ if (scols_line_set_data(ln, COL_MODE, modbuf))
+ goto fail;
+
+ /* SIZE; already allocated string, use scols_line_refer_data() */
+ p = size_to_human_string(0, st->st_size);
+ if (!p || scols_line_refer_data(ln, COL_SIZE, p))
+ goto fail;
+
+ /* NAME */
+ if (scols_line_set_data(ln, COL_NAME, name))
+ goto fail;
+
+ /* colors */
+ if (scols_table_colors_wanted(tb)) {
+ struct libscols_cell *ce = scols_line_get_cell(ln, COL_NAME);
+
+ if (S_ISDIR(mode))
+ scols_cell_set_color(ce, "blue");
+ else if (S_ISLNK(mode))
+ scols_cell_set_color(ce, "cyan");
+ else if (S_ISBLK(mode))
+ scols_cell_set_color(ce, "magenta");
+ else if ((mode & S_IXOTH) || (mode & S_IXGRP) || (mode & S_IXUSR))
+ scols_cell_set_color(ce, "green");
+ }
+
+ if (S_ISDIR(st->st_mode)) {
+ int fd;
+
+ if (parent_fd >= 0)
+ fd = openat(parent_fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
+ else
+ fd = open(name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
+ if (fd >= 0) {
+ rc = add_children(tb, ln, fd);
+ close(fd);
+ }
+ }
+ return rc;
+fail:
+ err(EXIT_FAILURE, "failed to create cell data");
+ return -1;
+}
+
+/* read all entries from directory addressed by @fd */
+static int add_children(struct libscols_table *tb,
+ struct libscols_line *ln,
+ int fd)
+{
+ DIR *dir;
+ struct dirent *d;
+
+ dir = fdopendir(fd);
+ if (!dir)
+ return -errno;
+
+ while ((d = readdir(dir))) {
+ struct stat st;
+
+ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
+ continue;
+ if (fstatat(fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0)
+ continue;
+ add_line_from_stat(tb, ln, fd, &st, d->d_name);
+ }
+ closedir(dir);
+ return 0;
+}
+
+static void add_lines(struct libscols_table *tb, const char *dirname)
+{
+ struct stat st;
+
+ if (lstat(dirname, &st))
+ err(EXIT_FAILURE, "%s", dirname);
+
+ add_line_from_stat(tb, NULL, -1, &st, 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(" -J, --json use JSON output format\n", out);
+ fputs(" -r, --raw use raw output format\n", out);
+ fputs(" -s, --sort sort by NAME\n", out);
+ fputs(" -x, --tree-sort keep tree-like order (also for --list)\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);
+}
+
+int main(int argc, char *argv[])
+{
+ struct libscols_table *tb;
+ int c, notree = 0, nstart = -1, nend = -1, sort = 0, force_tree_sort = 0;
+
+
+ static const struct option longopts[] = {
+ { "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' },
+ { "sort", 0, NULL, 's' },
+ { "tree-sort", 0, NULL, 'x' },
+ { 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, "ciJlnprS:sE:x", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'c':
+ scols_table_set_column_separator(tb, ",");
+ scols_table_enable_raw(tb, 1);
+ notree = 1;
+ break;
+ 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;
+ case 'n':
+ scols_table_enable_noheadings(tb, 1);
+ break;
+ case 'p':
+ scols_table_enable_export(tb, 1);
+ notree = 1;
+ break;
+ case 'r':
+ scols_table_enable_raw(tb, 1);
+ notree = 1;
+ break;
+ case 'S':
+ nstart = strtos32_or_err(optarg, "failed to parse range start") - 1;
+ break;
+ case 's':
+ sort = 1;
+ break;
+ case 'E':
+ nend = strtos32_or_err(optarg, "failed to parse range end") - 1;
+ break;
+ case 'x':
+ force_tree_sort = 1;
+ break;
+ default:
+ usage(stderr);
+ }
+ }
+
+ scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
+ setup_columns(tb, notree);
+
+ if (optind == argc)
+ add_lines(tb, ".");
+ else while (optind < argc)
+ add_lines(tb, argv[optind++]);
+
+ if (sort)
+ scols_sort_table(tb, sort_column);
+ if (force_tree_sort)
+ scols_sort_table_by_tree(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 0000000..795bef7
--- /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/smartcols.pc.in b/libsmartcols/smartcols.pc.in
new file mode 100644
index 0000000..0b16739
--- /dev/null
+++ b/libsmartcols/smartcols.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: smartcols
+Description: table or tree library
+Version: @LIBSMARTCOLS_VERSION@
+Cflags: -I${includedir}/libsmartcols
+Libs: -L${libdir} -lsmartcols
diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am
new file mode 100644
index 0000000..2bb19fd
--- /dev/null
+++ b/libsmartcols/src/Makemodule.am
@@ -0,0 +1,62 @@
+
+
+# smartcols.h is generated, so it's stored in builddir!
+smartcolsincdir = $(includedir)/libsmartcols
+nodist_smartcolsinc_HEADERS = libsmartcols/src/libsmartcols.h
+
+usrlib_exec_LTLIBRARIES += libsmartcols.la
+libsmartcols_la_SOURCES= \
+ include/list.h \
+ \
+ libsmartcols/src/smartcolsP.h \
+ libsmartcols/src/iter.c \
+ libsmartcols/src/symbols.c \
+ libsmartcols/src/cell.c \
+ libsmartcols/src/column.c \
+ libsmartcols/src/line.c \
+ libsmartcols/src/table.c \
+ libsmartcols/src/print.c \
+ libsmartcols/src/print-api.c \
+ libsmartcols/src/version.c \
+ libsmartcols/src/calculate.c \
+ libsmartcols/src/grouping.c \
+ libsmartcols/src/walk.c \
+ libsmartcols/src/init.c
+
+libsmartcols_la_LIBADD = $(LDADD) libcommon.la
+
+libsmartcols_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(SOLIB_CFLAGS) \
+ -I$(ul_libsmartcols_incdir) \
+ -I$(top_srcdir)/libsmartcols/src
+
+EXTRA_libsmartcols_la_DEPENDENCIES = \
+ libsmartcols/src/libsmartcols.sym
+
+libsmartcols_la_LDFLAGS = $(SOLIB_LDFLAGS)
+if HAVE_VSCRIPT
+libsmartcols_la_LDFLAGS += $(VSCRIPT_LDFLAGS),$(top_srcdir)/libsmartcols/src/libsmartcols.sym
+endif
+libsmartcols_la_LDFLAGS += -version-info $(LIBSMARTCOLS_VERSION_INFO)
+
+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); \
+ 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'); \
+ (cd $(DESTDIR)$(usrlib_execdir) && \
+ rm -f libsmartcols.so && \
+ $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libsmartcols.so); \
+ fi
+
+uninstall-hook-libsmartcols:
+ rm -f $(DESTDIR)$(libdir)/libsmartcols.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-libsmartcols
+UNINSTALL_HOOKS += uninstall-hook-libsmartcols
diff --git a/libsmartcols/src/calculate.c b/libsmartcols/src/calculate.c
new file mode 100644
index 0000000..4f79408
--- /dev/null
+++ b/libsmartcols/src/calculate.c
@@ -0,0 +1,454 @@
+#include "smartcolsP.h"
+#include "mbsalign.h"
+
+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 %s",
+
+ cl->header.data, cl->seqnum, cl->width,
+ cl->width_hint > 1 ? (int) cl->width_hint :
+ (int) (cl->width_hint * tb->termwidth),
+ cl->width_avg,
+ cl->width_max,
+ cl->width_min,
+ cl->is_extreme ? "yes" : "not",
+ cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
+}
+
+static void dbg_columns(struct libscols_table *tb)
+{
+ struct libscols_iter itr;
+ struct libscols_column *cl;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0)
+ dbg_column(tb, cl);
+}
+
+static int count_cell_width(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ struct ul_buffer *buf)
+{
+ size_t len;
+ char *data;
+ int rc;
+
+ rc = __cell_to_buffer(tb, ln, cl, buf);
+ if (rc)
+ return rc;
+
+ data = ul_buffer_get_data(buf, NULL, NULL);
+ if (!data)
+ len = 0;
+ else if (scols_column_is_customwrap(cl))
+ len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data);
+ else if (scols_table_is_noencoding(tb))
+ len = mbs_width(data);
+ else
+ len = mbs_safe_width(data);
+
+ if (len == (size_t) -1) /* ignore broken multibyte strings */
+ len = 0;
+ cl->width_max = max(len, cl->width_max);
+
+ if (cl->is_extreme && cl->width_avg && len > cl->width_avg * 2)
+ return 0;
+
+ if (scols_column_is_noextremes(cl)) {
+ cl->extreme_sum += len;
+ cl->extreme_count++;
+ }
+ cl->width = max(len, cl->width);
+ if (scols_column_is_tree(cl)) {
+ size_t treewidth = ul_buffer_get_safe_pointer_width(buf, SCOLS_BUFPTR_TREEEND);
+ cl->width_treeart = max(cl->width_treeart, treewidth);
+ }
+ return 0;
+}
+
+
+static int walk_count_cell_width(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ void *data)
+{
+ return count_cell_width(tb, ln, cl, (struct ul_buffer *) data);
+}
+
+/*
+ * This function counts column width.
+ *
+ * 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,
+ struct ul_buffer *buf)
+{
+ int rc = 0, no_header = 0;
+
+ assert(tb);
+ assert(cl);
+
+ cl->width = 0;
+ if (!cl->width_min) {
+ const char *data;
+
+ 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--;
+ }
+
+ data = scols_cell_get_data(&cl->header);
+ if (data) {
+ size_t len = scols_table_is_noencoding(tb) ?
+ mbs_width(data) : mbs_safe_width(data);
+ cl->width_min = max(cl->width_min, len);
+ } else
+ no_header = 1;
+
+ if (!cl->width_min)
+ cl->width_min = 1;
+ }
+
+ if (scols_table_is_tree(tb)) {
+ /* Count width for tree */
+ rc = scols_walk_tree(tb, cl, walk_count_cell_width, (void *) buf);
+ if (rc)
+ goto done;
+ } else {
+ /* Count width for list */
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ rc = count_cell_width(tb, ln, cl, buf);
+ if (rc)
+ goto done;
+ }
+ }
+
+ if (scols_column_is_tree(cl) && has_groups(tb)) {
+ /* We don't fill buffer with groups tree ascii art during width
+ * calculation. The print function only enlarge grpset[] and we
+ * calculate final width from grpset_size.
+ */
+ size_t gprwidth = tb->grpset_size + 1;
+ cl->width_treeart += gprwidth;
+ cl->width_max += gprwidth;
+ cl->width += gprwidth;
+ if (cl->extreme_count)
+ cl->extreme_sum += gprwidth;
+ }
+
+ if (cl->extreme_count && cl->width_avg == 0) {
+ cl->width_avg = cl->extreme_sum / cl->extreme_count;
+ if (cl->width_avg && cl->width_max > cl->width_avg * 2)
+ cl->is_extreme = 1;
+ }
+
+ /* enlarge to minimal width */
+ if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
+ cl->width = cl->width_min;
+
+ /* 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;
+
+
+ /* Column without header and data, set minimal size to zero (default is 1) */
+ if (cl->width_max == 0 && no_header && cl->width_min == 1 && cl->width <= 1)
+ cl->width = cl->width_min = 0;
+
+done:
+ ON_DBG(COL, dbg_column(tb, cl));
+ return rc;
+}
+
+/*
+ * This is core of the scols_* voodoo...
+ */
+int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf)
+{
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+ size_t width = 0, width_min = 0; /* output width */
+ int stage, rc = 0;
+ int extremes = 0, group_ncolumns = 0;
+ size_t colsepsz;
+
+
+ DBG(TAB, ul_debugobj(tb, "-----calculate-(termwidth=%zu)-----", tb->termwidth));
+ tb->is_dummy_print = 1;
+
+ colsepsz = scols_table_is_noencoding(tb) ?
+ mbs_width(colsep(tb)) :
+ mbs_safe_width(colsep(tb));
+
+ if (has_groups(tb))
+ group_ncolumns = 1;
+
+ /* 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;
+
+ /* we print groups chart only for the for the first tree column */
+ if (scols_column_is_tree(cl) && group_ncolumns == 1) {
+ cl->is_groups = 1;
+ group_ncolumns++;
+ }
+
+ rc = count_column_width(tb, cl, buf);
+ if (rc)
+ goto done;
+
+ is_last = is_last_column(cl);
+
+ width += cl->width + (is_last ? 0 : colsepsz); /* separator for non-last column */
+ width_min += cl->width_min + (is_last ? 0 : colsepsz);
+ if (cl->is_extreme)
+ extremes++;
+ }
+
+ if (!tb->is_term) {
+ DBG(TAB, ul_debugobj(tb, " non-terminal output"));
+ goto done;
+ }
+
+ /* 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)"));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ size_t org_width;
+
+ if (!cl->is_extreme || scols_column_is_hidden(cl))
+ continue;
+
+ org_width = cl->width;
+ rc = count_column_width(tb, cl, buf);
+ if (rc)
+ goto done;
+
+ if (org_width > cl->width)
+ width -= org_width - cl->width;
+ else
+ extremes--; /* hmm... nothing reduced */
+ }
+ }
+
+ if (width < tb->termwidth) {
+ if (extremes) {
+ 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 || scols_column_is_hidden(cl))
+ continue;
+
+ /* this column is too large, ignore?
+ if (cl->width_max - cl->width >
+ (tb->termwidth - width))
+ continue;
+ */
+
+ add = tb->termwidth - width;
+ if (add && cl->width + add > cl->width_max)
+ add = cl->width_max - cl->width;
+
+ cl->width += add;
+ width += add;
+
+ if (width == tb->termwidth)
+ break;
+ }
+ }
+
+ if (width < tb->termwidth && scols_table_is_maxout(tb)) {
+ DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
+
+ /* 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)
+ break;
+ }
+ }
+ } else if (width < tb->termwidth) {
+ /* enlarge the last column */
+ struct libscols_column *col = list_entry(
+ tb->tb_columns.prev, struct libscols_column, cl_columns);
+
+ DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
+
+ if (!scols_column_is_right(col)) {
+ col->width += tb->termwidth - width;
+ width = tb->termwidth;
+ }
+ }
+ }
+
+ /* 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.
+ */
+ for (stage = 1; width > tb->termwidth && stage <= 3; ) {
+ size_t org_width = width;
+
+ 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;
+
+ /* never truncate if already minimal width */
+ if (cl->width == cl->width_min)
+ continue;
+
+ /* never truncate the tree */
+ if (scols_column_is_tree(cl) && width <= cl->width_treeart)
+ continue;
+
+ /* nothing to truncate */
+ if (cl->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--;
+ 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;
+ }
+
+ /* 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;
+ }
+ }
+ }
+done:
+ tb->is_dummy_print = 0;
+ DBG(TAB, ul_debugobj(tb, "-----final width: %zu (rc=%d)-----", width, rc));
+ ON_DBG(TAB, dbg_columns(tb));
+
+ return rc;
+}
diff --git a/libsmartcols/src/cell.c b/libsmartcols/src/cell.c
new file mode 100644
index 0000000..4cd6e59
--- /dev/null
+++ b/libsmartcols/src/cell.c
@@ -0,0 +1,257 @@
+/*
+ * cell.c - functions for table handling at the cell level
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: cell
+ * @title: Cell
+ * @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
+ * destroys all line cells too.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "smartcolsP.h"
+
+/*
+ * The cell has no ref-counting, free() and new() functions. All is
+ * handled by libscols_line.
+ */
+
+/**
+ * scols_reset_cell:
+ * @ce: pointer to a struct libscols_cell instance
+ *
+ * Frees the cell's internal data and resets its status.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_reset_cell(struct libscols_cell *ce)
+{
+ if (!ce)
+ return -EINVAL;
+
+ /*DBG(CELL, ul_debugobj(ce, "reset"));*/
+ free(ce->data);
+ free(ce->color);
+ memset(ce, 0, sizeof(*ce));
+ return 0;
+}
+
+/**
+ * scols_cell_set_data:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @data: data (used for scols_print_table())
+ *
+ * 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 *data)
+{
+ return strdup_to_struct_member(ce, data, data);
+}
+
+/**
+ * scols_cell_refer_data:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @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
+ * for situations when the data for the cell are already composed in allocated
+ * memory (e.g. asprintf()) to avoid extra unnecessary strdup().
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_refer_data(struct libscols_cell *ce, char *data)
+{
+ if (!ce)
+ return -EINVAL;
+ free(ce->data);
+ ce->data = data;
+ return 0;
+}
+
+/**
+ * scols_cell_get_data:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Returns: data in @ce or NULL.
+ */
+const char *scols_cell_get_data(const struct libscols_cell *ce)
+{
+ return ce ? ce->data : NULL;
+}
+
+/**
+ * scols_cell_set_userdata:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @data: private user data
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_set_userdata(struct libscols_cell *ce, void *data)
+{
+ if (!ce)
+ return -EINVAL;
+ ce->userdata = data;
+ return 0;
+}
+
+/**
+ * scols_cell_get_userdata
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Returns: user data
+ */
+void *scols_cell_get_userdata(struct libscols_cell *ce)
+{
+ return ce->userdata;
+}
+
+/**
+ * scols_cmpstr_cells:
+ * @a: pointer to cell
+ * @b: pointer to cell
+ * @data: unused pointer to private data (defined by API)
+ *
+ * Compares cells data by strcmp(). The function is designed for
+ * scols_column_set_cmpfunc() and scols_sort_table().
+ *
+ * Returns: follows strcmp() return values.
+ */
+int scols_cmpstr_cells(struct libscols_cell *a,
+ struct libscols_cell *b,
+ __attribute__((__unused__)) void *data)
+{
+ const char *adata, *bdata;
+
+ if (a == b)
+ return 0;
+
+ adata = scols_cell_get_data(a);
+ bdata = scols_cell_get_data(b);
+
+ if (adata == NULL && bdata == NULL)
+ return 0;
+ if (adata == NULL)
+ return -1;
+ if (bdata == NULL)
+ return 1;
+ return strcmp(adata, bdata);
+}
+
+/**
+ * scols_cell_set_color:
+ * @ce: a pointer to a struct libscols_cell instance
+ * @color: color name or ESC sequence
+ *
+ * Set the color of @ce to @color.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_set_color(struct libscols_cell *ce, const char *color)
+{
+ if (color && isalpha(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
+ }
+ return strdup_to_struct_member(ce, color, color);
+}
+
+/**
+ * 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;
+ ce->flags = flags;
+ return 0;
+}
+
+/**
+ * scols_cell_get_flags:
+ * @ce: a pointer to a struct libscols_cell instance
+ *
+ * Returns: the current flags
+ */
+int scols_cell_get_flags(const struct libscols_cell *ce)
+{
+ 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;
+ if (ce->flags & SCOLS_CELL_FL_CENTER)
+ return SCOLS_CELL_FL_CENTER;
+
+ return SCOLS_CELL_FL_LEFT; /* default */
+}
+
+/**
+ * scols_cell_copy_content:
+ * @dest: a pointer to a struct libscols_cell instance
+ * @src: a pointer to an immutable struct libscols_cell instance
+ *
+ * Copy the contents of @src into @dest.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_cell_copy_content(struct libscols_cell *dest,
+ const struct libscols_cell *src)
+{
+ int rc;
+
+ 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(src, "copy"));
+ return rc;
+}
diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c
new file mode 100644
index 0000000..d5a00f0
--- /dev/null
+++ b/libsmartcols/src/column.c
@@ -0,0 +1,643 @@
+/*
+ * column.c - functions for table handling at the column level
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: column
+ * @title: Column
+ * @short_description: defines output columns formats, headers, etc.
+ *
+ * An API to access and modify per-column data and information.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "mbsalign.h"
+
+#include "smartcolsP.h"
+
+/**
+ * scols_new_column:
+ *
+ * Allocates space for a new column.
+ *
+ * Returns: a pointer to a new struct libscols_column instance, NULL in case of an ENOMEM error.
+ */
+struct libscols_column *scols_new_column(void)
+{
+ struct libscols_column *cl;
+
+ cl = calloc(1, sizeof(*cl));
+ if (!cl)
+ return NULL;
+ DBG(COL, ul_debugobj(cl, "alloc"));
+ cl->refcount = 1;
+ INIT_LIST_HEAD(&cl->cl_columns);
+ return cl;
+}
+
+/**
+ * scols_ref_column:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Increases the refcount of @cl.
+ */
+void scols_ref_column(struct libscols_column *cl)
+{
+ if (cl)
+ cl->refcount++;
+}
+
+/**
+ * scols_unref_column:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Decreases the refcount of @cl. When the count falls to zero, the instance
+ * is automatically deallocated.
+ */
+void scols_unref_column(struct libscols_column *cl)
+{
+ if (cl && --cl->refcount <= 0) {
+ DBG(COL, ul_debugobj(cl, "dealloc"));
+ list_del(&cl->cl_columns);
+ scols_reset_cell(&cl->header);
+ free(cl->color);
+ free(cl->safechars);
+ free(cl->pending_data_buf);
+ free(cl->shellvar);
+ free(cl);
+ }
+}
+
+/**
+ * scols_copy_column:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Creates a new column and copies @cl's data over to it.
+ *
+ * Returns: a pointer to a new struct libscols_column instance.
+ */
+struct libscols_column *scols_copy_column(const struct libscols_column *cl)
+{
+ struct libscols_column *ret;
+
+ if (!cl)
+ return NULL;
+ ret = scols_new_column();
+ if (!ret)
+ return NULL;
+
+ DBG(COL, ul_debugobj(cl, "copy"));
+
+ if (scols_column_set_color(ret, cl->color))
+ goto err;
+ if (scols_cell_copy_content(&ret->header, &cl->header))
+ goto err;
+
+ ret->width = cl->width;
+ ret->width_min = cl->width_min;
+ ret->width_max = cl->width_max;
+ ret->width_avg = cl->width_avg;
+ ret->width_hint = cl->width_hint;
+ ret->flags = cl->flags;
+ ret->is_extreme = cl->is_extreme;
+ ret->is_groups = cl->is_groups;
+
+ return ret;
+err:
+ scols_unref_column(ret);
+ return NULL;
+}
+
+/**
+ * scols_column_set_whint:
+ * @cl: a pointer to a struct libscols_column instance
+ * @whint: a width hint
+ *
+ * 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)
+{
+ if (!cl)
+ return -EINVAL;
+
+ cl->width_hint = whint;
+ return 0;
+}
+
+/**
+ * scols_column_get_whint:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: The width hint of column @cl, a negative value in case of an error.
+ */
+double scols_column_get_whint(const struct libscols_column *cl)
+{
+ return cl->width_hint;
+}
+
+/**
+ * scols_column_set_flags:
+ * @cl: a pointer to a struct libscols_column instance
+ * @flags: a flag mask
+ *
+ * Sets the flags of @cl to @flags.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_column_set_flags(struct libscols_column *cl, int flags)
+{
+ 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--;
+ }
+
+ DBG(COL, ul_debugobj(cl, "setting flags from 0%x to 0%x", cl->flags, flags));
+ 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(const struct libscols_column *cl)
+{
+ return cl->flags;
+}
+
+/**
+ * scols_column_get_header:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: A pointer to a struct libscols_cell instance, representing the
+ * header info of column @cl or NULL in case of an error.
+ */
+struct libscols_cell *scols_column_get_header(struct libscols_column *cl)
+{
+ return &cl->header;
+}
+
+/**
+ * scols_column_set_name:
+ * @cl: a pointer to a struct libscols_column instance
+ * @name: column name
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.38
+ */
+int scols_column_set_name(struct libscols_column *cl, const char *name)
+{
+ struct libscols_cell *hr = scols_column_get_header(cl);
+
+ if (!hr)
+ return -EINVAL;
+
+ free(cl->shellvar);
+ cl->shellvar = NULL;
+
+ return scols_cell_set_data(hr, name);
+}
+
+/**
+ * scols_column_get_name:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: A pointer to a column name, which is stored in column header
+ *
+ * Since: 2.38
+ */
+const char *scols_column_get_name(struct libscols_column *cl)
+{
+ return scols_cell_get_data(&cl->header);
+}
+
+/**
+ * scols_column_get_name_as_shellvar
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Like scols_column_get_name(), but column name is modified to be compatible with shells
+ * requirements for variable names.
+ *
+ * Since: 2.38
+ */
+const char *scols_column_get_name_as_shellvar(struct libscols_column *cl)
+{
+ if (!cl->shellvar) {
+ const char *s, *name = scols_column_get_name(cl);
+ char *p;
+ size_t sz;
+
+ if (!name || !*name)
+ return NULL;
+
+ /* "1FOO%" --> "_1FOO_PCT */
+ sz = strlen(name) + 1 + 3;
+ p = cl->shellvar = calloc(1, sz + 1);
+ if (!cl->shellvar)
+ return NULL;
+
+ /* convert "1FOO" to "_1FOO" */
+ if (!isalpha(*name))
+ *p++ = '_';
+
+ /* replace all "bad" chars with "_" */
+ for (s = name; *s; s++)
+ *p++ = !isalnum(*s) ? '_' : *s;
+
+ if (!*s && *(s - 1) == '%') {
+ *p++ = 'P';
+ *p++ = 'C';
+ *p++ = 'T';
+ }
+ }
+ return cl->shellvar;
+}
+
+
+/**
+ * scols_column_set_color:
+ * @cl: a pointer to a struct libscols_column instance
+ * @color: color name or ESC sequence
+ *
+ * The default color for data cells and column header.
+ *
+ * If you want to set header specific color then use scols_column_get_header()
+ * and scols_cell_set_color().
+ *
+ * If you want to set data cell specific color the use scols_line_get_cell() +
+ * scols_cell_set_color().
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_column_set_color(struct libscols_column *cl, const char *color)
+{
+ if (color && isalpha(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
+ }
+ return strdup_to_struct_member(cl, color, color);
+}
+
+/**
+ * scols_column_get_color:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Returns: The current color setting of the 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)))
+{
+ 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 = cl->table && scols_table_is_noencoding(cl->table) ?
+ mbs_nwidth(data, p - data) :
+ mbs_safe_nwidth(data, p - data, NULL);
+ p++;
+ } else {
+ sz = cl->table && scols_table_is_noencoding(cl->table) ?
+ mbs_width(data) :
+ mbs_safe_width(data);
+ }
+ sum = max(sum, sz);
+ data = p;
+ }
+
+ return sum;
+}
+
+/**
+ * scols_column_set_cmpfunc:
+ * @cl: column
+ * @cmp: pointer to compare function
+ * @data: private data for cmp function
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_column_set_cmpfunc(struct libscols_column *cl,
+ int (*cmp)(struct libscols_cell *,
+ struct libscols_cell *,
+ void *),
+ void *data)
+{
+ if (!cl)
+ return -EINVAL;
+
+ cl->cmpfunc = cmp;
+ cl->cmpfunc_data = data;
+ return 0;
+}
+
+/**
+ * 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
+ *
+ * Extends SCOLS_FL_WRAP and can be used 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.
+ *
+ * Since: 2.29
+ */
+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)
+{
+ if (!cl)
+ return -EINVAL;
+
+ 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:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag tree.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_tree(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_TREE ? 1 : 0;
+}
+/**
+ * scols_column_is_right:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag right.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_right(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_RIGHT ? 1 : 0;
+}
+/**
+ * scols_column_is_strict_width:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag strict_width.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_strict_width(const struct libscols_column *cl)
+{
+ return cl->flags & SCOLS_FL_STRICTWIDTH ? 1 : 0;
+}
+/**
+ * scols_column_is_noextremes:
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Gets the value of @cl's flag no_extremes.
+ *
+ * Returns: 0 or 1
+ */
+int scols_column_is_noextremes(const struct libscols_column *cl)
+{
+ 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/grouping.c b/libsmartcols/src/grouping.c
new file mode 100644
index 0000000..0b27cb2
--- /dev/null
+++ b/libsmartcols/src/grouping.c
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2018 Karel Zak <kzak@redhat.com>
+ */
+#include "smartcolsP.h"
+
+/**
+ * SECTION: grouping
+ * @title: Grouping
+ * @short_description: lines grouing
+ *
+ * Lines groups manipulation API. The grouping API can be used to create M:N
+ * relations between lines and on tree-like output it prints extra chart to
+ * visualize these relations. The group has unlimited number of members and
+ * group childs. See libsmartcols/sample/grouping* for more details.
+ */
+
+/* Private API */
+void scols_ref_group(struct libscols_group *gr)
+{
+ if (gr)
+ gr->refcount++;
+}
+
+void scols_group_remove_children(struct libscols_group *gr)
+{
+ if (!gr)
+ return;
+
+ while (!list_empty(&gr->gr_children)) {
+ struct libscols_line *ln = list_entry(gr->gr_children.next,
+ struct libscols_line, ln_children);
+
+ DBG(GROUP, ul_debugobj(gr, "remove child"));
+ list_del_init(&ln->ln_children);
+ scols_ref_group(ln->parent_group);
+ ln->parent_group = NULL;
+ scols_unref_line(ln);
+ }
+}
+
+void scols_group_remove_members(struct libscols_group *gr)
+{
+ if (!gr)
+ return;
+
+ while (!list_empty(&gr->gr_members)) {
+ struct libscols_line *ln = list_entry(gr->gr_members.next,
+ struct libscols_line, ln_groups);
+
+ DBG(GROUP, ul_debugobj(gr, "remove member [%p]", ln));
+ list_del_init(&ln->ln_groups);
+
+ scols_unref_group(ln->group);
+ ln->group->nmembers++;
+ ln->group = NULL;
+
+ scols_unref_line(ln);
+ }
+}
+
+/* note group has to be already without members to deallocate */
+void scols_unref_group(struct libscols_group *gr)
+{
+ if (gr && --gr->refcount <= 0) {
+ DBG(GROUP, ul_debugobj(gr, "dealloc"));
+ scols_group_remove_children(gr);
+ list_del(&gr->gr_groups);
+ free(gr);
+ return;
+ }
+}
+
+
+static void groups_fix_members_order(struct libscols_line *ln)
+{
+ struct libscols_iter itr;
+ struct libscols_line *child;
+
+ if (ln->group) {
+ INIT_LIST_HEAD(&ln->ln_groups);
+ list_add_tail(&ln->ln_groups, &ln->group->gr_members);
+ DBG(GROUP, ul_debugobj(ln->group, "fixing member line=%p [%zu/%zu]",
+ ln, ln->group->nmembers,
+ list_count_entries(&ln->group->gr_members)));
+ }
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_line_next_child(ln, &itr, &child) == 0)
+ groups_fix_members_order(child);
+
+ /*
+ * We modify gr_members list, so is_last_group_member() does not have
+ * to provide reliable answer, we need to verify by list_count_entries().
+ */
+ if (ln->group
+ && is_last_group_member(ln)
+ && ln->group->nmembers == list_count_entries(&ln->group->gr_members)) {
+
+ DBG(GROUP, ul_debugobj(ln->group, "fixing childs"));
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_line_next_group_child(ln, &itr, &child) == 0)
+ groups_fix_members_order(child);
+ }
+}
+
+void scols_groups_fix_members_order(struct libscols_table *tb)
+{
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+ struct libscols_group *gr;
+
+ /* remove all from groups lists */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_group(tb, &itr, &gr) == 0) {
+ while (!list_empty(&gr->gr_members)) {
+ struct libscols_line *line = list_entry(gr->gr_members.next,
+ struct libscols_line, ln_groups);
+ list_del_init(&line->ln_groups);
+ }
+ }
+
+ /* add again to the groups list in order we walk in tree */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (ln->parent || ln->parent_group)
+ continue;
+ groups_fix_members_order(ln);
+ }
+
+ /* If group child is member of another group *
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_group(tb, &itr, &gr) == 0) {
+ struct libscols_iter xitr;
+ struct libscols_line *child;
+
+ scols_reset_iter(&xitr, SCOLS_ITER_FORWARD);
+ while (scols_line_next_group_child(ln, &xitr, &child) == 0)
+ groups_fix_members_order(child);
+ }
+ */
+}
+
+static inline const char *group_state_to_string(int state)
+{
+ static const char *grpstates[] = {
+ [SCOLS_GSTATE_NONE] = "none",
+ [SCOLS_GSTATE_FIRST_MEMBER] = "1st-member",
+ [SCOLS_GSTATE_MIDDLE_MEMBER] = "middle-member",
+ [SCOLS_GSTATE_LAST_MEMBER] = "last-member",
+ [SCOLS_GSTATE_MIDDLE_CHILD] = "middle-child",
+ [SCOLS_GSTATE_LAST_CHILD] = "last-child",
+ [SCOLS_GSTATE_CONT_MEMBERS] = "continue-members",
+ [SCOLS_GSTATE_CONT_CHILDREN] = "continue-children"
+ };
+
+ assert(state >= 0);
+ assert((size_t) state < ARRAY_SIZE(grpstates));
+
+ return grpstates[state];
+}
+/*
+static void grpset_debug(struct libscols_table *tb, struct libscols_line *ln)
+{
+ size_t i;
+
+ for (i = 0; i < tb->grpset_size; i++) {
+ if (tb->grpset[i]) {
+ struct libscols_group *gr = tb->grpset[i];
+
+ if (ln)
+ DBG(LINE, ul_debugobj(ln, "grpset[%zu]: %p %s", i,
+ gr, group_state_to_string(gr->state)));
+ else
+ DBG(LINE, ul_debug("grpset[%zu]: %p %s", i,
+ gr, group_state_to_string(gr->state)));
+ } else if (ln) {
+ DBG(LINE, ul_debugobj(ln, "grpset[%zu]: free", i));
+ } else
+ DBG(LINE, ul_debug("grpset[%zu]: free", i));
+ }
+}
+*/
+static int group_state_for_line(struct libscols_group *gr, struct libscols_line *ln)
+{
+ if (gr->state == SCOLS_GSTATE_NONE &&
+ (ln->group != gr || !is_first_group_member(ln)))
+ /*
+ * NONE is possible to translate to FIRST_MEMBER only, and only if
+ * line group matches with the current group.
+ */
+ return SCOLS_GSTATE_NONE;
+
+ if (ln->group != gr && ln->parent_group != gr) {
+ /* Not our line, continue */
+ if (gr->state == SCOLS_GSTATE_FIRST_MEMBER ||
+ gr->state == SCOLS_GSTATE_MIDDLE_MEMBER ||
+ gr->state == SCOLS_GSTATE_CONT_MEMBERS)
+ return SCOLS_GSTATE_CONT_MEMBERS;
+
+ if (gr->state == SCOLS_GSTATE_LAST_MEMBER ||
+ gr->state == SCOLS_GSTATE_MIDDLE_CHILD ||
+ gr->state == SCOLS_GSTATE_CONT_CHILDREN)
+ return SCOLS_GSTATE_CONT_CHILDREN;
+
+ } else if (ln->group == gr && is_first_group_member(ln)) {
+ return SCOLS_GSTATE_FIRST_MEMBER;
+
+ } else if (ln->group == gr && is_last_group_member(ln)) {
+ return SCOLS_GSTATE_LAST_MEMBER;
+
+ } else if (ln->group == gr && is_group_member(ln)) {
+ return SCOLS_GSTATE_MIDDLE_MEMBER;
+
+ } else if (ln->parent_group == gr && is_last_group_child(ln)) {
+ return SCOLS_GSTATE_LAST_CHILD;
+
+ } else if (ln->parent_group == gr && is_group_child(ln)) {
+ return SCOLS_GSTATE_MIDDLE_CHILD;
+ }
+
+ return SCOLS_GSTATE_NONE;
+}
+
+/*
+ * apply new @state to the chunk (addressed by @xx) of grpset used for the group (@gr)
+ */
+static void grpset_apply_group_state(struct libscols_group **xx, int state, struct libscols_group *gr)
+{
+ size_t i;
+
+ DBG(GROUP, ul_debugobj(gr, " applying state to grpset"));
+
+ /* gr->state holds the old state, @state is the new state
+ */
+ for (i = 0; i < SCOLS_GRPSET_CHUNKSIZ; i++)
+ xx[i] = state == SCOLS_GSTATE_NONE ? NULL : gr;
+
+ gr->state = state;
+}
+
+static struct libscols_group **grpset_locate_freespace(struct libscols_table *tb, int chunks, int prepend)
+{
+ size_t i, avail = 0;
+ struct libscols_group **tmp, **first = NULL;
+ const size_t wanted = chunks * SCOLS_GRPSET_CHUNKSIZ;
+
+ if (!tb->grpset_size)
+ prepend = 0;
+ /*
+ DBG(TAB, ul_debugobj(tb, "orig grpset:"));
+ grpset_debug(tb, NULL);
+ */
+ if (prepend) {
+ for (i = tb->grpset_size - 1; ; i--) {
+ if (tb->grpset[i] == NULL) {
+ first = &tb->grpset[i];
+ avail++;
+ } else
+ avail = 0;
+ if (avail == wanted)
+ goto done;
+ if (i == 0)
+ break;
+ }
+ } else {
+ for (i = 0; i < tb->grpset_size; i++) {
+ if (tb->grpset[i] == NULL) {
+ if (avail == 0)
+ first = &tb->grpset[i];
+ avail++;
+ } else
+ avail = 0;
+ if (avail == wanted)
+ goto done;
+ }
+ }
+
+ DBG(TAB, ul_debugobj(tb, " realocate grpset [sz: old=%zu, new=%zu, new_chunks=%d]",
+ tb->grpset_size, tb->grpset_size + wanted, chunks));
+
+ tmp = realloc(tb->grpset, (tb->grpset_size + wanted) * sizeof(struct libscols_group *));
+ if (!tmp)
+ return NULL;
+
+ tb->grpset = tmp;
+
+ if (prepend) {
+ DBG(TAB, ul_debugobj(tb, " prepending free space"));
+ char *dest = (char *) tb->grpset;
+
+ memmove( dest + (wanted * sizeof(struct libscols_group *)),
+ tb->grpset,
+ tb->grpset_size * sizeof(struct libscols_group *));
+ first = tb->grpset;
+ } else {
+ first = tb->grpset + tb->grpset_size;
+ }
+
+ memset(first, 0, wanted * sizeof(struct libscols_group *));
+ tb->grpset_size += wanted;
+
+done:
+ /*
+ DBG(TAB, ul_debugobj(tb, "new grpset:"));
+ grpset_debug(tb, NULL);
+ */
+ return first;
+}
+
+static struct libscols_group **grpset_locate_group(struct libscols_table *tb, struct libscols_group *gr)
+{
+ size_t i;
+
+ for (i = 0; i < tb->grpset_size; i++) {
+ if (gr == tb->grpset[i])
+ return &tb->grpset[i];
+ }
+
+ return NULL;
+}
+
+
+static int grpset_update(struct libscols_table *tb, struct libscols_line *ln, struct libscols_group *gr)
+{
+ struct libscols_group **xx;
+ int state;
+
+ DBG(LINE, ul_debugobj(ln, " group [%p] grpset update [grpset size=%zu]", gr, tb->grpset_size));
+
+ /* new state, note that gr->state still holds the original state */
+ state = group_state_for_line(gr, ln);
+ DBG(LINE, ul_debugobj(ln, " state %s --> %s",
+ group_state_to_string(gr->state),
+ group_state_to_string(state)));
+
+ if (state == SCOLS_GSTATE_FIRST_MEMBER && gr->state != SCOLS_GSTATE_NONE) {
+ DBG(LINE, ul_debugobj(ln, "wrong group initialization (%s)", group_state_to_string(gr->state)));
+ abort();
+ }
+ if (state != SCOLS_GSTATE_NONE && gr->state == SCOLS_GSTATE_LAST_CHILD) {
+ DBG(LINE, ul_debugobj(ln, "wrong group termination (%s)", group_state_to_string(gr->state)));
+ abort();
+ }
+ if (gr->state == SCOLS_GSTATE_LAST_MEMBER &&
+ !(state == SCOLS_GSTATE_LAST_CHILD ||
+ state == SCOLS_GSTATE_CONT_CHILDREN ||
+ state == SCOLS_GSTATE_MIDDLE_CHILD ||
+ state == SCOLS_GSTATE_NONE)) {
+ DBG(LINE, ul_debugobj(ln, "wrong group member->child order"));
+ abort();
+ }
+
+ /* should not happen; probably wrong line... */
+ if (gr->state == SCOLS_GSTATE_NONE && state == SCOLS_GSTATE_NONE)
+ return 0;
+
+ /* locate place in grpset where we draw the group */
+ if (!tb->grpset || gr->state == SCOLS_GSTATE_NONE)
+ xx = grpset_locate_freespace(tb, 1, 1);
+ else
+ xx = grpset_locate_group(tb, gr);
+ if (!xx) {
+ DBG(LINE, ul_debugobj(ln, "failed to locate group or reallocate grpset"));
+ return -ENOMEM;
+ }
+
+ grpset_apply_group_state(xx, state, gr);
+ /*ON_DBG(LINE, grpset_debug(tb, ln));*/
+ return 0;
+}
+
+static int grpset_update_active_groups(struct libscols_table *tb, struct libscols_line *ln)
+{
+ int rc = 0;
+ size_t i;
+ struct libscols_group *last = NULL;
+
+ DBG(LINE, ul_debugobj(ln, " update for active groups"));
+
+ for (i = 0; i < tb->grpset_size; i++) {
+ struct libscols_group *gr = tb->grpset[i];
+
+ if (!gr || last == gr)
+ continue;
+ last = gr;
+ rc = grpset_update(tb, ln, gr);
+ if (rc)
+ break;
+ }
+
+ DBG(LINE, ul_debugobj(ln, " <- active groups updated [rc=%d]", rc));
+ return rc;
+}
+
+int scols_groups_update_grpset(struct libscols_table *tb, struct libscols_line *ln)
+{
+ int rc = 0;
+
+ DBG(LINE, ul_debugobj(ln, " grpset update [line: group=%p, parent_group=%p",
+ ln->group, ln->parent_group));
+
+ rc = grpset_update_active_groups(tb, ln);
+ if (!rc && ln->group && ln->group->state == SCOLS_GSTATE_NONE) {
+ DBG(LINE, ul_debugobj(ln, " introduce a new group"));
+ rc = grpset_update(tb, ln, ln->group);
+ }
+ return rc;
+}
+
+void scols_groups_reset_state(struct libscols_table *tb)
+{
+ struct libscols_iter itr;
+ struct libscols_group *gr;
+
+ DBG(TAB, ul_debugobj(tb, "reset groups states"));
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_group(tb, &itr, &gr) == 0) {
+ DBG(GROUP, ul_debugobj(gr, " reset to NONE"));
+ gr->state = SCOLS_GSTATE_NONE;
+ }
+
+ if (tb->grpset) {
+ DBG(TAB, ul_debugobj(tb, " zeroize grpset"));
+ memset(tb->grpset, 0, tb->grpset_size * sizeof(struct libscols_group *));
+ }
+ tb->ngrpchlds_pending = 0;
+}
+
+static void add_member(struct libscols_group *gr, struct libscols_line *ln)
+{
+ DBG(GROUP, ul_debugobj(gr, "add member %p", ln));
+
+ ln->group = gr;
+ gr->nmembers++;
+ scols_ref_group(gr);
+
+ INIT_LIST_HEAD(&ln->ln_groups);
+ list_add_tail(&ln->ln_groups, &gr->gr_members);
+ scols_ref_line(ln);
+}
+
+/*
+ * Returns first group which is ready to print group children.
+ *
+ * This function scans grpset[] in backward order and returns first group
+ * with SCOLS_GSTATE_CONT_CHILDREN or SCOLS_GSTATE_LAST_MEMBER state.
+ */
+struct libscols_group *scols_grpset_get_printable_children(struct libscols_table *tb)
+{
+ size_t i;
+
+ for (i = tb->grpset_size; i > 0; i -= SCOLS_GRPSET_CHUNKSIZ) {
+ struct libscols_group *gr = tb->grpset[i-1];
+
+ if (gr == NULL)
+ continue;
+ if (gr->state == SCOLS_GSTATE_CONT_CHILDREN ||
+ gr->state == SCOLS_GSTATE_LAST_MEMBER)
+ return gr;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * scols_table_group_lines:
+ * @tb: a pointer to a struct libscols_table instance
+ * @ln: new group member
+ * @member: group member
+ * @id: group identifier (unused, not implemented yet), use zero.
+ *
+ * This function add line @ln to group of lines represented by @member. If the
+ * group is not yet defined (@member is not member of any group) than a new one
+ * is allocated.
+ *
+ * The @ln maybe a NULL -- in this case only a new group is allocated if not
+ * defined yet.
+ *
+ * Note that the same line cannot be member of more groups (not implemented
+ * yet). The child of any group can be member of another group.
+ *
+ * The @id is not used for now, use 0. The plan is to use it to support
+ * multi-group membership in future.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_table_group_lines( struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_line *member,
+ __attribute__((__unused__)) int id)
+{
+ struct libscols_group *gr = NULL;
+
+ if (!tb || !member) {
+ DBG(GROUP, ul_debugobj(gr, "failed group lines (no table or member)"));
+ return -EINVAL;
+ }
+ if (ln) {
+ if (ln->group && !member->group) {
+ DBG(GROUP, ul_debugobj(gr, "failed group lines (new group, line member of another)"));
+ return -EINVAL;
+ }
+ if (ln->group && member->group && ln->group != member->group) {
+ DBG(GROUP, ul_debugobj(gr, "failed group lines (groups mismatch bwteen member and line"));
+ return -EINVAL;
+ }
+ }
+
+ gr = member->group;
+
+ /* create a new group */
+ if (!gr) {
+ gr = calloc(1, sizeof(*gr));
+ if (!gr)
+ return -ENOMEM;
+ DBG(GROUP, ul_debugobj(gr, "alloc"));
+ gr->refcount = 1;
+ INIT_LIST_HEAD(&gr->gr_members);
+ INIT_LIST_HEAD(&gr->gr_children);
+ INIT_LIST_HEAD(&gr->gr_groups);
+
+ /* add group to the table */
+ list_add_tail(&gr->gr_groups, &tb->tb_groups);
+
+ /* add the first member */
+ add_member(gr, member);
+ }
+
+ /* add to group */
+ if (ln && !ln->group)
+ add_member(gr, ln);
+
+ return 0;
+}
+
+/**
+ * scols_line_link_group:
+ * @ln: line instance
+ * @member: group member
+ * @id: group identifier (unused, not implemented yet))
+ *
+ * Define @ln as child of group represented by group @member. The line @ln
+ * cannot be child of any other line. It's possible to create group->child or
+ * parent->child relationship, but no both for the same line (child).
+ *
+ * The @id is not used for now, use 0. The plan is to use it to support
+ * multi-group membership in future.
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_line_link_group(struct libscols_line *ln, struct libscols_line *member,
+ __attribute__((__unused__)) int id)
+{
+ if (!ln || !member || !member->group || ln->parent)
+ return -EINVAL;
+
+ if (!list_empty(&ln->ln_children))
+ return -EINVAL;
+
+ DBG(GROUP, ul_debugobj(member->group, "add child"));
+
+ list_add_tail(&ln->ln_children, &member->group->gr_children);
+ scols_ref_line(ln);
+
+ ln->parent_group = member->group;
+ scols_ref_group(member->group);
+
+ return 0;
+}
diff --git a/libsmartcols/src/init.c b/libsmartcols/src/init.c
new file mode 100644
index 0000000..dfd7510
--- /dev/null
+++ b/libsmartcols/src/init.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: init
+ * @title: Library initialization
+ * @short_description: initialize debugging
+ *
+ * The library debug stuff.
+ */
+
+#include <stdarg.h>
+
+#include "smartcolsP.h"
+
+UL_DEBUG_DEFINE_MASK(libsmartcols);
+UL_DEBUG_DEFINE_MASKNAMES(libsmartcols) =
+{
+ { "all", SCOLS_DEBUG_ALL, "info about all subsystems" },
+ { "buff", SCOLS_DEBUG_BUFF, "output buffer utils" },
+ { "cell", SCOLS_DEBUG_CELL, "table cell utils" },
+ { "col", SCOLS_DEBUG_COL, "cols utils" },
+ { "help", SCOLS_DEBUG_HELP, "this help" },
+ { "group", SCOLS_DEBUG_GROUP, "lines grouping utils" },
+ { "line", SCOLS_DEBUG_LINE, "table line utils" },
+ { "tab", SCOLS_DEBUG_TAB, "table utils" },
+ { NULL, 0, NULL }
+};
+
+/**
+ * scols_init_debug:
+ * @mask: debug mask (0xffff to enable full debugging)
+ *
+ * If the @mask is not specified, then this function reads
+ * the LIBSMARTCOLS_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. Calling
+ * this function twice has no effect.
+ */
+void scols_init_debug(int mask)
+{
+ if (libsmartcols_debug_mask)
+ return;
+
+ __UL_INIT_DEBUG_FROM_ENV(libsmartcols, SCOLS_DEBUG_, mask, LIBSMARTCOLS_DEBUG);
+
+ if (libsmartcols_debug_mask != SCOLS_DEBUG_INIT
+ && libsmartcols_debug_mask != (SCOLS_DEBUG_HELP|SCOLS_DEBUG_INIT)) {
+ const char *ver = NULL;
+
+ scols_get_library_version(&ver);
+
+ DBG(INIT, ul_debug("library debug mask: 0x%04x", libsmartcols_debug_mask));
+ DBG(INIT, ul_debug("library version: %s", ver));
+ }
+ ON_DBG(HELP, ul_debug_print_masks("LIBSMARTCOLS_DEBUG",
+ UL_DEBUG_MASKNAMES(libsmartcols)));
+}
diff --git a/libsmartcols/src/iter.c b/libsmartcols/src/iter.c
new file mode 100644
index 0000000..91cc080
--- /dev/null
+++ b/libsmartcols/src/iter.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009-2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: iter
+ * @title: Iterator
+ * @short_description: unified iterator
+ *
+ * The iterator keeps the direction and the last position
+ * for access to the internal library tables/lists.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "smartcolsP.h"
+
+/**
+ * scols_new_iter:
+ * @direction: SCOLS_INTER_{FOR,BACK}WARD direction
+ *
+ * Returns: newly allocated generic libmount iterator.
+ */
+struct libscols_iter *scols_new_iter(int direction)
+{
+ struct libscols_iter *itr = calloc(1, sizeof(*itr));
+ if (!itr)
+ return NULL;
+ itr->direction = direction;
+ return itr;
+}
+
+/**
+ * scols_free_iter:
+ * @itr: iterator pointer
+ *
+ * Deallocates the iterator.
+ */
+void scols_free_iter(struct libscols_iter *itr)
+{
+ free(itr);
+}
+
+/**
+ * scols_reset_iter:
+ * @itr: iterator pointer
+ * @direction: SCOLS_INTER_{FOR,BACK}WARD or -1 to keep the direction unchanged
+ *
+ * Resets the iterator.
+ */
+void scols_reset_iter(struct libscols_iter *itr, int direction)
+{
+ if (direction == -1)
+ direction = itr->direction;
+
+ memset(itr, 0, sizeof(*itr));
+ itr->direction = direction;
+}
+
+/**
+ * scols_iter_get_direction:
+ * @itr: iterator pointer
+ *
+ * Returns: SCOLS_INTER_{FOR,BACK}WARD
+ */
+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
new file mode 100644
index 0000000..6b64bc8
--- /dev/null
+++ b/libsmartcols/src/libsmartcols.h.in
@@ -0,0 +1,346 @@
+/*
+ * Prints table or tree.
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef _LIBSMARTCOLS_H
+#define _LIBSMARTCOLS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+/**
+ * LIBSMARTCOLS_VERSION:
+ *
+ * Library version string
+ */
+#define LIBSMARTCOLS_VERSION "@LIBSMARTCOLS_VERSION@"
+
+/**
+ * libscols_iter:
+ *
+ * Generic iterator
+ */
+struct libscols_iter;
+
+/**
+ * libscols_symbols:
+ *
+ * Symbol groups for printing tree hierarchies
+ */
+struct libscols_symbols;
+
+/**
+ * libscols_cell:
+ *
+ * A cell - the smallest library object
+ */
+struct libscols_cell;
+
+/**
+ * libscols_line:
+ *
+ * A line - an array of cells
+ */
+struct libscols_line;
+
+/**
+ * libscols_table:
+ *
+ * A table - The most abstract object, encapsulating lines, columns, symbols and cells
+ */
+struct libscols_table;
+
+/**
+ * libscols_column:
+ *
+ * A column - defines the number of columns and column names
+ */
+struct libscols_column;
+
+/* iter.c */
+enum {
+
+ SCOLS_ITER_FORWARD = 0,
+ SCOLS_ITER_BACKWARD
+};
+
+/*
+ * Column flags
+ */
+enum {
+ SCOLS_FL_TRUNC = (1 << 0), /* truncate fields data if necessary */
+ SCOLS_FL_TREE = (1 << 1), /* use tree "ascii art" */
+ 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,
+ SCOLS_JSON_ARRAY_STRING = 3, /* e.g. for multi-line (SCOLS_FL_WRAP) cells */
+ SCOLS_JSON_ARRAY_NUMBER = 4,
+ SCOLS_JSON_BOOLEAN_OPTIONAL = 5,
+};
+
+/*
+ * 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(const struct libscols_iter *itr);
+
+/* init.c */
+extern void scols_init_debug(int mask);
+
+/* version.c */
+extern int scols_parse_version_string(const char *ver_string);
+extern int scols_get_library_version(const char **ver_string);
+
+/* symbols.c */
+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 *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);
+
+extern int scols_symbols_set_group_vertical(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_horizontal(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_first_member(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_last_member(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_middle_member(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_last_child(struct libscols_symbols *sy, const char *str);
+extern int scols_symbols_set_group_middle_child(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 *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(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(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(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(const struct libscols_column *cl);
+extern struct libscols_table *scols_column_get_table(const struct libscols_column *cl);
+
+extern int scols_column_set_name(struct libscols_column *cl, const char *name);
+extern const char *scols_column_get_name(struct libscols_column *cl);
+extern const char *scols_column_get_name_as_shellvar(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);
+extern void scols_unref_line(struct libscols_line *ln);
+extern int scols_line_alloc_cells(struct libscols_line *ln, size_t n);
+extern void scols_line_free_cells(struct libscols_line *ln);
+extern int scols_line_set_userdata(struct libscols_line *ln, void *data);
+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(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(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 int scols_line_set_column_data(struct libscols_line *ln, struct libscols_column *cl, const char *data);
+extern const char *scols_line_get_column_data(struct libscols_line *ln, struct libscols_column *cl);
+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(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_shellvar(const struct libscols_table *tb);
+extern int scols_table_is_maxout(const struct libscols_table *tb);
+extern int scols_table_is_minout(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_shellvar(struct libscols_table *tb, int enable);
+extern int scols_table_enable_maxout(struct libscols_table *tb, int enable);
+extern int scols_table_enable_minout(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);
+
+extern struct libscols_table *scols_new_table(void);
+extern void scols_ref_table(struct libscols_table *tb);
+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 int scols_table_set_columns_iter(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_column *cl);
+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);
+extern void scols_table_remove_lines(struct libscols_table *tb);
+extern int scols_table_next_line(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_line **ln);
+extern struct libscols_line *scols_table_new_line(struct libscols_table *tb, struct libscols_line *parent);
+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(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);
+
+/* grouping.c */
+int scols_line_link_group(struct libscols_line *ln, struct libscols_line *member, int id);
+int scols_table_group_lines(struct libscols_table *tb, struct libscols_line *ln,
+ struct libscols_line *member, int id);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSMARTCOLS_H */
diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym
new file mode 100644
index 0000000..309535f
--- /dev/null
+++ b/libsmartcols/src/libsmartcols.sym
@@ -0,0 +1,211 @@
+/*
+ * symbols since util-linux 2.25
+ *
+ * Copyright (C) 2014-2016 Karel Zak <kzak@redhat.com>
+ */
+SMARTCOLS_2.25 {
+global:
+ scols_cell_copy_content;
+ scols_cell_get_color;
+ scols_cell_get_data;
+ scols_cell_get_userdata;
+ scols_cell_refer_data;
+ scols_cell_set_color;
+ scols_cell_set_data;
+ scols_cell_set_userdata;
+ scols_cmpstr_cells;
+ scols_column_get_color;
+ scols_column_get_flags;
+ scols_column_get_header;
+ scols_column_get_whint;
+ scols_column_is_noextremes;
+ scols_column_is_right;
+ scols_column_is_strict_width;
+ scols_column_is_tree;
+ scols_column_is_trunc;
+ scols_column_set_cmpfunc;
+ scols_column_set_color;
+ scols_column_set_flags;
+ scols_column_set_whint;
+ scols_copy_column;
+ scols_copy_line;
+ scols_copy_symbols;
+ scols_copy_table;
+ scols_free_iter;
+ scols_get_library_version;
+ scols_init_debug;
+ scols_iter_get_direction;
+ scols_line_add_child;
+ scols_line_alloc_cells;
+ scols_line_free_cells;
+ scols_line_get_cell;
+ scols_line_get_color;
+ scols_line_get_column_cell;
+ scols_line_get_ncells;
+ scols_line_get_parent;
+ scols_line_get_userdata;
+ scols_line_has_children;
+ scols_line_next_child;
+ scols_line_refer_data;
+ scols_line_remove_child;
+ scols_line_set_color;
+ scols_line_set_data;
+ scols_line_set_userdata;
+ scols_new_column;
+ scols_new_iter;
+ scols_new_line;
+ scols_new_symbols;
+ scols_new_table;
+ scols_parse_version_string;
+ scols_print_table;
+ scols_print_table_to_string;
+ scols_ref_column;
+ scols_ref_line;
+ scols_ref_symbols;
+ scols_ref_table;
+ scols_reset_cell;
+ scols_reset_iter;
+ scols_sort_table;
+ scols_symbols_set_branch;
+ scols_symbols_set_right;
+ scols_symbols_set_vertical;
+ scols_table_add_column;
+ scols_table_add_line;
+ scols_table_colors_wanted;
+ scols_table_enable_ascii;
+ scols_table_enable_colors;
+ scols_table_enable_export;
+ scols_table_enable_maxout;
+ scols_table_enable_noheadings;
+ 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_ncols;
+ scols_table_get_nlines;
+ scols_table_get_stream;
+ scols_table_is_ascii;
+ scols_table_is_empty;
+ scols_table_is_export;
+ scols_table_is_maxout;
+ scols_table_is_noheadings;
+ scols_table_is_raw;
+ scols_table_is_tree;
+ scols_table_new_column;
+ scols_table_new_line;
+ scols_table_next_column;
+ scols_table_next_line;
+ scols_table_reduce_termwidth;
+ scols_table_remove_column;
+ scols_table_remove_columns;
+ scols_table_remove_line;
+ scols_table_remove_lines;
+ scols_table_set_column_separator;
+ scols_table_set_line_separator;
+ scols_table_set_stream;
+ scols_table_set_symbols;
+ scols_unref_column;
+ scols_unref_line;
+ scols_unref_symbols;
+ scols_unref_table;
+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;
+
+SMARTCOLS_2.34 {
+ scols_table_group_lines;
+ scols_line_link_group;
+ scols_symbols_set_group_vertical;
+ scols_symbols_set_group_horizontal;
+ scols_symbols_set_group_first_member;
+ scols_symbols_set_group_last_member;
+ scols_symbols_set_group_middle_member;
+ scols_symbols_set_group_last_child;
+ scols_symbols_set_group_middle_child;
+} SMARTCOLS_2.33;
+
+SMARTCOLS_2.35 {
+ scols_table_enable_minout;
+ scols_table_is_minout;
+ scols_table_set_columns_iter;
+} SMARTCOLS_2.34;
+
+SMARTCOLS_2.38 {
+ scols_line_get_column_data;
+ scols_column_set_name;
+ scols_column_get_name;
+ scols_column_get_name_as_shellvar;
+ scols_table_is_shellvar;
+ scols_table_enable_shellvar;
+} SMARTCOLS_2.35;
diff --git a/libsmartcols/src/line.c b/libsmartcols/src/line.c
new file mode 100644
index 0000000..a5d39b4
--- /dev/null
+++ b/libsmartcols/src/line.c
@@ -0,0 +1,560 @@
+/*
+ * line.c - functions for table handling at the line level
+ *
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: line
+ * @title: Line
+ * @short_description: cells container, also keeps tree (parent->child) information
+ *
+ * An API to access and modify per-line data and information.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "smartcolsP.h"
+
+/**
+ * scols_new_line:
+ *
+ * Note that the line is allocated without cells, the cells will be allocated
+ * later when you add the line to the table. If you want to use the line
+ * without table then you have to explicitly allocate the cells by
+ * scols_line_alloc_cells().
+ *
+ * Returns: a pointer to a new struct libscols_line instance.
+ */
+struct libscols_line *scols_new_line(void)
+{
+ struct libscols_line *ln;
+
+ ln = calloc(1, sizeof(*ln));
+ if (!ln)
+ return NULL;
+
+ DBG(LINE, ul_debugobj(ln, "alloc"));
+ ln->refcount = 1;
+ INIT_LIST_HEAD(&ln->ln_lines);
+ INIT_LIST_HEAD(&ln->ln_children);
+ INIT_LIST_HEAD(&ln->ln_branch);
+ INIT_LIST_HEAD(&ln->ln_groups);
+ return ln;
+}
+
+/**
+ * scols_ref_line:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Increases the refcount of @ln.
+ */
+void scols_ref_line(struct libscols_line *ln)
+{
+ if (ln)
+ ln->refcount++;
+}
+
+/**
+ * scols_unref_line:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Decreases the refcount of @ln. When the count falls to zero, the instance
+ * is automatically deallocated.
+ */
+void scols_unref_line(struct libscols_line *ln)
+{
+ if (ln && --ln->refcount <= 0) {
+ DBG(CELL, ul_debugobj(ln, "dealloc"));
+ list_del(&ln->ln_lines);
+ list_del(&ln->ln_children);
+ list_del(&ln->ln_groups);
+ scols_unref_group(ln->group);
+ scols_line_free_cells(ln);
+ free(ln->color);
+ free(ln);
+ return;
+ }
+}
+
+/**
+ * scols_line_free_cells:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Frees the allocated cells referenced to by @ln.
+ */
+void scols_line_free_cells(struct libscols_line *ln)
+{
+ size_t i;
+
+ if (!ln || !ln->cells)
+ return;
+
+ DBG(LINE, ul_debugobj(ln, "free cells"));
+
+ for (i = 0; i < ln->ncells; i++)
+ scols_reset_cell(&ln->cells[i]);
+
+ free(ln->cells);
+ ln->ncells = 0;
+ ln->cells = NULL;
+}
+
+/**
+ * scols_line_alloc_cells:
+ * @ln: a pointer to a struct libscols_line instance
+ * @n: the number of elements
+ *
+ * Allocates space for @n cells. This function is optional,
+ * and libsmartcols automatically allocates necessary cells
+ * according to number of columns in the table when you add
+ * the line to the table. See scols_table_add_line().
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
+{
+ struct libscols_cell *ce;
+
+ if (!ln)
+ return -EINVAL;
+ if (ln->ncells == n)
+ return 0;
+
+ if (!n) {
+ scols_line_free_cells(ln);
+ return 0;
+ }
+
+ DBG(LINE, ul_debugobj(ln, "alloc %zu cells", n));
+
+ ce = realloc(ln->cells, n * sizeof(struct libscols_cell));
+ if (!ce)
+ return -errno;
+
+ if (n > ln->ncells)
+ memset(ce + ln->ncells, 0,
+ (n - ln->ncells) * sizeof(struct libscols_cell));
+
+ ln->cells = ce;
+ ln->ncells = 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
+ * @data: user data
+ *
+ * Binds @data to @ln.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_set_userdata(struct libscols_line *ln, void *data)
+{
+ if (!ln)
+ return -EINVAL;
+ ln->userdata = data;
+ return 0;
+}
+
+/**
+ * scols_line_get_userdata:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: user data
+ */
+void *scols_line_get_userdata(struct libscols_line *ln)
+{
+ return ln->userdata;
+}
+
+/**
+ * scols_line_remove_child:
+ * @ln: a pointer to a struct libscols_line instance
+ * @child: a pointer to a struct libscols_line instance
+ *
+ * Removes @child as a child of @ln.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child)
+{
+ if (!ln || !child)
+ return -EINVAL;
+
+ DBG(LINE, ul_debugobj(ln, "remove child"));
+
+ list_del_init(&child->ln_children);
+ child->parent = NULL;
+ scols_unref_line(child);
+
+ scols_unref_line(ln);
+ return 0;
+}
+
+/**
+ * scols_line_add_child:
+ * @ln: a pointer to a struct libscols_line instance
+ * @child: a pointer to a struct libscols_line instance
+ *
+ * Sets @child as a child of @ln.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_add_child(struct libscols_line *ln, struct libscols_line *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);
+
+ /* new reference from parent to child */
+ list_add_tail(&child->ln_children, &ln->ln_branch);
+
+ /* new reference from child to parent */
+ child->parent = ln;
+ return 0;
+}
+
+/**
+ * scols_line_get_parent:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * 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(const struct libscols_line *ln)
+{
+ return ln ? ln->parent : NULL;
+}
+
+/**
+ * scols_line_has_children:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: 1 if @ln has any children, otherwise 0.
+ */
+int scols_line_has_children(struct libscols_line *ln)
+{
+ return ln ? !list_empty(&ln->ln_branch) : 0;
+}
+
+/**
+ * scols_line_next_child:
+ * @ln: a pointer to a struct libscols_line instance
+ * @itr: a pointer to a struct libscols_iter instance
+ * @chld: a pointer to a pointer to a struct libscols_line instance
+ *
+ * Finds the next child and returns a pointer to it via @chld.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_next_child(struct libscols_line *ln,
+ struct libscols_iter *itr,
+ struct libscols_line **chld)
+{
+ int rc = 1;
+
+ if (!ln || !itr || !chld)
+ return -EINVAL;
+ *chld = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &ln->ln_branch);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/* private API */
+int scols_line_next_group_child(struct libscols_line *ln,
+ struct libscols_iter *itr,
+ struct libscols_line **chld)
+{
+ int rc = 1;
+
+ if (!ln || !itr || !chld || !ln->group)
+ return -EINVAL;
+ *chld = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &ln->group->gr_children);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children);
+ rc = 0;
+ }
+
+ 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: 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)
+{
+ if (color && isalnum(*color)) {
+ color = color_sequence_from_colorname(color);
+ if (!color)
+ return -EINVAL;
+ }
+ return strdup_to_struct_member(ln, color, color);
+}
+
+/**
+ * scols_line_get_color:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: @ln's color string, NULL in case of an error.
+ */
+const char *scols_line_get_color(const struct libscols_line *ln)
+{
+ return ln->color;
+}
+
+/**
+ * scols_line_get_ncells:
+ * @ln: a pointer to a struct libscols_line instance
+ *
+ * Returns: number of cells
+ */
+size_t scols_line_get_ncells(const struct libscols_line *ln)
+{
+ return ln->ncells;
+}
+
+/**
+ * scols_line_get_cell:
+ * @ln: a pointer to a struct libscols_line instance
+ * @n: cell number to retrieve
+ *
+ * Returns: the @n-th cell in @ln, NULL in case of an error.
+ */
+struct libscols_cell *scols_line_get_cell(struct libscols_line *ln,
+ size_t n)
+{
+ if (!ln || n >= ln->ncells)
+ return NULL;
+ return &ln->cells[n];
+}
+
+/**
+ * scols_line_get_column_cell:
+ * @ln: a pointer to a struct libscols_line instance
+ * @cl: pointer to cell
+ *
+ * Like scols_line_get_cell() by cell is referenced by column.
+ *
+ * Returns: the @n-th cell in @ln, NULL in case of an error.
+ */
+struct libscols_cell *scols_line_get_column_cell(
+ struct libscols_line *ln,
+ struct libscols_column *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_line instance
+ * @n: number of the cell, whose data is to be set
+ * @data: actual data to set
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data)
+{
+ struct libscols_cell *ce = scols_line_get_cell(ln, n);
+
+ if (!ce)
+ return -EINVAL;
+ 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_get_column_data:
+ * @ln: a pointer to a struct libscols_line instance
+ * @cl: column, whose data is to be get
+ *
+ * See also scols_cell_get_data()
+ *
+ * Returns: cell data or NULL.
+ *
+ * Since: 2.38
+ */
+const char *scols_line_get_column_data(struct libscols_line *ln,
+ struct libscols_column *cl)
+{
+ struct libscols_cell *cell = scols_line_get_column_cell(ln, cl);
+
+ return cell ? scols_cell_get_data(cell) : NULL;
+}
+
+
+/**
+ * scols_line_refer_data:
+ * @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
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data)
+{
+ struct libscols_cell *ce = scols_line_get_cell(ln, n);
+
+ if (!ce)
+ return -EINVAL;
+ 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_line instance
+ *
+ * Returns: A newly allocated copy of @ln, NULL in case of an error.
+ */
+struct libscols_line *scols_copy_line(const struct libscols_line *ln)
+{
+ struct libscols_line *ret;
+ size_t i;
+
+ if (!ln)
+ return NULL;
+
+ ret = scols_new_line();
+ if (!ret)
+ return NULL;
+ if (scols_line_set_color(ret, ln->color))
+ goto err;
+ if (scols_line_alloc_cells(ret, ln->ncells))
+ goto err;
+
+ ret->userdata = ln->userdata;
+ ret->ncells = ln->ncells;
+ ret->seqnum = ln->seqnum;
+
+ DBG(LINE, ul_debugobj(ln, "copy"));
+
+ for (i = 0; i < ret->ncells; ++i) {
+ if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i]))
+ goto err;
+ }
+
+ return ret;
+err:
+ scols_unref_line(ret);
+ return NULL;
+}
diff --git a/libsmartcols/src/print-api.c b/libsmartcols/src/print-api.c
new file mode 100644
index 0000000..52b2664
--- /dev/null
+++ b/libsmartcols/src/print-api.c
@@ -0,0 +1,220 @@
+#include "smartcolsP.h"
+
+/**
+ * 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 ul_buffer buf = UL_INIT_BUFFER;
+ struct libscols_iter itr;
+ int rc;
+
+ if (scols_table_is_tree(tb))
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing range from API"));
+
+ rc = __scols_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 = __scols_print_header(tb, &buf);
+ if (rc)
+ goto done;
+ }
+
+ rc = __scols_print_range(tb, &buf, &itr, end);
+done:
+ __scols_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 do_print_table(struct libscols_table *tb, int *is_empty)
+{
+ int rc = 0;
+ struct ul_buffer buf = UL_INIT_BUFFER;
+
+ 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 (scols_table_is_json(tb)) {
+ ul_jsonwrt_init(&tb->json, tb->out, 0);
+ ul_jsonwrt_root_open(&tb->json);
+ ul_jsonwrt_array_open(&tb->json, tb->name ? tb->name : "");
+ ul_jsonwrt_array_close(&tb->json);
+ ul_jsonwrt_root_close(&tb->json);
+ } else if (is_empty)
+ *is_empty = 1;
+ return 0;
+ }
+
+ tb->header_printed = 0;
+ rc = __scols_initialize_printing(tb, &buf);
+ if (rc)
+ return rc;
+
+ if (scols_table_is_json(tb)) {
+ ul_jsonwrt_root_open(&tb->json);
+ ul_jsonwrt_array_open(&tb->json, tb->name ? tb->name : "");
+ }
+
+ if (tb->format == SCOLS_FMT_HUMAN)
+ __scols_print_title(tb);
+
+ rc = __scols_print_header(tb, &buf);
+ if (rc)
+ goto done;
+
+ if (scols_table_is_tree(tb))
+ rc = __scols_print_tree(tb, &buf);
+ else
+ rc = __scols_print_table(tb, &buf);
+
+ if (scols_table_is_json(tb)) {
+ ul_jsonwrt_array_close(&tb->json);
+ ul_jsonwrt_root_close(&tb->json);
+ }
+done:
+ __scols_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 = do_print_table(tb, &empty);
+
+ if (rc == 0 && !empty && !scols_table_is_json(tb))
+ fputc('\n', tb->out);
+ return rc;
+}
+
+/**
+ * scols_print_table_to_string:
+ * @tb: table
+ * @data: pointer to the beginning of a memory area to print to
+ *
+ * Prints the table to @data.
+ *
+ * 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)
+{
+ FILE *stream, *old_stream;
+ size_t sz;
+ int rc;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "printing 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 = do_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
diff --git a/libsmartcols/src/print.c b/libsmartcols/src/print.c
new file mode 100644
index 0000000..906df7e
--- /dev/null
+++ b/libsmartcols/src/print.c
@@ -0,0 +1,1243 @@
+ /* print.c - functions to print table
+ *
+ * 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.
+ */
+
+/**
+ * SECTION: table_print
+ * @title: Table print
+ * @short_description: output functions
+ *
+ * Table output API.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <ctype.h>
+
+#include "mbsalign.h"
+#include "carefulputc.h"
+#include "smartcolsP.h"
+
+/* 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->tree_branch ? (tb)->symbols->tree_branch : "|-")
+#define vertical_symbol(tb) ((tb)->symbols->tree_vert ? (tb)->symbols->tree_vert : "| ")
+#define right_symbol(tb) ((tb)->symbols->tree_right ? (tb)->symbols->tree_right : "`-")
+
+#define grp_vertical_symbol(tb) ((tb)->symbols->group_vert ? (tb)->symbols->group_vert : "|")
+#define grp_horizontal_symbol(tb) ((tb)->symbols->group_horz ? (tb)->symbols->group_horz : "-")
+#define grp_m_first_symbol(tb) ((tb)->symbols->group_first_member ? (tb)->symbols->group_first_member : ",->")
+#define grp_m_last_symbol(tb) ((tb)->symbols->group_last_member ? (tb)->symbols->group_last_member : "\\->")
+#define grp_m_middle_symbol(tb) ((tb)->symbols->group_middle_member ? (tb)->symbols->group_middle_member : "|->")
+#define grp_c_middle_symbol(tb) ((tb)->symbols->group_middle_child ? (tb)->symbols->group_middle_child : "|-")
+#define grp_c_last_symbol(tb) ((tb)->symbols->group_last_child ? (tb)->symbols->group_last_child : "`-")
+
+#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)
+
+static int is_next_columns_empty(
+ struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln)
+{
+ struct libscols_iter itr;
+
+ if (!tb || !cl)
+ return 0;
+ if (is_last_column(cl))
+ return 1;
+ if (!ln)
+ return 0;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ scols_table_set_columns_iter(tb, &itr, cl);
+
+ /* skip current column */
+ scols_table_next_column(tb, &itr, &cl);
+
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ struct libscols_cell *ce;
+ const char *data = NULL;
+
+ if (scols_column_is_hidden(cl))
+ continue;
+ if (scols_column_is_tree(cl))
+ return 0;
+
+ ce = scols_line_get_cell(ln, cl->seqnum);
+ if (ce)
+ data = scols_cell_get_data(ce);
+ if (data && *data)
+ return 0;
+ }
+ return 1;
+}
+
+/* returns pointer to the end of used data */
+static int tree_ascii_art_to_buffer(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct ul_buffer *buf)
+{
+ const char *art;
+ int rc;
+
+ assert(ln);
+ assert(buf);
+
+ if (!ln->parent)
+ return 0;
+
+ rc = tree_ascii_art_to_buffer(tb, ln->parent, buf);
+ if (rc)
+ return rc;
+
+ if (is_last_child(ln))
+ art = " ";
+ else
+ art = vertical_symbol(tb);
+
+ return ul_buffer_append_string(buf, art);
+}
+
+static int grpset_is_empty( struct libscols_table *tb,
+ size_t idx,
+ size_t *rest)
+{
+ size_t i;
+
+ for (i = idx; i < tb->grpset_size; i++) {
+ if (tb->grpset[i] == NULL) {
+ if (rest)
+ (*rest)++;
+ } else
+ return 0;
+ }
+ return 1;
+}
+
+static int groups_ascii_art_to_buffer( struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct ul_buffer *buf,
+ int empty)
+{
+ int filled = 0;
+ size_t i, rest = 0;
+ const char *filler = cellpadding_symbol(tb);
+
+ if (!has_groups(tb))
+ return 0;
+
+ DBG(LINE, ul_debugobj(ln, "printing groups chart"));
+
+ if (tb->is_dummy_print)
+ return 0; /* allocate grpset[] only */
+
+ for (i = 0; i < tb->grpset_size; i += SCOLS_GRPSET_CHUNKSIZ) {
+ struct libscols_group *gr = tb->grpset[i];
+
+ if (!gr) {
+ ul_buffer_append_ntimes(buf, SCOLS_GRPSET_CHUNKSIZ, cellpadding_symbol(tb));
+ continue;
+ }
+
+ /*
+ * Empty cells (multi-line entries, etc.), print vertical symbols only
+ * to show that the group continues.
+ */
+ if (empty) {
+ switch (gr->state) {
+ case SCOLS_GSTATE_FIRST_MEMBER:
+ case SCOLS_GSTATE_MIDDLE_MEMBER:
+ case SCOLS_GSTATE_CONT_MEMBERS:
+ ul_buffer_append_string(buf, grp_vertical_symbol(tb));
+ ul_buffer_append_ntimes(buf, 2, filler);
+ break;
+
+ case SCOLS_GSTATE_LAST_MEMBER:
+ case SCOLS_GSTATE_MIDDLE_CHILD:
+ case SCOLS_GSTATE_CONT_CHILDREN:
+ ul_buffer_append_string(buf, filler);
+ ul_buffer_append_string(buf, grp_vertical_symbol(tb));
+ ul_buffer_append_string(buf, filler);
+ break;
+ case SCOLS_GSTATE_LAST_CHILD:
+ ul_buffer_append_ntimes(buf, 3, filler);
+ break;
+ }
+ continue;
+ }
+
+ /*
+ * Regular cell
+ */
+ switch (gr->state) {
+ case SCOLS_GSTATE_FIRST_MEMBER:
+ ul_buffer_append_string(buf, grp_m_first_symbol(tb));
+ break;
+ case SCOLS_GSTATE_MIDDLE_MEMBER:
+ ul_buffer_append_string(buf, grp_m_middle_symbol(tb));
+ break;
+ case SCOLS_GSTATE_LAST_MEMBER:
+ ul_buffer_append_string(buf, grp_m_last_symbol(tb));
+ break;
+ case SCOLS_GSTATE_CONT_MEMBERS:
+ ul_buffer_append_string(buf, grp_vertical_symbol(tb));
+ ul_buffer_append_ntimes(buf, 2, filler);
+ break;
+ case SCOLS_GSTATE_MIDDLE_CHILD:
+ ul_buffer_append_string(buf, filler);
+ ul_buffer_append_string(buf, grp_c_middle_symbol(tb));
+ if (grpset_is_empty(tb, i + SCOLS_GRPSET_CHUNKSIZ, &rest)) {
+ ul_buffer_append_ntimes(buf, rest+1, grp_horizontal_symbol(tb));
+ filled = 1;
+ }
+ filler = grp_horizontal_symbol(tb);
+ break;
+ case SCOLS_GSTATE_LAST_CHILD:
+ ul_buffer_append_string(buf, cellpadding_symbol(tb));
+ ul_buffer_append_string(buf, grp_c_last_symbol(tb));
+ if (grpset_is_empty(tb, i + SCOLS_GRPSET_CHUNKSIZ, &rest)) {
+ ul_buffer_append_ntimes(buf, rest+1, grp_horizontal_symbol(tb));
+ filled = 1;
+ }
+ filler = grp_horizontal_symbol(tb);
+ break;
+ case SCOLS_GSTATE_CONT_CHILDREN:
+ ul_buffer_append_string(buf, filler);
+ ul_buffer_append_string(buf, grp_vertical_symbol(tb));
+ ul_buffer_append_string(buf, filler);
+ break;
+ }
+
+ if (filled)
+ break;
+ }
+
+ if (!filled)
+ ul_buffer_append_string(buf, filler);
+ 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;
+}
+
+static void fputs_color_reset(struct libscols_table *tb)
+{
+ if (tb->cur_color) {
+ fputs(UL_COLOR_RESET, tb->out);
+ tb->cur_color = NULL;
+ }
+}
+
+static void fputs_color(struct libscols_table *tb, const char *color)
+{
+ if (tb->cur_color)
+ fputs_color_reset(tb);
+
+ tb->cur_color = color;
+ if (color)
+ fputs(color, tb->out);
+}
+
+static const char *get_cell_color(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln,
+ struct libscols_cell *ce)
+{
+ const char *color = NULL;
+
+ if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN)
+ return NULL;
+ if (ce)
+ color = ce->color;
+ if (!color && (!ln || !ln->color) && cl)
+ color = cl->color;
+ return color;
+}
+
+/* switch from line color to cell/column color */
+static void fputs_color_cell_open(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln,
+ struct libscols_cell *ce)
+{
+ const char *color = get_cell_color(tb, cl, ln, ce);
+
+ if (color)
+ fputs_color(tb, color);
+}
+
+/* switch from cell/column color to line color or reset */
+static void fputs_color_cell_close(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln,
+ struct libscols_cell *ce)
+{
+ const char *color = get_cell_color(tb, cl, ln, ce);
+
+ if (color)
+ fputs_color(tb, ln ? ln->color : NULL);
+}
+
+/* switch to line color */
+static void fputs_color_line_open(struct libscols_table *tb,
+ struct libscols_line *ln)
+{
+ if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN)
+ return;
+ fputs_color(tb, ln ? ln->color : NULL);
+}
+
+/* switch off all colors */
+static void fputs_color_line_close(struct libscols_table *tb)
+{
+ if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN)
+ return;
+ fputs_color_reset(tb);
+}
+
+/* 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 */
+ struct libscols_cell *ce,
+ size_t bufsz)
+{
+ size_t len_pad = 0; /* in screen cells as opposed to bytes */
+
+ DBG(COL, ul_debugobj(cl, " printing empty cell"));
+
+ fputs_color_cell_open(tb, cl, ln, ce);
+
+ /* generate tree/group ASCII-art rather than padding
+ */
+ if (ln && scols_column_is_tree(cl)) {
+ struct ul_buffer art = UL_INIT_BUFFER;
+ char *data;
+
+ if (ul_buffer_alloc_data(&art, bufsz) != 0)
+ goto done;
+
+ if (cl->is_groups)
+ groups_ascii_art_to_buffer(tb, ln, &art, 1);
+
+ tree_ascii_art_to_buffer(tb, ln, &art);
+
+ if (!list_empty(&ln->ln_branch) && has_pending_data(tb))
+ ul_buffer_append_string(&art, vertical_symbol(tb));
+
+ if (scols_table_is_noencoding(tb))
+ data = ul_buffer_get_data(&art, NULL, &len_pad);
+ else
+ data = ul_buffer_get_safe_data(&art, NULL, &len_pad, NULL);
+
+ if (data && len_pad)
+ fputs(data, tb->out);
+ ul_buffer_free_data(&art);
+ }
+
+done:
+ /* minout -- don't fill */
+ if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) {
+ fputs_color_cell_close(tb, cl, ln, ce);
+ return;
+ }
+
+ /* default -- fill except last column */
+ if (!scols_table_is_maxout(tb) && is_last_column(cl)) {
+ fputs_color_cell_close(tb, cl, ln, ce);
+ return;
+ }
+
+ /* fill rest of cell with space */
+ for(; len_pad < cl->width; ++len_pad)
+ fputs(cellpadding_symbol(tb), tb->out);
+
+ fputs_color_cell_close(tb, cl, ln, ce);
+
+ if (!is_last_column(cl))
+ fputs(colsep(tb), tb->out);
+}
+
+
+
+/* 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 */
+ struct libscols_cell *ce,
+ size_t bufsz)
+{
+ size_t i;
+
+ assert(tb);
+ assert(cl);
+
+ DBG(LINE, ul_debugobj(ln, "printing newline padding"));
+
+ fputs(linesep(tb), tb->out); /* line break */
+ tb->termlines_used++;
+
+ fputs_color_line_open(tb, ln);
+
+ /* 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, ce, bufsz);
+
+ fputs_color_line_close(tb);
+}
+
+/*
+ * 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)
+{
+ 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 = scols_table_is_noencoding(tb) ?
+ mbs_nwidth(data, bytes) :
+ 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);
+
+ fputs_color_cell_open(tb, cl, ln, ce);
+
+ fputs(data, tb->out);
+ free(data);
+
+ /* minout -- don't fill */
+ if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) {
+ fputs_color_cell_close(tb, cl, ln, ce);
+ return 0;
+ }
+
+ /* default -- fill except last column */
+ if (!scols_table_is_maxout(tb) && is_last_column(cl)) {
+ fputs_color_cell_close(tb, cl, ln, ce);
+ return 0;
+ }
+
+ /* fill rest of cell with space */
+ for(i = len; i < width; i++)
+ fputs(cellpadding_symbol(tb), tb->out);
+
+ fputs_color_cell_close(tb, cl, ln, ce);
+
+ if (!is_last_column(cl))
+ fputs(colsep(tb), tb->out);
+
+ return 0;
+err:
+ free(data);
+ return -errno;
+}
+
+static void print_json_data(struct libscols_table *tb,
+ struct libscols_column *cl,
+ const char *name,
+ char *data)
+{
+ switch (cl->json_type) {
+ case SCOLS_JSON_STRING:
+ /* name: "aaa" */
+ ul_jsonwrt_value_s(&tb->json, name, data);
+ break;
+ case SCOLS_JSON_NUMBER:
+ /* name: 123 */
+ ul_jsonwrt_value_raw(&tb->json, name, data);
+ break;
+ case SCOLS_JSON_BOOLEAN:
+ case SCOLS_JSON_BOOLEAN_OPTIONAL:
+ /* name: true|false|null */
+ if (cl->json_type == SCOLS_JSON_BOOLEAN_OPTIONAL && (!*data || !strcmp(data, "-"))) {
+ ul_jsonwrt_value_null(&tb->json, name);
+ } else {
+ ul_jsonwrt_value_boolean(&tb->json, name,
+ !*data ? 0 :
+ *data == '0' ? 0 :
+ *data == 'N' || *data == 'n' ? 0 : 1);
+ }
+ break;
+ case SCOLS_JSON_ARRAY_STRING:
+ case SCOLS_JSON_ARRAY_NUMBER:
+ /* name: [ "aaa", "bbb", "ccc" ] */
+ ul_jsonwrt_array_open(&tb->json, name);
+
+ if (!scols_column_is_customwrap(cl))
+ ul_jsonwrt_value_s(&tb->json, NULL, data);
+ else do {
+ char *next = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data);
+
+ if (cl->json_type == SCOLS_JSON_ARRAY_STRING)
+ ul_jsonwrt_value_s(&tb->json, NULL, data);
+ else
+ ul_jsonwrt_value_raw(&tb->json, NULL, data);
+ data = next;
+ } while (data);
+
+ ul_jsonwrt_array_close(&tb->json);
+ break;
+ }
+}
+
+static int print_data(struct libscols_table *tb,
+ struct libscols_column *cl,
+ struct libscols_line *ln, /* optional */
+ struct libscols_cell *ce, /* optional */
+ struct ul_buffer *buf)
+{
+ size_t len = 0, i, width, bytes;
+ char *data, *nextchunk;
+ const char *name = NULL;
+ int is_last;
+
+ assert(tb);
+ assert(cl);
+
+ data = ul_buffer_get_data(buf, NULL, NULL);
+ if (!data)
+ data = "";
+
+ if (tb->format != SCOLS_FMT_HUMAN) {
+ name = scols_table_is_shellvar(tb) ?
+ scols_column_get_name_as_shellvar(cl) :
+ scols_column_get_name(cl);
+ }
+
+ is_last = is_last_column(cl);
+
+ if (is_last && scols_table_is_json(tb) &&
+ scols_table_is_tree(tb) && has_children(ln))
+ /* "children": [] is the real last value */
+ is_last = 0;
+
+ switch (tb->format) {
+ case SCOLS_FMT_RAW:
+ fputs_nonblank(data, tb->out);
+ if (!is_last)
+ fputs(colsep(tb), tb->out);
+ return 0;
+
+ case SCOLS_FMT_EXPORT:
+ fputs(name, tb->out);
+ fputc('=', tb->out);
+ fputs_quoted(data, tb->out);
+ if (!is_last)
+ fputs(colsep(tb), tb->out);
+ return 0;
+
+ case SCOLS_FMT_JSON:
+ print_json_data(tb, cl, name, data);
+ return 0;
+
+ case SCOLS_FMT_HUMAN:
+ break; /* continue below */
+ }
+
+ /* Encode. Note that 'len' and 'width' are number of cells, not bytes.
+ */
+ if (scols_table_is_noencoding(tb))
+ data = ul_buffer_get_data(buf, &bytes, &len);
+ else
+ data = ul_buffer_get_safe_data(buf, &bytes, &len, scols_column_get_safechars(cl));
+
+ if (!data)
+ 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 = scols_table_is_noencoding(tb) ?
+ mbs_nwidth(data, bytes) :
+ mbs_safe_nwidth(data, bytes, NULL);
+ }
+
+ 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' */
+ }
+
+ /* 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;
+ }
+
+ fputs_color_cell_open(tb, cl, ln, ce);
+
+ if (data && *data) {
+ if (scols_column_is_right(cl)) {
+ for (i = len; i < width; i++)
+ fputs(cellpadding_symbol(tb), tb->out);
+ len = width;
+ }
+ fputs(data, tb->out);
+
+ }
+
+ /* minout -- don't fill */
+ if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) {
+ fputs_color_cell_close(tb, cl, ln, ce);
+ return 0;
+ }
+
+ /* default -- fill except last column */
+ if (!scols_table_is_maxout(tb) && is_last) {
+ fputs_color_cell_close(tb, cl, ln, ce);
+ return 0;
+ }
+
+ /* fill rest of cell with space */
+ for(i = len; i < width; i++)
+ fputs(cellpadding_symbol(tb), tb->out);
+
+ fputs_color_cell_close(tb, cl, ln, ce);
+
+ if (len > width && !scols_column_is_trunc(cl)) {
+ DBG(COL, ul_debugobj(cl, "*** data len=%zu > column width=%zu", len, width));
+ print_newline_padding(tb, cl, ln, ce, ul_buffer_get_bufsiz(buf)); /* next column starts on next line */
+
+ } else if (!is_last)
+ fputs(colsep(tb), tb->out); /* columns separator */
+
+ return 0;
+}
+
+int __cell_to_buffer(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ struct ul_buffer *buf)
+{
+ const char *data;
+ struct libscols_cell *ce;
+ int rc = 0;
+
+ assert(tb);
+ assert(ln);
+ assert(cl);
+ assert(buf);
+ assert(cl->seqnum <= tb->ncols);
+
+ ul_buffer_reset_data(buf);
+
+ ce = scols_line_get_cell(ln, cl->seqnum);
+ data = ce ? scols_cell_get_data(ce) : NULL;
+
+ if (!scols_column_is_tree(cl))
+ return data ? ul_buffer_append_string(buf, data) : 0;
+
+ /*
+ * Group stuff
+ */
+ if (!scols_table_is_json(tb) && cl->is_groups)
+ rc = groups_ascii_art_to_buffer(tb, ln, buf, 0);
+
+ /*
+ * Tree stuff
+ */
+ if (!rc && ln->parent && !scols_table_is_json(tb)) {
+ rc = tree_ascii_art_to_buffer(tb, ln->parent, buf);
+
+ if (!rc && is_last_child(ln))
+ rc = ul_buffer_append_string(buf, right_symbol(tb));
+ else if (!rc)
+ rc = ul_buffer_append_string(buf, branch_symbol(tb));
+ }
+
+ if (!rc && (ln->parent || cl->is_groups) && !scols_table_is_json(tb))
+ ul_buffer_save_pointer(buf, SCOLS_BUFPTR_TREEEND);
+
+ if (!rc && data)
+ rc = ul_buffer_append_string(buf, data);
+ return rc;
+}
+
+/*
+ * 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 ul_buffer *buf)
+{
+ int rc = 0, pending = 0;
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+
+ assert(ln);
+
+ DBG(LINE, ul_debugobj(ln, " printing line"));
+
+ fputs_color_line_open(tb, ln);
+
+ /* 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 == 0)
+ rc = print_data(tb, cl, ln,
+ scols_line_get_cell(ln, cl->seqnum),
+ buf);
+ if (rc == 0 && cl->pending_data)
+ pending = 1;
+ }
+ fputs_color_line_close(tb);
+
+ /* extra lines of the multi-line cells */
+ while (rc == 0 && pending) {
+ DBG(LINE, ul_debugobj(ln, "printing pending data"));
+ pending = 0;
+ fputs(linesep(tb), tb->out);
+ fputs_color_line_open(tb, ln);
+ 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, NULL, ul_buffer_get_bufsiz(buf));
+ }
+ fputs_color_line_close(tb);
+ }
+
+ return 0;
+}
+
+int __scols_print_title(struct libscols_table *tb)
+{
+ int rc;
+ 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)
+ fputs_color(tb, tb->title.color);
+
+ fputs(title, tb->out);
+
+ if (tb->colors_wanted)
+ fputs_color_reset(tb);
+
+ fputc('\n', tb->out);
+ rc = 0;
+done:
+ free(buf);
+ free(title);
+ DBG(TAB, ul_debugobj(tb, "printing title done [rc=%d]", rc));
+ return rc;
+}
+
+int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf)
+{
+ int rc = 0;
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+
+ assert(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 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;
+
+ ul_buffer_reset_data(buf);
+
+ if (cl->is_groups
+ && scols_table_is_tree(tb) && scols_column_is_tree(cl)) {
+ size_t i;
+ for (i = 0; i < tb->grpset_size + 1; i++) {
+ rc = ul_buffer_append_data(buf, " ", 1);
+ if (rc)
+ break;
+ }
+ }
+ if (!rc)
+ rc = ul_buffer_append_string(buf,
+ scols_table_is_shellvar(tb) ?
+ scols_column_get_name_as_shellvar(cl) :
+ scols_column_get_name(cl));
+ if (!rc)
+ rc = print_data(tb, cl, NULL, &cl->header, buf);
+ }
+
+ 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, rc=%d]",
+ tb->header_next, tb->termlines_used, rc));
+ return rc;
+}
+
+
+int __scols_print_range(struct libscols_table *tb,
+ struct ul_buffer *buf,
+ struct libscols_iter *itr,
+ struct libscols_line *end)
+{
+ int rc = 0;
+ struct libscols_line *ln;
+
+ assert(tb);
+ DBG(TAB, ul_debugobj(tb, "printing range"));
+
+ while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) {
+
+ int last = scols_iter_is_last(itr);
+
+ if (scols_table_is_json(tb))
+ ul_jsonwrt_object_open(&tb->json, NULL);
+
+ rc = print_line(tb, ln, buf);
+
+ if (scols_table_is_json(tb))
+ ul_jsonwrt_object_close(&tb->json);
+ else if (last == 0 && tb->no_linesep == 0) {
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
+
+ if (end && ln == end)
+ break;
+
+ if (!last && want_repeat_header(tb))
+ __scols_print_header(tb, buf);
+ }
+
+ return rc;
+
+}
+
+int __scols_print_table(struct libscols_table *tb, struct ul_buffer *buf)
+{
+ struct libscols_iter itr;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ return __scols_print_range(tb, buf, &itr, NULL);
+}
+
+/* scols_walk_tree() callback to print tree line */
+static int print_tree_line(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl __attribute__((__unused__)),
+ void *data)
+{
+ struct ul_buffer *buf = (struct ul_buffer *) data;
+ int rc;
+
+ DBG(LINE, ul_debugobj(ln, " printing tree line"));
+
+ if (scols_table_is_json(tb))
+ ul_jsonwrt_object_open(&tb->json, NULL);
+
+ rc = print_line(tb, ln, buf);
+ if (rc)
+ return rc;
+
+ if (has_children(ln)) {
+ if (scols_table_is_json(tb))
+ ul_jsonwrt_array_open(&tb->json, "children");
+ else {
+ /* between parent and child is separator */
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
+ } else {
+ int last;
+
+ /* terminate all open last children for JSON */
+ if (scols_table_is_json(tb)) {
+ do {
+ last = (is_child(ln) && is_last_child(ln)) ||
+ (is_tree_root(ln) && is_last_tree_root(tb, ln));
+
+ ul_jsonwrt_object_close(&tb->json);
+ if (last && is_child(ln))
+ ul_jsonwrt_array_close(&tb->json);
+ ln = ln->parent;
+ } while(ln && last);
+
+ } else if (tb->no_linesep == 0) {
+ int last_in_tree = scols_walk_is_last(tb, ln);
+
+ if (last_in_tree == 0) {
+ /* standard output */
+ fputs(linesep(tb), tb->out);
+ tb->termlines_used++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int __scols_print_tree(struct libscols_table *tb, struct ul_buffer *buf)
+{
+ assert(tb);
+ DBG(TAB, ul_debugobj(tb, "----printing-tree-----"));
+
+ return scols_walk_tree(tb, NULL, print_tree_line, (void *) buf);
+}
+
+static size_t strlen_line(struct libscols_line *ln)
+{
+ size_t i, sz = 0;
+
+ assert(ln);
+
+ for (i = 0; i < ln->ncells; i++) {
+ struct libscols_cell *ce = scols_line_get_cell(ln, i);
+ const char *data = ce ? scols_cell_get_data(ce) : NULL;
+
+ sz += data ? strlen(data) : 0;
+ }
+
+ return sz;
+}
+
+void __scols_cleanup_printing(struct libscols_table *tb, struct ul_buffer *buf)
+{
+ if (!tb)
+ return;
+
+ ul_buffer_free_data(buf);
+
+ if (tb->priv_symbols) {
+ scols_table_set_symbols(tb, NULL);
+ tb->priv_symbols = 0;
+ }
+}
+
+int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf)
+{
+ size_t bufsz, extra_bufsz = 0;
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+ int rc;
+
+ DBG(TAB, ul_debugobj(tb, "initialize printing"));
+
+ 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;
+
+ if (!tb->is_term || tb->format != SCOLS_FMT_HUMAN || scols_table_is_tree(tb))
+ tb->header_repeat = 0;
+
+ /*
+ * 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:
+ ul_jsonwrt_init(&tb->json, tb->out, 0);
+ extra_bufsz += tb->nlines * 3; /* indentation */
+ /* 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;
+
+ if (scols_column_get_name(cl))
+ extra_bufsz += strlen(scols_column_get_name(cl)); /* 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;
+
+ sz = strlen_line(ln) + extra_bufsz;
+ if (sz > bufsz)
+ bufsz = sz;
+ }
+
+ /* pre-allocate space for data */
+ rc = ul_buffer_alloc_data(buf, bufsz + 1); /* data + space for \0 */
+ if (rc)
+ goto err;
+
+ /*
+ * Make sure groups members are in the same orders as the tree
+ */
+ if (has_groups(tb) && scols_table_is_tree(tb))
+ scols_groups_fix_members_order(tb);
+
+ if (tb->format == SCOLS_FMT_HUMAN) {
+ rc = __scols_calculate(tb, buf);
+ if (rc != 0)
+ goto err;
+ }
+
+ return 0;
+err:
+ __scols_cleanup_printing(tb, buf);
+ return rc;
+}
+
diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h
new file mode 100644
index 0000000..8b83a14
--- /dev/null
+++ b/libsmartcols/src/smartcolsP.h
@@ -0,0 +1,449 @@
+/*
+ * smartcolsP.h - private library header file
+ *
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#ifndef _LIBSMARTCOLS_PRIVATE_H
+#define _LIBSMARTCOLS_PRIVATE_H
+
+#include "c.h"
+#include "list.h"
+#include "strutils.h"
+#include "color-names.h"
+#include "jsonwrt.h"
+#include "debug.h"
+#include "buffer.h"
+
+#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)
+#define SCOLS_DEBUG_TAB (1 << 4)
+#define SCOLS_DEBUG_COL (1 << 5)
+#define SCOLS_DEBUG_BUFF (1 << 6)
+#define SCOLS_DEBUG_GROUP (1 << 7)
+#define SCOLS_DEBUG_ALL 0xFFFF
+
+UL_DEBUG_DECLARE_MASK(libsmartcols);
+#define DBG(m, x) __UL_DBG(libsmartcols, SCOLS_DEBUG_, m, x)
+#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)
+#include "debugobj.h"
+
+#define SCOLS_BUFPTR_TREEEND 0
+
+/*
+ * Generic iterator
+ */
+struct libscols_iter {
+ struct list_head *p; /* current position */
+ struct list_head *head; /* start position */
+ int direction; /* SCOLS_ITER_{FOR,BACK}WARD */
+};
+
+/*
+ * Tree symbols
+ */
+struct libscols_symbols {
+ int refcount;
+
+ char *tree_branch;
+ char *tree_vert;
+ char *tree_right;
+
+ char *group_vert;
+ char *group_horz;
+ char *group_first_member;
+ char *group_last_member;
+ char *group_middle_member;
+ char *group_last_child;
+ char *group_middle_child;
+
+ char *title_padding;
+ char *cell_padding;
+};
+
+/*
+ * Table cells
+ */
+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
+ */
+struct libscols_column {
+ int refcount; /* reference counter */
+ size_t seqnum; /* column index */
+
+ size_t width; /* real column width */
+ 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) */
+
+ size_t extreme_sum;
+ int extreme_count;
+
+ int json_type; /* SCOLS_JSON_* */
+
+ int flags;
+ 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; /* column name with color etc. */
+ char *shellvar; /* raw colum name in shell compatible format */
+
+ struct list_head cl_columns; /* member of table->tb_columns */
+
+ struct libscols_table *table;
+
+ unsigned int is_extreme : 1, /* extreme width in the column */
+ is_groups : 1; /* print group chart */
+
+};
+
+#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
+#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
+
+enum {
+ SCOLS_GSTATE_NONE = 0, /* not activate yet */
+ SCOLS_GSTATE_FIRST_MEMBER,
+ SCOLS_GSTATE_MIDDLE_MEMBER,
+ SCOLS_GSTATE_LAST_MEMBER,
+ SCOLS_GSTATE_MIDDLE_CHILD,
+ SCOLS_GSTATE_LAST_CHILD,
+ SCOLS_GSTATE_CONT_MEMBERS,
+ SCOLS_GSTATE_CONT_CHILDREN
+};
+
+/*
+ * Every group needs at least 3 columns
+ */
+#define SCOLS_GRPSET_CHUNKSIZ 3
+
+struct libscols_group {
+ int refcount;
+
+ size_t nmembers;
+
+ struct list_head gr_members; /* head of line->ln_group */
+ struct list_head gr_children; /* head of line->ln_children */
+ struct list_head gr_groups; /* member of table->tb_groups */
+
+ int state; /* SCOLS_GSTATE_* */
+};
+
+/*
+ * Table line
+ */
+struct libscols_line {
+ int refcount;
+ size_t seqnum;
+
+ void *userdata;
+ char *color; /* default line color */
+
+ struct libscols_cell *cells; /* array with data */
+ size_t ncells; /* number of cells */
+
+ struct list_head ln_lines; /* member of table->tb_lines */
+ struct list_head ln_branch; /* head of line->ln_children */
+ struct list_head ln_children; /* member of line->ln_children or group->gr_children */
+ struct list_head ln_groups; /* member of group->gr_groups */
+
+ struct libscols_line *parent;
+ struct libscols_group *parent_group; /* for group childs */
+ struct libscols_group *group; /* for group members */
+};
+
+enum {
+ SCOLS_FMT_HUMAN = 0, /* default, human readable */
+ SCOLS_FMT_RAW, /* space separated */
+ SCOLS_FMT_EXPORT, /* COLNAME="data" ... */
+ SCOLS_FMT_JSON /* http://en.wikipedia.org/wiki/JSON */
+};
+
+/*
+ * The table
+ */
+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 (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 */
+ char *linesep; /* line separator */
+
+ struct list_head tb_columns; /* list of columns, items: column->cl_columns */
+ struct list_head tb_lines; /* list of lines; items: line->ln_lines */
+
+ struct list_head tb_groups; /* all defined groups */
+ struct libscols_group **grpset;
+ size_t grpset_size;
+
+ size_t ngrpchlds_pending; /* groups with not yet printed children */
+ struct libscols_line *walk_last_tree_root; /* last root, used by scols_walk_() */
+
+ struct libscols_column *dflt_sort_column; /* default sort column, set by scols_sort_table() */
+
+ struct libscols_symbols *symbols;
+ struct libscols_cell title; /* optional table title (for humans) */
+
+ struct ul_jsonwrt json; /* JSON formatting */
+
+ int format; /* SCOLS_FMT_* */
+
+ size_t termlines_used; /* printed line counter */
+ size_t header_next; /* where repeat header */
+
+ const char *cur_color; /* current active color when printing */
+
+ /* flags */
+ unsigned int ascii :1, /* don't use unicode */
+ colors_wanted :1, /* enable colors */
+ is_term :1, /* isatty() */
+ padding_debug :1, /* output visible padding chars */
+ is_dummy_print :1, /* printing used for width calculation only */
+ is_shellvar :1, /* shell compatible column names */
+ maxout :1, /* maximize output */
+ minout :1, /* minimize output (mutually exclusive to maxout) */
+ header_repeat :1, /* print header after libscols_table->termheight */
+ header_printed :1, /* header already printed */
+ priv_symbols :1, /* default private symbols */
+ walk_last_done :1, /* last tree root walked */
+ 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)
+#define IS_ITER_BACKWARD(_i) ((_i)->direction == SCOLS_ITER_BACKWARD)
+
+#define SCOLS_ITER_INIT(itr, list) \
+ do { \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (list)->next : (list)->prev; \
+ (itr)->head = (list); \
+ } while(0)
+
+#define SCOLS_ITER_ITERATE(itr, res, restype, member) \
+ do { \
+ res = list_entry((itr)->p, restype, member); \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (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;
+}
+
+/*
+ * line.c
+ */
+int scols_line_next_group_child(struct libscols_line *ln,
+ struct libscols_iter *itr,
+ struct libscols_line **chld);
+
+
+/*
+ * table.c
+ */
+int scols_table_next_group(struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_group **gr);
+
+/*
+ * grouping.c
+ */
+void scols_ref_group(struct libscols_group *gr);
+void scols_group_remove_children(struct libscols_group *gr);
+void scols_group_remove_members(struct libscols_group *gr);
+void scols_unref_group(struct libscols_group *gr);
+void scols_groups_fix_members_order(struct libscols_table *tb);
+int scols_groups_update_grpset(struct libscols_table *tb, struct libscols_line *ln);
+void scols_groups_reset_state(struct libscols_table *tb);
+struct libscols_group *scols_grpset_get_printable_children(struct libscols_table *tb);
+
+/*
+ * walk.c
+ */
+extern int scols_walk_tree(struct libscols_table *tb,
+ struct libscols_column *cl,
+ int (*callback)(struct libscols_table *,
+ struct libscols_line *,
+ struct libscols_column *,
+ void *),
+ void *data);
+extern int scols_walk_is_last(struct libscols_table *tb, struct libscols_line *ln);
+
+/*
+ * calculate.c
+ */
+extern int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf);
+
+/*
+ * print.c
+ */
+extern int __cell_to_buffer(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ struct ul_buffer *buf);
+
+void __scols_cleanup_printing(struct libscols_table *tb, struct ul_buffer *buf);
+int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf);
+int __scols_print_tree(struct libscols_table *tb, struct ul_buffer *buf);
+int __scols_print_table(struct libscols_table *tb, struct ul_buffer *buf);
+int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf);
+int __scols_print_title(struct libscols_table *tb);
+int __scols_print_range(struct libscols_table *tb,
+ struct ul_buffer *buf,
+ struct libscols_iter *itr,
+ struct libscols_line *end);
+
+static inline int is_tree_root(struct libscols_line *ln)
+{
+ return ln && !ln->parent && !ln->parent_group;
+}
+
+static inline int is_last_tree_root(struct libscols_table *tb, struct libscols_line *ln)
+{
+ if (!ln || !tb || tb->walk_last_tree_root != ln)
+ return 0;
+
+ return 1;
+}
+
+static inline int is_child(struct libscols_line *ln)
+{
+ return ln && ln->parent;
+}
+
+static inline int is_last_child(struct libscols_line *ln)
+{
+ if (!ln || !ln->parent)
+ return 0;
+
+ return list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch);
+}
+
+static inline int is_first_child(struct libscols_line *ln)
+{
+ if (!ln || !ln->parent)
+ return 0;
+
+ return list_entry_is_first(&ln->ln_children, &ln->parent->ln_branch);
+}
+
+
+static inline int is_last_column(struct libscols_column *cl)
+{
+ struct libscols_column *next;
+
+ if (list_entry_is_last(&cl->cl_columns, &cl->table->tb_columns))
+ 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 inline int is_last_group_member(struct libscols_line *ln)
+{
+ if (!ln || !ln->group)
+ return 0;
+
+ return list_entry_is_last(&ln->ln_groups, &ln->group->gr_members);
+}
+
+static inline int is_first_group_member(struct libscols_line *ln)
+{
+ if (!ln || !ln->group)
+ return 0;
+
+ return list_entry_is_first(&ln->ln_groups, &ln->group->gr_members);
+}
+
+static inline int is_group_member(struct libscols_line *ln)
+{
+ return ln && ln->group;
+}
+
+static inline int is_last_group_child(struct libscols_line *ln)
+{
+ if (!ln || !ln->parent_group)
+ return 0;
+
+ return list_entry_is_last(&ln->ln_children, &ln->parent_group->gr_children);
+}
+
+static inline int is_group_child(struct libscols_line *ln)
+{
+ return ln && ln->parent_group;
+}
+
+static inline int has_groups(struct libscols_table *tb)
+{
+ return tb && !list_empty(&tb->tb_groups);
+}
+
+static inline int has_children(struct libscols_line *ln)
+{
+ return ln && !list_empty(&ln->ln_branch);
+}
+
+static inline int has_group_children(struct libscols_line *ln)
+{
+ return ln && ln->group && !list_empty(&ln->group->gr_children);
+}
+
+#endif /* _LIBSMARTCOLS_PRIVATE_H */
diff --git a/libsmartcols/src/symbols.c b/libsmartcols/src/symbols.c
new file mode 100644
index 0000000..2fadfc7
--- /dev/null
+++ b/libsmartcols/src/symbols.c
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ */
+
+/**
+ * SECTION: symbols
+ * @title: Symbols
+ * @short_description: can be used to overwrite default output chars (for ascii art)
+ *
+ * An API to access and modify data and information per symbol/symbol group.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "smartcolsP.h"
+
+/**
+ * scols_new_symbols:
+ *
+ * Returns: a pointer to a newly allocated struct libscols_symbols instance.
+ */
+struct libscols_symbols *scols_new_symbols(void)
+{
+ struct libscols_symbols *sy = calloc(1, sizeof(struct libscols_symbols));
+
+ if (!sy)
+ return NULL;
+ sy->refcount = 1;
+ return sy;
+}
+
+/**
+ * scols_ref_symbols:
+ * @sy: a pointer to a struct libscols_symbols instance
+ *
+ * Increases the refcount of @sy.
+ */
+void scols_ref_symbols(struct libscols_symbols *sy)
+{
+ if (sy)
+ sy->refcount++;
+}
+
+/**
+ * scols_unref_symbols:
+ * @sy: a pointer to a struct libscols_symbols instance
+ *
+ * Decreases the refcount of @sy.
+ */
+void scols_unref_symbols(struct libscols_symbols *sy)
+{
+ if (sy && --sy->refcount <= 0) {
+ free(sy->tree_branch);
+ free(sy->tree_vert);
+ free(sy->tree_right);
+ free(sy->group_last_member);
+ free(sy->group_middle_member);
+ free(sy->group_first_member);
+ free(sy->group_vert);
+ free(sy->group_horz);
+ free(sy->group_last_child);
+ free(sy->group_middle_child);
+ free(sy->title_padding);
+ free(sy->cell_padding);
+ free(sy);
+ }
+}
+
+/**
+ * scols_symbols_set_branch:
+ * @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 *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, tree_branch, str);
+}
+
+/**
+ * scols_symbols_set_vertical:
+ * @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 *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, tree_vert, str);
+}
+
+/**
+ * scols_symbols_set_right:
+ * @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 *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, tree_right, str);
+}
+
+/**
+ * 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);
+}
+
+/**
+ * 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_symbols_set_group_vertical:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the vertival line
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_vertical(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_vert, str);
+}
+
+/**
+ * scols_symbols_set_group_horizontal:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent the horizontal line
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_horizontal(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_horz, str);
+}
+
+/**
+ * scols_symbols_set_group_first_member:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent first member
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_first_member(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_first_member, str);
+}
+
+/**
+ * scols_symbols_set_group_last_member:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent last member
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_last_member(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_last_member, str);
+}
+
+/**
+ * scols_symbols_set_group_middle:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent middle member
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_middle_member(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_middle_member, str);
+}
+
+/**
+ * scols_symbols_set_group_last_child:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent last child
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_last_child(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_last_child, str);
+}
+
+/**
+ * scols_symbols_set_group_middle_child:
+ * @sy: a pointer to a struct libscols_symbols instance
+ * @str: a string which will represent last child
+ *
+ * Returns: 0, a negative value in case of an error.
+ *
+ * Since: 2.34
+ */
+int scols_symbols_set_group_middle_child(struct libscols_symbols *sy, const char *str)
+{
+ return strdup_to_struct_member(sy, group_middle_child, str);
+}
+
+/**
+ * scols_copy_symbols:
+ * @sy: a pointer to a struct libscols_symbols instance
+ *
+ * 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 *sy)
+{
+ struct libscols_symbols *ret;
+ int rc;
+
+ assert(sy);
+ if (!sy)
+ return NULL;
+
+ ret = scols_new_symbols();
+ if (!ret)
+ return NULL;
+
+ rc = scols_symbols_set_branch(ret, sy->tree_branch);
+ if (!rc)
+ rc = scols_symbols_set_vertical(ret, sy->tree_vert);
+ if (!rc)
+ rc = scols_symbols_set_right(ret, sy->tree_right);
+ if (!rc)
+ rc = scols_symbols_set_group_vertical(ret, sy->group_vert);
+ if (!rc)
+ rc = scols_symbols_set_group_horizontal(ret, sy->group_horz);
+ if (!rc)
+ rc = scols_symbols_set_group_first_member(ret, sy->group_first_member);
+ if (!rc)
+ rc = scols_symbols_set_group_last_member(ret, sy->group_last_member);
+ if (!rc)
+ rc = scols_symbols_set_group_middle_member(ret, sy->group_middle_member);
+ if (!rc)
+ rc = scols_symbols_set_group_middle_child(ret, sy->group_middle_child);
+ if (!rc)
+ rc = scols_symbols_set_group_last_child(ret, sy->group_last_child);
+ if (!rc)
+ 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
new file mode 100644
index 0000000..30e9194
--- /dev/null
+++ b/libsmartcols/src/table.c
@@ -0,0 +1,1754 @@
+/*
+ * table.c - functions handling the data at the table level
+ *
+ * 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.
+ */
+
+/**
+ * SECTION: table
+ * @title: Table
+ * @short_description: container for rows and columns
+ *
+ * Table data manipulation API.
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <ctype.h>
+
+#include "nls.h"
+#include "ttyutils.h"
+#include "smartcolsP.h"
+
+#ifdef HAVE_WIDECHAR
+#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char | */
+#define UTF_VR "\342\224\234" /* U+251C, Vertical and right |- */
+#define UTF_H "\342\224\200" /* U+2500, Horizontal - */
+#define UTF_UR "\342\224\224" /* U+2514, Up and right '- */
+
+#define UTF_V3 "\342\224\206" /* U+2506 Triple Dash Vertical | */
+#define UTF_H3 "\342\224\210" /* U+2504 Triple Dash Horizontal - */
+#define UTF_DR "\342\224\214" /* U+250C Down and Right ,- */
+#define UTF_DH "\342\224\254" /* U+252C Down and Horizontal |' */
+
+#define UTF_TR "\342\226\266" /* U+25B6 Black Right-Pointing Triangle > */
+#endif /* !HAVE_WIDECHAR */
+
+#define is_last_column(_tb, _cl) \
+ 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 already initialized! */
+
+ 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:
+ *
+ * Returns: A newly allocated table.
+ */
+struct libscols_table *scols_new_table(void)
+{
+ struct libscols_table *tb;
+ int c, l;
+
+ tb = calloc(1, sizeof(struct libscols_table));
+ if (!tb)
+ return NULL;
+
+ 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);
+ INIT_LIST_HEAD(&tb->tb_groups);
+
+ DBG(TAB, ul_debugobj(tb, "alloc"));
+ ON_DBG(INIT, check_padding_debug(tb));
+
+ return tb;
+}
+
+/**
+ * scols_ref_table:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Increases the refcount of @tb.
+ */
+void scols_ref_table(struct libscols_table *tb)
+{
+ if (tb)
+ tb->refcount++;
+}
+
+static void scols_table_remove_groups(struct libscols_table *tb)
+{
+ while (!list_empty(&tb->tb_groups)) {
+ struct libscols_group *gr = list_entry(tb->tb_groups.next,
+ struct libscols_group, gr_groups);
+ scols_group_remove_children(gr);
+ scols_group_remove_members(gr);
+ scols_unref_group(gr);
+ }
+}
+
+/**
+ * scols_unref_table:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Decreases the refcount of @tb. When the count falls to zero, the instance
+ * is automatically deallocated.
+ */
+void scols_unref_table(struct libscols_table *tb)
+{
+ if (tb && (--tb->refcount <= 0)) {
+ DBG(TAB, ul_debugobj(tb, "dealloc <-"));
+ scols_table_remove_groups(tb);
+ scols_table_remove_lines(tb);
+ scols_table_remove_columns(tb);
+ scols_unref_symbols(tb->symbols);
+ scols_reset_cell(&tb->title);
+ free(tb->grpset);
+ free(tb->linesep);
+ free(tb->colsep);
+ free(tb->name);
+ free(tb);
+ DBG(TAB, ul_debug("<- done"));
+ }
+}
+
+/* Private API */
+int scols_table_next_group(struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_group **gr)
+{
+ int rc = 1;
+
+ if (!tb || !itr || !gr)
+ return -EINVAL;
+ *gr = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &tb->tb_groups);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *gr, struct libscols_group, gr_groups);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * 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. 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)
+{
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+ int rc = 0;
+
+ if (!tb || !cl || cl->table)
+ return -EINVAL;
+
+ if (!list_empty(&cl->cl_columns))
+ return -EINVAL;
+
+ if (cl->flags & SCOLS_FL_TREE)
+ tb->ntreecols++;
+
+ 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);
+
+ if (list_empty(&tb->tb_lines))
+ return 0;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+
+ /* Realloc line cell arrays
+ */
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ rc = scols_line_alloc_cells(ln, tb->ncols);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * scols_table_remove_column:
+ * @tb: a pointer to a struct libscols_table instance
+ * @cl: a pointer to a struct libscols_column instance
+ *
+ * Removes @cl from @tb.
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_remove_column(struct libscols_table *tb,
+ struct libscols_column *cl)
+{
+ if (!tb || !cl || !list_empty(&tb->tb_lines))
+ return -EINVAL;
+
+ if (cl->flags & SCOLS_FL_TREE)
+ tb->ntreecols--;
+ if (tb->dflt_sort_column == cl)
+ tb->dflt_sort_column = NULL;
+
+ DBG(TAB, ul_debugobj(tb, "remove column"));
+ list_del_init(&cl->cl_columns);
+ tb->ncols--;
+ cl->table = NULL;
+ scols_unref_column(cl);
+ return 0;
+}
+
+/**
+ * scols_table_remove_columns:
+ * @tb: a pointer to a struct libscols_table instance
+ *
+ * Removes all of @tb's columns.
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_remove_columns(struct libscols_table *tb)
+{
+ if (!tb || !list_empty(&tb->tb_lines))
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "remove all columns"));
+ while (!list_empty(&tb->tb_columns)) {
+ struct libscols_column *cl = list_entry(tb->tb_columns.next,
+ struct libscols_column, cl_columns);
+ scols_table_remove_column(tb, cl);
+ }
+ 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: 0 < N < 1)
+ * @flags: flags integer
+ *
+ * This is shortcut for
+ *
+ * cl = scols_new_column();
+ * scols_column_set_....(cl, ...);
+ * scols_table_add_column(tb, cl);
+ *
+ * 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
+ *
+ * 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.
+ *
+ *
+ * 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:
+ *
+ * #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
+ * .
+ * .
+ * scols_line_get_cell(line, 0); // FOO column
+ * scols_line_get_cell(line, 1); // BAR column
+ *
+ * Returns: newly allocated column
+ */
+struct libscols_column *scols_table_new_column(struct libscols_table *tb,
+ const char *name,
+ double whint,
+ int flags)
+{
+ struct libscols_column *cl;
+
+ if (!tb)
+ return NULL;
+
+ DBG(TAB, ul_debugobj(tb, "new column name=%s, whint=%g, flags=%d",
+ name, whint, flags));
+ cl = scols_new_column();
+ if (!cl)
+ return NULL;
+
+ if (scols_column_set_name(cl, name))
+ goto err;
+ scols_column_set_whint(cl, whint);
+ scols_column_set_flags(cl, flags);
+
+ if (scols_table_add_column(tb, cl)) /* this increments column ref-counter */
+ goto err;
+
+ scols_unref_column(cl);
+ return cl;
+err:
+ scols_unref_column(cl);
+ return NULL;
+}
+
+/**
+ * scols_table_next_column:
+ * @tb: a pointer to a struct libscols_table instance
+ * @itr: a pointer to a struct libscols_iter instance
+ * @cl: a pointer to a pointer to a struct libscols_column instance
+ *
+ * Returns the next column of @tb via @cl.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_next_column(struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_column **cl)
+{
+ int rc = 1;
+
+ if (!tb || !itr || !cl)
+ return -EINVAL;
+ *cl = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &tb->tb_columns);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *cl, struct libscols_column, cl_columns);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * scols_table_set_columns_iter:
+ * @tb: tab pointer
+ * @itr: iterator
+ * @cl: tab entry
+ *
+ * Sets @iter to the position of @cl in the file @tb.
+ *
+ * Returns: 0 on success, negative number in case of error.
+ *
+ * Since: 2.35
+ */
+int scols_table_set_columns_iter(
+ struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_column *cl)
+{
+ if (!tb || !itr || !cl)
+ return -EINVAL;
+
+ if (cl->table != tb)
+ return -EINVAL;
+
+ SCOLS_ITER_INIT(itr, &tb->tb_columns);
+ itr->p = &cl->cl_columns;
+
+ return 0;
+}
+
+/**
+ * scols_table_get_ncols:
+ * @tb: table
+ *
+ * Returns: the ncols table member.
+ */
+size_t scols_table_get_ncols(const struct libscols_table *tb)
+{
+ return tb->ncols;
+}
+
+/**
+ * scols_table_get_nlines:
+ * @tb: table
+ *
+ * Returns: the nlines table member.
+ */
+size_t scols_table_get_nlines(const struct libscols_table *tb)
+{
+ return tb->nlines;
+}
+
+/**
+ * scols_table_set_stream:
+ * @tb: table
+ * @stream: output stream
+ *
+ * Sets the output stream for table @tb.
+ *
+ * Returns: 0, a negative number in case of an error.
+ */
+int scols_table_set_stream(struct libscols_table *tb, FILE *stream)
+{
+ assert(tb);
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "setting alternative stream"));
+ tb->out = stream;
+ return 0;
+}
+
+/**
+ * scols_table_get_stream:
+ * @tb: table
+ *
+ * Gets the output stream for table @tb.
+ *
+ * Returns: stream pointer, NULL in case of an error or an unset stream.
+ */
+FILE *scols_table_get_stream(const struct libscols_table *tb)
+{
+ return tb->out;
+}
+
+/**
+ * scols_table_reduce_termwidth:
+ * @tb: table
+ * @reduce: width
+ *
+ * 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)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "reduce terminal width: %zu", reduce));
+ tb->termreduce = reduce;
+ return 0;
+}
+
+/**
+ * scols_table_get_column:
+ * @tb: table
+ * @n: number of column (0..N)
+ *
+ * Returns: pointer to column or NULL
+ */
+struct libscols_column *scols_table_get_column(struct libscols_table *tb,
+ size_t n)
+{
+ struct libscols_iter itr;
+ struct libscols_column *cl;
+
+ if (!tb)
+ return NULL;
+ if (n >= tb->ncols)
+ return NULL;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ if (cl->seqnum == n)
+ return cl;
+ }
+ return NULL;
+}
+
+/**
+ * scols_table_add_line:
+ * @tb: table
+ * @ln: line
+ *
+ * Note that this function calls scols_line_alloc_cells() if number
+ * of the cells in the line is too small for @tb.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln)
+{
+ if (!tb || !ln)
+ return -EINVAL;
+
+ if (!list_empty(&ln->ln_lines))
+ return -EINVAL;
+
+ if (tb->ncols > ln->ncells) {
+ int rc = scols_line_alloc_cells(ln, tb->ncols);
+ if (rc)
+ return rc;
+ }
+
+ DBG(TAB, ul_debugobj(tb, "add line"));
+ list_add_tail(&ln->ln_lines, &tb->tb_lines);
+ ln->seqnum = tb->nlines++;
+ scols_ref_line(ln);
+ return 0;
+}
+
+/**
+ * scols_table_remove_line:
+ * @tb: table
+ * @ln: line
+ *
+ * Note that this function does not destroy the parent<->child relationship between lines.
+ * You have to call scols_line_remove_child()
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_remove_line(struct libscols_table *tb,
+ struct libscols_line *ln)
+{
+ if (!tb || !ln)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "remove line"));
+ list_del_init(&ln->ln_lines);
+ tb->nlines--;
+ scols_unref_line(ln);
+ return 0;
+}
+
+/**
+ * scols_table_remove_lines:
+ * @tb: table
+ *
+ * This empties the table and also destroys all the parent<->child relationships.
+ */
+void scols_table_remove_lines(struct libscols_table *tb)
+{
+ if (!tb)
+ return;
+
+ DBG(TAB, ul_debugobj(tb, "remove all lines"));
+ while (!list_empty(&tb->tb_lines)) {
+ struct libscols_line *ln = list_entry(tb->tb_lines.next,
+ struct libscols_line, ln_lines);
+ if (ln->parent)
+ scols_line_remove_child(ln->parent, ln);
+ scols_table_remove_line(tb, ln);
+ }
+}
+
+/**
+ * scols_table_next_line:
+ * @tb: a pointer to a struct libscols_table instance
+ * @itr: a pointer to a struct libscols_iter instance
+ * @ln: a pointer to a pointer to a struct libscols_line instance
+ *
+ * Finds the next line and returns a pointer to it via @ln.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_next_line(struct libscols_table *tb,
+ struct libscols_iter *itr,
+ struct libscols_line **ln)
+{
+ int rc = 1;
+
+ if (!tb || !itr || !ln)
+ return -EINVAL;
+ *ln = NULL;
+
+ if (!itr->head)
+ SCOLS_ITER_INIT(itr, &tb->tb_lines);
+ if (itr->p != itr->head) {
+ SCOLS_ITER_ITERATE(itr, *ln, struct libscols_line, ln_lines);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * scols_table_new_line:
+ * @tb: table
+ * @parent: parental line or NULL
+ *
+ * This is shortcut for
+ *
+ * ln = scols_new_line();
+ * scols_table_add_line(tb, ln);
+ * scols_line_add_child(parent, ln);
+ *
+ *
+ * Returns: newly allocate line
+ */
+struct libscols_line *scols_table_new_line(struct libscols_table *tb,
+ struct libscols_line *parent)
+{
+ struct libscols_line *ln;
+
+ if (!tb)
+ return NULL;
+
+ ln = scols_new_line();
+ if (!ln)
+ return NULL;
+
+ if (scols_table_add_line(tb, ln))
+ goto err;
+ if (parent)
+ scols_line_add_child(parent, ln);
+
+ scols_unref_line(ln); /* ref-counter incremented by scols_table_add_line() */
+ return ln;
+err:
+ scols_unref_line(ln);
+ return NULL;
+}
+
+/**
+ * scols_table_get_line:
+ * @tb: table
+ * @n: column number (0..N)
+ *
+ * Returns: a line or NULL
+ */
+struct libscols_line *scols_table_get_line(struct libscols_table *tb,
+ size_t n)
+{
+ struct libscols_iter itr;
+ struct libscols_line *ln;
+
+ if (!tb)
+ return NULL;
+ if (n >= tb->nlines)
+ return NULL;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (ln->seqnum == n)
+ return ln;
+ }
+ return NULL;
+}
+
+/**
+ * scols_copy_table:
+ * @tb: table
+ *
+ * Creates a new independent table copy, except struct libscols_symbols that
+ * are shared between the tables.
+ *
+ * Returns: a newly allocated copy of @tb
+ */
+struct libscols_table *scols_copy_table(struct libscols_table *tb)
+{
+ struct libscols_table *ret;
+ struct libscols_line *ln;
+ struct libscols_column *cl;
+ struct libscols_iter itr;
+
+ if (!tb)
+ return NULL;
+ ret = scols_new_table();
+ if (!ret)
+ return NULL;
+
+ DBG(TAB, ul_debugobj(tb, "copy"));
+
+ if (tb->symbols)
+ scols_table_set_symbols(ret, tb->symbols);
+
+ /* columns */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ cl = scols_copy_column(cl);
+ if (!cl)
+ goto err;
+ if (scols_table_add_column(ret, cl))
+ goto err;
+ scols_unref_column(cl);
+ }
+
+ /* lines */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ struct libscols_line *newln = scols_copy_line(ln);
+ if (!newln)
+ goto err;
+ if (scols_table_add_line(ret, newln))
+ goto err;
+ if (ln->parent) {
+ struct libscols_line *p =
+ scols_table_get_line(ret, ln->parent->seqnum);
+ if (p)
+ scols_line_add_child(p, newln);
+ }
+ scols_unref_line(newln);
+ }
+
+ /* separators */
+ if (scols_table_set_column_separator(ret, tb->colsep) ||
+ scols_table_set_line_separator(ret, tb->linesep))
+ goto err;
+
+ return ret;
+err:
+ scols_unref_table(ret);
+ 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")) {
+ /* tree chart */
+ 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);
+ /* groups chart */
+ scols_symbols_set_group_horizontal(sy, UTF_H3);
+ scols_symbols_set_group_vertical(sy, UTF_V3);
+
+ scols_symbols_set_group_first_member(sy, UTF_DR UTF_H3 UTF_TR);
+ scols_symbols_set_group_last_member(sy, UTF_UR UTF_DH UTF_TR);
+ scols_symbols_set_group_middle_member(sy, UTF_VR UTF_H3 UTF_TR);
+ scols_symbols_set_group_last_child(sy, UTF_UR UTF_H3);
+ scols_symbols_set_group_middle_child(sy, UTF_VR UTF_H3);
+ } else
+#endif
+ {
+ /* tree chart */
+ scols_symbols_set_branch(sy, "|-");
+ scols_symbols_set_vertical(sy, "| ");
+ scols_symbols_set_right(sy, "`-");
+ /* groups chart */
+ scols_symbols_set_group_horizontal(sy, "-");
+ scols_symbols_set_group_vertical(sy, "|");
+
+ scols_symbols_set_group_first_member(sy, ",->");
+ scols_symbols_set_group_last_member(sy, "'->");
+ scols_symbols_set_group_middle_member(sy, "|->");
+ scols_symbols_set_group_last_child(sy, "`-");
+ scols_symbols_set_group_middle_child(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 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)
+{
+ if (!tb)
+ return -EINVAL;
+
+ /* 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);
+ }
+ 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
+ * @enable: 1 or 0
+ *
+ * Enable/disable colors.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_colors(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "colors: %s", enable ? "ENABLE" : "DISABLE"));
+ 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, 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)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "raw: %s", enable ? "ENABLE" : "DISABLE"));
+ if (enable)
+ tb->format = SCOLS_FMT_RAW;
+ else if (tb->format == SCOLS_FMT_RAW)
+ tb->format = 0;
+ 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
+ * @enable: 1 or 0
+ *
+ * Enable/disable export output format (COLUMNAME="value" ...).
+ * The parsable output formats (export and raw) are mutually exclusive.
+ *
+ * See also scols_table_enable_shellvar(). Note that in version 2.37 (and only
+ * in this version) scols_table_enable_shellvar() functionality has been
+ * automatically enabled for "export" format. This behavior has been reverted
+ * in version 2.38 due to backward compatibility issues. Now it's necessary to
+ * explicitly call scols_table_enable_shellvar().
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_export(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "export: %s", enable ? "ENABLE" : "DISABLE"));
+ if (enable)
+ tb->format = SCOLS_FMT_EXPORT;
+ else if (tb->format == SCOLS_FMT_EXPORT)
+ tb->format = 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_shellvar:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Force library to print column names to be compatible with shell requirements
+ * to variable names. For example "1FOO%" will be printed as "_1FOO_PCT".
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.38
+ */
+int scols_table_enable_shellvar(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "shellvar: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->is_shellvar = enable ? 1 : 0;
+ return 0;
+}
+
+
+/**
+ * scols_table_enable_ascii:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * The ASCII-only output is relevant for tree-like outputs. The library
+ * checks if the current environment is UTF8 compatible by default. This
+ * function overrides this check and force the library to use ASCII chars
+ * for the tree.
+ *
+ * If a custom libcols_symbols are specified (see scols_table_set_symbols()
+ * then ASCII flag setting is ignored.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_ascii(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "ascii: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->ascii = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_noheadings:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Enable/disable header line.
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_noheadings(struct libscols_table *tb, int enable)
+{
+ if (!tb)
+ return -EINVAL;
+ DBG(TAB, ul_debugobj(tb, "noheading: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->no_headings = enable ? 1 : 0;
+ 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
+ * @enable: 1 or 0
+ *
+ * The extra space after last column is ignored by default. The output
+ * maximization add padding for all columns.
+ *
+ * This setting is mutually exclusive to scols_table_enable_minout().
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ */
+int scols_table_enable_maxout(struct libscols_table *tb, int enable)
+{
+ if (!tb || tb->minout)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "maxout: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->maxout = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * scols_table_enable_minout:
+ * @tb: table
+ * @enable: 1 or 0
+ *
+ * Force library to terminate line after last column with data. The extra
+ * padding is not added to the empty cells at the end of the line. The default is fill
+ * tailing empty cells except the last line cell.
+ *
+ * This setting is mutually exclusive to scols_table_enable_maxout().
+ *
+ * Returns: 0 on success, negative number in case of an error.
+ *
+ * Since: 2.35
+ */
+int scols_table_enable_minout(struct libscols_table *tb, int enable)
+{
+ if (!tb || tb->maxout)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "minout: %s", enable ? "ENABLE" : "DISABLE"));
+ tb->minout = enable ? 1 : 0;
+ 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(const struct libscols_table *tb)
+{
+ return tb->colors_wanted;
+}
+
+/**
+ * scols_table_is_empty:
+ * @tb: table
+ *
+ * Returns: 1 if the table is empty.
+ */
+int scols_table_is_empty(const struct libscols_table *tb)
+{
+ return !tb->nlines;
+}
+
+/**
+ * scols_table_is_ascii:
+ * @tb: table
+ *
+ * Returns: 1 if ASCII tree is enabled.
+ */
+int scols_table_is_ascii(const struct libscols_table *tb)
+{
+ return tb->ascii;
+}
+
+/**
+ * scols_table_is_noheadings:
+ * @tb: table
+ *
+ * Returns: 1 if header output is disabled.
+ */
+int scols_table_is_noheadings(const struct libscols_table *tb)
+{
+ 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;
+}
+
+/**
+ * scols_table_is_export:
+ * @tb: table
+ *
+ * Returns: 1 if export output format is enabled.
+ */
+int scols_table_is_export(const struct libscols_table *tb)
+{
+ return tb->format == SCOLS_FMT_EXPORT;
+}
+
+/**
+ * scols_table_is_shellvar:
+ * @tb: table
+ *
+ * Returns: 1 if column names has to be compatible with shell requirements
+ * to variable names
+ *
+ * Since: 2.38
+ */
+int scols_table_is_shellvar(const struct libscols_table *tb)
+{
+ return tb->is_shellvar;
+}
+
+/**
+ * scols_table_is_raw:
+ * @tb: table
+ *
+ * Returns: 1 if raw output format is enabled.
+ */
+int scols_table_is_raw(const struct libscols_table *tb)
+{
+ 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 or 0
+ */
+int scols_table_is_maxout(const struct libscols_table *tb)
+{
+ return tb->maxout;
+}
+
+/**
+ * scols_table_is_minout
+ * @tb: table
+ *
+ * Returns: 1 if output minimization is enabled or 0
+ *
+ * Since: 2.35
+ */
+int scols_table_is_minout(const struct libscols_table *tb)
+{
+ return tb->minout;
+}
+
+/**
+ * scols_table_is_tree:
+ * @tb: table
+ *
+ * Returns: returns 1 tree-like output is expected.
+ */
+int scols_table_is_tree(const struct libscols_table *tb)
+{
+ return tb->ntreecols > 0;
+}
+
+/**
+ * scols_table_set_column_separator:
+ * @tb: table
+ * @sep: separator
+ *
+ * Sets the column separator of @tb to @sep.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_set_column_separator(struct libscols_table *tb, const char *sep)
+{
+ return strdup_to_struct_member(tb, colsep, sep);
+}
+
+/**
+ * scols_table_set_line_separator:
+ * @tb: table
+ * @sep: separator
+ *
+ * Sets the line separator of @tb to @sep.
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_table_set_line_separator(struct libscols_table *tb, const char *sep)
+{
+ return strdup_to_struct_member(tb, linesep, sep);
+}
+
+/**
+ * scols_table_get_column_separator:
+ * @tb: table
+ *
+ * Returns: @tb column separator, NULL in case of an error
+ */
+const char *scols_table_get_column_separator(const struct libscols_table *tb)
+{
+ return tb->colsep;
+}
+
+/**
+ * scols_table_get_line_separator:
+ * @tb: table
+ *
+ * Returns: @tb line separator, NULL in case of an error
+ */
+const char *scols_table_get_line_separator(const struct libscols_table *tb)
+{
+ return tb->linesep;
+}
+/* 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;
+ struct libscols_cell *ca, *cb;
+
+ assert(a);
+ assert(b);
+ assert(cl);
+
+ ra = list_entry(a, struct libscols_line, ln_lines);
+ rb = list_entry(b, struct libscols_line, ln_lines);
+ ca = scols_line_get_cell(ra, cl->seqnum);
+ cb = scols_line_get_cell(rb, cl->seqnum);
+
+ 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)) {
+ 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);
+ }
+
+ if (is_first_group_member(ln)) {
+ list_for_each(p, &ln->group->gr_children) {
+ struct libscols_line *chld =
+ list_entry(p, struct libscols_line, ln_children);
+ sort_line_children(chld, cl);
+ }
+
+ list_sort(&ln->group->gr_children, cells_cmp_wrapper_children, cl);
+ }
+
+ return 0;
+}
+
+static int __scols_sort_tree(struct libscols_table *tb, struct libscols_column *cl)
+{
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+
+ if (!tb || !cl || !cl->cmpfunc)
+ return -EINVAL;
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0)
+ sort_line_children(ln, cl);
+ return 0;
+}
+
+/**
+ * scols_sort_table:
+ * @tb: table
+ * @cl: order by this column or NULL
+ *
+ * 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.
+ *
+ * The column @cl is saved as the default sort column to the @tb and the next time
+ * is possible to call scols_sort_table(tb, NULL). The saved column is also used by
+ * scols_sort_table_by_tree().
+ *
+ * Returns: 0, a negative value in case of an error.
+ */
+int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl)
+{
+ if (!tb)
+ return -EINVAL;
+ if (!cl)
+ cl = tb->dflt_sort_column;
+ if (!cl || !cl->cmpfunc)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "sorting table by %zu column", cl->seqnum));
+ list_sort(&tb->tb_lines, cells_cmp_wrapper_lines, cl);
+
+ if (scols_table_is_tree(tb))
+ __scols_sort_tree(tb, cl);
+
+ if (cl && cl != tb->dflt_sort_column)
+ tb->dflt_sort_column = cl;
+
+ return 0;
+}
+
+/*
+ * Move all @ln's children after @ln in the table.
+ */
+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 (after @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 by default.
+ *
+ * The children of the lines are sorted according to the default sort column
+ * if scols_sort_table() has been previously called.
+ *
+ * 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"));
+
+ if (tb->dflt_sort_column)
+ __scols_sort_tree(tb, tb->dflt_sort_column);
+
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0)
+ 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/version.c b/libsmartcols/src/version.c
new file mode 100644
index 0000000..e592ccc
--- /dev/null
+++ b/libsmartcols/src/version.c
@@ -0,0 +1,62 @@
+/*
+ * version.c - Return the version of the library
+ *
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * See COPYING.libmount for the License of this software.
+ */
+
+/**
+ * SECTION: version-utils
+ * @title: Version functions
+ * @short_description: functions to get the library version.
+ *
+ * Note that library version is not the same thing as SONAME version. The
+ * libsmarcols uses symbols versioning and SONAME is not modified for releases.
+ *
+ * The library version and symbols version follow util-linux package versioning.
+ */
+
+#include <ctype.h>
+
+#include "smartcolsP.h"
+
+static const char *lib_version = LIBSMARTCOLS_VERSION;
+
+/**
+ * scols_parse_version_string:
+ * @ver_string: version string (e.g "2.18.0")
+ *
+ * Returns: release version code.
+ */
+int scols_parse_version_string(const char *ver_string)
+{
+ const char *cp;
+ int version = 0;
+
+ assert(ver_string);
+
+ for (cp = ver_string; *cp; cp++) {
+ if (*cp == '.')
+ continue;
+ if (!isdigit(*cp))
+ break;
+ version = (version * 10) + (*cp - '0');
+ }
+ return version;
+}
+
+/**
+ * scols_get_library_version:
+ * @ver_string: return pointer to the static library version string if not NULL
+ *
+ * Returns: release version number.
+ */
+int scols_get_library_version(const char **ver_string)
+{
+ if (ver_string)
+ *ver_string = lib_version;
+
+ return scols_parse_version_string(lib_version);
+}
+
diff --git a/libsmartcols/src/walk.c b/libsmartcols/src/walk.c
new file mode 100644
index 0000000..a75fde6
--- /dev/null
+++ b/libsmartcols/src/walk.c
@@ -0,0 +1,152 @@
+#include "smartcolsP.h"
+
+static int walk_line(struct libscols_table *tb,
+ struct libscols_line *ln,
+ struct libscols_column *cl,
+ int (*callback)(struct libscols_table *,
+ struct libscols_line *,
+ struct libscols_column *,
+ void *),
+ void *data)
+{
+ int rc = 0;
+
+ DBG(LINE, ul_debugobj(ln, " wall line"));
+
+ /* we list group children in __scols_print_tree() after tree root node */
+ if (is_group_member(ln) && is_last_group_member(ln) && has_group_children(ln))
+ tb->ngrpchlds_pending++;
+
+ if (has_groups(tb))
+ rc = scols_groups_update_grpset(tb, ln);
+ if (rc == 0)
+ rc = callback(tb, ln, cl, data);
+
+ /* children */
+ if (rc == 0 && has_children(ln)) {
+ struct list_head *p;
+
+ DBG(LINE, ul_debugobj(ln, " children walk"));
+
+ list_for_each(p, &ln->ln_branch) {
+ struct libscols_line *chld = list_entry(p,
+ struct libscols_line, ln_children);
+
+ rc = walk_line(tb, chld, cl, callback, data);
+ if (rc)
+ break;
+ }
+ }
+
+ DBG(LINE, ul_debugobj(ln, "<- walk line done [rc=%d]", rc));
+ return rc;
+}
+
+/* last line in the tree? */
+int scols_walk_is_last(struct libscols_table *tb, struct libscols_line *ln)
+{
+ if (tb->walk_last_done == 0)
+ return 0;
+ if (tb->ngrpchlds_pending > 0)
+ return 0;
+ if (has_children(ln))
+ return 0;
+ if (is_tree_root(ln) && !is_last_tree_root(tb, ln))
+ return 0;
+ if (is_group_member(ln) && (!is_last_group_member(ln) || has_group_children(ln)))
+ return 0;
+ if (is_child(ln)) {
+ struct libscols_line *parent = ln->parent;
+
+ if (!is_last_child(ln))
+ return 0;
+ while (parent) {
+ if (is_child(parent) && !is_last_child(parent))
+ return 0;
+ if (!parent->parent)
+ break;
+ parent = parent->parent;
+ }
+ if (is_tree_root(parent) && !is_last_tree_root(tb, parent))
+ return 0;
+ }
+ if (is_group_child(ln) && !is_last_group_child(ln))
+ return 0;
+
+ DBG(LINE, ul_debugobj(ln, "last in table"));
+ return 1;
+}
+
+int scols_walk_tree(struct libscols_table *tb,
+ struct libscols_column *cl,
+ int (*callback)(struct libscols_table *,
+ struct libscols_line *,
+ struct libscols_column *,
+ void *),
+ void *data)
+{
+ int rc = 0;
+ struct libscols_line *ln;
+ struct libscols_iter itr;
+
+ assert(tb);
+ DBG(TAB, ul_debugobj(tb, ">> walk start"));
+
+ /* init */
+ tb->ngrpchlds_pending = 0;
+ tb->walk_last_tree_root = NULL;
+ tb->walk_last_done = 0;
+
+ if (has_groups(tb))
+ scols_groups_reset_state(tb);
+
+ /* set pointer to last tree root */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (!tb->walk_last_tree_root)
+ tb->walk_last_tree_root = ln;
+ if (is_child(ln) || is_group_child(ln))
+ continue;
+ tb->walk_last_tree_root = ln;
+ }
+
+ /* walk */
+ scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
+ if (ln->parent || ln->parent_group)
+ continue;
+
+ if (tb->walk_last_tree_root == ln)
+ tb->walk_last_done = 1;
+ rc = walk_line(tb, ln, cl, callback, data);
+
+ /* walk group's children */
+ while (rc == 0 && tb->ngrpchlds_pending) {
+ struct libscols_group *gr = scols_grpset_get_printable_children(tb);
+ struct list_head *p;
+
+ DBG(LINE, ul_debugobj(ln, " walk group children [pending=%zu]", tb->ngrpchlds_pending));
+ if (!gr) {
+ DBG(LINE, ul_debugobj(ln, " *** ngrpchlds_pending counter invalid"));
+ tb->ngrpchlds_pending = 0;
+ break;
+ }
+
+ tb->ngrpchlds_pending--;
+
+ list_for_each(p, &gr->gr_children) {
+ struct libscols_line *chld =
+ list_entry(p, struct libscols_line, ln_children);
+
+ rc = walk_line(tb, chld, cl, callback, data);
+ if (rc)
+ break;
+ }
+ }
+ }
+
+ tb->ngrpchlds_pending = 0;
+ tb->walk_last_done = 0;
+ DBG(TAB, ul_debugobj(tb, "<< walk end [rc=%d]", rc));
+ return rc;
+}