diff --git a/SOURCES/0150-libblkid-minix-Match-minix-superblock-types.patch b/SOURCES/0150-libblkid-minix-Match-minix-superblock-types.patch
deleted file mode 100644
index cd86e04..0000000
--- a/SOURCES/0150-libblkid-minix-Match-minix-superblock-types.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 4ce5726c0ed93e7607c5076f587546c32cf83c2f Mon Sep 17 00:00:00 2001
-From: Nate Clark <nate@neworld.us>
-Date: Wed, 4 Jan 2017 15:24:22 -0500
-Subject: [PATCH 150/152] libblkid/minix: Match minix superblock types
-
-All of the types in the minix super block are unsigned but in
-probe_minix they were being treated as signed. This would cause some of
-the extra sanity checks to pass on a non minix device. The types were
-updated to match the return types of the helper functions in
-disk-utils/minix_programs.h
-
-This can be checked by creating a swap partition with one of these UUIDs
-35f1f264-2468-471a-bc85-acc9f4bc04a3
-35f1f264-6824-471a-bc85-acc9f4bc04a3
-35f1f264-2478-471a-bc85-acc9f4bc04a3
-35f1f264-7824-471a-bc85-acc9f4bc04a3
-
-Prior to this change they would all be considered minix and swap by
-blkid.
-
-Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681
-Upstream: http://github.com/karelzak/util-linux/commit/a9975c1072c4975ec2df958188a80d89cabc6171
-Signed-off-by: Nate Clark <nate@neworld.us>
----
- libblkid/src/superblocks/minix.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
-index 3e80e5b22..a20d51f2c 100644
---- a/libblkid/src/superblocks/minix.c
-+++ b/libblkid/src/superblocks/minix.c
-@@ -87,7 +87,8 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
- 
- 	if (version <= 2) {
- 		struct minix_super_block *sb = (struct minix_super_block *) data;
--		int zones, ninodes, imaps, zmaps, firstz;
-+		unsigned long zones, ninodes, imaps, zmaps;
-+		off_t firstz;
- 
- 		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
- 			return 1;
--- 
-2.14.4
-
diff --git a/SOURCES/0150-lsns-missing-ns-name-is-not-error.patch b/SOURCES/0150-lsns-missing-ns-name-is-not-error.patch
new file mode 100644
index 0000000..33bf09e
--- /dev/null
+++ b/SOURCES/0150-lsns-missing-ns-name-is-not-error.patch
@@ -0,0 +1,44 @@
+From 8694f63002c6b765e72c36fbf8ed46164d5303e5 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Mon, 15 Aug 2016 11:02:18 +0200
+Subject: [PATCH 150/173] lsns: missing ns/<name> is not error
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For example user namespace is optional it does not make sense to
+ignore process completely if the ns/user file is missing.
+
+Reported-by: Michał Bartoszkiewicz <mbartoszkiewicz@gmail.com>
+Upstream: http://github.com/karelzak/util-linux/commit/3082f8518f2739e9f68e660f1749acdd2b9d7a97
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1543428
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/lsns.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c
+index fb53a16a4..b8841b7a3 100644
+--- a/sys-utils/lsns.c
++++ b/sys-utils/lsns.c
+@@ -204,7 +204,7 @@ static inline const struct colinfo *get_column_info(unsigned num)
+ 	return &infos[ get_column_id(num) ];
+ }
+ 
+-static ino_t get_ns_ino(int dir, const char *nsname, ino_t *ino)
++static int get_ns_ino(int dir, const char *nsname, ino_t *ino)
+ {
+ 	struct stat st;
+ 	char path[16];
+@@ -269,7 +269,7 @@ static int read_process(struct lsns *ls, pid_t pid)
+ 			continue;
+ 
+ 		rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i]);
+-		if (rc && rc != -EACCES)
++		if (rc && rc != -EACCES && rc != -ENOENT)
+ 			goto done;
+ 		rc = 0;
+ 	}
+-- 
+2.14.4
+
diff --git a/SOURCES/0151-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch b/SOURCES/0151-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch
deleted file mode 100644
index d646253..0000000
--- a/SOURCES/0151-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 289f0cd75f168f86702913658ebe2dbfd8e987c6 Mon Sep 17 00:00:00 2001
-From: Nate Clark <nate@neworld.us>
-Date: Wed, 4 Jan 2017 15:21:17 -0500
-Subject: [PATCH 151/152] libblkid/minix: Sanity check superblock s_state for v
- 1 and 2
-
-Swap devices with specific values in the uuid can look like minix
-devices to blkid. Add an extra check to make sure the state of the
-filesystem has valid state flags.
-
-A couple of offending swap uuids include:
-35f1f264-137f-471a-bc85-acc9f4bc04a3
-35f1f264-7f13-471a-bc85-acc9f4bc04a3
-35f1f264-138f-471a-bc85-acc9f4bc04a3
-35f1f264-8f13-471a-bc85-acc9f4bc04a3
-
-Without this change a swap device with any of those uuids would be
-detected as minix and swap by blkid.
-
-Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681
-Upstream: http://github.com/karelzak/util-linux/commit/892553b1a41b449f58462f123eca2bf2c6c56b33
-Signed-off-by: Nate Clark <nate@neworld.us>
----
- libblkid/src/superblocks/minix.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
-index a20d51f2c..4e70fda8f 100644
---- a/libblkid/src/superblocks/minix.c
-+++ b/libblkid/src/superblocks/minix.c
-@@ -93,6 +93,10 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
- 		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
- 			return 1;
- 
-+		uint16_t state = minix_swab16(swabme, sb->s_state);
-+		if ((state & (MINIX_VALID_FS | MINIX_ERROR_FS)) != state)
-+			return 1;
-+
- 		zones = version == 2 ? minix_swab32(swabme, sb->s_zones) :
- 				       minix_swab16(swabme, sb->s_nzones);
- 		ninodes = minix_swab16(swabme, sb->s_ninodes);
--- 
-2.14.4
-
diff --git a/SOURCES/0151-lsns-Fix-parser-for-proc-pid-stat-which-is-including.patch b/SOURCES/0151-lsns-Fix-parser-for-proc-pid-stat-which-is-including.patch
new file mode 100644
index 0000000..bad3a76
--- /dev/null
+++ b/SOURCES/0151-lsns-Fix-parser-for-proc-pid-stat-which-is-including.patch
@@ -0,0 +1,81 @@
+From 7a151a3d74b2972410103b684803e6d6b8fda15b Mon Sep 17 00:00:00 2001
+From: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
+Date: Wed, 23 Nov 2016 14:13:34 +0900
+Subject: [PATCH 151/173] lsns: Fix parser for /proc/<pid>/stat which is
+ including space in comm
+
+For example, child process of spamd has
+
+    32031 (spamd child) S 32026 32026 32026 0 -1 4210752 338 0 0 0 ...
+
+fscanf("%d %*s %c %d*[^\n]") in read_process() can't parse above as we
+expected, because %s only skips non-whitespace. I.e. it parses like
+following,
+
+    32031 (spamd child) S 32026 32026 32026 0 -1 4210752 338 0 0 0 ...
+    +---+ +----+ +
+      %d    %*s  %c
+
+and returns 2 (pid=32031, state=c).
+
+This fixes it by skipping task->comm part manually.
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1543428
+Upstream: http://github.com/karelzak/util-linux/commit/3fcbd7978980dc1a29c626b701333e27599e506d
+Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
+---
+ sys-utils/lsns.c | 30 ++++++++++++++++++++++++++----
+ 1 file changed, 26 insertions(+), 4 deletions(-)
+
+diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c
+index b8841b7a3..d32756508 100644
+--- a/sys-utils/lsns.c
++++ b/sys-utils/lsns.c
+@@ -217,6 +217,30 @@ static int get_ns_ino(int dir, const char *nsname, ino_t *ino)
+ 	return 0;
+ }
+ 
++static int parse_proc_stat(FILE *fp, pid_t *pid, char *state, pid_t *ppid)
++{
++	char *line = NULL, *p;
++	size_t len = 0;
++	int rc;
++
++	if (getline(&line, &len, fp) < 0) {
++		rc = -errno;
++		goto error;
++	}
++
++	p = strrchr(line, ')');
++	if (p == NULL ||
++	    sscanf(line, "%d (", pid) != 1 ||
++	    sscanf(p, ") %c %d*[^\n]", state, ppid) != 2) {
++		rc = -EINVAL;
++		goto error;
++	}
++	rc = 0;
++
++error:
++	free(line);
++	return rc;
++}
+ 
+ static int read_process(struct lsns *ls, pid_t pid)
+ {
+@@ -255,11 +279,9 @@ static int read_process(struct lsns *ls, pid_t pid)
+ 		rc = -errno;
+ 		goto done;
+ 	}
+-	rc = fscanf(f, "%d %*s %c %d*[^\n]", &p->pid, &p->state, &p->ppid);
+-	if (rc != 3) {
+-		rc = rc < 0 ? -errno : -EINVAL;
++	rc = parse_proc_stat(f, &p->pid, &p->state, &p->ppid);
++	if (rc < 0)
+ 		goto done;
+-	}
+ 	rc = 0;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(p->ns_ids); i++) {
+-- 
+2.14.4
+
diff --git a/SOURCES/0152-libblkid-minix-Use-same-checks-for-version-3.patch b/SOURCES/0152-libblkid-minix-Use-same-checks-for-version-3.patch
deleted file mode 100644
index 31bb209..0000000
--- a/SOURCES/0152-libblkid-minix-Use-same-checks-for-version-3.patch
+++ /dev/null
@@ -1,90 +0,0 @@
-From 0ade71743e5f96fbdb0f4a85c3eef22b88363f13 Mon Sep 17 00:00:00 2001
-From: Nate Clark <nate@neworld.us>
-Date: Wed, 4 Jan 2017 15:24:32 -0500
-Subject: [PATCH 152/152] libblkid/minix: Use same checks for version 3
-
-fsck.minix performs the same sanity checks on all versions of the
-superblock. Update the probe to perform the same sanity checks so it is
-less likely a different type of filesystem will be identified as minix.
-
-Upstream: http://github.com/karelzak/util-linux/commit/f82c804869bb8613fa0924e3111b7eb55bb04fcd
-Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681
-Signed-off-by: Nate Clark <nate@neworld.us>
-Signed-off-by: Karel Zak <kzak@redhat.com>
----
- libblkid/src/superblocks/minix.c | 38 +++++++++++++++++++++++---------------
- 1 file changed, 23 insertions(+), 15 deletions(-)
-
-diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
-index 4e70fda8f..21b3bf8bb 100644
---- a/libblkid/src/superblocks/minix.c
-+++ b/libblkid/src/superblocks/minix.c
-@@ -75,6 +75,9 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
- 	unsigned char *ext;
- 	const unsigned char *data;
- 	int version = 0, swabme = 0;
-+	unsigned long zones, ninodes, imaps, zmaps;
-+	off_t firstz;
-+	size_t zone_size;
- 
- 	data = blkid_probe_get_buffer(pr, 1024,
- 			max(sizeof(struct minix_super_block),
-@@ -85,14 +88,9 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
- 	if (version < 1)
- 		return 1;
- 
-+
- 	if (version <= 2) {
- 		struct minix_super_block *sb = (struct minix_super_block *) data;
--		unsigned long zones, ninodes, imaps, zmaps;
--		off_t firstz;
--
--		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
--			return 1;
--
- 		uint16_t state = minix_swab16(swabme, sb->s_state);
- 		if ((state & (MINIX_VALID_FS | MINIX_ERROR_FS)) != state)
- 			return 1;
-@@ -103,20 +101,30 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
- 		imaps   = minix_swab16(swabme, sb->s_imap_blocks);
- 		zmaps   = minix_swab16(swabme, sb->s_zmap_blocks);
- 		firstz  = minix_swab16(swabme, sb->s_firstdatazone);
--
--		/* sanity checks to be sure that the FS is really minix */
--		if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1)
--			return 1;
--		if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1)
--			return 1;
--
-+		zone_size = sb->s_log_zone_size;
- 	} else if (version == 3) {
- 		struct minix3_super_block *sb = (struct minix3_super_block *) data;
- 
--		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
--			return 1;
-+		zones = minix_swab32(swabme, sb->s_zones);
-+		ninodes = minix_swab32(swabme, sb->s_ninodes);
-+		imaps   = minix_swab16(swabme, sb->s_imap_blocks);
-+		zmaps   = minix_swab16(swabme, sb->s_zmap_blocks);
-+		firstz  = minix_swab16(swabme, sb->s_firstdatazone);
-+		zone_size = sb->s_log_zone_size;
- 	}
- 
-+	/* sanity checks to be sure that the FS is really minix.
-+	 * see disk-utils/fsck.minix.c read_superblock
-+	 */
-+	if (zone_size != 0 || ninodes == 0 || ninodes == UINT32_MAX)
-+		return 1;
-+	if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1)
-+		return 1;
-+	if (firstz > (off_t) zones)
-+		return 1;
-+	if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1)
-+		return 1;
-+
- 	/* unfortunately, some parts of ext3 is sometimes possible to
- 	 * interpreted as minix superblock. So check for extN magic
- 	 * string. (For extN magic string and offsets see ext.c.)
--- 
-2.14.4
-
diff --git a/SOURCES/0152-libsmartcols-add-basic-tools-necessary-for-new-versi.patch b/SOURCES/0152-libsmartcols-add-basic-tools-necessary-for-new-versi.patch
new file mode 100644
index 0000000..31db3c6
--- /dev/null
+++ b/SOURCES/0152-libsmartcols-add-basic-tools-necessary-for-new-versi.patch
@@ -0,0 +1,757 @@
+From c465ce9765273e8e1227b192e1917826ac4eaaf7 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 31 May 2018 11:13:31 +0200
+Subject: [PATCH 152/173] libsmartcols: add basic tools necessary for new
+ version
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1561350
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ configure.ac          |   3 +
+ include/carefulputc.h |  96 +++++++++++++++++++++++++++-
+ include/colors.h      |   5 ++
+ include/mbsalign.h    |   9 ++-
+ include/strutils.h    |  13 ++--
+ include/ttyutils.h    |   1 +
+ lib/Makemodule.am     |   1 +
+ lib/color-names.c     |  57 +++++++++++++++++
+ lib/mbsalign.c        | 172 +++++++++++++++++++++++++++++++++++++++++---------
+ lib/ttyutils.c        |  52 +++++++++++++++
+ libfdisk/src/ask.c    |   4 +-
+ 11 files changed, 373 insertions(+), 40 deletions(-)
+ create mode 100644 lib/color-names.c
+
+diff --git a/configure.ac b/configure.ac
+index d561e01d0..8cf317dc0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -133,6 +133,9 @@ AC_SUBST([BSD_WARN_CFLAGS])
+ dnl libtool-2
+ LT_INIT
+ 
++dnl check supported linker flags
++AX_CHECK_VSCRIPT
++
+ m4_ifndef([PKG_PROG_PKG_CONFIG],
+   [m4_fatal([Could not locate the pkg-config autoconf
+     macros. These are usually located in /usr/share/aclocal/pkg.m4.
+diff --git a/include/carefulputc.h b/include/carefulputc.h
+index a54498cfd..613d94c1e 100644
+--- a/include/carefulputc.h
++++ b/include/carefulputc.h
+@@ -26,7 +26,87 @@ static inline int carefulputc(int c, FILE *fp) {
+ 	return (ret < 0) ? EOF : 0;
+ }
+ 
+-static inline void fputs_quoted(const char *data, FILE *out)
++/*
++ * Backported for RHEL7.6 libsmartcols
++ */
++
++/*
++ * Requirements enumerated via testing (V8, Firefox, IE11):
++ *
++ * var charsToEscape = [];
++ * for (var i = 0; i < 65535; i += 1) {
++ *	try {
++ *		JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}');
++ *	} catch (e) {
++ *		charsToEscape.push(i);
++ *	}
++ * }
++ */
++static inline void fputs_quoted_case_json(const char *data, FILE *out, int dir)
++{
++	const char *p;
++
++	fputc('"', out);
++	for (p = data; p && *p; p++) {
++
++		const unsigned char c = (unsigned char) *p;
++
++		/* From http://www.json.org
++		 *
++		 * The double-quote and backslashes would break out a string or
++		 * init an escape sequence if not escaped.
++		 *
++		 * Note that single-quotes and forward slashes, while they're
++		 * in the JSON spec, don't break double-quoted strings.
++		 */
++		if (c == '"' || c == '\\') {
++			fputc('\\', out);
++			fputc(c, out);
++			continue;
++		}
++
++		/* All non-control characters OK; do the case swap as required. */
++		if (c >= 0x20) {
++			fputc(dir ==  1 ? toupper(c) :
++			      dir == -1 ? tolower(c) : *p, out);
++			continue;
++		}
++
++		/* In addition, all chars under ' ' break Node's/V8/Chrome's, and
++		 * Firefox's JSON.parse function
++		 */
++		switch (c) {
++			/* Handle short-hand cases to reduce output size.  C
++			 * has most of the same stuff here, so if there's an
++			 * "Escape for C" function somewhere in the STL, we
++			 * should probably be using it.
++			 */
++			case '\b':
++				fputs("\\b", out);
++				break;
++			case '\t':
++				fputs("\\t", out);
++				break;
++			case '\n':
++				fputs("\\n", out);
++				break;
++			case '\f':
++				fputs("\\f", out);
++				break;
++			case '\r':
++				fputs("\\r", out);
++				break;
++			default:
++				/* Other assorted control characters */
++				fprintf(out, "\\u00%02x", c);
++				break;
++		}
++	}
++	fputc('"', out);
++}
++
++
++static inline void fputs_quoted_case(const char *data, FILE *out, int dir)
+ {
+ 	const char *p;
+ 
+@@ -34,16 +114,28 @@ static inline void fputs_quoted(const char *data, FILE *out)
+ 	for (p = data; p && *p; p++) {
+ 		if ((unsigned char) *p == 0x22 ||		/* " */
+ 		    (unsigned char) *p == 0x5c ||		/* \ */
++		    (unsigned char) *p == 0x60 ||		/* ` */
++		    (unsigned char) *p == 0x24 ||		/* $ */
+ 		    !isprint((unsigned char) *p) ||
+ 		    iscntrl((unsigned char) *p)) {
+ 
+ 			fprintf(out, "\\x%02x", (unsigned char) *p);
+ 		} else
+-			fputc(*p, out);
++			fputc(dir ==  1 ? toupper(*p) :
++			      dir == -1 ? tolower(*p) :
++			      *p, out);
+ 	}
+ 	fputc('"', out);
+ }
+ 
++#define fputs_quoted(_d, _o)		fputs_quoted_case(_d, _o, 0)
++#define fputs_quoted_upper(_d, _o)	fputs_quoted_case(_d, _o, 1)
++#define fputs_quoted_lower(_d, _o)	fputs_quoted_case(_d, _o, -1)
++
++#define fputs_quoted_json(_d, _o)       fputs_quoted_case_json(_d, _o, 0)
++#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1)
++#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1)
++
+ static inline void fputs_nonblank(const char *data, FILE *out)
+ {
+ 	const char *p;
+diff --git a/include/colors.h b/include/colors.h
+index dd77bf6df..39c0edf46 100644
+--- a/include/colors.h
++++ b/include/colors.h
+@@ -38,6 +38,11 @@
+ 
+ #define UL_COLOR_WHITE		"\033[1;37m"
+ 
++/* maximal length of human readable name of ESC seq. */
++#define UL_COLORNAME_MAXSZ      32
++
++extern const char *color_sequence_from_colorname(const char *str);
++
+ /* Initialize the global variable OUT_IS_TERM */
+ extern int colors_init(void);
+ 
+diff --git a/include/mbsalign.h b/include/mbsalign.h
+index 5eaf606e5..0c28e6f69 100644
+--- a/include/mbsalign.h
++++ b/include/mbsalign.h
+@@ -46,11 +46,18 @@ extern size_t mbsalign (const char *src, char *dest,
+ 			size_t dest_size,  size_t *width,
+ 			mbs_align_t align, int flags);
+ 
++extern size_t mbsalign_with_padding (const char *src, char *dest, size_t dest_size,
++	               size_t *width, mbs_align_t align, int flags,
++		       int padchar);
++
+ extern size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz);
+ extern size_t mbs_safe_width(const char *s);
+ 
+ extern char *mbs_safe_encode(const char *s, size_t *width);
+-extern char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf);
++extern char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf, const char *safechars);
+ extern size_t mbs_safe_encode_size(size_t bytes);
+ 
++extern char *mbs_invalid_encode(const char *s, size_t *width);
++extern char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf);
++
+ #endif /* UTIL_LINUX_MBSALIGN_H */
+diff --git a/include/strutils.h b/include/strutils.h
+index 1f028e4ed..822fb7d49 100644
+--- a/include/strutils.h
++++ b/include/strutils.h
+@@ -6,6 +6,7 @@
+ #include <string.h>
+ #include <sys/types.h>
+ #include <stdio.h>
++#include <errno.h>
+ 
+ /* default strtoxx_or_err() exit code */
+ #ifndef STRTOXX_EXIT_CODE
+@@ -57,20 +58,24 @@ static inline void xstrncpy(char *dest, const char *src, size_t n)
+ 	dest[n-1] = 0;
+ }
+ 
+-static inline char *strdup_to_offset(void *stru, size_t offset, const char *str)
++static inline int strdup_to_offset(void *stru, size_t offset, const char *str)
+ {
+ 	char *n = NULL;
+-	char **o = (char **) ((char *) stru + offset);
++	char **o;
+ 
++	if (!stru)
++		return -EINVAL;
++
++	o = (char **) ((char *) stru + offset);
+ 	if (str) {
+ 		n = strdup(str);
+ 		if (!n)
+-			return NULL;
++			return -ENOMEM;
+ 	}
+ 
+ 	free(*o);
+ 	*o = n;
+-	return n;
++	return 0;
+ }
+ 
+ #define strdup_to_struct_member(_s, _m, _str) \
+diff --git a/include/ttyutils.h b/include/ttyutils.h
+index 13495ba96..47fe34472 100644
+--- a/include/ttyutils.h
++++ b/include/ttyutils.h
+@@ -47,6 +47,7 @@ struct chardata {
+ 	        (ptr)->capslock = 0;         \
+ 	} while (0)
+ 
++extern int get_terminal_dimension(int *cols, int *lines);
+ extern int get_terminal_width(void);
+ extern int get_terminal_name(int fd, const char **path, const char **name,
+ 			     const char **number);
+diff --git a/lib/Makemodule.am b/lib/Makemodule.am
+index acae27afb..714233c40 100644
+--- a/lib/Makemodule.am
++++ b/lib/Makemodule.am
+@@ -6,6 +6,7 @@ libcommon_la_SOURCES = \
+ 	lib/blkdev.c \
+ 	lib/canonicalize.c \
+ 	lib/colors.c \
++	lib/color-names.c \
+ 	lib/crc32.c \
+ 	lib/env.c \
+ 	lib/idcache.c \
+diff --git a/lib/color-names.c b/lib/color-names.c
+new file mode 100644
+index 000000000..cf37670a9
+--- /dev/null
++++ b/lib/color-names.c
+@@ -0,0 +1,57 @@
++
++#include "c.h"
++#include "colors.h"
++
++struct ul_color_name {
++	const char *name;
++	const char *seq;
++};
++
++/*
++ * qsort/bsearch buddy
++ */
++static int cmp_color_name(const void *a0, const void *b0)
++{
++	struct ul_color_name	*a = (struct ul_color_name *) a0,
++				*b = (struct ul_color_name *) b0;
++	return strcmp(a->name, b->name);
++}
++
++/*
++ * Maintains human readable color names
++ */
++const char *color_sequence_from_colorname(const char *str)
++{
++	static const struct ul_color_name basic_schemes[] = {
++		{ "black",	UL_COLOR_BLACK           },
++		{ "blink",      UL_COLOR_BLINK           },
++		{ "blue",	UL_COLOR_BLUE            },
++		{ "bold",       UL_COLOR_BOLD		 },
++		{ "brown",	UL_COLOR_BROWN           },
++		{ "cyan",	UL_COLOR_CYAN            },
++		{ "darkgray",	UL_COLOR_DARK_GRAY       },
++		{ "gray",	UL_COLOR_GRAY            },
++		{ "green",	UL_COLOR_GREEN           },
++		{ "halfbright", UL_COLOR_HALFBRIGHT	 },
++		{ "lightblue",	UL_COLOR_BOLD_BLUE       },
++		{ "lightcyan",	UL_COLOR_BOLD_CYAN       },
++		{ "lightgray,",	UL_COLOR_GRAY            },
++		{ "lightgreen", UL_COLOR_BOLD_GREEN      },
++		{ "lightmagenta", UL_COLOR_BOLD_MAGENTA  },
++		{ "lightred",	UL_COLOR_BOLD_RED        },
++		{ "magenta",	UL_COLOR_MAGENTA         },
++		{ "red",	UL_COLOR_RED             },
++		{ "reset",      UL_COLOR_RESET,          },
++		{ "reverse",    UL_COLOR_REVERSE         },
++		{ "yellow",	UL_COLOR_BOLD_YELLOW     },
++	};
++	struct ul_color_name key = { .name = (char *) str }, *res;
++
++	if (!str)
++		return NULL;
++
++	res = bsearch(&key, basic_schemes, ARRAY_SIZE(basic_schemes),
++				sizeof(struct ul_color_name),
++				cmp_color_name);
++	return res ? res->seq : NULL;
++}
+diff --git a/lib/mbsalign.c b/lib/mbsalign.c
+index b307d19f7..8fdab9ee9 100644
+--- a/lib/mbsalign.c
++++ b/lib/mbsalign.c
+@@ -27,9 +27,9 @@
+ 
+ #include "c.h"
+ #include "mbsalign.h"
++#include "strutils.h"
+ #include "widechar.h"
+ 
+-#ifdef HAVE_WIDECHAR
+ /* Replace non printable chars.
+    Note \t and \n etc. are non printable.
+    Return 1 if replacement made, 0 otherwise.  */
+@@ -43,17 +43,19 @@
+  */
+ size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz)
+ {
+-	mbstate_t st;
+ 	const char *p = buf, *last = buf;
+ 	size_t width = 0, bytes = 0;
+ 
++#ifdef HAVE_WIDECHAR
++	mbstate_t st;
+ 	memset(&st, 0, sizeof(st));
+-
++#endif
+ 	if (p && *p && bufsz)
+ 		last = p + (bufsz - 1);
+ 
+ 	while (p && *p && p <= last) {
+-		if (iscntrl((unsigned char) *p)) {
++		if ((p < last && *p == '\\' && *(p + 1) == 'x')
++		    || iscntrl((unsigned char) *p)) {
+ 			width += 4, bytes += 4;		/* *p encoded to \x?? */
+ 			p++;
+ 		}
+@@ -106,28 +108,36 @@ size_t mbs_safe_width(const char *s)
+ 
+ /*
+  * Copy @s to @buf and replace control and non-printable chars with
+- * \x?? hex sequence. The @width returns number of cells.
++ * \x?? hex sequence. The @width returns number of cells. The @safechars
++ * are not encoded.
+  *
+  * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s)))
+  * bytes.
+  */
+-char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf)
++char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf, const char *safechars)
+ {
+-	mbstate_t st;
+ 	const char *p = s;
+ 	char *r;
+ 	size_t sz = s ? strlen(s) : 0;
+ 
++#ifdef HAVE_WIDECHAR
++	mbstate_t st;
++	memset(&st, 0, sizeof(st));
++#endif
+ 	if (!sz || !buf)
+ 		return NULL;
+ 
+-	memset(&st, 0, sizeof(st));
+-
+ 	r = buf;
+ 	*width = 0;
+ 
+ 	while (p && *p) {
+-		if (iscntrl((unsigned char) *p)) {
++		if (safechars && strchr(safechars, *p)) {
++			*r++ = *p++;
++			continue;
++		}
++
++		if ((*p == '\\' && *(p + 1) == 'x')
++		    || iscntrl((unsigned char) *p)) {
+ 			sprintf(r, "\\x%02x", (unsigned char) *p);
+ 			r += 4;
+ 			*width += 4;
+@@ -152,13 +162,13 @@ char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf)
+ 					r += 4;
+ 					*width += 4;
+ 				} else {
+-					width++;
++					(*width)++;
+ 					*r++ = *p;
+ 				}
+ 			} else if (!iswprint(wc)) {
+ 				size_t i;
+ 				for (i = 0; i < len; i++) {
+-					sprintf(r, "\\x%02x", (unsigned char) *p);
++					sprintf(r, "\\x%02x", (unsigned char) p[i]);
+ 					r += 4;
+ 					*width += 4;
+ 				}
+@@ -177,13 +187,76 @@ char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf)
+ 			*width += 4;
+ 		} else {
+ 			*r++ = *p++;
+-			*width++;
++			(*width)++;
+ 		}
+ #endif
+ 	}
+ 
+ 	*r = '\0';
++	return buf;
++}
+ 
++/*
++ * Copy @s to @buf and replace broken sequences to \x?? hex sequence. The
++ * @width returns number of cells. The @safechars are not encoded.
++ *
++ * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s)))
++ * bytes.
++ */
++char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf)
++{
++	const char *p = s;
++	char *r;
++	size_t sz = s ? strlen(s) : 0;
++
++#ifdef HAVE_WIDECHAR
++	mbstate_t st;
++	memset(&st, 0, sizeof(st));
++#endif
++	if (!sz || !buf)
++		return NULL;
++
++	r = buf;
++	*width = 0;
++
++	while (p && *p) {
++#ifdef HAVE_WIDECHAR
++		wchar_t wc;
++		size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
++#else
++		size_t len = 1;
++#endif
++
++		if (len == 0)
++			break;		/* end of string */
++
++		if (len == (size_t) -1 || len == (size_t) -2) {
++			len = 1;
++			/*
++			 * Not valid multibyte sequence -- maybe it's
++			 * printable char according to the current locales.
++			 */
++			if (!isprint((unsigned char) *p)) {
++				sprintf(r, "\\x%02x", (unsigned char) *p);
++				r += 4;
++				*width += 4;
++			} else {
++				(*width)++;
++				*r++ = *p;
++			}
++		} else if (*p == '\\' && *(p + 1) == 'x') {
++			sprintf(r, "\\x%02x", (unsigned char) *p);
++			r += 4;
++			*width += 4;
++		} else {
++			memcpy(r, p, len);
++			r += len;
++			*width += wcwidth(wc);
++		}
++		p += len;
++	}
++
++	*r = '\0';
+ 	return buf;
+ }
+ 
+@@ -199,17 +272,39 @@ size_t mbs_safe_encode_size(size_t bytes)
+ char *mbs_safe_encode(const char *s, size_t *width)
+ {
+ 	size_t sz = s ? strlen(s) : 0;
+-	char *buf;
++	char *buf, *ret = NULL;
+ 
+ 	if (!sz)
+ 		return NULL;
+ 	buf = malloc(mbs_safe_encode_size(sz));
+-	if (!buf)
+-		return NULL;
++	if (buf)
++		ret = mbs_safe_encode_to_buffer(s, width, buf, NULL);
++	if (!ret)
++		free(buf);
++	return ret;
++}
+ 
+-	return mbs_safe_encode_to_buffer(s, width, buf);
++/*
++ * Returns allocated string where all broken widechars chars are
++ * replaced with \x?? hex sequence.
++ */
++char *mbs_invalid_encode(const char *s, size_t *width)
++{
++	size_t sz = s ? strlen(s) : 0;
++	char *buf, *ret = NULL;
++
++	if (!sz)
++		return NULL;
++	buf = malloc(mbs_safe_encode_size(sz));
++	if (buf)
++		ret = mbs_invalid_encode_to_buffer(s, width, buf);
++	if (!ret)
++		free(buf);
++	return ret;
+ }
+ 
++#ifdef HAVE_WIDECHAR
++
+ static bool
+ wc_ensure_printable (wchar_t *wchars)
+ {
+@@ -246,6 +341,7 @@ wc_truncate (wchar_t *wc, size_t width)
+         }
+       if (cells + next_cells > width)
+         break;
++
+       cells += next_cells;
+       wc++;
+     }
+@@ -273,7 +369,7 @@ rpl_wcswidth (const wchar_t *s, size_t n)
+ 
+   return ret;
+ }
+-#endif
++#endif /* HAVE_WIDECHAR */
+ 
+ /* Truncate multi-byte string to @width and returns number of
+  * bytes of the new string @str, and in @width returns number
+@@ -290,7 +386,7 @@ mbs_truncate(char *str, size_t *width)
+ 	if (sz == (ssize_t) -1)
+ 		goto done;
+ 
+-	wcs = malloc((sz + 1) * sizeof(wchar_t));
++	wcs = calloc(1, (sz + 1) * sizeof(wchar_t));
+ 	if (!wcs)
+ 		goto done;
+ 
+@@ -301,7 +397,7 @@ mbs_truncate(char *str, size_t *width)
+ done:
+ 	free(wcs);
+ #else
+-	if (*width < bytes)
++	if (bytes >= 0 && *width < (size_t) bytes)
+ 		bytes = *width;
+ #endif
+ 	if (bytes >= 0)
+@@ -315,16 +411,23 @@ done:
+    A pointer to the terminating NUL is returned.  */
+ 
+ static char*
+-mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces)
++mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces, int padchar)
+ {
+   /* FIXME: Should we pad with "figure space" (\u2007)
+      if non ascii data present?  */
+-  while (n_spaces-- && (dest < dest_end))
+-    *dest++ = ' ';
++  for (/* nothing */; n_spaces && (dest < dest_end); n_spaces--)
++    *dest++ = padchar;
+   *dest = '\0';
+   return dest;
+ }
+ 
++size_t
++mbsalign (const char *src, char *dest, size_t dest_size,
++          size_t *width, mbs_align_t align, int flags)
++{
++	return mbsalign_with_padding(src, dest, dest_size, width, align, flags, ' ');
++}
++
+ /* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte
+    characters; write the result into the DEST_SIZE-byte buffer, DEST.
+    ALIGNMENT specifies whether to left- or right-justify or to center.
+@@ -339,8 +442,14 @@ mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces)
+    Update *WIDTH to indicate how many columns were used before padding.  */
+ 
+ size_t
+-mbsalign (const char *src, char *dest, size_t dest_size,
+-          size_t *width, mbs_align_t align, int flags)
++mbsalign_with_padding (const char *src, char *dest, size_t dest_size,
++	               size_t *width, mbs_align_t align,
++#ifdef HAVE_WIDECHAR
++		       int flags,
++#else
++		       int flags __attribute__((__unused__)),
++#endif
++		       int padchar)
+ {
+   size_t ret = -1;
+   size_t src_size = strlen (src) + 1;
+@@ -350,10 +459,11 @@ mbsalign (const char *src, char *dest, size_t dest_size,
+   size_t n_cols = src_size - 1;
+   size_t n_used_bytes = n_cols; /* Not including NUL */
+   size_t n_spaces = 0, space_left;
++
++#ifdef HAVE_WIDECHAR
+   bool conversion = false;
+   bool wc_enabled = false;
+ 
+-#ifdef HAVE_WIDECHAR
+   /* In multi-byte locales convert to wide characters
+      to allow easy truncation. Also determine number
+      of screen columns used.  */
+@@ -407,9 +517,9 @@ mbsalign (const char *src, char *dest, size_t dest_size,
+         n_cols = wc_truncate (str_wc, *width);
+         n_used_bytes = wcstombs (newstr, str_wc, src_size);
+     }
+-#endif
+ 
+ mbsalign_unibyte:
++#endif
+ 
+   if (n_cols > *width) /* Unibyte truncation required.  */
+     {
+@@ -451,14 +561,14 @@ mbsalign_unibyte:
+ 	  abort();
+         }
+ 
+-      dest = mbs_align_pad (dest, dest_end, start_spaces);
++      dest = mbs_align_pad (dest, dest_end, start_spaces, padchar);
+       space_left = dest_end - dest;
+       dest = mempcpy (dest, str_to_print, min (n_used_bytes, space_left));
+-      mbs_align_pad (dest, dest_end, end_spaces);
++      mbs_align_pad (dest, dest_end, end_spaces, padchar);
+     }
+-
++#ifdef HAVE_WIDECHAR
+ mbsalign_cleanup:
+-
++#endif
+   free (str_wc);
+   free (newstr);
+ 
+diff --git a/lib/ttyutils.c b/lib/ttyutils.c
+index ea551e26c..91497e763 100644
+--- a/lib/ttyutils.c
++++ b/lib/ttyutils.c
+@@ -9,6 +9,58 @@
+ #include "c.h"
+ #include "ttyutils.h"
+ 
++/*
++ * Backported for RHEL7.6 libsmartcols
++ */
++static int get_env_int(const char *name)
++{
++	const char *cp = getenv(name);
++
++	if (cp) {
++		char *end = NULL;
++		long x;
++
++		errno = 0;
++		x = strtol(cp, &end, 10);
++
++		if (errno == 0 && end && *end == '\0' && end > cp &&
++		    x > 0 && x <= INT_MAX)
++			return x;
++	}
++
++	return -1;
++}
++
++int get_terminal_dimension(int *cols, int *lines)
++{
++	int c = 0, l = 0;
++
++#if defined(TIOCGWINSZ)
++	struct winsize	w_win;
++	if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &w_win) == 0) {
++		c = w_win.ws_col;
++		l = w_win.ws_row;
++	}
++#elif defined(TIOCGSIZE)
++	struct ttysize	t_win;
++	if (ioctl (STDOUT_FILENO, TIOCGSIZE, &t_win) == 0) {
++		c = t_win.ts_cols;
++		l = t_win.ts_lines;
++	}
++#endif
++
++	if (cols && c <= 0)
++		c = get_env_int("COLUMNS");
++	if (lines && l <= 0)
++		l = get_env_int("LINES");
++
++	if (cols)
++		*cols = c;
++	if (lines)
++		*lines = l;
++	return 0;
++}
++
+ int get_terminal_width(void)
+ {
+ #ifdef TIOCGSIZE
+diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c
+index cdb4d0124..a10f3dc82 100644
+--- a/libfdisk/src/ask.c
++++ b/libfdisk/src/ask.c
+@@ -42,7 +42,7 @@ const char *fdisk_ask_get_query(struct fdisk_ask *ask)
+ int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str)
+ {
+ 	assert(ask);
+-	return !strdup_to_struct_member(ask, query, str) ? -ENOMEM : 0;
++	return strdup_to_struct_member(ask, query, str);
+ }
+ 
+ int fdisk_ask_get_type(struct fdisk_ask *ask)
+@@ -90,7 +90,7 @@ const char *fdisk_ask_number_get_range(struct fdisk_ask *ask)
+ int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range)
+ {
+ 	assert(ask);
+-	return !strdup_to_struct_member(ask, data.num.range, range) ? -ENOMEM : 0;
++	return strdup_to_struct_member(ask, data.num.range, range);
+ }
+ 
+ uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask)
+-- 
+2.14.4
+
diff --git a/SOURCES/0153-libsmartcols-backport-upstream-version-v2.32-158-gc0.patch b/SOURCES/0153-libsmartcols-backport-upstream-version-v2.32-158-gc0.patch
new file mode 100644
index 0000000..ab8ef8d
--- /dev/null
+++ b/SOURCES/0153-libsmartcols-backport-upstream-version-v2.32-158-gc0.patch
@@ -0,0 +1,6320 @@
+From fc6853caa0f3e4f8f10404e58f2dbf9f0df88bd4 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 31 May 2018 11:44:35 +0200
+Subject: [PATCH 153/173] libsmartcols: backport upstream version
+ v2.32-158-gc0bdff999
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1561350
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ Makefile.am                                 |    1 +
+ configure.ac                                |    3 -
+ libsmartcols/Makemodule.am                  |    3 +-
+ libsmartcols/docs/Makefile.am               |    4 +-
+ libsmartcols/docs/libsmartcols-docs.xml     |   28 +-
+ libsmartcols/docs/libsmartcols-sections.txt |   47 +-
+ libsmartcols/samples/Makemodule.am          |   37 +
+ libsmartcols/samples/continuous.c           |  138 +++
+ libsmartcols/samples/fromfile.c             |  344 +++++++
+ libsmartcols/samples/maxout.c               |   56 ++
+ libsmartcols/samples/title.c                |  135 +++
+ libsmartcols/{src/test.c => samples/tree.c} |   77 +-
+ libsmartcols/samples/wrap.c                 |  111 +++
+ libsmartcols/src/Makemodule.am              |   41 +-
+ libsmartcols/src/cell.c                     |  111 ++-
+ libsmartcols/src/column.c                   |  351 +++++--
+ libsmartcols/src/iter.c                     |    2 +-
+ libsmartcols/src/libsmartcols.h.in          |  156 +++-
+ libsmartcols/src/libsmartcols.sym           |   72 ++
+ libsmartcols/src/line.c                     |  173 ++--
+ libsmartcols/src/smartcolsP.h               |   70 +-
+ libsmartcols/src/symbols.c                  |  114 ++-
+ libsmartcols/src/table.c                    |  851 +++++++++++++----
+ libsmartcols/src/table_print.c              | 1342 ++++++++++++++++++++++-----
+ 24 files changed, 3530 insertions(+), 737 deletions(-)
+ create mode 100644 libsmartcols/samples/Makemodule.am
+ create mode 100644 libsmartcols/samples/continuous.c
+ create mode 100644 libsmartcols/samples/fromfile.c
+ create mode 100644 libsmartcols/samples/maxout.c
+ create mode 100644 libsmartcols/samples/title.c
+ rename libsmartcols/{src/test.c => samples/tree.c} (68%)
+ create mode 100644 libsmartcols/samples/wrap.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 67464e4b2..7d5fa10e9 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -118,6 +118,7 @@ edit_cmd = sed \
+ 	 -e 's|@VERSION[@]|$(VERSION)|g' \
+ 	 -e 's|@LIBUUID_VERSION[@]|$(LIBUUID_VERSION)|g' \
+ 	 -e 's|@LIBMOUNT_VERSION[@]|$(LIBMOUNT_VERSION)|g' \
++	 -e 's|@LIBSMARTCOLS_VERSION[@]|$(LIBSMARTCOLS_VERSION)|g' \
+ 	 -e 's|@LIBBLKID_VERSION[@]|$(LIBBLKID_VERSION)|g'
+ 
+ CLEANFILES += $(PATHFILES)
+diff --git a/configure.ac b/configure.ac
+index 8cf317dc0..d561e01d0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -133,9 +133,6 @@ AC_SUBST([BSD_WARN_CFLAGS])
+ dnl libtool-2
+ LT_INIT
+ 
+-dnl check supported linker flags
+-AX_CHECK_VSCRIPT
+-
+ m4_ifndef([PKG_PROG_PKG_CONFIG],
+   [m4_fatal([Could not locate the pkg-config autoconf
+     macros. These are usually located in /usr/share/aclocal/pkg.m4.
+diff --git a/libsmartcols/Makemodule.am b/libsmartcols/Makemodule.am
+index 0089712f1..012848b2b 100644
+--- a/libsmartcols/Makemodule.am
++++ b/libsmartcols/Makemodule.am
+@@ -1,13 +1,14 @@
+ if BUILD_LIBSMARTCOLS
+ 
+ include libsmartcols/src/Makemodule.am
++include libsmartcols/samples/Makemodule.am
+ 
+ if ENABLE_GTK_DOC
+ # Docs uses separate Makefiles
+ SUBDIRS += libsmartcols/docs
+ endif
+ 
+-# noinst for RHEL7: pkgconfig_DATA += libsmartcols/smartcols.pc
++pkgconfig_DATA += libsmartcols/smartcols.pc
+ PATHFILES      += libsmartcols/smartcols.pc
+ EXTRA_DIST     += libsmartcols/COPYING
+ 
+diff --git a/libsmartcols/docs/Makefile.am b/libsmartcols/docs/Makefile.am
+index c5aa2237c..e8a7600e9 100644
+--- a/libsmartcols/docs/Makefile.am
++++ b/libsmartcols/docs/Makefile.am
+@@ -32,7 +32,7 @@ SCAN_OPTIONS=
+ 
+ # Extra options to supply to gtkdoc-mkdb.
+ # e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
+-MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space mnt
++MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space scols
+ 
+ # Extra options to supply to gtkdoc-mktmpl
+ # e.g. MKTMPL_OPTIONS=--only-section-tmpl
+@@ -67,7 +67,7 @@ HTML_IMAGES=
+ # e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+ content_files = $(builddir)/version.xml
+ 
+-# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
++# SGML files where gtk-doc abbreviations (#GtkWidget) are expanded
+ # These files must be listed here *and* in content_files
+ # e.g. expand_content_files=running.sgml
+ expand_content_files=
+diff --git a/libsmartcols/docs/libsmartcols-docs.xml b/libsmartcols/docs/libsmartcols-docs.xml
+index 4976ba701..02ee1ffe1 100644
+--- a/libsmartcols/docs/libsmartcols-docs.xml
++++ b/libsmartcols/docs/libsmartcols-docs.xml
+@@ -9,12 +9,12 @@
+     <title>libsmartcols Reference Manual</title>
+     <releaseinfo>for libsmartcols version &version;</releaseinfo>
+     <copyright>
+-      <year>2014</year>
++      <year>2014-2018</year>
+       <holder>Karel Zak &lt;kzak@redhat.com&gt;</holder>
+     </copyright>
+   </bookinfo>
+ 
+-  <part id="gtk">
++  <part id="overview">
+     <title>libsmartcols Overview</title>
+     <partintro>
+     <para>
+@@ -22,7 +22,7 @@ The libsmartcols library is used for smart adaptive formatting of tabular data.
+     </para>
+     <para>
+ The library is part of the util-linux package since version 2.25 and is
+-available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
++available from https://www.kernel.org/pub/linux/utils/util-linux/.
+     </para>
+   </partintro>
+  </part>
+@@ -45,8 +45,28 @@ available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+     <xi:include href="xml/version-utils.xml"/>
+     <xi:include href="xml/init.xml"/>
+   </part>
+-  <index id="api-index-full">
++  <index id="api-index">
+     <title>API Index</title>
+     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+   </index>
++  <index role="2.27">
++    <title>Index of new symbols in 2.27</title>
++    <xi:include href="xml/api-index-2.27.xml"><xi:fallback /></xi:include>
++  </index>
++  <index role="2.28">
++    <title>Index of new symbols in 2.28</title>
++    <xi:include href="xml/api-index-2.28.xml"><xi:fallback /></xi:include>
++  </index>
++  <index role="2.29">
++    <title>Index of new symbols in 2.29</title>
++    <xi:include href="xml/api-index-2.29.xml"><xi:fallback /></xi:include>
++  </index>
++  <index role="2.30">
++    <title>Index of new symbols in 2.30</title>
++    <xi:include href="xml/api-index-2.30.xml"><xi:fallback /></xi:include>
++  </index>
++  <index role="2.31">
++    <title>Index of new symbols in 2.31</title>
++    <xi:include href="xml/api-index-2.31.xml"><xi:fallback /></xi:include>
++  </index>
+ </book>
+diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt
+index 2b8180c52..79786b544 100644
+--- a/libsmartcols/docs/libsmartcols-sections.txt
++++ b/libsmartcols/docs/libsmartcols-sections.txt
+@@ -2,12 +2,15 @@
+ <FILE>cell</FILE>
+ libscols_cell
+ scols_cell_copy_content
++scols_cell_get_alignment
+ scols_cell_get_color
+ scols_cell_get_data
++scols_cell_get_flags
+ scols_cell_get_userdata
+ scols_cell_refer_data
+ scols_cell_set_color
+ scols_cell_set_data
++scols_cell_set_flags
+ scols_cell_set_userdata
+ scols_cmpstr_cells
+ scols_reset_cell
+@@ -19,20 +22,32 @@ libscols_column
+ scols_column_get_color
+ scols_column_get_flags
+ scols_column_get_header
++scols_column_get_json_type
++scols_column_get_safechars
++scols_column_get_table
+ scols_column_get_whint
++scols_column_get_width
++scols_column_is_customwrap
++scols_column_is_hidden
+ scols_column_is_noextremes
+ scols_column_is_right
+ scols_column_is_strict_width
+ scols_column_is_tree
+ scols_column_is_trunc
++scols_column_is_wrap
+ scols_column_set_cmpfunc
+ scols_column_set_color
+ scols_column_set_flags
++scols_column_set_json_type
++scols_column_set_safechars
+ scols_column_set_whint
++scols_column_set_wrapfunc
+ scols_copy_column
+ scols_new_column
+ scols_ref_column
+ scols_unref_column
++scols_wrapnl_chunksize
++scols_wrapnl_nextchunk
+ </SECTION>
+ 
+ <SECTION>
+@@ -58,10 +73,13 @@ scols_line_get_ncells
+ scols_line_get_parent
+ scols_line_get_userdata
+ scols_line_has_children
++scols_line_is_ancestor
+ scols_line_next_child
++scols_line_refer_column_data
+ scols_line_refer_data
+ scols_line_remove_child
+ scols_line_set_color
++scols_line_set_column_data
+ scols_line_set_data
+ scols_line_set_userdata
+ scols_new_line
+@@ -78,6 +96,8 @@ scols_ref_symbols
+ scols_symbols_set_branch
+ scols_symbols_set_right
+ scols_symbols_set_vertical
++scols_symbols_set_title_padding
++scols_symbols_set_cell_padding
+ scols_unref_symbols
+ </SECTION>
+ 
+@@ -87,29 +107,48 @@ libscols_table
+ scols_copy_table
+ scols_new_table
+ scols_ref_table
++scols_sort_table
++scols_sort_table_by_tree
+ scols_table_add_column
+ scols_table_add_line
+ scols_table_colors_wanted
+ scols_table_enable_ascii
+ scols_table_enable_colors
++scols_table_enable_noencoding
+ scols_table_enable_export
++scols_table_enable_header_repeat
++scols_table_enable_json
+ scols_table_enable_maxout
+ scols_table_enable_noheadings
++scols_table_enable_nolinesep
++scols_table_enable_nowrap
+ scols_table_enable_raw
+ scols_table_get_column
+ scols_table_get_column_separator
+ scols_table_get_line
+ scols_table_get_line_separator
++scols_table_get_name
+ scols_table_get_ncols
+ scols_table_get_nlines
+ scols_table_get_stream
++scols_table_get_symbols
++scols_table_get_termforce
++scols_table_get_termheight
++scols_table_get_termwidth
++scols_table_get_title
+ scols_table_is_ascii
+ scols_table_is_empty
+ scols_table_is_export
++scols_table_is_header_repeat
++scols_table_is_json
+ scols_table_is_maxout
+ scols_table_is_noheadings
++scols_table_is_noencoding
++scols_table_is_nolinesep
++scols_table_is_nowrap
+ scols_table_is_raw
+ scols_table_is_tree
++scols_table_move_column
+ scols_table_new_column
+ scols_table_new_line
+ scols_table_next_column
+@@ -120,10 +159,14 @@ scols_table_remove_columns
+ scols_table_remove_line
+ scols_table_remove_lines
+ scols_table_set_column_separator
++scols_table_set_default_symbols
+ scols_table_set_line_separator
++scols_table_set_name
+ scols_table_set_stream
+ scols_table_set_symbols
+-scols_sort_table
++scols_table_set_termforce
++scols_table_set_termheight
++scols_table_set_termwidth
+ scols_unref_table
+ </SECTION>
+ 
+@@ -131,6 +174,8 @@ scols_unref_table
+ <FILE>table_print</FILE>
+ scols_print_table
+ scols_print_table_to_string
++scols_table_print_range
++scols_table_print_range_to_string
+ </SECTION>
+ 
+ <SECTION>
+diff --git a/libsmartcols/samples/Makemodule.am b/libsmartcols/samples/Makemodule.am
+new file mode 100644
+index 000000000..0e0208f04
+--- /dev/null
++++ b/libsmartcols/samples/Makemodule.am
+@@ -0,0 +1,37 @@
++
++check_PROGRAMS += \
++	sample-scols-title \
++	sample-scols-wrap \
++	sample-scols-continuous \
++	sample-scols-fromfile \
++	sample-scols-maxout
++
++sample_scols_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \
++                      -I$(ul_libsmartcols_incdir)
++sample_scols_ldadd = libsmartcols.la $(LDADD)
++
++check_PROGRAMS += sample-scols-tree
++sample_scols_tree_SOURCES = libsmartcols/samples/tree.c
++sample_scols_tree_LDADD = $(sample_scols_ldadd) libcommon.la
++sample_scols_tree_CFLAGS = $(sample_scols_cflags)
++
++sample_scols_title_SOURCES = libsmartcols/samples/title.c
++sample_scols_title_LDADD = $(sample_scols_ldadd) libcommon.la
++sample_scols_title_CFLAGS = $(sample_scols_cflags)
++
++sample_scols_wrap_SOURCES = libsmartcols/samples/wrap.c
++sample_scols_wrap_LDADD = $(sample_scols_ldadd)
++sample_scols_wrap_CFLAGS = $(sample_scols_cflags)
++
++sample_scols_continuous_SOURCES = libsmartcols/samples/continuous.c
++sample_scols_continuous_LDADD = $(sample_scols_ldadd) libcommon.la
++sample_scols_continuous_CFLAGS = $(sample_scols_cflags)
++
++sample_scols_maxout_SOURCES = libsmartcols/samples/maxout.c
++sample_scols_maxout_LDADD = $(sample_scols_ldadd)
++sample_scols_maxout_CFLAGS = $(sample_scols_cflags)
++
++sample_scols_fromfile_SOURCES = libsmartcols/samples/fromfile.c
++sample_scols_fromfile_LDADD = $(sample_scols_ldadd) libcommon.la
++sample_scols_fromfile_CFLAGS = $(sample_scols_cflags)
++
+diff --git a/libsmartcols/samples/continuous.c b/libsmartcols/samples/continuous.c
+new file mode 100644
+index 000000000..8f9d13e6b
+--- /dev/null
++++ b/libsmartcols/samples/continuous.c
+@@ -0,0 +1,138 @@
++/*
++ * Copyright (C) 2016 Karel Zak <kzak@redhat.com>
++ *
++ * This file may be redistributed under the terms of the
++ * GNU Lesser General Public License.
++ */
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/time.h>
++
++#include "c.h"
++#include "nls.h"
++#include "strutils.h"
++#include "xalloc.h"
++
++#include "libsmartcols.h"
++
++#define TIME_PERIOD	3.0	/* seconds */
++
++enum { COL_NUM, COL_DATA, COL_TIME };
++
++static double time_diff(struct timeval *a, struct timeval *b)
++{
++	return (a->tv_sec - b->tv_sec) + (a->tv_usec - b->tv_usec) / 1E6;
++}
++
++/* add columns to the @tb */
++static void setup_columns(struct libscols_table *tb)
++{
++	scols_table_enable_maxout(tb, 1);
++	if (!scols_table_new_column(tb, "#NUM", 0.1, SCOLS_FL_RIGHT))
++		goto fail;
++	if (!scols_table_new_column(tb, "DATA", 0.7, 0))
++		goto fail;
++	if (!scols_table_new_column(tb, "TIME", 0.2, 0))
++		goto fail;
++	return;
++fail:
++	scols_unref_table(tb);
++	err(EXIT_FAILURE, "failed to create output columns");
++}
++
++static struct libscols_line *add_line(struct libscols_table *tb, size_t i)
++{
++	char *p;
++	struct libscols_line *ln = scols_table_new_line(tb, NULL);
++
++	if (!ln)
++		err(EXIT_FAILURE, "failed to create output line");
++
++	xasprintf(&p, "%zu", i);
++	if (scols_line_refer_data(ln, COL_NUM, p))
++		goto fail;
++
++	xasprintf(&p, "data-%02zu-%02zu-%02zu-end", i + 1, i + 2, i + 3);
++	if (scols_line_refer_data(ln, COL_DATA, p))
++		goto fail;
++
++	return ln;
++fail:
++	scols_unref_table(tb);
++	err(EXIT_FAILURE, "failed to create output line");
++}
++
++int main(int argc, char *argv[])
++{
++	struct libscols_table *tb;
++	size_t i;
++	struct timeval last;
++
++	scols_init_debug(0);
++
++	tb = scols_new_table();
++	if (!tb)
++		err(EXIT_FAILURE, "failed to create output table");
++
++	setup_columns(tb);
++	gettimeofday(&last, NULL);
++
++	for (i = 0; i < 10; i++) {
++		struct libscols_line *line;
++		struct timeval now;
++		int done = 0;
++		char *timecell = xmalloc( sizeof(stringify_value(UINT_MAX)) );
++
++		line = add_line(tb, i);
++
++		/* Make a reference from cell data to the buffer, then we can
++		 * update cell data without any interaction with libsmartcols
++		 */
++		scols_line_refer_data(line, COL_TIME, timecell);
++
++		do {
++			double diff;
++
++			gettimeofday(&now, NULL);
++			diff = time_diff(&now, &last);
++
++			if (now.tv_sec == last.tv_sec + (long) TIME_PERIOD)
++				done = 1;
++			else
++				usleep(100000);
++
++			/* update "TIME" cell data */
++			sprintf(timecell, "%f [%3d%%]", diff,
++				done ? 100 : (int)(diff / (TIME_PERIOD / 100.0)));
++
++			/* Note that libsmartcols don't print \n for last line
++			 * in the table, but if you print a line somewhere in
++			 * the midle of the table you need
++			 *
++			 *    scols_table_enable_nolinesep(tb, !done);
++			 *
++			 * to disable line breaks. In this example it's
++			 * unnecessary as we print the latest line only.
++			 */
++
++			/* print the line */
++			scols_table_print_range(tb, line, NULL);
++
++			if (!done) {
++				/* terminal is waiting for \n, fflush() to force output */
++				fflush(scols_table_get_stream(tb));
++				/* move to the begin of the line */
++				fputc('\r', scols_table_get_stream(tb));
++			} else
++				fputc('\n', scols_table_get_stream(tb));
++		} while (!done);
++
++		last = now;
++	}
++
++	scols_unref_table(tb);
++	return EXIT_SUCCESS;
++}
+diff --git a/libsmartcols/samples/fromfile.c b/libsmartcols/samples/fromfile.c
+new file mode 100644
+index 000000000..c1ab728fd
+--- /dev/null
++++ b/libsmartcols/samples/fromfile.c
+@@ -0,0 +1,344 @@
++/*
++ * Copyright (C) 2016 Karel Zak <kzak@redhat.com>
++ *
++ * This file may be redistributed under the terms of the
++ * GNU Lesser General Public License.
++ */
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <dirent.h>
++#include <getopt.h>
++
++#include "c.h"
++#include "nls.h"
++#include "strutils.h"
++#include "xalloc.h"
++#include "optutils.h"
++
++#include "libsmartcols.h"
++
++struct column_flag {
++	const char *name;
++	int mask;
++};
++
++static const struct column_flag flags[] = {
++	{ "trunc",	SCOLS_FL_TRUNC },
++	{ "tree",	SCOLS_FL_TREE },
++	{ "right",	SCOLS_FL_RIGHT },
++	{ "strictwidth",SCOLS_FL_STRICTWIDTH },
++	{ "noextremes", SCOLS_FL_NOEXTREMES },
++	{ "hidden",	SCOLS_FL_HIDDEN },
++	{ "wrap",	SCOLS_FL_WRAP },
++	{ "wrapnl",	SCOLS_FL_WRAP },
++	{ "none",	0 }
++};
++
++static long name_to_flag(const char *name, size_t namesz)
++{
++	size_t i;
++
++	for (i = 0; i < ARRAY_SIZE(flags); i++) {
++		const char *cn = flags[i].name;
++
++		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
++			return flags[i].mask;
++	}
++	warnx("unknown flag: %s", name);
++	return -1;
++}
++
++static int parse_column_flags(char *str)
++{
++	unsigned long num_flags = 0;
++
++	if (string_to_bitmask(str, &num_flags, name_to_flag))
++		err(EXIT_FAILURE, "failed to parse column flags");
++
++	return num_flags;
++}
++
++static struct libscols_column *parse_column(FILE *f)
++{
++	size_t len = 0;
++	char *line = NULL;
++	int nlines = 0;
++
++	struct libscols_column *cl = NULL;
++
++	while (getline(&line, &len, f) != -1) {
++
++		char *p = strrchr(line, '\n');
++		if (p)
++			*p = '\0';
++
++		switch (nlines) {
++		case 0: /* NAME */
++		{
++			struct libscols_cell *hr;
++
++			cl = scols_new_column();
++			if (!cl)
++				goto fail;
++			hr = scols_column_get_header(cl);
++			if (!hr || scols_cell_set_data(hr, line))
++				goto fail;
++			break;
++		}
++		case 1: /* WIDTH-HINT */
++		{
++			double whint = strtod_or_err(line, "failed to parse column whint");
++			if (scols_column_set_whint(cl, whint))
++				goto fail;
++			break;
++		}
++		case 2: /* FLAGS */
++		{
++			int num_flags = parse_column_flags(line);
++			if (scols_column_set_flags(cl, num_flags))
++				goto fail;
++			if (strcmp(line, "wrapnl") == 0) {
++				scols_column_set_wrapfunc(cl,
++						scols_wrapnl_chunksize,
++						scols_wrapnl_nextchunk,
++						NULL);
++				scols_column_set_safechars(cl, "\n");
++			}
++			break;
++		}
++		case 3: /* COLOR */
++			if (scols_column_set_color(cl, line))
++				goto fail;
++			break;
++		default:
++			break;
++		}
++
++		nlines++;
++	}
++
++	free(line);
++	return cl;
++fail:
++	free(line);
++	scols_unref_column(cl);
++	return NULL;
++}
++
++static int parse_column_data(FILE *f, struct libscols_table *tb, int col)
++{
++	size_t len = 0, nlines = 0;
++	int i;
++	char *str = NULL;
++
++	while ((i = getline(&str, &len, f)) != -1) {
++
++		struct libscols_line *ln;
++		char *p = strrchr(str, '\n');
++		if (p)
++			*p = '\0';
++
++		while ((p = strrchr(str, '\\')) && *(p + 1) == 'n') {
++			*p = '\n';
++			memmove(p + 1, p + 2, i - (p + 2 - str));
++		}
++
++		ln = scols_table_get_line(tb, nlines++);
++		if (!ln)
++			break;
++
++		scols_line_set_data(ln, col, str);
++	}
++
++	free(str);
++	return 0;
++
++}
++
++static struct libscols_line *get_line_with_id(struct libscols_table *tb,
++						int col_id, const char *id)
++{
++	struct libscols_line *ln;
++	struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
++
++	while (scols_table_next_line(tb, itr, &ln) == 0) {
++		struct libscols_cell *ce = scols_line_get_cell(ln, col_id);
++		const char *data = ce ? scols_cell_get_data(ce) : NULL;
++
++		if (data && strcmp(data, id) == 0)
++			break;
++	}
++
++	scols_free_iter(itr);
++	return ln;
++}
++
++static void compose_tree(struct libscols_table *tb, int parent_col, int id_col)
++{
++	struct libscols_line *ln;
++	struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
++
++	while (scols_table_next_line(tb, itr, &ln) == 0) {
++		struct libscols_line *parent = NULL;
++		struct libscols_cell *ce = scols_line_get_cell(ln, parent_col);
++		const char *data = ce ? scols_cell_get_data(ce) : NULL;
++
++		if (data)
++			parent = get_line_with_id(tb, id_col, data);
++		if (parent)
++			scols_line_add_child(parent, ln);
++	}
++
++	scols_free_iter(itr);
++}
++
++
++static void __attribute__((__noreturn__)) usage(void)
++{
++	FILE *out = stdout;
++	fprintf(out,
++		"\n %s [options] <column-data-file> ...\n\n", program_invocation_short_name);
++
++	fputs(" -m, --maxout                   fill all terminal width\n", out);
++	fputs(" -c, --column <file>            column definition\n", out);
++	fputs(" -n, --nlines <num>             number of lines\n", out);
++	fputs(" -J, --json                     JSON output format\n", out);
++	fputs(" -r, --raw                      RAW output format\n", out);
++	fputs(" -E, --export                   use key=\"value\" output format\n", out);
++	fputs(" -C, --colsep <str>             set columns separator\n", out);
++	fputs(" -w, --width <num>              hardcode terminal width\n", out);
++	fputs(" -p, --tree-parent-column <n>   parent column\n", out);
++	fputs(" -i, --tree-id-column <n>       id column\n", out);
++	fputs(" -h, --help                     this help\n", out);
++	fputs("\n", out);
++
++	exit(EXIT_SUCCESS);
++}
++
++int main(int argc, char *argv[])
++{
++	struct libscols_table *tb;
++	int c, n, nlines = 0;
++	int parent_col = -1, id_col = -1;
++
++	static const struct option longopts[] = {
++		{ "maxout", 0, NULL, 'm' },
++		{ "column", 1, NULL, 'c' },
++		{ "nlines", 1, NULL, 'n' },
++		{ "width",  1, NULL, 'w' },
++		{ "tree-parent-column", 1, NULL, 'p' },
++		{ "tree-id-column",	1, NULL, 'i' },
++		{ "json",   0, NULL, 'J' },
++		{ "raw",    0, NULL, 'r' },
++		{ "export", 0, NULL, 'E' },
++		{ "colsep",  1, NULL, 'C' },
++		{ "help",   0, NULL, 'h' },
++		{ NULL, 0, NULL, 0 },
++	};
++
++	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
++		{ 'E', 'J', 'r' },
++		{ 0 }
++	};
++	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
++
++	setlocale(LC_ALL, "");	/* just to have enable UTF8 chars */
++	scols_init_debug(0);
++
++	tb = scols_new_table();
++	if (!tb)
++		err(EXIT_FAILURE, "failed to create output table");
++
++	while((c = getopt_long(argc, argv, "hCc:Ei:Jmn:p:rw:", longopts, NULL)) != -1) {
++
++		err_exclusive_options(c, longopts, excl, excl_st);
++
++		switch(c) {
++		case 'c': /* add column from file */
++		{
++			struct libscols_column *cl;
++			FILE *f = fopen(optarg, "r");
++
++			if (!f)
++				err(EXIT_FAILURE, "%s: open failed", optarg);
++			cl = parse_column(f);
++			if (cl && scols_table_add_column(tb, cl))
++				err(EXIT_FAILURE, "%s: failed to add column", optarg);
++			scols_unref_column(cl);
++			fclose(f);
++			break;
++		}
++		case 'p':
++			parent_col = strtou32_or_err(optarg, "failed to parse tree PARENT column");
++			break;
++		case 'i':
++			id_col = strtou32_or_err(optarg, "failed to parse tree ID column");
++			break;
++		case 'J':
++			scols_table_enable_json(tb, 1);
++			scols_table_set_name(tb, "testtable");
++			break;
++		case 'm':
++			scols_table_enable_maxout(tb, TRUE);
++			break;
++		case 'r':
++			scols_table_enable_raw(tb, TRUE);
++			break;
++		case 'E':
++			scols_table_enable_export(tb, TRUE);
++			break;
++		case 'C':
++			scols_table_set_column_separator(tb, optarg);
++			break;
++		case 'n':
++			nlines = strtou32_or_err(optarg, "failed to parse number of lines");
++			break;
++		case 'w':
++			scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS);
++			scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width"));
++			break;
++		case 'h':
++			usage();
++		default:
++			errtryhelp(EXIT_FAILURE);
++		}
++	}
++
++	if (nlines <= 0)
++		errx(EXIT_FAILURE, "--nlines not set");
++
++	for (n = 0; n < nlines; n++) {
++		struct libscols_line *ln = scols_new_line();
++
++		if (!ln || scols_table_add_line(tb, ln))
++			err(EXIT_FAILURE, "failed to add a new line");
++
++		scols_unref_line(ln);
++	}
++
++	n = 0;
++
++	while (optind < argc) {
++		FILE *f = fopen(argv[optind], "r");
++
++		if (!f)
++			err(EXIT_FAILURE, "%s: open failed", argv[optind]);
++
++		parse_column_data(f, tb, n);
++		optind++;
++		n++;
++	}
++
++	if (scols_table_is_tree(tb) && parent_col >= 0 && id_col >= 0)
++		compose_tree(tb, parent_col, id_col);
++
++	scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
++
++	scols_print_table(tb);
++	scols_unref_table(tb);
++	return EXIT_SUCCESS;
++}
+diff --git a/libsmartcols/samples/maxout.c b/libsmartcols/samples/maxout.c
+new file mode 100644
+index 000000000..07a05e13f
+--- /dev/null
++++ b/libsmartcols/samples/maxout.c
+@@ -0,0 +1,56 @@
++/*
++ * Copyright (C) 2016 Karel Zak <kzak@redhat.com>
++ *
++ * This file may be redistributed under the terms of the
++ * GNU Lesser General Public License.
++ */
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <dirent.h>
++#include <getopt.h>
++
++#include "c.h"
++#include "nls.h"
++#include "libsmartcols.h"
++
++enum { COL_LEFT, COL_FOO, COL_RIGHT };
++
++int main(int argc, char *argv[])
++{
++	struct libscols_table *tb;
++	int rc = -1, nlines = 3;
++
++	setlocale(LC_ALL, "");	/* just to have enable UTF8 chars */
++
++	scols_init_debug(0);
++
++	tb = scols_new_table();
++	if (!tb)
++		err(EXIT_FAILURE, "failed to create output table");
++
++	scols_table_enable_maxout(tb, TRUE);
++	if (!scols_table_new_column(tb, "LEFT", 0, 0))
++		goto done;
++	if (!scols_table_new_column(tb, "FOO", 0, 0))
++		goto done;
++	if (!scols_table_new_column(tb, "RIGHT", 0, SCOLS_FL_RIGHT))
++		goto done;
++
++	while (nlines--) {
++		struct libscols_line *ln = scols_table_new_line(tb, NULL);
++
++		scols_line_set_data(ln, COL_LEFT, "A");
++		scols_line_set_data(ln, COL_FOO, "B");
++		scols_line_set_data(ln, COL_RIGHT, "C");
++	}
++
++	scols_print_table(tb);
++	rc = 0;
++done:
++	scols_unref_table(tb);
++	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
++}
+diff --git a/libsmartcols/samples/title.c b/libsmartcols/samples/title.c
+new file mode 100644
+index 000000000..131400da4
+--- /dev/null
++++ b/libsmartcols/samples/title.c
+@@ -0,0 +1,135 @@
++/*
++ * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
++ *
++ * This file may be redistributed under the terms of the
++ * GNU Lesser General Public License.
++ */
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <dirent.h>
++#include <getopt.h>
++
++#include "c.h"
++#include "nls.h"
++#include "strutils.h"
++#include "xalloc.h"
++
++#include "libsmartcols.h"
++
++
++enum { COL_NAME, COL_DATA };
++
++/* add columns to the @tb */
++static void setup_columns(struct libscols_table *tb)
++{
++	if (!scols_table_new_column(tb, "NAME", 0, 0))
++		goto fail;
++	if (!scols_table_new_column(tb, "DATA", 0, 0))
++		goto fail;
++	return;
++fail:
++	scols_unref_table(tb);
++	err(EXIT_FAILURE, "failed to create output columns");
++}
++
++static void add_line(struct libscols_table *tb, const char *name, const char *data)
++{
++	struct libscols_line *ln = scols_table_new_line(tb, NULL);
++	if (!ln)
++		err(EXIT_FAILURE, "failed to create output line");
++
++	if (scols_line_set_data(ln, COL_NAME, name))
++		goto fail;
++	if (scols_line_set_data(ln, COL_DATA, data))
++		goto fail;
++	return;
++fail:
++	scols_unref_table(tb);
++	err(EXIT_FAILURE, "failed to create output line");
++}
++
++int main(int argc, char *argv[])
++{
++	struct libscols_table *tb;
++	struct libscols_symbols *sy;
++	struct libscols_cell *title;
++	int c;
++
++	static const struct option longopts[] = {
++		{ "maxout", 0, NULL, 'm' },
++		{ "width",  1, NULL, 'w' },
++		{ "help",   1, NULL, 'h' },
++
++		{ NULL, 0, NULL, 0 },
++	};
++
++	setlocale(LC_ALL, "");	/* just to have enable UTF8 chars */
++
++	scols_init_debug(0);
++
++	tb = scols_new_table();
++	if (!tb)
++		err(EXIT_FAILURE, "failed to create output table");
++
++	while((c = getopt_long(argc, argv, "hmw:", longopts, NULL)) != -1) {
++		switch(c) {
++		case 'h':
++			printf("%s [--help | --maxout | --width <num>]\n", program_invocation_short_name);
++			break;
++		case 'm':
++			scols_table_enable_maxout(tb, TRUE);
++			break;
++		case 'w':
++			scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS);
++			scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width"));
++			break;
++		}
++	}
++
++	scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
++	setup_columns(tb);
++	add_line(tb, "foo", "bla bla bla");
++	add_line(tb, "bar", "alb alb alb");
++
++	title = scols_table_get_title(tb);
++
++	/* right */
++	scols_cell_set_data(title, "This is right title");
++	scols_cell_set_color(title, "red");
++	scols_cell_set_flags(title, SCOLS_CELL_FL_RIGHT);
++	scols_print_table(tb);
++
++	/* left without padding */
++	scols_cell_set_data(title, "This is left title (without padding)");
++	scols_cell_set_color(title, "yellow");
++	scols_cell_set_flags(title, SCOLS_CELL_FL_LEFT);
++	scols_print_table(tb);
++
++	/* center */
++	sy = scols_new_symbols();
++	if (!sy)
++		err_oom();
++	scols_table_set_symbols(tb, sy);
++	scols_unref_symbols(sy);
++
++	scols_symbols_set_title_padding(sy, "=");
++	scols_cell_set_data(title, "This is center title (with padding)");
++	scols_cell_set_color(title, "green");
++	scols_cell_set_flags(title, SCOLS_CELL_FL_CENTER);
++	scols_print_table(tb);
++
++	/* left with padding */
++	scols_symbols_set_title_padding(sy, "-");
++	scols_cell_set_data(title, "This is left title (with padding)");
++	scols_cell_set_color(title, "blue");
++	scols_cell_set_flags(title, SCOLS_CELL_FL_LEFT);
++	scols_print_table(tb);
++
++
++	scols_unref_table(tb);
++	return EXIT_SUCCESS;
++}
+diff --git a/libsmartcols/src/test.c b/libsmartcols/samples/tree.c
+similarity index 68%
+rename from libsmartcols/src/test.c
+rename to libsmartcols/samples/tree.c
+index dd87fd38b..0cdb99420 100644
+--- a/libsmartcols/src/test.c
++++ b/libsmartcols/samples/tree.c
+@@ -39,7 +39,7 @@ static void setup_columns(struct libscols_table *tb, int notree)
+ 	return;
+ fail:
+ 	scols_unref_table(tb);
+-	err(EXIT_FAILURE, "faild to create output columns");
++	err(EXIT_FAILURE, "failed to create output columns");
+ }
+ 
+ /* add a new line to @tb, the content is based on @st */
+@@ -104,7 +104,7 @@ fail:
+ 	return -1;
+ }
+ 
+-/* read all entrines from directory addressed by @fd */
++/* read all entries from directory addressed by @fd */
+ static int add_children(struct libscols_table *tb,
+ 			struct libscols_line *ln,
+ 			int fd)
+@@ -142,12 +142,15 @@ static void add_lines(struct libscols_table *tb, const char *dirname)
+ static void __attribute__((__noreturn__)) usage(FILE *out)
+ {
+ 	fprintf(out, " %s [options] [<dir> ...]\n\n", program_invocation_short_name);
+-	fputs(" -c, --csv            display a csv-like output\n", out);
+-	fputs(" -i, --ascii          use ascii characters only\n", out);
+-	fputs(" -l, --list           use list format output\n", out);
+-	fputs(" -n, --noheadings     don't print headings\n", out);
+-	fputs(" -p, --pairs          use key=\"value\" output format\n", out);
+-	fputs(" -r, --raw            use raw output format\n", out);
++	fputs(" -c, --csv               display a csv-like output\n", out);
++	fputs(" -i, --ascii             use ascii characters only\n", out);
++	fputs(" -l, --list              use list format output\n", out);
++	fputs(" -n, --noheadings        don't print headings\n", out);
++	fputs(" -p, --pairs             use key=\"value\" output format\n", out);
++	fputs(" -J, --json              use JSON output format\n", out);
++	fputs(" -r, --raw               use raw output format\n", out);
++	fputs(" -S, --range-start <n>   first line to print\n", out);
++	fputs(" -E, --range-end <n>     last line to print\n", out);
+ 
+ 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+@@ -155,17 +158,20 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
+ int main(int argc, char *argv[])
+ {
+ 	struct libscols_table *tb;
+-	int c, notree = 0;
++	int c, notree = 0, nstart = -1, nend = -1;
++
+ 
+ 	static const struct option longopts[] = {
+-		{ "ascii",	0, 0, 'i' },
+-		{ "csv",        0, 0, 'c' },
+-		{ "list",       0, 0, 'l' },
+-		{ "noheadings",	0, 0, 'n' },
+-		{ "pairs",      0, 0, 'p' },
+-		{ "raw",      0, 0, 'r' },
+-
+-		{ NULL, 0, 0, 0 },
++		{ "ascii",	0, NULL, 'i' },
++		{ "csv",        0, NULL, 'c' },
++		{ "list",       0, NULL, 'l' },
++		{ "noheadings",	0, NULL, 'n' },
++		{ "pairs",      0, NULL, 'p' },
++		{ "json",       0, NULL, 'J' },
++		{ "raw",        0, NULL, 'r' },
++		{ "range-start",1, NULL, 'S' },
++		{ "range-end",  1, NULL, 'E' },
++		{ NULL, 0, NULL, 0 },
+ 	};
+ 
+ 	setlocale(LC_ALL, "");	/* just to have enable UTF8 chars */
+@@ -174,9 +180,9 @@ int main(int argc, char *argv[])
+ 
+ 	tb = scols_new_table();
+ 	if (!tb)
+-		err(EXIT_FAILURE, "faild to create output table");
++		err(EXIT_FAILURE, "failed to create output table");
+ 
+-	while((c = getopt_long(argc, argv, "cilnpr", longopts, NULL)) != -1) {
++	while((c = getopt_long(argc, argv, "ciJlnprS:E:", longopts, NULL)) != -1) {
+ 		switch(c) {
+ 		case 'c':
+ 			scols_table_set_column_separator(tb, ",");
+@@ -186,6 +192,10 @@ int main(int argc, char *argv[])
+ 		case 'i':
+ 			scols_table_enable_ascii(tb, 1);
+ 			break;
++		case 'J':
++			scols_table_set_name(tb, "scolstest");
++			scols_table_enable_json(tb, 1);
++			break;
+ 		case 'l':
+ 			notree = 1;
+ 			break;
+@@ -200,19 +210,40 @@ int main(int argc, char *argv[])
+ 			scols_table_enable_raw(tb, 1);
+ 			notree = 1;
+ 			break;
++		case 'S':
++			nstart = strtos32_or_err(optarg, "failed to parse range start") - 1;
++			break;
++		case 'E':
++			nend = strtos32_or_err(optarg, "failed to parse range end") - 1;
++			break;
+ 		default:
+ 			usage(stderr);
+ 		}
+ 	}
+ 
+-	scols_table_enable_colors(tb, 1);
++	scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
+ 	setup_columns(tb, notree);
+ 
+-	while (optind < argc)
++	if (optind == argc)
++		add_lines(tb, ".");
++	else while (optind < argc)
+ 		add_lines(tb, argv[optind++]);
+ 
+-	scols_print_table(tb);
+-	scols_unref_table(tb);
++	if (nstart >= 0 || nend >= 0) {
++		/* print subset */
++		struct libscols_line *start = NULL, *end = NULL;
++
++		if (nstart >= 0)
++			start = scols_table_get_line(tb, nstart);
++		if (nend >= 0)
++			end = scols_table_get_line(tb, nend);
+ 
++		if (start || end)
++			scols_table_print_range(tb, start, end);
++	} else
++		/* print all table */
++		scols_print_table(tb);
++
++	scols_unref_table(tb);
+ 	return EXIT_SUCCESS;
+ }
+diff --git a/libsmartcols/samples/wrap.c b/libsmartcols/samples/wrap.c
+new file mode 100644
+index 000000000..795bef714
+--- /dev/null
++++ b/libsmartcols/samples/wrap.c
+@@ -0,0 +1,111 @@
++/*
++ * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
++ *
++ * This file may be redistributed under the terms of the
++ * GNU Lesser General Public License.
++ */
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <dirent.h>
++#include <getopt.h>
++
++#include "c.h"
++#include "nls.h"
++#include "strutils.h"
++#include "xalloc.h"
++
++#include "libsmartcols.h"
++
++
++enum { COL_NAME, COL_DESC, COL_FOO, COL_LIKE, COL_TEXT };
++
++/* add columns to the @tb */
++static void setup_columns(struct libscols_table *tb)
++{
++	if (!scols_table_new_column(tb, "NAME", 0, SCOLS_FL_TREE))
++		goto fail;
++	if (!scols_table_new_column(tb, "DESC", 0, 0))
++		goto fail;
++	if (!scols_table_new_column(tb, "FOO", 0, SCOLS_FL_WRAP))
++		goto fail;
++	if (!scols_table_new_column(tb, "LIKE", 0, SCOLS_FL_RIGHT))
++		goto fail;
++	if (!scols_table_new_column(tb, "TEXT", 0, SCOLS_FL_WRAP))
++		goto fail;
++	return;
++fail:
++	scols_unref_table(tb);
++	err(EXIT_FAILURE, "failed to create output columns");
++}
++
++static char *gen_text(const char *prefix, const char *sub_prefix, char *buf, size_t sz)
++{
++	int x = snprintf(buf, sz,  "%s-%s-", prefix, sub_prefix);
++
++	for ( ; (size_t)x < sz - 1; x++)
++		buf[x] = *prefix;
++
++	buf[x++] = 'x';
++	buf[x] = '\0';
++	return buf;
++}
++
++static struct libscols_line * add_line(	struct libscols_table *tb,
++					struct libscols_line *parent,
++					const char *prefix)
++{
++	char buf[BUFSIZ];
++	struct libscols_line *ln = scols_table_new_line(tb, parent);
++	if (!ln)
++		err(EXIT_FAILURE, "failed to create output line");
++
++	if (scols_line_set_data(ln, COL_NAME, gen_text(prefix, "N", buf, 15)))
++		goto fail;
++	if (scols_line_set_data(ln, COL_DESC, gen_text(prefix, "D", buf, 10)))
++		goto fail;
++	if (scols_line_set_data(ln, COL_FOO, gen_text(prefix, "U", buf, 55)))
++		goto fail;
++	if (scols_line_set_data(ln, COL_LIKE, "1"))
++		goto fail;
++	if (scols_line_set_data(ln, COL_TEXT, gen_text(prefix, "T", buf, 50)))
++		goto fail;
++	return ln;
++fail:
++	scols_unref_table(tb);
++	err(EXIT_FAILURE, "failed to create output line");
++}
++
++int main(int argc, char *argv[])
++{
++	struct libscols_table *tb;
++	struct libscols_line *ln, *xln;
++
++	setlocale(LC_ALL, "");	/* just to have enable UTF8 chars */
++
++	scols_init_debug(0);
++
++	tb = scols_new_table();
++	if (!tb)
++		err(EXIT_FAILURE, "failed to create output table");
++
++	scols_table_enable_colors(tb, isatty(STDOUT_FILENO));
++	setup_columns(tb);
++
++	ln = add_line(tb, NULL, "A");
++	add_line(tb, ln, "aa");
++	add_line(tb, ln, "ab");
++
++	ln = add_line(tb, NULL, "B");
++	xln = add_line(tb, ln, "ba");
++	add_line(tb, xln, "baa");
++	add_line(tb, xln, "bab");
++	add_line(tb, ln, "bb");
++
++	scols_print_table(tb);
++	scols_unref_table(tb);
++	return EXIT_SUCCESS;
++}
+diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am
+index bfe8c75c1..952d5e58f 100644
+--- a/libsmartcols/src/Makemodule.am
++++ b/libsmartcols/src/Makemodule.am
+@@ -1,10 +1,10 @@
+ 
+ 
+-## smartcols.h is generated, so it's stored in builddir! (no distribute in RHEL7)
+-#smartcolsincdir = $(includedir)/libsmartcols
+-#nodist_smartcolsinc_HEADERS = $(top_builddir)/libsmartcols/src/libsmartcols.h
++# smartcols.h is generated, so it's stored in builddir!
++smartcolsincdir = $(includedir)/libsmartcols
++nodist_smartcolsinc_HEADERS = libsmartcols/src/libsmartcols.h
+ 
+-noinst_LTLIBRARIES += libsmartcols.la
++usrlib_exec_LTLIBRARIES += libsmartcols.la
+ libsmartcols_la_SOURCES= \
+ 	include/list.h \
+ 	\
+@@ -17,49 +17,32 @@ libsmartcols_la_SOURCES= \
+ 	libsmartcols/src/table.c \
+ 	libsmartcols/src/table_print.c \
+ 	libsmartcols/src/version.c \
+-	libsmartcols/src/init.c \
+-	$(nodist_smartcolsinc_HEADERS)
++	libsmartcols/src/init.c
+ 
+-nodist_libsmartcols_la_SOURCES = libsmartcols/src/smartcolsP.h
+-
+-libsmartcols_la_LIBADD = libcommon.la
++libsmartcols_la_LIBADD = $(LDADD) libcommon.la
+ 
+ libsmartcols_la_CFLAGS = \
++	$(AM_CFLAGS) \
+ 	$(SOLIB_CFLAGS) \
+ 	-I$(ul_libsmartcols_incdir) \
+ 	-I$(top_srcdir)/libsmartcols/src
+ 
+-libsmartcols_la_DEPENDENCIES = \
+-	libcommon.la \
+-	libsmartcols/src/libsmartcols.sym \
+-	libsmartcols/src/libsmartcols.h.in
++EXTRA_libsmartcols_la_DEPENDENCIES = \
++	libsmartcols/src/libsmartcols.sym
+ 
+ libsmartcols_la_LDFLAGS = \
+ 	$(SOLIB_LDFLAGS) \
+ 	-Wl,--version-script=$(top_srcdir)/libsmartcols/src/libsmartcols.sym \
+ 	-version-info $(LIBSMARTCOLS_VERSION_INFO)
+ 
+-EXTRA_DIST += \
+-	libsmartcols/src/libsmartcols.sym \
+-	libsmartcols/src/libsmartcols.h.in
+-
+-
+-if BUILD_LIBSMARTCOLS_TESTS
+-check_PROGRAMS += test_smartcols
+-
+-libsmartcols_tests_cflags = $(libsmartcols_la_CFLAGS)
+-libsmartcols_tests_ldadd  = libsmartcols.la libcommon.la
+-
+-test_smartcols_SOURCES = libsmartcols/src/test.c
+-test_smartcols_CFLAGS = $(libsmartcols_tests_cflags)
+-test_smartcols_LDADD = $(libsmartcols_tests_ldadd)
+-endif # BUILD_LIBSMARTCOLS_TESTS
+ 
++EXTRA_DIST += \
++	libsmartcols/src/libsmartcols.sym
+ 
+ # move lib from $(usrlib_execdir) to $(libdir) if needed
+ install-exec-hook-libsmartcols:
+ 	if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libsmartcols.so"; then \
+-		mkdir -p $(DESTDIR)$(libdir); \
++		$(MKDIR_P) $(DESTDIR)$(libdir); \
+ 		mv $(DESTDIR)$(usrlib_execdir)/libsmartcols.so.* $(DESTDIR)$(libdir); \
+ 		so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libsmartcols.so); \
+ 		so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+diff --git a/libsmartcols/src/cell.c b/libsmartcols/src/cell.c
+index ea41f698f..0717a2d09 100644
+--- a/libsmartcols/src/cell.c
++++ b/libsmartcols/src/cell.c
+@@ -11,7 +11,7 @@
+ /**
+  * SECTION: cell
+  * @title: Cell
+- * @short_description: cell API
++ * @short_description: container for your data
+  *
+  * An API to access and modify per-cell data and information. Note that cell is
+  * always part of the line. If you destroy (un-reference) a line than it
+@@ -41,8 +41,6 @@
+  */
+ int scols_reset_cell(struct libscols_cell *ce)
+ {
+-	assert(ce);
+-
+ 	if (!ce)
+ 		return -EINVAL;
+ 
+@@ -56,34 +54,21 @@ int scols_reset_cell(struct libscols_cell *ce)
+ /**
+  * scols_cell_set_data:
+  * @ce: a pointer to a struct libscols_cell instance
+- * @str: data (used for scols_printtable())
++ * @data: data (used for scols_print_table())
+  *
+- * Stores a copy of the @str in @ce.
++ * Stores a copy of the @str in @ce, the old data are deallocated by free().
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+-int scols_cell_set_data(struct libscols_cell *ce, const char *str)
++int scols_cell_set_data(struct libscols_cell *ce, const char *data)
+ {
+-	char *p = NULL;
+-
+-	assert(ce);
+-
+-	if (!ce)
+-		return -EINVAL;
+-	if (str) {
+-		p = strdup(str);
+-		if (!p)
+-			return -ENOMEM;
+-	}
+-	free(ce->data);
+-	ce->data = p;
+-	return 0;
++	return strdup_to_struct_member(ce, data, data);
+ }
+ 
+ /**
+  * scols_cell_refer_data:
+  * @ce: a pointer to a struct libscols_cell instance
+- * @str: data (used for scols_printtable())
++ * @data: data (used for scols_print_table())
+  *
+  * Adds a reference to @str to @ce. The pointer is deallocated by
+  * scols_reset_cell() or scols_unref_line(). This function is mostly designed
+@@ -92,14 +77,12 @@ int scols_cell_set_data(struct libscols_cell *ce, const char *str)
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+-int scols_cell_refer_data(struct libscols_cell *ce, char *str)
++int scols_cell_refer_data(struct libscols_cell *ce, char *data)
+ {
+-	assert(ce);
+-
+ 	if (!ce)
+ 		return -EINVAL;
+ 	free(ce->data);
+-	ce->data = str;
++	ce->data = data;
+ 	return 0;
+ }
+ 
+@@ -111,7 +94,6 @@ int scols_cell_refer_data(struct libscols_cell *ce, char *str)
+  */
+ const char *scols_cell_get_data(const struct libscols_cell *ce)
+ {
+-	assert(ce);
+ 	return ce ? ce->data : NULL;
+ }
+ 
+@@ -124,8 +106,6 @@ const char *scols_cell_get_data(const struct libscols_cell *ce)
+  */
+ int scols_cell_set_userdata(struct libscols_cell *ce, void *data)
+ {
+-	assert(ce);
+-
+ 	if (!ce)
+ 		return -EINVAL;
+ 	ce->userdata = data;
+@@ -140,7 +120,7 @@ int scols_cell_set_userdata(struct libscols_cell *ce, void *data)
+  */
+ void *scols_cell_get_userdata(struct libscols_cell *ce)
+ {
+-	return ce ? ce->userdata : NULL;
++	return ce->userdata;
+ }
+ 
+ /**
+@@ -178,7 +158,7 @@ int scols_cmpstr_cells(struct libscols_cell *a,
+ /**
+  * scols_cell_set_color:
+  * @ce: a pointer to a struct libscols_cell instance
+- * @color: ESC sequence
++ * @color: color name or ESC sequence
+  *
+  * Set the color of @ce to @color.
+  *
+@@ -186,32 +166,70 @@ int scols_cmpstr_cells(struct libscols_cell *a,
+  */
+ int scols_cell_set_color(struct libscols_cell *ce, const char *color)
+ {
+-	char *p = NULL;
++	if (color && isalpha(*color)) {
++		color = color_sequence_from_colorname(color);
++		if (!color)
++			return -EINVAL;
++	}
++	return strdup_to_struct_member(ce, color, color);
++}
+ 
+-	assert(ce);
++/**
++ * scols_cell_get_color:
++ * @ce: a pointer to a struct libscols_cell instance
++ *
++ * Returns: the current color of @ce.
++ */
++const char *scols_cell_get_color(const struct libscols_cell *ce)
++{
++	return ce->color;
++}
+ 
++/**
++ * scols_cell_set_flags:
++ * @ce: a pointer to a struct libscols_cell instance
++ * @flags: SCOLS_CELL_FL_* flags
++ *
++ * Note that cells in the table are always aligned by column flags. The cell
++ * flags are used for table title only (now).
++ *
++ * Returns: 0, a negative value in case of an error.
++ */
++int scols_cell_set_flags(struct libscols_cell *ce, int flags)
++{
+ 	if (!ce)
+ 		return -EINVAL;
+-	if (color) {
+-		p = strdup(color);
+-		if (!p)
+-			return -ENOMEM;
+-	}
+-	free(ce->color);
+-	ce->color = p;
++	ce->flags = flags;
+ 	return 0;
+ }
+ 
+ /**
+- * scols_cell_get_color:
++ * scols_cell_get_flags:
+  * @ce: a pointer to a struct libscols_cell instance
+  *
+- * Returns: the current color of @ce.
++ * Returns: the current flags
+  */
+-const char *scols_cell_get_color(const struct libscols_cell *ce)
++int scols_cell_get_flags(const struct libscols_cell *ce)
+ {
+-	assert(ce);
+-	return ce ? ce->color : NULL;
++	return ce->flags;
++}
++
++/**
++ * scols_cell_get_alignment:
++ * @ce: a pointer to a struct libscols_cell instance
++ *
++ * Since: 2.30
++ *
++ * Returns: SCOLS_CELL_FL_{RIGHT,CELNTER,LEFT}
++ */
++int scols_cell_get_alignment(const struct libscols_cell *ce)
++{
++	if (ce->flags & SCOLS_CELL_FL_RIGHT)
++		return SCOLS_CELL_FL_RIGHT;
++	else if (ce->flags & SCOLS_CELL_FL_CENTER)
++		return SCOLS_CELL_FL_CENTER;
++
++	return SCOLS_CELL_FL_LEFT;	/* default */
+ }
+ 
+ /**
+@@ -228,15 +246,12 @@ int scols_cell_copy_content(struct libscols_cell *dest,
+ {
+ 	int rc;
+ 
+-	assert(dest);
+-	assert(src);
+-
+ 	rc = scols_cell_set_data(dest, scols_cell_get_data(src));
+ 	if (!rc)
+ 		rc = scols_cell_set_color(dest, scols_cell_get_color(src));
+ 	if (!rc)
+ 		dest->userdata = src->userdata;
+ 
+-	DBG(CELL, ul_debugobj((void *) src, "copy into %p", dest));
++	DBG(CELL, ul_debugobj(src, "copy"));
+ 	return rc;
+ }
+diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c
+index d1d10a6d0..e9d6dc404 100644
+--- a/libsmartcols/src/column.c
++++ b/libsmartcols/src/column.c
+@@ -11,7 +11,7 @@
+ /**
+  * SECTION: column
+  * @title: Column
+- * @short_description: column API
++ * @short_description: defines output columns formats, headers, etc.
+  *
+  * An API to access and modify per-column data and information.
+  */
+@@ -22,6 +22,8 @@
+ #include <string.h>
+ #include <ctype.h>
+ 
++#include "mbsalign.h"
++
+ #include "smartcolsP.h"
+ 
+ /**
+@@ -29,7 +31,7 @@
+  *
+  * Allocates space for a new column.
+  *
+- * Returns: a pointer to a new struct libscols_cell instance, NULL in case of an ENOMEM error.
++ * Returns: a pointer to a new struct libscols_column instance, NULL in case of an ENOMEM error.
+  */
+ struct libscols_column *scols_new_column(void)
+ {
+@@ -70,6 +72,8 @@ void scols_unref_column(struct libscols_column *cl)
+ 		list_del(&cl->cl_columns);
+ 		scols_reset_cell(&cl->header);
+ 		free(cl->color);
++		free(cl->safechars);
++		free(cl->pending_data_buf);
+ 		free(cl);
+ 	}
+ }
+@@ -86,14 +90,13 @@ struct libscols_column *scols_copy_column(const struct libscols_column *cl)
+ {
+ 	struct libscols_column *ret;
+ 
+-	assert (cl);
+ 	if (!cl)
+ 		return NULL;
+ 	ret = scols_new_column();
+ 	if (!ret)
+ 		return NULL;
+ 
+-	DBG(COL, ul_debugobj((void *) cl, "copy to %p", ret));
++	DBG(COL, ul_debugobj(cl, "copy"));
+ 
+ 	if (scols_column_set_color(ret, cl->color))
+ 		goto err;
+@@ -119,14 +122,12 @@ err:
+  * @cl: a pointer to a struct libscols_column instance
+  * @whint: a width hint
+  *
+- * Sets the width hint of column @cl to @whint.
++ * Sets the width hint of column @cl to @whint. See scols_table_new_column().
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+ int scols_column_set_whint(struct libscols_column *cl, double whint)
+ {
+-	assert(cl);
+-
+ 	if (!cl)
+ 		return -EINVAL;
+ 
+@@ -140,10 +141,9 @@ int scols_column_set_whint(struct libscols_column *cl, double whint)
+  *
+  * Returns: The width hint of column @cl, a negative value in case of an error.
+  */
+-double scols_column_get_whint(struct libscols_column *cl)
++double scols_column_get_whint(const struct libscols_column *cl)
+ {
+-	assert(cl);
+-	return cl ? cl->width_hint : -EINVAL;
++	return cl->width_hint;
+ }
+ 
+ /**
+@@ -157,25 +157,78 @@ double scols_column_get_whint(struct libscols_column *cl)
+  */
+ int scols_column_set_flags(struct libscols_column *cl, int flags)
+ {
+-	assert(cl);
+-
+ 	if (!cl)
+ 		return -EINVAL;
+ 
++	if (cl->table) {
++		if (!(cl->flags & SCOLS_FL_TREE) && (flags & SCOLS_FL_TREE))
++			cl->table->ntreecols++;
++		else if ((cl->flags & SCOLS_FL_TREE) && !(flags & SCOLS_FL_TREE))
++			cl->table->ntreecols--;
++	}
++
+ 	cl->flags = flags;
+ 	return 0;
+ }
+ 
++/**
++ * scols_column_set_json_type:
++ * @cl: a pointer to a struct libscols_column instance
++ * @type: SCOLS_JSON_* type
++ *
++ * Sets the type used for JSON formatting, the default is SCOLS_JSON_STRING.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.33
++ */
++int scols_column_set_json_type(struct libscols_column *cl, int type)
++{
++	if (!cl)
++		return -EINVAL;
++
++	cl->json_type = type;
++	return 0;
++
++}
++
++/**
++ * scols_column_get_json_type:
++ * @cl: a pointer to a struct libscols_column instance
++ *
++ * Note that SCOLS_JSON_BOOLEAN interprets NULL, empty strings, '0', 'N' and
++ * 'n' as "false"; and everything else as "true".
++ *
++ * Returns: JSON type used for formatting or a negative value in case of an error.
++ *
++ * Since: 2.33
++ */
++int scols_column_get_json_type(const struct libscols_column *cl)
++{
++	return cl ? cl->json_type : -EINVAL;
++}
++
++
++/**
++ * scols_column_get_table:
++ * @cl: a pointer to a struct libscols_column instance
++ *
++ * Returns: pointer to the table where columns is used
++ */
++struct libscols_table *scols_column_get_table(const struct libscols_column *cl)
++{
++	return cl->table;
++}
++
+ /**
+  * scols_column_get_flags:
+  * @cl: a pointer to a struct libscols_column instance
+  *
+  * Returns: The flag mask of @cl, a negative value in case of an error.
+  */
+-int scols_column_get_flags(struct libscols_column *cl)
++int scols_column_get_flags(const struct libscols_column *cl)
+ {
+-	assert(cl);
+-	return cl ? cl->flags : -EINVAL;
++	return cl->flags;
+ }
+ 
+ /**
+@@ -187,14 +240,13 @@ int scols_column_get_flags(struct libscols_column *cl)
+  */
+ struct libscols_cell *scols_column_get_header(struct libscols_column *cl)
+ {
+-	assert(cl);
+-	return cl ? &cl->header : NULL;
++	return &cl->header;
+ }
+ 
+ /**
+  * scols_column_set_color:
+  * @cl: a pointer to a struct libscols_column instance
+- * @color: ESC sequence
++ * @color: color name or ESC sequence
+  *
+  * The default color for data cells and column header.
+  *
+@@ -208,20 +260,12 @@ struct libscols_cell *scols_column_get_header(struct libscols_column *cl)
+  */
+ int scols_column_set_color(struct libscols_column *cl, const char *color)
+ {
+-	char *p = NULL;
+-
+-	assert(cl);
+-	if (!cl)
+-		return -EINVAL;
+-	if (color) {
+-		p = strdup(color);
+-		if (!p)
+-			return -ENOMEM;
++	if (color && isalpha(*color)) {
++		color = color_sequence_from_colorname(color);
++		if (!color)
++			return -EINVAL;
+ 	}
+-
+-	free(cl->color);
+-	cl->color = p;
+-	return 0;
++	return strdup_to_struct_member(cl, color, color);
+ }
+ 
+ /**
+@@ -230,12 +274,79 @@ int scols_column_set_color(struct libscols_column *cl, const char *color)
+  *
+  * Returns: The current color setting of the column @cl.
+  */
+-const char *scols_column_get_color(struct libscols_column *cl)
++const char *scols_column_get_color(const struct libscols_column *cl)
++{
++	return cl->color;
++}
++
++/**
++ * scols_wrapnl_nextchunk:
++ * @cl: a pointer to a struct libscols_column instance
++ * @data: string
++ * @userdata: callback private data
++ *
++ * This is built-in function for scols_column_set_wrapfunc(). This function
++ * terminates the current chunk by \0 and returns pointer to the begin of
++ * the next chunk. The chunks are based on \n.
++ *
++ * For example for data "AAA\nBBB\nCCC" the next chunk is "BBB".
++ *
++ * Returns: next chunk
++ *
++ * Since: 2.29
++ */
++char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unused)),
++			char *data,
++			void *userdata __attribute__((unused)))
+ {
+-	assert(cl);
+-	return cl ? cl->color : NULL;
++	char *p = data ? strchr(data, '\n') : NULL;
++
++	if (p) {
++		*p = '\0';
++		return p + 1;
++	}
++	return NULL;
+ }
+ 
++/**
++ * scols_wrapnl_chunksize:
++ * @cl: a pointer to a struct libscols_column instance
++ * @data: string
++ * @userdata: callback private data
++ *
++ * Analyzes @data and returns size of the largest chunk. The chunks are based
++ * on \n. For example for data "AAA\nBBB\nCCCC" the largest chunk size is 4.
++ *
++ * Note that the size has to be based on number of terminal cells rather than
++ * bytes to support multu-byte output.
++ *
++ * Returns: size of the largest chunk.
++ *
++ * Since: 2.29
++ */
++size_t scols_wrapnl_chunksize(const struct libscols_column *cl __attribute__((unused)),
++		const char *data,
++		void *userdata __attribute__((unused)))
++{
++	size_t sum = 0;
++
++	while (data && *data) {
++		const char *p;
++		size_t sz;
++
++		p = strchr(data, '\n');
++		if (p) {
++			sz = mbs_safe_nwidth(data, p - data, NULL);
++			p++;
++		} else
++			sz = mbs_safe_width(data);
++
++		sum = max(sum, sz);
++		data = p;
++	}
++
++	return sum;
++}
+ 
+ /**
+  * scols_column_set_cmpfunc:
+@@ -251,7 +362,6 @@ int scols_column_set_cmpfunc(struct libscols_column *cl,
+ 				   void *),
+ 			void *data)
+ {
+-	assert(cl);
+ 	if (!cl)
+ 		return -EINVAL;
+ 
+@@ -261,19 +371,114 @@ int scols_column_set_cmpfunc(struct libscols_column *cl,
+ }
+ 
+ /**
+- * scols_column_is_trunc:
++ * scols_column_set_wrapfunc:
+  * @cl: a pointer to a struct libscols_column instance
++ * @wrap_chunksize: function to return size of the largest chink of data
++ * @wrap_nextchunk: function to return next zero terminated data
++ * @userdata: optional stuff for callbacks
+  *
+- * Gets the value of @cl's flag trunc.
++ * Extends SCOLS_FL_WRAP and allows to set custom wrap function. The default
++ * is to wrap by column size, but you can create functions to wrap for example
++ * after \n or after words, etc.
++ *
++ * Returns: 0, a negative value in case of an error.
+  *
+- * Returns: trunc flag value, negative value in case of an error.
++ * Since: 2.29
+  */
+-int scols_column_is_trunc(struct libscols_column *cl)
++int scols_column_set_wrapfunc(struct libscols_column *cl,
++			size_t (*wrap_chunksize)(const struct libscols_column *,
++						 const char *,
++						 void *),
++			char * (*wrap_nextchunk)(const struct libscols_column *,
++						 char *,
++						 void *),
++			void *userdata)
+ {
+-	assert(cl);
+ 	if (!cl)
+ 		return -EINVAL;
+-	return cl->flags & SCOLS_FL_TRUNC;
++
++	cl->wrap_nextchunk = wrap_nextchunk;
++	cl->wrap_chunksize = wrap_chunksize;
++	cl->wrapfunc_data = userdata;
++	return 0;
++}
++
++/**
++ * scols_column_set_safechars:
++ * @cl: a pointer to a struct libscols_column instance
++ * @safe: safe characters (e.g. "\n\t")
++ *
++ * Use for bytes you don't want to encode on output. This is for example
++ * necessary if you want to use custom wrap function based on \n, in this case
++ * you have to set "\n" as a safe char.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.29
++ */
++int scols_column_set_safechars(struct libscols_column *cl, const char *safe)
++{
++	return strdup_to_struct_member(cl, safechars, safe);
++}
++
++/**
++ * scols_column_get_safechars:
++ * @cl: a pointer to a struct libscols_column instance
++ *
++ * Returns: safe chars
++ *
++ * Since: 2.29
++ */
++const char *scols_column_get_safechars(const struct libscols_column *cl)
++{
++	return cl->safechars;
++}
++
++/**
++ * scols_column_get_width:
++ * @cl: a pointer to a struct libscols_column instance
++ *
++ * Important note: the column width is unknown until library starts printing
++ * (width is calculated before printing). The function is usable for example in
++ * nextchunk() callback specified by scols_column_set_wrapfunc().
++ *
++ * See also scols_column_get_whint(), it returns wanted size (!= final size).
++ *
++ * Returns: column width
++ *
++ * Since: 2.29
++ */
++size_t scols_column_get_width(const struct libscols_column *cl)
++{
++	return cl->width;
++}
++
++/**
++ * scols_column_is_hidden:
++ * @cl: a pointer to a struct libscols_column instance
++ *
++ * Gets the value of @cl's flag hidden.
++ *
++ * Returns: 0 or 1
++ *
++ * Since: 2.27
++ */
++int scols_column_is_hidden(const struct libscols_column *cl)
++{
++	return cl->flags & SCOLS_FL_HIDDEN ? 1 : 0;
++}
++
++/**
++ * scols_column_is_trunc:
++ * @cl: a pointer to a struct libscols_column instance
++ *
++ * Gets the value of @cl's flag trunc.
++ *
++ * Returns: 0 or 1
++ */
++int scols_column_is_trunc(const struct libscols_column *cl)
++{
++	return cl->flags & SCOLS_FL_TRUNC ? 1 : 0;
+ }
+ /**
+  * scols_column_is_tree:
+@@ -281,14 +486,11 @@ int scols_column_is_trunc(struct libscols_column *cl)
+  *
+  * Gets the value of @cl's flag tree.
+  *
+- * Returns: tree flag value, negative value in case of an error.
++ * Returns: 0 or 1
+  */
+-int scols_column_is_tree(struct libscols_column *cl)
++int scols_column_is_tree(const struct libscols_column *cl)
+ {
+-	assert(cl);
+-	if (!cl)
+-		return -EINVAL;
+-	return cl->flags & SCOLS_FL_TREE;
++	return cl->flags & SCOLS_FL_TREE ? 1 : 0;
+ }
+ /**
+  * scols_column_is_right:
+@@ -296,14 +498,11 @@ int scols_column_is_tree(struct libscols_column *cl)
+  *
+  * Gets the value of @cl's flag right.
+  *
+- * Returns: right flag value, negative value in case of an error.
++ * Returns: 0 or 1
+  */
+-int scols_column_is_right(struct libscols_column *cl)
++int scols_column_is_right(const struct libscols_column *cl)
+ {
+-	assert(cl);
+-	if (!cl)
+-		return -EINVAL;
+-	return cl->flags & SCOLS_FL_RIGHT;
++	return cl->flags & SCOLS_FL_RIGHT ? 1 : 0;
+ }
+ /**
+  * scols_column_is_strict_width:
+@@ -311,14 +510,11 @@ int scols_column_is_right(struct libscols_column *cl)
+  *
+  * Gets the value of @cl's flag strict_width.
+  *
+- * Returns: strict_width flag value, negative value in case of an error.
++ * Returns: 0 or 1
+  */
+-int scols_column_is_strict_width(struct libscols_column *cl)
++int scols_column_is_strict_width(const struct libscols_column *cl)
+ {
+-	assert(cl);
+-	if (!cl)
+-		return -EINVAL;
+-	return cl->flags & SCOLS_FL_STRICTWIDTH;
++	return cl->flags & SCOLS_FL_STRICTWIDTH ? 1 : 0;
+ }
+ /**
+  * scols_column_is_noextremes:
+@@ -326,12 +522,37 @@ int scols_column_is_strict_width(struct libscols_column *cl)
+  *
+  * Gets the value of @cl's flag no_extremes.
+  *
+- * Returns: no_extremes flag value, negative value in case of an error.
++ * Returns: 0 or 1
+  */
+-int scols_column_is_noextremes(struct libscols_column *cl)
++int scols_column_is_noextremes(const struct libscols_column *cl)
+ {
+-	assert(cl);
+-	if (!cl)
+-		return -EINVAL;
+-	return cl->flags & SCOLS_FL_NOEXTREMES;
++	return cl->flags & SCOLS_FL_NOEXTREMES ? 1 : 0;
++}
++/**
++ * scols_column_is_wrap:
++ * @cl: a pointer to a struct libscols_column instance
++ *
++ * Gets the value of @cl's flag wrap.
++ *
++ * Returns: 0 or 1
++ *
++ * Since: 2.28
++ */
++int scols_column_is_wrap(const struct libscols_column *cl)
++{
++	return cl->flags & SCOLS_FL_WRAP ? 1 : 0;
++}
++/**
++ * scols_column_is_customwrap:
++ * @cl: a pointer to a struct libscols_column instance
++ *
++ * Returns: 0 or 1
++ *
++ * Since: 2.29
++ */
++int scols_column_is_customwrap(const struct libscols_column *cl)
++{
++	return (cl->flags & SCOLS_FL_WRAP)
++		&& cl->wrap_chunksize
++		&& cl->wrap_nextchunk ? 1 : 0;
+ }
+diff --git a/libsmartcols/src/iter.c b/libsmartcols/src/iter.c
+index 72c7865a8..91cc08009 100644
+--- a/libsmartcols/src/iter.c
++++ b/libsmartcols/src/iter.c
+@@ -68,7 +68,7 @@ void scols_reset_iter(struct libscols_iter *itr, int direction)
+  *
+  * Returns: SCOLS_INTER_{FOR,BACK}WARD
+  */
+-int scols_iter_get_direction(struct libscols_iter *itr)
++int scols_iter_get_direction(const struct libscols_iter *itr)
+ {
+ 	return itr->direction;
+ }
+diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in
+index e61256022..f8be0bc04 100644
+--- a/libsmartcols/src/libsmartcols.h.in
++++ b/libsmartcols/src/libsmartcols.h.in
+@@ -83,12 +83,33 @@ enum {
+ 	SCOLS_FL_RIGHT	     = (1 << 2),   /* align to the right */
+ 	SCOLS_FL_STRICTWIDTH = (1 << 3),   /* don't reduce width if column is empty */
+ 	SCOLS_FL_NOEXTREMES  = (1 << 4),   /* ignore extreme fields when count column width*/
++	SCOLS_FL_HIDDEN	     = (1 << 5),   /* maintain data, but don't print */
++	SCOLS_FL_WRAP	     = (1 << 6)    /* wrap long lines to multi-line cells */
++};
++
++/*
++ * Column JSON types
++ */
++enum {
++	SCOLS_JSON_STRING    = 0,	/* default */
++	SCOLS_JSON_NUMBER    = 1,
++	SCOLS_JSON_BOOLEAN   = 2
++};
++
++/*
++ * Cell flags, see scols_cell_set_flags() before use
++ */
++enum {
++	/* alignment evaluated in order: right,center,left */
++	SCOLS_CELL_FL_LEFT    = 0,
++	SCOLS_CELL_FL_CENTER  = (1 << 0),
++	SCOLS_CELL_FL_RIGHT   = (1 << 1)
+ };
+ 
+ extern struct libscols_iter *scols_new_iter(int direction);
+ extern void scols_free_iter(struct libscols_iter *itr);
+ extern void scols_reset_iter(struct libscols_iter *itr, int direction);
+-extern int scols_iter_get_direction(struct libscols_iter *itr);
++extern int scols_iter_get_direction(const struct libscols_iter *itr);
+ 
+ /* init.c */
+ extern void scols_init_debug(int mask);
+@@ -101,50 +122,78 @@ extern int scols_get_library_version(const char **ver_string);
+ extern struct libscols_symbols *scols_new_symbols(void);
+ extern void scols_ref_symbols(struct libscols_symbols *sy);
+ extern void scols_unref_symbols(struct libscols_symbols *sy);
+-extern struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sb);
+-extern int scols_symbols_set_branch(struct libscols_symbols *sb, const char *str);
+-extern int scols_symbols_set_vertical(struct libscols_symbols *sb, const char *str);
+-extern int scols_symbols_set_right(struct libscols_symbols *sb, const char *str);
++extern struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sy);
++extern int scols_symbols_set_branch(struct libscols_symbols *sy, const char *str);
++extern int scols_symbols_set_vertical(struct libscols_symbols *sy, const char *str);
++extern int scols_symbols_set_right(struct libscols_symbols *sy, const char *str);
++extern int scols_symbols_set_title_padding(struct libscols_symbols *sy, const char *str);
++extern int scols_symbols_set_cell_padding(struct libscols_symbols *sy, const char *str);
+ 
+ /* cell.c */
+ extern int scols_reset_cell(struct libscols_cell *ce);
+ extern int scols_cell_copy_content(struct libscols_cell *dest,
+ 				   const struct libscols_cell *src);
+-extern int scols_cell_set_data(struct libscols_cell *ce, const char *str);
+-extern int scols_cell_refer_data(struct libscols_cell *ce, char *str);
++extern int scols_cell_set_data(struct libscols_cell *ce, const char *data);
++extern int scols_cell_refer_data(struct libscols_cell *ce, char *data);
+ extern const char *scols_cell_get_data(const struct libscols_cell *ce);
+ extern int scols_cell_set_color(struct libscols_cell *ce, const char *color);
+ extern const char *scols_cell_get_color(const struct libscols_cell *ce);
+ 
++extern int scols_cell_set_flags(struct libscols_cell *ce, int flags);
++extern int scols_cell_get_flags(const struct libscols_cell *ce);
++extern int scols_cell_get_alignment(const struct libscols_cell *ce);
++
+ extern void *scols_cell_get_userdata(struct libscols_cell *ce);
+ extern int scols_cell_set_userdata(struct libscols_cell *ce, void *data);
+ 
+ extern int scols_cmpstr_cells(struct libscols_cell *a,
+ 			      struct libscols_cell *b, void *data);
+ /* column.c */
+-extern int scols_column_is_tree(struct libscols_column *cl);
+-extern int scols_column_is_trunc(struct libscols_column *cl);
+-extern int scols_column_is_right(struct libscols_column *cl);
+-extern int scols_column_is_strict_width(struct libscols_column *cl);
+-extern int scols_column_is_noextremes(struct libscols_column *cl);
++extern int scols_column_is_tree(const struct libscols_column *cl);
++extern int scols_column_is_trunc(const struct libscols_column *cl);
++extern int scols_column_is_right(const struct libscols_column *cl);
++extern int scols_column_is_strict_width(const struct libscols_column *cl);
++extern int scols_column_is_hidden(const struct libscols_column *cl);
++extern int scols_column_is_noextremes(const struct libscols_column *cl);
++extern int scols_column_is_wrap(const struct libscols_column *cl);
++extern int scols_column_is_customwrap(const struct libscols_column *cl);
++
++extern size_t scols_column_get_width(const struct libscols_column *cl);
++
++extern int scols_column_set_safechars(struct libscols_column *cl, const char *safe);
++extern const char *scols_column_get_safechars(const struct libscols_column *cl);
++
++extern int scols_column_set_json_type(struct libscols_column *cl, int type);
++extern int scols_column_get_json_type(const struct libscols_column *cl);
+ 
+ extern int scols_column_set_flags(struct libscols_column *cl, int flags);
+-extern int scols_column_get_flags(struct libscols_column *cl);
++extern int scols_column_get_flags(const struct libscols_column *cl);
+ extern struct libscols_column *scols_new_column(void);
+ extern void scols_ref_column(struct libscols_column *cl);
+ extern void scols_unref_column(struct libscols_column *cl);
+ extern struct libscols_column *scols_copy_column(const struct libscols_column *cl);
+ extern int scols_column_set_whint(struct libscols_column *cl, double whint);
+-extern double scols_column_get_whint(struct libscols_column *cl);
++extern double scols_column_get_whint(const struct libscols_column *cl);
+ extern struct libscols_cell *scols_column_get_header(struct libscols_column *cl);
+ extern int scols_column_set_color(struct libscols_column *cl, const char *color);
+-extern const char *scols_column_get_color(struct libscols_column *cl);
++extern const char *scols_column_get_color(const struct libscols_column *cl);
++extern struct libscols_table *scols_column_get_table(const struct libscols_column *cl);
+ 
+ extern int scols_column_set_cmpfunc(struct libscols_column *cl,
+ 			int (*cmp)(struct libscols_cell *a,
+ 				   struct libscols_cell *b, void *),
+ 			void *data);
+ 
++extern int scols_column_set_wrapfunc(struct libscols_column *cl,
++			size_t (*wrap_chunksize)(const struct libscols_column *,
++					 const char *, void *),
++			char * (*wrap_nextchunk)(const struct libscols_column *,
++					 char *, void *),
++			void *userdata);
++
++extern char *scols_wrapnl_nextchunk(const struct libscols_column *cl, char *data, void *userdata);
++extern size_t scols_wrapnl_chunksize(const struct libscols_column *cl, const char *data, void *userdata);
++
+ /* line.c */
+ extern struct libscols_line *scols_new_line(void);
+ extern void scols_ref_line(struct libscols_line *ln);
+@@ -156,36 +205,52 @@ extern void *scols_line_get_userdata(struct libscols_line *ln);
+ extern int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child);
+ extern int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child);
+ extern int scols_line_has_children(struct libscols_line *ln);
++extern int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent);
+ extern int scols_line_next_child(struct libscols_line *ln,
+ 			  struct libscols_iter *itr, struct libscols_line **chld);
+-extern struct libscols_line *scols_line_get_parent(struct libscols_line *ln);
++extern struct libscols_line *scols_line_get_parent(const struct libscols_line *ln);
+ extern int scols_line_set_color(struct libscols_line *ln, const char *color);
+-extern const char *scols_line_get_color(struct libscols_line *ln);
+-extern size_t scols_line_get_ncells(struct libscols_line *ln);
++extern const char *scols_line_get_color(const struct libscols_line *ln);
++extern size_t scols_line_get_ncells(const struct libscols_line *ln);
+ extern struct libscols_cell *scols_line_get_cell(struct libscols_line *ln, size_t n);
+ extern struct libscols_cell *scols_line_get_column_cell(
+ 		                        struct libscols_line *ln,
+ 		                        struct libscols_column *cl);
+ extern int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data);
+ extern int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data);
+-extern struct libscols_line *scols_copy_line(struct libscols_line *ln);
++extern int scols_line_set_column_data(struct libscols_line *ln, struct libscols_column *cl, const char *data);
++extern int scols_line_refer_column_data(struct libscols_line *ln, struct libscols_column *cl, char *data);
++extern struct libscols_line *scols_copy_line(const struct libscols_line *ln);
+ 
+ /* table */
+-extern int scols_table_colors_wanted(struct libscols_table *tb);
+-extern int scols_table_is_raw(struct libscols_table *tb);
+-extern int scols_table_is_ascii(struct libscols_table *tb);
+-extern int scols_table_is_noheadings(struct libscols_table *tb);
+-extern int scols_table_is_empty(struct libscols_table *tb);
+-extern int scols_table_is_export(struct libscols_table *tb);
+-extern int scols_table_is_maxout(struct libscols_table *tb);
+-extern int scols_table_is_tree(struct libscols_table *tb);
++extern int scols_table_colors_wanted(const struct libscols_table *tb);
++extern int scols_table_set_name(struct libscols_table *tb, const char *name);
++extern const char *scols_table_get_name(const struct libscols_table *tb);
++extern struct libscols_cell *scols_table_get_title(struct libscols_table *tb);
++extern int scols_table_is_raw(const struct libscols_table *tb);
++extern int scols_table_is_ascii(const struct libscols_table *tb);
++extern int scols_table_is_json(const struct libscols_table *tb);
++extern int scols_table_is_noheadings(const struct libscols_table *tb);
++extern int scols_table_is_header_repeat(const struct libscols_table *tb);
++extern int scols_table_is_empty(const struct libscols_table *tb);
++extern int scols_table_is_export(const struct libscols_table *tb);
++extern int scols_table_is_maxout(const struct libscols_table *tb);
++extern int scols_table_is_nowrap(const struct libscols_table *tb);
++extern int scols_table_is_nolinesep(const struct libscols_table *tb);
++extern int scols_table_is_tree(const struct libscols_table *tb);
++extern int scols_table_is_noencoding(const struct libscols_table *tb);
+ 
+ extern int scols_table_enable_colors(struct libscols_table *tb, int enable);
+ extern int scols_table_enable_raw(struct libscols_table *tb, int enable);
+ extern int scols_table_enable_ascii(struct libscols_table *tb, int enable);
++extern int scols_table_enable_json(struct libscols_table *tb, int enable);
+ extern int scols_table_enable_noheadings(struct libscols_table *tb, int enable);
++extern int scols_table_enable_header_repeat(struct libscols_table *tb, int enable);
+ extern int scols_table_enable_export(struct libscols_table *tb, int enable);
+ extern int scols_table_enable_maxout(struct libscols_table *tb, int enable);
++extern int scols_table_enable_nowrap(struct libscols_table *tb, int enable);
++extern int scols_table_enable_nolinesep(struct libscols_table *tb, int enable);
++extern int scols_table_enable_noencoding(struct libscols_table *tb, int enable);
+ 
+ extern int scols_table_set_column_separator(struct libscols_table *tb, const char *sep);
+ extern int scols_table_set_line_separator(struct libscols_table *tb, const char *sep);
+@@ -196,12 +261,13 @@ extern void scols_unref_table(struct libscols_table *tb);
+ extern int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl);
+ extern int scols_table_remove_column(struct libscols_table *tb, struct libscols_column *cl);
+ extern int scols_table_remove_columns(struct libscols_table *tb);
++extern int scols_table_move_column(struct libscols_table *tb, struct libscols_column *pre, struct libscols_column *cl);
+ extern struct libscols_column *scols_table_new_column(struct libscols_table *tb, const char *name, double whint, int flags);
+ extern int scols_table_next_column(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_column **cl);
+-extern char *scols_table_get_column_separator(struct libscols_table *tb);
+-extern char *scols_table_get_line_separator(struct libscols_table *tb);
+-extern int scols_table_get_ncols(struct libscols_table *tb);
+-extern int scols_table_get_nlines(struct libscols_table *tb);
++extern const char *scols_table_get_column_separator(const struct libscols_table *tb);
++extern const char *scols_table_get_line_separator(const struct libscols_table *tb);
++extern size_t scols_table_get_ncols(const struct libscols_table *tb);
++extern size_t scols_table_get_nlines(const struct libscols_table *tb);
+ extern struct libscols_column *scols_table_get_column(struct libscols_table *tb, size_t n);
+ extern int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln);
+ extern int scols_table_remove_line(struct libscols_table *tb, struct libscols_line *ln);
+@@ -211,17 +277,43 @@ extern struct libscols_line *scols_table_new_line(struct libscols_table *tb, str
+ extern struct libscols_line *scols_table_get_line(struct libscols_table *tb, size_t n);
+ extern struct libscols_table *scols_copy_table(struct libscols_table *tb);
+ extern int scols_table_set_symbols(struct libscols_table *tb, struct libscols_symbols *sy);
++extern int scols_table_set_default_symbols(struct libscols_table *tb);
++extern struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb);
+ 
+ extern int scols_table_set_stream(struct libscols_table *tb, FILE *stream);
+-extern FILE *scols_table_get_stream(struct libscols_table *tb);
++extern FILE *scols_table_get_stream(const struct libscols_table *tb);
+ extern int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce);
+ 
+ extern int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl);
++extern int scols_sort_table_by_tree(struct libscols_table *tb);
++/*
++ *
++ */
++enum {
++	SCOLS_TERMFORCE_AUTO = 0,
++	SCOLS_TERMFORCE_NEVER,
++	SCOLS_TERMFORCE_ALWAYS
++};
++extern int scols_table_set_termforce(struct libscols_table *tb, int force);
++extern int scols_table_get_termforce(const struct libscols_table *tb);
++extern int scols_table_set_termwidth(struct libscols_table *tb, size_t width);
++extern size_t scols_table_get_termwidth(const struct libscols_table *tb);
++extern int scols_table_set_termheight(struct libscols_table *tb, size_t height);
++extern size_t scols_table_get_termheight(const struct libscols_table *tb);
++
+ 
+ /* table_print.c */
+ extern int scols_print_table(struct libscols_table *tb);
+ extern int scols_print_table_to_string(struct libscols_table *tb, char **data);
+ 
++extern int scols_table_print_range(	struct libscols_table *tb,
++					struct libscols_line *start,
++					struct libscols_line *end);
++extern int scols_table_print_range_to_string(	struct libscols_table *tb,
++						struct libscols_line *start,
++						struct libscols_line *end,
++						char **data);
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym
+index a4de524f4..331a55554 100644
+--- a/libsmartcols/src/libsmartcols.sym
++++ b/libsmartcols/src/libsmartcols.sym
+@@ -1,5 +1,7 @@
+ /*
+  * symbols since util-linux 2.25
++ *
++ * Copyright (C) 2014-2016 Karel Zak <kzak@redhat.com>
+  */
+ SMARTCOLS_2.25 {
+ global:
+@@ -110,3 +112,73 @@ global:
+ local:
+ 	*;
+ };
++
++SMARTCOLS_2.27 {
++global:
++	scols_column_is_hidden;
++	scols_table_enable_json;
++	scols_table_is_json;
++	scols_table_set_name;
++} SMARTCOLS_2.25;
++
++SMARTCOLS_2.28 {
++global:
++	scols_column_is_wrap;
++	scols_line_refer_column_data;
++	scols_line_set_column_data;
++	scols_symbols_set_title_padding;
++	scols_table_enable_nowrap;
++	scols_table_get_title;
++	scols_cell_get_flags;
++	scols_cell_set_flags;
++	scols_table_print_range;
++	scols_table_print_range_to_string;
++	scols_table_enable_nolinesep;
++} SMARTCOLS_2.27;
++
++SMARTCOLS_2.29 {
++global:
++	scols_column_get_safechars;
++	scols_column_get_table;
++	scols_column_get_width;
++	scols_column_is_customwrap;
++	scols_column_set_safechars;
++	scols_column_set_wrapfunc;
++	scols_symbols_set_cell_padding;
++	scols_table_get_name;
++	scols_table_get_symbols;
++	scols_table_get_termforce;
++	scols_table_get_termwidth;
++	scols_table_is_nolinesep;
++	scols_table_is_nowrap;
++	scols_table_set_default_symbols;
++	scols_table_set_termforce;
++	scols_table_set_termwidth;
++	scols_wrapnl_chunksize;
++	scols_wrapnl_nextchunk;
++} SMARTCOLS_2.28;
++
++
++SMARTCOLS_2.30 {
++global:
++	scols_cell_get_alignment;
++	scols_table_move_column;
++	scols_sort_table_by_tree;
++	scols_line_is_ancestor;
++} SMARTCOLS_2.29;
++
++
++SMARTCOLS_2.31 {
++	scols_table_set_termheight;
++	scols_table_get_termheight;
++	scols_table_is_header_repeat;
++	scols_table_enable_header_repeat;
++	scols_table_enable_noencoding;
++	scols_table_is_noencoding;
++} SMARTCOLS_2.30;
++
++
++SMARTCOLS_2.33 {
++	scols_column_set_json_type;
++	scols_column_get_json_type;
++} SMARTCOLS_2.31;
+diff --git a/libsmartcols/src/line.c b/libsmartcols/src/line.c
+index debfeab78..60be2c135 100644
+--- a/libsmartcols/src/line.c
++++ b/libsmartcols/src/line.c
+@@ -11,7 +11,7 @@
+ /**
+  * SECTION: line
+  * @title: Line
+- * @short_description: line API
++ * @short_description: cells container, also keeps tree (parent->child) information
+  *
+  * An API to access and modify per-line data and information.
+  */
+@@ -71,7 +71,6 @@ void scols_ref_line(struct libscols_line *ln)
+  */
+ void scols_unref_line(struct libscols_line *ln)
+ {
+-
+ 	if (ln && --ln->refcount <= 0) {
+ 		DBG(CELL, ul_debugobj(ln, "dealloc"));
+ 		list_del(&ln->ln_lines);
+@@ -122,8 +121,6 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
+ {
+ 	struct libscols_cell *ce;
+ 
+-	assert(ln);
+-
+ 	if (!ln)
+ 		return -EINVAL;
+ 	if (ln->ncells == n)
+@@ -149,6 +146,35 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
+ 	return 0;
+ }
+ 
++int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn)
++{
++	struct libscols_cell ce;
++
++	if (!ln || newn >= ln->ncells || oldn >= ln->ncells)
++		return -EINVAL;
++	if (oldn == newn)
++		return 0;
++
++	DBG(LINE, ul_debugobj(ln, "move cells[%zu] -> cells[%zu]", oldn, newn));
++
++	/* remember data from old position */
++	memcpy(&ce, &ln->cells[oldn], sizeof(struct libscols_cell));
++
++	/* remove old position (move data behind oldn to oldn) */
++	if (oldn + 1 < ln->ncells)
++		memmove(ln->cells + oldn, ln->cells + oldn + 1,
++			(ln->ncells - oldn - 1) * sizeof(struct libscols_cell));
++
++	/* create a space for new position */
++	if (newn + 1 < ln->ncells)
++		memmove(ln->cells + newn + 1, ln->cells + newn,
++			(ln->ncells - newn - 1) * sizeof(struct libscols_cell));
++
++	/* copy original data to new position */
++	memcpy(&ln->cells[newn], &ce, sizeof(struct libscols_cell));
++	return 0;
++}
++
+ /**
+  * scols_line_set_userdata:
+  * @ln: a pointer to a struct libscols_line instance
+@@ -160,7 +186,6 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
+  */
+ int scols_line_set_userdata(struct libscols_line *ln, void *data)
+ {
+-	assert(ln);
+ 	if (!ln)
+ 		return -EINVAL;
+ 	ln->userdata = data;
+@@ -171,12 +196,11 @@ int scols_line_set_userdata(struct libscols_line *ln, void *data)
+  * scols_line_get_userdata:
+  * @ln: a pointer to a struct libscols_line instance
+  *
+- * Returns: 0, a negative value in case of an error.
++ * Returns: user data
+  */
+ void *scols_line_get_userdata(struct libscols_line *ln)
+ {
+-	assert(ln);
+-	return ln ? ln->userdata : NULL;
++	return ln->userdata;
+ }
+ 
+ /**
+@@ -190,13 +214,10 @@ void *scols_line_get_userdata(struct libscols_line *ln)
+  */
+ int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child)
+ {
+-	assert(ln);
+-	assert(child);
+-
+ 	if (!ln || !child)
+ 		return -EINVAL;
+ 
+-	DBG(LINE, ul_debugobj(ln, "remove child %p", child));
++	DBG(LINE, ul_debugobj(ln, "remove child"));
+ 
+ 	list_del_init(&child->ln_children);
+ 	child->parent = NULL;
+@@ -217,26 +238,22 @@ int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *chil
+  */
+ int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child)
+ {
+-	assert(ln);
+-	assert(child);
+-
+ 	if (!ln || !child)
+ 		return -EINVAL;
+ 
++	DBG(LINE, ul_debugobj(ln, "add child"));
++	scols_ref_line(child);
++	scols_ref_line(ln);
++
+ 	/* unref old<->parent */
+ 	if (child->parent)
+ 		scols_line_remove_child(child->parent, child);
+ 
+-	DBG(LINE, ul_debugobj(ln, "add child %p", child));
+-
+ 	/* new reference from parent to child */
+ 	list_add_tail(&child->ln_children, &ln->ln_branch);
+-	scols_ref_line(child);
+ 
+ 	/* new reference from child to parent */
+ 	child->parent = ln;
+-	scols_ref_line(ln);
+-
+ 	return 0;
+ }
+ 
+@@ -246,9 +263,8 @@ int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child)
+  *
+  * Returns: a pointer to @ln's parent, NULL in case it has no parent or if there was an error.
+  */
+-struct libscols_line *scols_line_get_parent(struct libscols_line *ln)
++struct libscols_line *scols_line_get_parent(const struct libscols_line *ln)
+ {
+-	assert(ln);
+ 	return ln ? ln->parent : NULL;
+ }
+ 
+@@ -260,7 +276,6 @@ struct libscols_line *scols_line_get_parent(struct libscols_line *ln)
+  */
+ int scols_line_has_children(struct libscols_line *ln)
+ {
+-	assert(ln);
+ 	return ln ? !list_empty(&ln->ln_branch) : 0;
+ }
+ 
+@@ -294,29 +309,44 @@ int scols_line_next_child(struct libscols_line *ln,
+ 	return rc;
+ }
+ 
++
++/**
++ * scols_line_is_ancestor:
++ * @ln: line
++ * @parent: potential parent
++ *
++ * The function is designed to detect circular dependencies between @ln and
++ * @parent. It checks if @ln is not any (grand) parent in the @parent's tree.
++ *
++ * Since: 2.30
++ *
++ * Returns: 0 or 1
++ */
++int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent)
++{
++	while (parent) {
++		if (parent == ln)
++			return 1;
++		parent = scols_line_get_parent(parent);
++	};
++	return 0;
++}
++
+ /**
+  * scols_line_set_color:
+  * @ln: a pointer to a struct libscols_line instance
+- * @color: ESC sequence
++ * @color: color name or ESC sequence
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+ int scols_line_set_color(struct libscols_line *ln, const char *color)
+ {
+-	char *p = NULL;
+-
+-	assert(ln);
+-	if (!ln)
+-		return -EINVAL;
+-	if (color) {
+-		p = strdup(color);
+-		if (!p)
+-			return -ENOMEM;
++	if (color && isalnum(*color)) {
++		color = color_sequence_from_colorname(color);
++		if (!color)
++			return -EINVAL;
+ 	}
+-
+-	free(ln->color);
+-	ln->color = p;
+-	return 0;
++	return strdup_to_struct_member(ln, color, color);
+ }
+ 
+ /**
+@@ -325,22 +355,20 @@ int scols_line_set_color(struct libscols_line *ln, const char *color)
+  *
+  * Returns: @ln's color string, NULL in case of an error.
+  */
+-const char *scols_line_get_color(struct libscols_line *ln)
++const char *scols_line_get_color(const struct libscols_line *ln)
+ {
+-	assert(ln);
+-	return ln ? ln->color : NULL;
++	return ln->color;
+ }
+ 
+ /**
+  * scols_line_get_ncells:
+  * @ln: a pointer to a struct libscols_line instance
+  *
+- * Returns: @ln's number of cells
++ * Returns: number of cells
+  */
+-size_t scols_line_get_ncells(struct libscols_line *ln)
++size_t scols_line_get_ncells(const struct libscols_line *ln)
+ {
+-	assert(ln);
+-	return ln ? ln->ncells : 0;
++	return ln->ncells;
+ }
+ 
+ /**
+@@ -353,8 +381,6 @@ size_t scols_line_get_ncells(struct libscols_line *ln)
+ struct libscols_cell *scols_line_get_cell(struct libscols_line *ln,
+ 					  size_t n)
+ {
+-	assert(ln);
+-
+ 	if (!ln || n >= ln->ncells)
+ 		return NULL;
+ 	return &ln->cells[n];
+@@ -373,15 +399,15 @@ struct libscols_cell *scols_line_get_column_cell(
+ 			struct libscols_line *ln,
+ 			struct libscols_column *cl)
+ {
+-	assert(ln);
+-	assert(cl);
++	if (!ln || !cl)
++		return NULL;
+ 
+ 	return scols_line_get_cell(ln, cl->seqnum);
+ }
+ 
+ /**
+  * scols_line_set_data:
+- * @ln: a pointer to a struct libscols_cell instance
++ * @ln: a pointer to a struct libscols_line instance
+  * @n: number of the cell, whose data is to be set
+  * @data: actual data to set
+  *
+@@ -396,9 +422,28 @@ int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data)
+ 	return scols_cell_set_data(ce, data);
+ }
+ 
++/**
++ * scols_line_set_column_data:
++ * @ln: a pointer to a struct libscols_line instance
++ * @cl: column, whose data is to be set
++ * @data: actual data to set
++ *
++ * The same as scols_line_set_data() but cell is referenced by column object.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.28
++ */
++int scols_line_set_column_data(struct libscols_line *ln,
++			       struct libscols_column *cl,
++			       const char *data)
++{
++	return scols_line_set_data(ln, cl->seqnum, data);
++}
++
+ /**
+  * scols_line_refer_data:
+- * @ln: a pointer to a struct libscols_cell instance
++ * @ln: a pointer to a struct libscols_line instance
+  * @n: number of the cell which will refer to @data
+  * @data: actual data to refer to
+  *
+@@ -413,18 +458,36 @@ int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data)
+ 	return scols_cell_refer_data(ce, data);
+ }
+ 
++/**
++ * scols_line_refer_column_data:
++ * @ln: a pointer to a struct libscols_line instance
++ * @cl: column, whose data is to be set
++ * @data: actual data to refer to
++ *
++ * The same as scols_line_refer_data() but cell is referenced by column object.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.28
++ */
++int scols_line_refer_column_data(struct libscols_line *ln,
++			       struct libscols_column *cl,
++			       char *data)
++{
++	return scols_line_refer_data(ln, cl->seqnum, data);
++}
++
+ /**
+  * scols_copy_line:
+- * @ln: a pointer to a struct libscols_cell instance
++ * @ln: a pointer to a struct libscols_line instance
+  *
+  * Returns: A newly allocated copy of @ln, NULL in case of an error.
+  */
+-struct libscols_line *scols_copy_line(struct libscols_line *ln)
++struct libscols_line *scols_copy_line(const struct libscols_line *ln)
+ {
+ 	struct libscols_line *ret;
+ 	size_t i;
+ 
+-	assert (ln);
+ 	if (!ln)
+ 		return NULL;
+ 
+@@ -440,7 +503,7 @@ struct libscols_line *scols_copy_line(struct libscols_line *ln)
+ 	ret->ncells   = ln->ncells;
+ 	ret->seqnum   = ln->seqnum;
+ 
+-	DBG(LINE, ul_debugobj(ln, "copy to %p", ret));
++	DBG(LINE, ul_debugobj(ln, "copy"));
+ 
+ 	for (i = 0; i < ret->ncells; ++i) {
+ 		if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i]))
+@@ -452,5 +515,3 @@ err:
+ 	scols_unref_line(ret);
+ 	return NULL;
+ }
+-
+-
+diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h
+index cea4f3101..398e6f064 100644
+--- a/libsmartcols/src/smartcolsP.h
++++ b/libsmartcols/src/smartcolsP.h
+@@ -13,23 +13,18 @@
+ 
+ #include "c.h"
+ #include "list.h"
++#include "strutils.h"
+ #include "colors.h"
+ #include "debug.h"
+ 
+-#include "libsmartcols.h"
+-
+-/* features */
+-#define CONFIG_LIBSMARTCOLS_ASSERT
++#include <assert.h>
+ 
+-#ifdef CONFIG_LIBSMARTCOLS_ASSERT
+-# include <assert.h>
+-#else
+-# define assert(x)
+-#endif
++#include "libsmartcols.h"
+ 
+ /*
+  * Debug
+  */
++#define SCOLS_DEBUG_HELP	(1 << 0)
+ #define SCOLS_DEBUG_INIT	(1 << 1)
+ #define SCOLS_DEBUG_CELL	(1 << 2)
+ #define SCOLS_DEBUG_LINE	(1 << 3)
+@@ -43,7 +38,7 @@ UL_DEBUG_DECLARE_MASK(libsmartcols);
+ #define ON_DBG(m, x)	__UL_DBG_CALL(libsmartcols, SCOLS_DEBUG_, m, x)
+ #define DBG_FLUSH	__UL_DBG_FLUSH(libsmartcols, SCOLS_DEBUG_)
+ 
+-#define UL_DEBUG_CURRENT_MASK  UL_DEBUG_MASK(libsmartcols)
++#define UL_DEBUG_CURRENT_MASK	UL_DEBUG_MASK(libsmartcols)
+ #include "debugobj.h"
+ 
+ /*
+@@ -63,6 +58,8 @@ struct libscols_symbols {
+ 	char	*branch;
+ 	char	*vert;
+ 	char	*right;
++	char	*title_padding;
++	char	*cell_padding;
+ };
+ 
+ /*
+@@ -72,8 +69,10 @@ struct libscols_cell {
+ 	char	*data;
+ 	char	*color;
+ 	void    *userdata;
++	int	flags;
+ };
+ 
++extern int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn);
+ 
+ /*
+  * Table column
+@@ -86,19 +85,36 @@ struct libscols_column {
+ 	size_t	width_min;	/* minimal width (usually header width) */
+ 	size_t  width_max;	/* maximal width */
+ 	size_t  width_avg;	/* average width, used to detect extreme fields */
++	size_t	width_treeart;	/* size of the tree ascii art */
+ 	double	width_hint;	/* hint (N < 1 is in percent of termwidth) */
+ 
++	int	json_type;	/* SCOLS_JSON_* */
++
+ 	int	flags;
+ 	int	is_extreme;
+ 	char	*color;		/* default column color */
++	char	*safechars;	/* do not encode this bytes */
++
++	char	*pending_data;
++	size_t	pending_data_sz;
++	char	*pending_data_buf;
+ 
+ 	int (*cmpfunc)(struct libscols_cell *,
+ 		       struct libscols_cell *,
+ 		       void *);			/* cells comparison function */
+ 	void *cmpfunc_data;
+ 
++	size_t (*wrap_chunksize)(const struct libscols_column *,
++			const char *, void *);
++	char *(*wrap_nextchunk)(const struct libscols_column *,
++			char *, void *);
++	void *wrapfunc_data;
++
++
+ 	struct libscols_cell	header;
+ 	struct list_head	cl_columns;
++
++	struct libscols_table	*table;
+ };
+ 
+ /*
+@@ -124,7 +140,8 @@ struct libscols_line {
+ enum {
+ 	SCOLS_FMT_HUMAN = 0,		/* default, human readable */
+ 	SCOLS_FMT_RAW,			/* space separated */
+-	SCOLS_FMT_EXPORT		/* COLNAME="data" ... */
++	SCOLS_FMT_EXPORT,		/* COLNAME="data" ... */
++	SCOLS_FMT_JSON			/* http://en.wikipedia.org/wiki/JSON */
+ };
+ 
+ /*
+@@ -132,11 +149,14 @@ enum {
+  */
+ struct libscols_table {
+ 	int	refcount;
++	char	*name;		/* optional table name (for JSON) */
+ 	size_t	ncols;		/* number of columns */
+ 	size_t  ntreecols;	/* number of columns with SCOLS_FL_TREE */
+ 	size_t	nlines;		/* number of lines */
+-	size_t	termwidth;	/* terminal width */
++	size_t	termwidth;	/* terminal width (number of columns) */
++	size_t  termheight;	/* terminal height  (number of lines) */
+ 	size_t  termreduce;	/* extra blank space */
++	int	termforce;	/* SCOLS_TERMFORCE_* */
+ 	FILE	*out;		/* output stream */
+ 
+ 	char	*colsep;	/* column separator */
+@@ -145,15 +165,28 @@ struct libscols_table {
+ 	struct list_head	tb_columns;
+ 	struct list_head	tb_lines;
+ 	struct libscols_symbols	*symbols;
++	struct libscols_cell	title;		/* optional table title (for humans) */
+ 
++	int	indent;		/* indention counter */
++	int	indent_last_sep;/* last printed has been line separator */
+ 	int	format;		/* SCOLS_FMT_* */
+ 
++	size_t	termlines_used;	/* printed line counter */
++	size_t	header_next;	/* where repeat header */
++
+ 	/* flags */
+ 	unsigned int	ascii		:1,	/* don't use unicode */
+ 			colors_wanted	:1,	/* enable colors */
+ 			is_term		:1,	/* isatty() */
+-			maxout		:1,	/* maximalize output */
+-			no_headings	:1;	/* don't print header */
++			padding_debug	:1,	/* output visible padding chars */
++			maxout		:1,	/* maximize output */
++			header_repeat   :1,     /* print header after libscols_table->termheight */
++			header_printed  :1,	/* header already printed */
++			priv_symbols	:1,	/* default private symbols */
++			no_headings	:1,	/* don't print header */
++			no_encode	:1,	/* don't care about control and non-printable chars */
++			no_linesep	:1,	/* don't print line separator */
++			no_wrap		:1;	/* never wrap lines */
+ };
+ 
+ #define IS_ITER_FORWARD(_i)	((_i)->direction == SCOLS_ITER_FORWARD)
+@@ -173,4 +206,13 @@ struct libscols_table {
+ 				(itr)->p->next : (itr)->p->prev; \
+ 	} while(0)
+ 
++
++static inline int scols_iter_is_last(const struct libscols_iter *itr)
++{
++	if (!itr || !itr->head || !itr->p)
++		return 0;
++
++	return itr->p == itr->head;
++}
++
+ #endif /* _LIBSMARTCOLS_PRIVATE_H */
+diff --git a/libsmartcols/src/symbols.c b/libsmartcols/src/symbols.c
+index 2b8f81dc9..6ddf1869b 100644
+--- a/libsmartcols/src/symbols.c
++++ b/libsmartcols/src/symbols.c
+@@ -2,6 +2,7 @@
+  * symbols.c - routines for symbol handling
+  *
+  * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
++ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
+  *
+  * This file may be redistributed under the terms of the
+  * GNU Lesser General Public License.
+@@ -10,7 +11,7 @@
+ /**
+  * SECTION: symbols
+  * @title: Symbols
+- * @short_description: symbols API
++ * @short_description: allows to overwrite default output chars (for ascii art)
+  *
+  * An API to access and modify data and information per symbol/symbol group.
+  */
+@@ -61,115 +62,112 @@ void scols_unref_symbols(struct libscols_symbols *sy)
+ 		free(sy->branch);
+ 		free(sy->vert);
+ 		free(sy->right);
++		free(sy->title_padding);
++		free(sy->cell_padding);
+ 		free(sy);
+ 	}
+ }
+ 
+ /**
+  * scols_symbols_set_branch:
+- * @sb: a pointer to a struct libscols_symbols instance
++ * @sy: a pointer to a struct libscols_symbols instance
+  * @str: a string which will represent the branch part of a tree output
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+-int scols_symbols_set_branch(struct libscols_symbols *sb, const char *str)
++int scols_symbols_set_branch(struct libscols_symbols *sy, const char *str)
+ {
+-	char *p = NULL;
+-
+-	assert(sb);
+-
+-	if (!sb)
+-		return -EINVAL;
+-	if (str) {
+-		p = strdup(str);
+-		if (!p)
+-			return -ENOMEM;
+-	}
+-	free(sb->branch);
+-	sb->branch = p;
+-	return 0;
++	return strdup_to_struct_member(sy, branch, str);
+ }
+ 
+ /**
+  * scols_symbols_set_vertical:
+- * @sb: a pointer to a struct libscols_symbols instance
++ * @sy: a pointer to a struct libscols_symbols instance
+  * @str: a string which will represent the vertical part of a tree output
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+-int scols_symbols_set_vertical(struct libscols_symbols *sb, const char *str)
++int scols_symbols_set_vertical(struct libscols_symbols *sy, const char *str)
+ {
+-	char *p = NULL;
+-
+-	assert(sb);
+-
+-	if (!sb)
+-		return -EINVAL;
+-	if (str) {
+-		p = strdup(str);
+-		if (!p)
+-			return -ENOMEM;
+-	}
+-	free(sb->vert);
+-	sb->vert = p;
+-	return 0;
++	return strdup_to_struct_member(sy, vert, str);
+ }
+ 
+ /**
+  * scols_symbols_set_right:
+- * @sb: a pointer to a struct libscols_symbols instance
++ * @sy: a pointer to a struct libscols_symbols instance
+  * @str: a string which will represent the right part of a tree output
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+-int scols_symbols_set_right(struct libscols_symbols *sb, const char *str)
++int scols_symbols_set_right(struct libscols_symbols *sy, const char *str)
+ {
+-	char *p = NULL;
++	return strdup_to_struct_member(sy, right, str);
++}
+ 
+-	assert(sb);
++/**
++ * scols_symbols_set_title_padding:
++ * @sy: a pointer to a struct libscols_symbols instance
++ * @str: a string which will represent the symbols which fill title output
++ *
++ * The current implementation uses only the first byte from the padding string.
++ * A multibyte chars are not supported yet.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.28
++ */
++int scols_symbols_set_title_padding(struct libscols_symbols *sy, const char *str)
++{
++	return strdup_to_struct_member(sy, title_padding, str);
++}
+ 
+-	if (!sb)
+-		return -EINVAL;
+-	if (str) {
+-		p = strdup(str);
+-		if (!p)
+-			return -ENOMEM;
+-	}
+-	free(sb->right);
+-	sb->right = p;
+-	return 0;
++/**
++ * scols_symbols_set_cell_padding:
++ * @sy: a pointer to a struct libscols_symbols instance
++ * @str: a string which will represent the symbols which fill cells
++ *
++ * The padding char has to take up just one cell on the terminal.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.29
++ */
++int scols_symbols_set_cell_padding(struct libscols_symbols *sy, const char *str)
++{
++	return strdup_to_struct_member(sy, cell_padding, str);
+ }
+ 
+ /**
+  * scols_copy_symbols:
+- * @sb: a pointer to a struct libscols_symbols instance
++ * @sy: a pointer to a struct libscols_symbols instance
+  *
+- * Returns: a newly allocated copy of the @sb symbol group or NULL in caes of an error.
++ * Returns: a newly allocated copy of the @sy symbol group or NULL in case of an error.
+  */
+-struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sb)
++struct libscols_symbols *scols_copy_symbols(const struct libscols_symbols *sy)
+ {
+ 	struct libscols_symbols *ret;
+ 	int rc;
+ 
+-	assert(sb);
+-	if (!sb)
++	assert(sy);
++	if (!sy)
+ 		return NULL;
+ 
+ 	ret = scols_new_symbols();
+ 	if (!ret)
+ 		return NULL;
+ 
+-	rc = scols_symbols_set_branch(ret, sb->branch);
++	rc = scols_symbols_set_branch(ret, sy->branch);
++	if (!rc)
++		rc = scols_symbols_set_vertical(ret, sy->vert);
+ 	if (!rc)
+-		rc = scols_symbols_set_vertical(ret, sb->vert);
++		rc = scols_symbols_set_right(ret, sy->right);
+ 	if (!rc)
+-		rc = scols_symbols_set_right(ret, sb->right);
++		rc = scols_symbols_set_title_padding(ret, sy->title_padding);
++	if (!rc)
++		rc = scols_symbols_set_cell_padding(ret, sy->cell_padding);
+ 	if (!rc)
+ 		return ret;
+ 
+ 	scols_unref_symbols(ret);
+ 	return NULL;
+-
+ }
+-
+-
+diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c
+index 8c404f858..979a09a39 100644
+--- a/libsmartcols/src/table.c
++++ b/libsmartcols/src/table.c
+@@ -3,6 +3,7 @@
+  *
+  * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
+  * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
++ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
+  *
+  * This file may be redistributed under the terms of the
+  * GNU Lesser General Public License.
+@@ -11,7 +12,7 @@
+ /**
+  * SECTION: table
+  * @title: Table
+- * @short_description: table data API
++ * @short_description: container for rows and columns
+  *
+  * Table data manipulation API.
+  */
+@@ -24,7 +25,7 @@
+ #include <ctype.h>
+ 
+ #include "nls.h"
+-#include "widechar.h"
++#include "ttyutils.h"
+ #include "smartcolsP.h"
+ 
+ #ifdef HAVE_WIDECHAR
+@@ -38,6 +39,20 @@
+ 		list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns)
+ 
+ 
++static void check_padding_debug(struct libscols_table *tb)
++{
++	const char *str;
++
++	assert(libsmartcols_debug_mask);	/* debug has to be enabled! */
++
++	str = getenv("LIBSMARTCOLS_DEBUG_PADDING");
++	if (!str || (strcmp(str, "on") != 0 && strcmp(str, "1") != 0))
++		return;
++
++	DBG(INIT, ul_debugobj(tb, "padding debug: ENABLE"));
++	tb->padding_debug = 1;
++}
++
+ /**
+  * scols_new_table:
+  *
+@@ -46,6 +61,7 @@
+ struct libscols_table *scols_new_table(void)
+ {
+ 	struct libscols_table *tb;
++	int c, l;
+ 
+ 	tb = calloc(1, sizeof(struct libscols_table));
+ 	if (!tb)
+@@ -54,10 +70,16 @@ struct libscols_table *scols_new_table(void)
+ 	tb->refcount = 1;
+ 	tb->out = stdout;
+ 
++	get_terminal_dimension(&c, &l);
++	tb->termwidth  = c > 0 ? c : 80;
++	tb->termheight = l > 0 ? l : 24;
++
+ 	INIT_LIST_HEAD(&tb->tb_lines);
+ 	INIT_LIST_HEAD(&tb->tb_columns);
+ 
+ 	DBG(TAB, ul_debugobj(tb, "alloc"));
++	ON_DBG(INIT, check_padding_debug(tb));
++
+ 	return tb;
+ }
+ 
+@@ -87,44 +109,102 @@ void scols_unref_table(struct libscols_table *tb)
+ 		scols_table_remove_lines(tb);
+ 		scols_table_remove_columns(tb);
+ 		scols_unref_symbols(tb->symbols);
++		scols_reset_cell(&tb->title);
+ 		free(tb->linesep);
+ 		free(tb->colsep);
++		free(tb->name);
+ 		free(tb);
+ 	}
+ }
+ 
++/**
++ * scols_table_set_name:
++ * @tb: a pointer to a struct libscols_table instance
++ * @name: a name
++ *
++ * The table name is used for example for JSON top level object name.
++ *
++ * Returns: 0, a negative number in case of an error.
++ *
++ * Since: 2.27
++ */
++int scols_table_set_name(struct libscols_table *tb, const char *name)
++{
++	return strdup_to_struct_member(tb, name, name);
++}
++
++/**
++ * scols_table_get_name:
++ * @tb: a pointer to a struct libscols_table instance
++ *
++ * Returns: The current name setting of the table @tb
++ *
++ * Since: 2.29
++ */
++const char *scols_table_get_name(const struct libscols_table *tb)
++{
++	return tb->name;
++}
++
++/**
++ * scols_table_get_title:
++ * @tb: a pointer to a struct libscols_table instance
++ *
++ * The returned pointer is possible to modify by cell functions. Note that
++ * title output alignment on non-tty is hardcoded to 80 output chars. For the
++ * regular terminal it's based on terminal width.
++ *
++ * Returns: Title of the table, or NULL in case of blank title.
++ *
++ * Since: 2.28
++ */
++struct libscols_cell *scols_table_get_title(struct libscols_table *tb)
++{
++	return &tb->title;
++}
++
+ /**
+  * scols_table_add_column:
+  * @tb: a pointer to a struct libscols_table instance
+  * @cl: a pointer to a struct libscols_column instance
+  *
+- * Adds @cl to @tb's column list.
++ * Adds @cl to @tb's column list. The column cannot be shared between more
++ * tables.
+  *
+  * Returns: 0, a negative number in case of an error.
+  */
+ int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl)
+ {
+-	assert(tb);
+-	assert(cl);
++	struct libscols_iter itr;
++	struct libscols_line *ln;
++	int rc = 0;
+ 
+-	if (!tb || !cl || !list_empty(&tb->tb_lines))
++	if (!tb || !cl || cl->table)
+ 		return -EINVAL;
+ 
+ 	if (cl->flags & SCOLS_FL_TREE)
+ 		tb->ntreecols++;
+ 
+-	DBG(TAB, ul_debugobj(tb, "add column %p", cl));
++	DBG(TAB, ul_debugobj(tb, "add column"));
+ 	list_add_tail(&cl->cl_columns, &tb->tb_columns);
+ 	cl->seqnum = tb->ncols++;
++	cl->table = tb;
+ 	scols_ref_column(cl);
+ 
+-	/* TODO:
+-	 *
+-	 * Currently it's possible to add/remove columns only if the table is
+-	 * empty (see list_empty(tb->tb_lines) above). It would be nice to
+-	 * enlarge/reduce lines cells[] always when we add/remove a new column.
++	if (list_empty(&tb->tb_lines))
++		return 0;
++
++	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++
++	/* Realloc line cell arrays
+ 	 */
+-	return 0;
++	while (scols_table_next_line(tb, &itr, &ln) == 0) {
++		rc = scols_line_alloc_cells(ln, tb->ncols);
++		if (rc)
++			break;
++	}
++
++	return rc;
+ }
+ 
+ /**
+@@ -139,18 +219,16 @@ int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl
+ int scols_table_remove_column(struct libscols_table *tb,
+ 			      struct libscols_column *cl)
+ {
+-	assert(tb);
+-	assert(cl);
+-
+ 	if (!tb || !cl || !list_empty(&tb->tb_lines))
+ 		return -EINVAL;
+ 
+ 	if (cl->flags & SCOLS_FL_TREE)
+ 		tb->ntreecols--;
+ 
+-	DBG(TAB, ul_debugobj(tb, "remove column %p", cl));
++	DBG(TAB, ul_debugobj(tb, "remove column"));
+ 	list_del_init(&cl->cl_columns);
+ 	tb->ncols--;
++	cl->table = NULL;
+ 	scols_unref_column(cl);
+ 	return 0;
+ }
+@@ -165,8 +243,6 @@ int scols_table_remove_column(struct libscols_table *tb,
+  */
+ int scols_table_remove_columns(struct libscols_table *tb)
+ {
+-	assert(tb);
+-
+ 	if (!tb || !list_empty(&tb->tb_lines))
+ 		return -EINVAL;
+ 
+@@ -179,12 +255,64 @@ int scols_table_remove_columns(struct libscols_table *tb)
+ 	return 0;
+ }
+ 
++/**
++ * scols_table_move_column:
++ * @tb: table
++ * @pre: column before the column
++ * @cl: column to move
++ *
++ * Move the @cl behind @pre. If the @pre is NULL then the @col is the first
++ * column in the table.
++ *
++ * Since: 2.30
++ *
++ * Returns: 0, a negative number in case of an error.
++ */
++int scols_table_move_column(struct libscols_table *tb,
++			    struct libscols_column *pre,
++			    struct libscols_column *cl)
++{
++	struct list_head *head;
++	struct libscols_iter itr;
++	struct libscols_column *p;
++	struct libscols_line *ln;
++	size_t n = 0, oldseq;
++
++	if (!tb || !cl)
++		return -EINVAL;
++
++	if (pre && pre->seqnum + 1 == cl->seqnum)
++		return 0;
++	if (pre == NULL && cl->seqnum == 0)
++		return 0;
++
++	DBG(TAB, ul_debugobj(tb, "move column %zu behind %zu",
++				cl->seqnum, pre? pre->seqnum : 0));
++
++	list_del_init(&cl->cl_columns);		/* remove from old position */
++
++	head = pre ? &pre->cl_columns : &tb->tb_columns;
++	list_add(&cl->cl_columns, head);	/* add to the new place */
++
++	oldseq = cl->seqnum;
++
++	/* fix seq. numbers */
++	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++	while (scols_table_next_column(tb, &itr, &p) == 0)
++		p->seqnum = n++;
++
++	/* move data in lines */
++	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++	while (scols_table_next_line(tb, &itr, &ln) == 0)
++		scols_line_move_cells(ln, cl->seqnum, oldseq);
++	return 0;
++}
+ 
+ /**
+  * scols_table_new_column:
+  * @tb: table
+  * @name: column header
+- * @whint: column width hint (absolute width: N > 1; relative width: N < 1)
++ * @whint: column width hint (absolute width: N > 1; relative width: 0 < N < 1)
+  * @flags: flags integer
+  *
+  * This is shortcut for
+@@ -193,17 +321,36 @@ int scols_table_remove_columns(struct libscols_table *tb)
+  *   scols_column_set_....(cl, ...);
+  *   scols_table_add_column(tb, cl);
+  *
+- * The column width is possible to define by three ways:
++ * The column width is possible to define by:
++ *
++ *  @whint: 0 < N < 1  : relative width, percent of terminal width
++ *
++ *  @whint: N >= 1     : absolute width, empty column will be truncated to
++ *                     the column header width if no specified STRICTWIDTH flag
+  *
+- *  @whint = 0..1    : relative width, percent of terminal width
++ * Note that if table has disabled "maxout" flag (disabled by default) than
++ * relative width is used as a hint only. It's possible that column will be
++ * narrow if the specified size is too large for column data.
+  *
+- *  @whint = 1..N    : absolute width, empty colum will be truncated to
+- *                     the column header width
+  *
+- *  @whint = 1..N
++ * If the width of all columns is greater than terminal width then library
++ * tries to reduce width of the individual columns. It's done in three stages:
+  *
+- * The column is necessary to address by
+- * sequential number. The first defined column has the colnum = 0. For example:
++ * #1 reduce columns with SCOLS_FL_TRUNC flag and with relative width if the
++ *    width is greater than width defined by @whint (@whint * terminal_width)
++ *
++ * #2 reduce all columns with SCOLS_FL_TRUNC flag
++ *
++ * #3 reduce all columns with relative width
++ *
++ * The next stage is always used if the previous stage is unsuccessful. Note
++ * that SCOLS_FL_WRAP is interpreted as SCOLS_FL_TRUNC when calculate column
++ * width (if custom wrap function is not specified), but the final text is not
++ * truncated, but wrapped to multi-line cell.
++ *
++ *
++ * The column is necessary to address by sequential number. The first defined
++ * column has the colnum = 0. For example:
+  *
+  *	scols_table_new_column(tab, "FOO", 0.5, 0);		// colnum = 0
+  *	scols_table_new_column(tab, "BAR", 0.5, 0);		// colnum = 1
+@@ -222,7 +369,6 @@ struct libscols_column *scols_table_new_column(struct libscols_table *tb,
+ 	struct libscols_column *cl;
+ 	struct libscols_cell *hr;
+ 
+-	assert (tb);
+ 	if (!tb)
+ 		return NULL;
+ 
+@@ -282,29 +428,26 @@ int scols_table_next_column(struct libscols_table *tb,
+ 	return rc;
+ }
+ 
+-
+ /**
+  * scols_table_get_ncols:
+  * @tb: table
+  *
+- * Returns: the ncols table member, a negative number in case of an error.
++ * Returns: the ncols table member.
+  */
+-int scols_table_get_ncols(struct libscols_table *tb)
++size_t scols_table_get_ncols(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb ? tb->ncols : -EINVAL;
++	return tb->ncols;
+ }
+ 
+ /**
+  * scols_table_get_nlines:
+  * @tb: table
+  *
+- * Returns: the nlines table member, a negative number in case of an error.
++ * Returns: the nlines table member.
+  */
+-int scols_table_get_nlines(struct libscols_table *tb)
++size_t scols_table_get_nlines(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb ? tb->nlines : -EINVAL;
++	return tb->nlines;
+ }
+ 
+ /**
+@@ -335,10 +478,9 @@ int scols_table_set_stream(struct libscols_table *tb, FILE *stream)
+  *
+  * Returns: stream pointer, NULL in case of an error or an unset stream.
+  */
+-FILE *scols_table_get_stream(struct libscols_table *tb)
++FILE *scols_table_get_stream(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb ? tb->out: NULL;
++	return tb->out;
+ }
+ 
+ /**
+@@ -346,13 +488,20 @@ FILE *scols_table_get_stream(struct libscols_table *tb)
+  * @tb: table
+  * @reduce: width
+  *
+- * Reduce the output width to @reduce.
++ * If necessary then libsmartcols use all terminal width, the @reduce setting
++ * provides extra space (for example for borders in ncurses applications).
++ *
++ * The @reduce must be smaller than terminal width, otherwise it's silently
++ * ignored. The reduction is not applied when STDOUT_FILENO is not terminal.
++ *
++ * Note that after output initialization (scols_table_print_* calls) the width
++ * will be reduced, this behavior affects subsequenced scols_table_get_termwidth()
++ * calls.
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+ int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce)
+ {
+-	assert(tb);
+ 	if (!tb)
+ 		return -EINVAL;
+ 
+@@ -374,7 +523,6 @@ struct libscols_column *scols_table_get_column(struct libscols_table *tb,
+ 	struct libscols_iter itr;
+ 	struct libscols_column *cl;
+ 
+-	assert(tb);
+ 	if (!tb)
+ 		return NULL;
+ 	if (n >= tb->ncols)
+@@ -400,11 +548,7 @@ struct libscols_column *scols_table_get_column(struct libscols_table *tb,
+  */
+ int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln)
+ {
+-
+-	assert(tb);
+-	assert(ln);
+-
+-	if (!tb || !ln)
++	if (!tb || !ln || tb->ncols == 0)
+ 		return -EINVAL;
+ 
+ 	if (tb->ncols > ln->ncells) {
+@@ -413,7 +557,7 @@ int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln)
+ 			return rc;
+ 	}
+ 
+-	DBG(TAB, ul_debugobj(tb, "add line %p", ln));
++	DBG(TAB, ul_debugobj(tb, "add line"));
+ 	list_add_tail(&ln->ln_lines, &tb->tb_lines);
+ 	ln->seqnum = tb->nlines++;
+ 	scols_ref_line(ln);
+@@ -433,13 +577,10 @@ int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln)
+ int scols_table_remove_line(struct libscols_table *tb,
+ 			    struct libscols_line *ln)
+ {
+-	assert(tb);
+-	assert(ln);
+-
+ 	if (!tb || !ln)
+ 		return -EINVAL;
+ 
+-	DBG(TAB, ul_debugobj(tb, "remove line %p", ln));
++	DBG(TAB, ul_debugobj(tb, "remove line"));
+ 	list_del_init(&ln->ln_lines);
+ 	tb->nlines--;
+ 	scols_unref_line(ln);
+@@ -454,7 +595,6 @@ int scols_table_remove_line(struct libscols_table *tb,
+  */
+ void scols_table_remove_lines(struct libscols_table *tb)
+ {
+-	assert(tb);
+ 	if (!tb)
+ 		return;
+ 
+@@ -517,9 +657,6 @@ struct libscols_line *scols_table_new_line(struct libscols_table *tb,
+ {
+ 	struct libscols_line *ln;
+ 
+-	assert(tb);
+-	assert(tb->ncols);
+-
+ 	if (!tb || !tb->ncols)
+ 		return NULL;
+ 
+@@ -544,13 +681,7 @@ err:
+  * @tb: table
+  * @n: column number (0..N)
+  *
+- * This is a shortcut for
+- *
+- *   ln = scols_new_line();
+- *   scols_line_set_....(cl, ...);
+- *   scols_table_add_line(tb, ln);
+- *
+- * Returns: a newly allocate line
++ * Returns: a line or NULL
+  */
+ struct libscols_line *scols_table_get_line(struct libscols_table *tb,
+ 					   size_t n)
+@@ -558,7 +689,6 @@ struct libscols_line *scols_table_get_line(struct libscols_table *tb,
+ 	struct libscols_iter itr;
+ 	struct libscols_line *ln;
+ 
+-	assert(tb);
+ 	if (!tb)
+ 		return NULL;
+ 	if (n >= tb->nlines)
+@@ -588,14 +718,13 @@ struct libscols_table *scols_copy_table(struct libscols_table *tb)
+ 	struct libscols_column *cl;
+ 	struct libscols_iter itr;
+ 
+-	assert(tb);
+ 	if (!tb)
+ 		return NULL;
+ 	ret = scols_new_table();
+ 	if (!ret)
+ 		return NULL;
+ 
+-	DBG(TAB, ul_debugobj(tb, "copy into %p", ret));
++	DBG(TAB, ul_debugobj(tb, "copy"));
+ 
+ 	if (tb->symbols)
+ 		scols_table_set_symbols(ret, tb->symbols);
+@@ -639,54 +768,141 @@ err:
+ 	return NULL;
+ }
+ 
++/**
++ * scols_table_set_default_symbols:
++ * @tb: table
++ *
++ * The library check the current environment to select ASCII or UTF8 symbols.
++ * This default behavior could be controlled by scols_table_enable_ascii().
++ *
++ * Use scols_table_set_symbols() to unset symbols or use your own setting.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.29
++ */
++int scols_table_set_default_symbols(struct libscols_table *tb)
++{
++	struct libscols_symbols *sy;
++	int rc;
++
++	if (!tb)
++		return -EINVAL;
++
++	DBG(TAB, ul_debugobj(tb, "setting default symbols"));
++
++	sy = scols_new_symbols();
++	if (!sy)
++		return -ENOMEM;
++
++#if defined(HAVE_WIDECHAR)
++	if (!scols_table_is_ascii(tb) &&
++	    !strcmp(nl_langinfo(CODESET), "UTF-8")) {
++		scols_symbols_set_branch(sy, UTF_VR UTF_H);
++		scols_symbols_set_vertical(sy, UTF_V " ");
++		scols_symbols_set_right(sy, UTF_UR UTF_H);
++	} else
++#endif
++	{
++		scols_symbols_set_branch(sy, "|-");
++		scols_symbols_set_vertical(sy, "| ");
++		scols_symbols_set_right(sy, "`-");
++	}
++	scols_symbols_set_title_padding(sy, " ");
++	scols_symbols_set_cell_padding(sy, " ");
++
++	rc = scols_table_set_symbols(tb, sy);
++	scols_unref_symbols(sy);
++	return rc;
++}
++
++
+ /**
+  * scols_table_set_symbols:
+  * @tb: table
+  * @sy: symbols or NULL
+  *
+  * Add a reference to @sy from the table. The symbols are used by library to
+- * draw tree output. If no symbols are specified then library checks the
+- * current environment to select ASCII or UTF8 symbols. This default behavior
+- * could be controlled by scols_table_enable_ascii().
++ * draw tree output. If no symbols are used for the table then library creates
++ * default temporary symbols to draw output by scols_table_set_default_symbols().
++ *
++ * If @sy is NULL then remove reference from the currently used symbols.
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+ int scols_table_set_symbols(struct libscols_table *tb,
+ 			    struct libscols_symbols *sy)
+ {
+-	assert(tb);
+-
+ 	if (!tb)
+ 		return -EINVAL;
+ 
+-	DBG(TAB, ul_debugobj(tb, "setting alternative symbols %p", sy));
+-
+-	if (tb->symbols)				/* unref old */
++	/* remove old */
++	if (tb->symbols) {
++		DBG(TAB, ul_debugobj(tb, "remove symbols reference"));
+ 		scols_unref_symbols(tb->symbols);
++		tb->symbols = NULL;
++	}
++
++	/* set new */
+ 	if (sy) {					/* ref user defined */
++		DBG(TAB, ul_debugobj(tb, "set symbols"));
+ 		tb->symbols = sy;
+ 		scols_ref_symbols(sy);
+-	} else {					/* default symbols */
+-		tb->symbols = scols_new_symbols();
+-		if (!tb->symbols)
+-			return -ENOMEM;
+-#if defined(HAVE_WIDECHAR)
+-		if (!scols_table_is_ascii(tb) &&
+-		    !strcmp(nl_langinfo(CODESET), "UTF-8")) {
+-			scols_symbols_set_branch(tb->symbols, UTF_VR UTF_H);
+-			scols_symbols_set_vertical(tb->symbols, UTF_V " ");
+-			scols_symbols_set_right(tb->symbols, UTF_UR UTF_H);
+-		} else
+-#endif
+-		{
+-			scols_symbols_set_branch(tb->symbols, "|-");
+-			scols_symbols_set_vertical(tb->symbols, "| ");
+-			scols_symbols_set_right(tb->symbols, "`-");
+-		}
+ 	}
++	return 0;
++}
++
++/**
++ * scols_table_get_symbols:
++ * @tb: table
++ *
++ * Returns: pointer to symbols table.
++ *
++ * Since: 2.29
++ */
++struct libscols_symbols *scols_table_get_symbols(const struct libscols_table *tb)
++{
++	return tb->symbols;
++}
++
++/**
++ * scols_table_enable_nolinesep:
++ * @tb: table
++ * @enable: 1 or 0
++ *
++ * Enable/disable line separator printing. This is useful if you want to
++ * re-printing the same line more than once (e.g. progress bar). Don't use it
++ * if you're not sure.
++ *
++ * Note that for the last line in the table the separator is disabled at all.
++ * The library differentiate between table terminator and line terminator
++ * (although for standard output \n byte is used in both cases).
++ *
++ * Returns: 0 on success, negative number in case of an error.
++ */
++int scols_table_enable_nolinesep(struct libscols_table *tb, int enable)
++{
++	if (!tb)
++		return -EINVAL;
+ 
++	DBG(TAB, ul_debugobj(tb, "nolinesep: %s", enable ? "ENABLE" : "DISABLE"));
++	tb->no_linesep = enable ? 1 : 0;
+ 	return 0;
+ }
++
++/**
++ * scols_table_is_nolinesep:
++ * @tb: a pointer to a struct libscols_table instance
++ *
++ * Returns: 1 if line separator printing is disabled.
++ *
++ * Since: 2.29
++ */
++int scols_table_is_nolinesep(const struct libscols_table *tb)
++{
++	return tb->no_linesep;
++}
++
+ /**
+  * scols_table_enable_colors:
+  * @tb: table
+@@ -698,7 +914,6 @@ int scols_table_set_symbols(struct libscols_table *tb,
+  */
+ int scols_table_enable_colors(struct libscols_table *tb, int enable)
+ {
+-	assert(tb);
+ 	if (!tb)
+ 		return -EINVAL;
+ 
+@@ -706,19 +921,19 @@ int scols_table_enable_colors(struct libscols_table *tb, int enable)
+ 	tb->colors_wanted = enable;
+ 	return 0;
+ }
++
+ /**
+  * scols_table_enable_raw:
+  * @tb: table
+  * @enable: 1 or 0
+  *
+  * Enable/disable raw output format. The parsable output formats
+- * (export and raw) are mutually exclusive.
++ * (export, raw, JSON, ...) are mutually exclusive.
+  *
+  * Returns: 0 on success, negative number in case of an error.
+  */
+ int scols_table_enable_raw(struct libscols_table *tb, int enable)
+ {
+-	assert(tb);
+ 	if (!tb)
+ 		return -EINVAL;
+ 
+@@ -730,6 +945,31 @@ int scols_table_enable_raw(struct libscols_table *tb, int enable)
+ 	return 0;
+ }
+ 
++/**
++ * scols_table_enable_json:
++ * @tb: table
++ * @enable: 1 or 0
++ *
++ * Enable/disable JSON output format. The parsable output formats
++ * (export, raw, JSON, ...) are mutually exclusive.
++ *
++ * Returns: 0 on success, negative number in case of an error.
++ *
++ * Since: 2.27
++ */
++int scols_table_enable_json(struct libscols_table *tb, int enable)
++{
++	if (!tb)
++		return -EINVAL;
++
++	DBG(TAB, ul_debugobj(tb, "json: %s", enable ? "ENABLE" : "DISABLE"));
++	if (enable)
++		tb->format = SCOLS_FMT_JSON;
++	else if (tb->format == SCOLS_FMT_JSON)
++		tb->format = 0;
++	return 0;
++}
++
+ /**
+  * scols_table_enable_export:
+  * @tb: table
+@@ -742,7 +982,6 @@ int scols_table_enable_raw(struct libscols_table *tb, int enable)
+  */
+ int scols_table_enable_export(struct libscols_table *tb, int enable)
+ {
+-	assert(tb);
+ 	if (!tb)
+ 		return -EINVAL;
+ 
+@@ -771,7 +1010,6 @@ int scols_table_enable_export(struct libscols_table *tb, int enable)
+  */
+ int scols_table_enable_ascii(struct libscols_table *tb, int enable)
+ {
+-	assert(tb);
+ 	if (!tb)
+ 		return -EINVAL;
+ 
+@@ -791,7 +1029,6 @@ int scols_table_enable_ascii(struct libscols_table *tb, int enable)
+  */
+ int scols_table_enable_noheadings(struct libscols_table *tb, int enable)
+ {
+-	assert(tb);
+ 	if (!tb)
+ 		return -EINVAL;
+ 	DBG(TAB, ul_debugobj(tb, "noheading: %s", enable ? "ENABLE" : "DISABLE"));
+@@ -799,6 +1036,28 @@ int scols_table_enable_noheadings(struct libscols_table *tb, int enable)
+ 	return 0;
+ }
+ 
++/**
++ * scols_table_enable_header_repeat:
++ * @tb: table
++ * @enable: 1 or 0
++ *
++ * Enable/disable header line repeat. The header line is printed only once by
++ * default.  Note that the flag will be silently ignored and disabled if the
++ * output is not on terminal or output format is JSON, raw, etc.
++ *
++ * Returns: 0 on success, negative number in case of an error.
++ *
++ * Since: 2.31
++ */
++int scols_table_enable_header_repeat(struct libscols_table *tb, int enable)
++{
++	if (!tb)
++		return -EINVAL;
++	DBG(TAB, ul_debugobj(tb, "header-repeat: %s", enable ? "ENABLE" : "DISABLE"));
++	tb->header_repeat = enable ? 1 : 0;
++	return 0;
++}
++
+ /**
+  * scols_table_enable_maxout:
+  * @tb: table
+@@ -811,7 +1070,6 @@ int scols_table_enable_noheadings(struct libscols_table *tb, int enable)
+  */
+ int scols_table_enable_maxout(struct libscols_table *tb, int enable)
+ {
+-	assert(tb);
+ 	if (!tb)
+ 		return -EINVAL;
+ 	DBG(TAB, ul_debugobj(tb, "maxout: %s", enable ? "ENABLE" : "DISABLE"));
+@@ -819,28 +1077,92 @@ int scols_table_enable_maxout(struct libscols_table *tb, int enable)
+ 	return 0;
+ }
+ 
++/**
++ * scols_table_enable_nowrap:
++ * @tb: table
++ * @enable: 1 or 0
++ *
++ * Never continue on next line, remove last column(s) when too large, truncate last column.
++ *
++ * Returns: 0 on success, negative number in case of an error.
++ *
++ * Since: 2.28
++ */
++int scols_table_enable_nowrap(struct libscols_table *tb, int enable)
++{
++	if (!tb)
++		return -EINVAL;
++	DBG(TAB, ul_debugobj(tb, "nowrap: %s", enable ? "ENABLE" : "DISABLE"));
++	tb->no_wrap = enable ? 1 : 0;
++	return 0;
++}
++
++/**
++ * scols_table_is_nowrap:
++ * @tb: a pointer to a struct libscols_table instance
++ *
++ * Returns: 1 if nowrap is enabled.
++ *
++ * Since: 2.29
++ */
++int scols_table_is_nowrap(const struct libscols_table *tb)
++{
++	return tb->no_wrap;
++}
++
++/**
++ * scols_table_enable_noencoding:
++ * @tb: table
++ * @enable: 1 or 0
++ *
++ * The library encode non-printable and control chars by \xHEX by default.
++ *
++ * Returns: 0 on success, negative number in case of an error.
++ *
++ * Since: 2.31
++ */
++int scols_table_enable_noencoding(struct libscols_table *tb, int enable)
++{
++	if (!tb)
++		return -EINVAL;
++	DBG(TAB, ul_debugobj(tb, "encoding: %s", enable ? "ENABLE" : "DISABLE"));
++	tb->no_encode = enable ? 1 : 0;
++	return 0;
++}
++
++/**
++ * scols_table_is_noencoding:
++ * @tb: a pointer to a struct libscols_table instance
++ *
++ * Returns: 1 if encoding is disabled.
++ *
++ * Since: 2.31
++ */
++int scols_table_is_noencoding(const struct libscols_table *tb)
++{
++	return tb->no_encode;
++}
++
+ /**
+  * scols_table_colors_wanted:
+  * @tb: table
+  *
+  * Returns: 1 if colors are enabled.
+  */
+-int scols_table_colors_wanted(struct libscols_table *tb)
++int scols_table_colors_wanted(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb && tb->colors_wanted;
++	return tb->colors_wanted;
+ }
+ 
+ /**
+  * scols_table_is_empty:
+  * @tb: table
+  *
+- * Returns: 1  if the table is empty.
++ * Returns: 1 if the table is empty.
+  */
+-int scols_table_is_empty(struct libscols_table *tb)
++int scols_table_is_empty(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return !tb || !tb->nlines;
++	return !tb->nlines;
+ }
+ 
+ /**
+@@ -849,10 +1171,9 @@ int scols_table_is_empty(struct libscols_table *tb)
+  *
+  * Returns: 1 if ASCII tree is enabled.
+  */
+-int scols_table_is_ascii(struct libscols_table *tb)
++int scols_table_is_ascii(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb && tb->ascii;
++	return tb->ascii;
+ }
+ 
+ /**
+@@ -861,10 +1182,22 @@ int scols_table_is_ascii(struct libscols_table *tb)
+  *
+  * Returns: 1 if header output is disabled.
+  */
+-int scols_table_is_noheadings(struct libscols_table *tb)
++int scols_table_is_noheadings(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb && tb->no_headings;
++	return tb->no_headings;
++}
++
++/**
++ * scols_table_is_header_repeat
++ * @tb: table
++ *
++ * Returns: 1 if header repeat is enabled.
++ *
++ * Since: 2.31
++ */
++int scols_table_is_header_repeat(const struct libscols_table *tb)
++{
++	return tb->header_repeat;
+ }
+ 
+ /**
+@@ -873,10 +1206,9 @@ int scols_table_is_noheadings(struct libscols_table *tb)
+  *
+  * Returns: 1 if export output format is enabled.
+  */
+-int scols_table_is_export(struct libscols_table *tb)
++int scols_table_is_export(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb && tb->format == SCOLS_FMT_EXPORT;
++	return tb->format == SCOLS_FMT_EXPORT;
+ }
+ 
+ /**
+@@ -885,23 +1217,33 @@ int scols_table_is_export(struct libscols_table *tb)
+  *
+  * Returns: 1 if raw output format is enabled.
+  */
+-int scols_table_is_raw(struct libscols_table *tb)
++int scols_table_is_raw(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb && tb->format == SCOLS_FMT_RAW;
++	return tb->format == SCOLS_FMT_RAW;
+ }
+ 
++/**
++ * scols_table_is_json:
++ * @tb: table
++ *
++ * Returns: 1 if JSON output format is enabled.
++ *
++ * Since: 2.27
++ */
++int scols_table_is_json(const struct libscols_table *tb)
++{
++	return tb->format == SCOLS_FMT_JSON;
++}
+ 
+ /**
+  * scols_table_is_maxout
+  * @tb: table
+  *
+- * Returns: 1 if output maximization is enabled, negative value in case of an error.
++ * Returns: 1 if output maximization is enabled or 0
+  */
+-int scols_table_is_maxout(struct libscols_table *tb)
++int scols_table_is_maxout(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb && tb->maxout;
++	return tb->maxout;
+ }
+ 
+ /**
+@@ -910,10 +1252,9 @@ int scols_table_is_maxout(struct libscols_table *tb)
+  *
+  * Returns: returns 1 tree-like output is expected.
+  */
+-int scols_table_is_tree(struct libscols_table *tb)
++int scols_table_is_tree(const struct libscols_table *tb)
+ {
+-	assert(tb);
+-	return tb && tb->ntreecols > 0;
++	return tb->ntreecols > 0;
+ }
+ 
+ /**
+@@ -922,29 +1263,12 @@ int scols_table_is_tree(struct libscols_table *tb)
+  * @sep: separator
+  *
+  * Sets the column separator of @tb to @sep.
+- * Please note that @sep should always take up a single cell in the output.
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+ int scols_table_set_column_separator(struct libscols_table *tb, const char *sep)
+ {
+-	char *p = NULL;
+-
+-	assert (tb);
+-
+-	if (!tb)
+-		return -EINVAL;
+-
+-	if (sep) {
+-		p = strdup(sep);
+-		if (!p)
+-			return -ENOMEM;
+-	}
+-
+-	DBG(TAB, ul_debugobj(tb, "new columns separator: %s", sep));
+-	free(tb->colsep);
+-	tb->colsep = p;
+-	return 0;
++	return strdup_to_struct_member(tb, colsep, sep);
+ }
+ 
+ /**
+@@ -958,23 +1282,7 @@ int scols_table_set_column_separator(struct libscols_table *tb, const char *sep)
+  */
+ int scols_table_set_line_separator(struct libscols_table *tb, const char *sep)
+ {
+-	char *p = NULL;
+-
+-	assert (tb);
+-
+-	if (!tb)
+-		return -EINVAL;
+-
+-	if (sep) {
+-		p = strdup(sep);
+-		if (!p)
+-			return -ENOMEM;
+-	}
+-
+-	DBG(TAB, ul_debugobj(tb, "new lines separator: %s", sep));
+-	free(tb->linesep);
+-	tb->linesep = p;
+-	return 0;
++	return strdup_to_struct_member(tb, linesep, sep);
+ }
+ 
+ /**
+@@ -983,12 +1291,8 @@ int scols_table_set_line_separator(struct libscols_table *tb, const char *sep)
+  *
+  * Returns: @tb column separator, NULL in case of an error
+  */
+-char *scols_table_get_column_separator(struct libscols_table *tb)
++const char *scols_table_get_column_separator(const struct libscols_table *tb)
+ {
+-	assert (tb);
+-
+-	if (!tb)
+-		return NULL;
+ 	return tb->colsep;
+ }
+ 
+@@ -998,17 +1302,12 @@ char *scols_table_get_column_separator(struct libscols_table *tb)
+  *
+  * Returns: @tb line separator, NULL in case of an error
+  */
+-char *scols_table_get_line_separator(struct libscols_table *tb)
++const char *scols_table_get_line_separator(const struct libscols_table *tb)
+ {
+-	assert (tb);
+-
+-	if (!tb)
+-		return NULL;
+ 	return tb->linesep;
+-
+ }
+-
+-static int cells_cmp_wrapper(struct list_head *a, struct list_head *b, void *data)
++/* for lines in the struct libscols_line->ln_lines list */
++static int cells_cmp_wrapper_lines(struct list_head *a, struct list_head *b, void *data)
+ {
+ 	struct libscols_column *cl = (struct libscols_column *) data;
+ 	struct libscols_line *ra, *rb;
+@@ -1026,24 +1325,218 @@ static int cells_cmp_wrapper(struct list_head *a, struct list_head *b, void *dat
+ 	return cl->cmpfunc(ca, cb, cl->cmpfunc_data);
+ }
+ 
++/* for lines in the struct libscols_line->ln_children list */
++static int cells_cmp_wrapper_children(struct list_head *a, struct list_head *b, void *data)
++{
++	struct libscols_column *cl = (struct libscols_column *) data;
++	struct libscols_line *ra, *rb;
++	struct libscols_cell *ca, *cb;
++
++	assert(a);
++	assert(b);
++	assert(cl);
++
++	ra = list_entry(a, struct libscols_line, ln_children);
++	rb = list_entry(b, struct libscols_line, ln_children);
++	ca = scols_line_get_cell(ra, cl->seqnum);
++	cb = scols_line_get_cell(rb, cl->seqnum);
++
++	return cl->cmpfunc(ca, cb, cl->cmpfunc_data);
++}
++
++
++static int sort_line_children(struct libscols_line *ln, struct libscols_column *cl)
++{
++	struct list_head *p;
++
++	if (list_empty(&ln->ln_branch))
++		return 0;
++
++	list_for_each(p, &ln->ln_branch) {
++		struct libscols_line *chld =
++				list_entry(p, struct libscols_line, ln_children);
++		sort_line_children(chld, cl);
++	}
++
++	list_sort(&ln->ln_branch, cells_cmp_wrapper_children, cl);
++	return 0;
++}
++
+ /**
+  * scols_sort_table:
+  * @tb: table
+  * @cl: order by this column
+  *
+- * Orders the table by the column. See also scols_column_set_cmpfunc().
++ * Orders the table by the column. See also scols_column_set_cmpfunc(). If the
++ * tree output is enabled then children in the tree are recursively sorted too.
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
+ int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl)
+ {
+-	assert(tb);
+-	assert(cl);
+-
+-	if (!tb || !cl)
++	if (!tb || !cl || !cl->cmpfunc)
+ 		return -EINVAL;
+ 
+ 	DBG(TAB, ul_debugobj(tb, "sorting table"));
+-	list_sort(&tb->tb_lines, cells_cmp_wrapper, cl);
++	list_sort(&tb->tb_lines, cells_cmp_wrapper_lines, cl);
++
++	if (scols_table_is_tree(tb)) {
++		struct libscols_line *ln;
++		struct libscols_iter itr;
++
++		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++		while (scols_table_next_line(tb, &itr, &ln) == 0)
++			sort_line_children(ln, cl);
++	}
++
++	return 0;
++}
++
++static struct libscols_line *move_line_and_children(struct libscols_line *ln, struct libscols_line *pre)
++{
++	if (pre) {
++		list_del_init(&ln->ln_lines);			/* remove from old position */
++	        list_add(&ln->ln_lines, &pre->ln_lines);        /* add to the new place (behind @pre) */
++	}
++	pre = ln;
++
++	if (!list_empty(&ln->ln_branch)) {
++		struct list_head *p;
++
++		list_for_each(p, &ln->ln_branch) {
++			struct libscols_line *chld =
++					list_entry(p, struct libscols_line, ln_children);
++			pre = move_line_and_children(chld, pre);
++		}
++	}
++
++	return pre;
++}
++
++/**
++ * scols_sort_table_by_tree:
++ * @tb: table
++ *
++ * Reorders lines in the table by parent->child relation. Note that order of
++ * the lines in the table is independent on the tree hierarchy.
++ *
++ * Since: 2.30
++ *
++ * Returns: 0, a negative value in case of an error.
++ */
++int scols_sort_table_by_tree(struct libscols_table *tb)
++{
++	struct libscols_line *ln;
++	struct libscols_iter itr;
++
++	if (!tb)
++		return -EINVAL;
++
++	DBG(TAB, ul_debugobj(tb, "sorting table by tree"));
++
++	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++	while (scols_table_next_line(tb, &itr, &ln) == 0) {
++		if (ln->parent)
++			continue;
++
++		move_line_and_children(ln, NULL);
++	}
++
++	return 0;
++}
++
++
++/**
++ * scols_table_set_termforce:
++ * @tb: table
++ * @force: SCOLS_TERMFORCE_{NEVER,ALWAYS,AUTO}
++ *
++ * Forces library to use stdout as terminal, non-terminal or use automatic
++ * detection (default).
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.29
++ */
++int scols_table_set_termforce(struct libscols_table *tb, int force)
++{
++	if (!tb)
++		return -EINVAL;
++	tb->termforce = force;
++	return 0;
++}
++
++/**
++ * scols_table_get_termforce:
++ * @tb: table
++ *
++ * Returns: SCOLS_TERMFORCE_{NEVER,ALWAYS,AUTO} or a negative value in case of an error.
++ *
++ * Since: 2.29
++ */
++int scols_table_get_termforce(const struct libscols_table *tb)
++{
++	return tb->termforce;
++}
++
++/**
++ * scols_table_set_termwidth
++ * @tb: table
++ * @width: terminal width
++ *
++ * The library automatically detects terminal width or defaults to 80 chars if
++ * detections is unsuccessful. This function override this behaviour.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.29
++ */
++int scols_table_set_termwidth(struct libscols_table *tb, size_t width)
++{
++	DBG(TAB, ul_debugobj(tb, "set terminatl width: %zu", width));
++	tb->termwidth = width;
+ 	return 0;
+ }
++
++/**
++ * scols_table_get_termwidth
++ * @tb: table
++ *
++ * Returns: terminal width.
++ */
++size_t scols_table_get_termwidth(const struct libscols_table *tb)
++{
++	return tb->termwidth;
++}
++
++/**
++ * scols_table_set_termheight
++ * @tb: table
++ * @height: terminal height (number of lines)
++ *
++ * The library automatically detects terminal height or defaults to 24 lines if
++ * detections is unsuccessful. This function override this behaviour.
++ *
++ * Returns: 0, a negative value in case of an error.
++ *
++ * Since: 2.31
++ */
++int scols_table_set_termheight(struct libscols_table *tb, size_t height)
++{
++	DBG(TAB, ul_debugobj(tb, "set terminatl height: %zu", height));
++	tb->termheight = height;
++	return 0;
++}
++
++/**
++ * scols_table_get_termheight
++ * @tb: table
++ *
++ * Returns: terminal height (number of lines).
++ *
++ * Since: 2.31
++ */
++size_t scols_table_get_termheight(const struct libscols_table *tb)
++{
++	return tb->termheight;
++}
+diff --git a/libsmartcols/src/table_print.c b/libsmartcols/src/table_print.c
+index c9f3d8f4b..10126fd79 100644
+--- a/libsmartcols/src/table_print.c
++++ b/libsmartcols/src/table_print.c
+@@ -2,6 +2,7 @@
+  * table.c - functions handling the data at the table level
+  *
+  * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
++ * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
+  *
+  * This file may be redistributed under the terms of the
+  * GNU Lesser General Public License.
+@@ -10,7 +11,7 @@
+ /**
+  * SECTION: table_print
+  * @title: Table print
+- * @short_description: table print API
++ * @short_description: output functions
+  *
+  * Table output API.
+  */
+@@ -21,13 +22,30 @@
+ #include <termios.h>
+ #include <ctype.h>
+ 
+-#include "nls.h"
+ #include "mbsalign.h"
+-#include "widechar.h"
+-#include "ttyutils.h"
+ #include "carefulputc.h"
+ #include "smartcolsP.h"
+ 
++#define colsep(tb)	((tb)->colsep ? (tb)->colsep : " ")
++#define linesep(tb)	((tb)->linesep ? (tb)->linesep : "\n")
++
++/* Fallback for symbols
++ *
++ * Note that by default library define all the symbols, but in case user does
++ * not define all symbols or if we extended the symbols struct then we need
++ * fallback to be more robust and backwardly compatible.
++ */
++#define titlepadding_symbol(tb)	((tb)->symbols->title_padding ? (tb)->symbols->title_padding : " ")
++#define branch_symbol(tb)	((tb)->symbols->branch ? (tb)->symbols->branch : "|-")
++#define vertical_symbol(tb)	((tb)->symbols->vert ? (tb)->symbols->vert : "| ")
++#define right_symbol(tb)	((tb)->symbols->right ? (tb)->symbols->right : "`-")
++
++#define cellpadding_symbol(tb)  ((tb)->padding_debug ? "." : \
++				 ((tb)->symbols->cell_padding ? (tb)->symbols->cell_padding: " "))
++
++#define want_repeat_header(tb)	(!(tb)->header_repeat || (tb)->header_next <= (tb)->termlines_used)
++
++
+ /* This is private struct to work with output data */
+ struct libscols_buffer {
+ 	char	*begin;		/* begin of the buffer */
+@@ -88,7 +106,6 @@ static int buffer_append_data(struct libscols_buffer *buf, const char *str)
+ 
+ 	if (maxsz <= sz)
+ 		return -EINVAL;
+-
+ 	memcpy(buf->cur, str, sz + 1);
+ 	buf->cur += sz;
+ 	return 0;
+@@ -100,7 +117,7 @@ static int buffer_set_data(struct libscols_buffer *buf, const char *str)
+ 	return rc ? rc : buffer_append_data(buf, str);
+ }
+ 
+-/* save the current buffer possition to art_idx */
++/* save the current buffer position to art_idx */
+ static void buffer_set_art_index(struct libscols_buffer *buf)
+ {
+ 	if (buf) {
+@@ -115,7 +132,10 @@ static char *buffer_get_data(struct libscols_buffer *buf)
+ }
+ 
+ /* encode data by mbs_safe_encode() to avoid control and non-printable chars */
+-static char *buffer_get_safe_data(struct libscols_buffer *buf, size_t *cells)
++static char *buffer_get_safe_data(struct libscols_table *tb,
++				  struct libscols_buffer *buf,
++				  size_t *cells,
++				  const char *safechars)
+ {
+ 	char *data = buffer_get_data(buf);
+ 	char *res = NULL;
+@@ -129,7 +149,14 @@ static char *buffer_get_safe_data(struct libscols_buffer *buf, size_t *cells)
+ 			goto nothing;
+ 	}
+ 
+-	res = mbs_safe_encode_to_buffer(data, cells, buf->encdata);
++	if (tb->no_encode) {
++		*cells = mbs_safe_width(data);
++		strcpy(buf->encdata, data);
++		res = buf->encdata;
++	} else {
++		res = mbs_safe_encode_to_buffer(data, cells, buf->encdata, safechars);
++	}
++
+ 	if (!res || !*cells || *cells == (size_t) -1)
+ 		goto nothing;
+ 	return res;
+@@ -151,11 +178,257 @@ static size_t buffer_get_safe_art_size(struct libscols_buffer *buf)
+ 	return bytes;
+ }
+ 
+-#define is_last_column(_tb, _cl) \
+-		list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns)
++/* returns pointer to the end of used data */
++static int line_ascii_art_to_buffer(struct libscols_table *tb,
++				    struct libscols_line *ln,
++				    struct libscols_buffer *buf)
++{
++	const char *art;
++	int rc;
++
++	assert(ln);
++	assert(buf);
+ 
+-#define colsep(tb) ((tb)->colsep ? (tb)->colsep : " ")
+-#define linesep(tb) ((tb)->linesep ? (tb)->linesep : "\n")
++	if (!ln->parent)
++		return 0;
++
++	rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
++	if (rc)
++		return rc;
++
++	if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
++		art = "  ";
++	else
++		art = vertical_symbol(tb);
++
++	return buffer_append_data(buf, art);
++}
++
++static int is_last_column(struct libscols_column *cl)
++{
++	int rc = list_entry_is_last(&cl->cl_columns, &cl->table->tb_columns);
++	struct libscols_column *next;
++
++	if (rc)
++		return 1;
++
++	next = list_entry(cl->cl_columns.next, struct libscols_column, cl_columns);
++	if (next && scols_column_is_hidden(next) && is_last_column(next))
++		return 1;
++	return 0;
++}
++
++
++static int has_pending_data(struct libscols_table *tb)
++{
++	struct libscols_column *cl;
++	struct libscols_iter itr;
++
++	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++	while (scols_table_next_column(tb, &itr, &cl) == 0) {
++		if (scols_column_is_hidden(cl))
++			continue;
++		if (cl->pending_data)
++			return 1;
++	}
++	return 0;
++}
++
++/* print padding or ASCII-art instead of data of @cl */
++static void print_empty_cell(struct libscols_table *tb,
++			  struct libscols_column *cl,
++			  struct libscols_line *ln,	/* optional */
++			  size_t bufsz)
++{
++	size_t len_pad = 0;		/* in screen cells as opposed to bytes */
++
++	/* generate tree ASCII-art rather than padding */
++	if (ln && scols_column_is_tree(cl)) {
++		if (!ln->parent) {
++			/* only print symbols->vert if followed by child */
++			if (!list_empty(&ln->ln_branch)) {
++				fputs(vertical_symbol(tb), tb->out);
++				len_pad = mbs_safe_width(vertical_symbol(tb));
++			}
++		} else {
++			/* use the same draw function as though we were intending to draw an L-shape */
++			struct libscols_buffer *art = new_buffer(bufsz);
++			char *data;
++
++			if (art) {
++				/* whatever the rc, len_pad will be sensible */
++				line_ascii_art_to_buffer(tb, ln, art);
++				if (!list_empty(&ln->ln_branch) && has_pending_data(tb))
++					buffer_append_data(art, vertical_symbol(tb));
++				data = buffer_get_safe_data(tb, art, &len_pad, NULL);
++				if (data && len_pad)
++					fputs(data, tb->out);
++				free_buffer(art);
++			}
++		}
++	}
++
++	if (is_last_column(cl))
++		return;
++
++	/* fill rest of cell with space */
++	for(; len_pad < cl->width; ++len_pad)
++		fputs(cellpadding_symbol(tb), tb->out);
++
++	fputs(colsep(tb), tb->out);
++}
++
++
++static const char *get_cell_color(struct libscols_table *tb,
++				  struct libscols_column *cl,
++				  struct libscols_line *ln,	/* optional */
++				  struct libscols_cell *ce)	/* optional */
++{
++	const char *color = NULL;
++
++	if (tb && tb->colors_wanted) {
++		if (ce)
++			color = ce->color;
++		if (ln && !color)
++			color = ln->color;
++		if (!color)
++			color = cl->color;
++	}
++	return color;
++}
++
++/* Fill the start of a line with padding (or with tree ascii-art).
++ *
++ * This is necessary after a long non-truncated column, as this requires the
++ * next column to be printed on the next line. For example (see 'DDD'):
++ *
++ * aaa bbb ccc ddd eee
++ * AAA BBB CCCCCCC
++ *             DDD EEE
++ * ^^^^^^^^^^^^
++ *  new line padding
++ */
++static void print_newline_padding(struct libscols_table *tb,
++				  struct libscols_column *cl,
++				  struct libscols_line *ln,	/* optional */
++				  size_t bufsz)
++{
++	size_t i;
++
++	assert(tb);
++	assert(cl);
++
++	fputs(linesep(tb), tb->out);		/* line break */
++	tb->termlines_used++;
++
++	/* fill cells after line break */
++	for (i = 0; i <= (size_t) cl->seqnum; i++)
++		print_empty_cell(tb, scols_table_get_column(tb, i), ln, bufsz);
++}
++
++/*
++ * Pending data
++ *
++ * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
++ * printed as usually and output is truncated to match column width.
++ *
++ * The rest of the long text is printed on next extra line(s). The extra lines
++ * don't exist in the table (not represented by libscols_line). The data for
++ * the extra lines are stored in libscols_column->pending_data_buf and the
++ * function print_line() adds extra lines until the buffer is not empty in all
++ * columns.
++ */
++
++/* set data that will be printed by extra lines */
++static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz)
++{
++	char *p = NULL;
++
++	if (data && *data) {
++		DBG(COL, ul_debugobj(cl, "setting pending data"));
++		assert(sz);
++		p = strdup(data);
++		if (!p)
++			return -ENOMEM;
++	}
++
++	free(cl->pending_data_buf);
++	cl->pending_data_buf = p;
++	cl->pending_data_sz = sz;
++	cl->pending_data = cl->pending_data_buf;
++	return 0;
++}
++
++/* the next extra line has been printed, move pending data cursor */
++static int step_pending_data(struct libscols_column *cl, size_t bytes)
++{
++	DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));
++
++	if (bytes >= cl->pending_data_sz)
++		return set_pending_data(cl, NULL, 0);
++
++	cl->pending_data += bytes;
++	cl->pending_data_sz -= bytes;
++	return 0;
++}
++
++/* print next pending data for the column @cl */
++static int print_pending_data(
++		struct libscols_table *tb,
++		struct libscols_column *cl,
++		struct libscols_line *ln,	/* optional */
++		struct libscols_cell *ce)
++{
++	const char *color = get_cell_color(tb, cl, ln, ce);
++	size_t width = cl->width, bytes;
++	size_t len = width, i;
++	char *data;
++	char *nextchunk = NULL;
++
++	if (!cl->pending_data)
++		return 0;
++	if (!width)
++		return -EINVAL;
++
++	DBG(COL, ul_debugobj(cl, "printing pending data"));
++
++	data = strdup(cl->pending_data);
++	if (!data)
++		goto err;
++
++	if (scols_column_is_customwrap(cl)
++	    && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
++		bytes = nextchunk - data;
++
++		len = mbs_safe_nwidth(data, bytes, NULL);
++	} else
++		bytes = mbs_truncate(data, &len);
++
++	if (bytes == (size_t) -1)
++		goto err;
++
++	if (bytes)
++		step_pending_data(cl, bytes);
++
++	if (color)
++		fputs(color, tb->out);
++	fputs(data, tb->out);
++	if (color)
++		fputs(UL_COLOR_RESET, tb->out);
++	free(data);
++
++	if (is_last_column(cl))
++		return 0;
++
++	for (i = len; i < width; i++)
++		fputs(cellpadding_symbol(tb), tb->out);		/* padding */
++
++	fputs(colsep(tb), tb->out);	/* columns separator */
++	return 0;
++err:
++	free(data);
++	return -errno;
++}
+ 
+ static int print_data(struct libscols_table *tb,
+ 		      struct libscols_column *cl,
+@@ -165,76 +438,120 @@ static int print_data(struct libscols_table *tb,
+ {
+ 	size_t len = 0, i, width, bytes;
+ 	const char *color = NULL;
+-	char *data;
++	char *data, *nextchunk;
++	int is_last;
+ 
+ 	assert(tb);
+ 	assert(cl);
+ 
+-	DBG(TAB, ul_debugobj(tb,
+-			" -> data, column=%p, line=%p, cell=%p, buff=%p",
+-			cl, ln, ce, buf));
+-
+ 	data = buffer_get_data(buf);
+ 	if (!data)
+ 		data = "";
+ 
+-	/* raw mode */
+-	if (scols_table_is_raw(tb)) {
++	is_last = is_last_column(cl);
++
++	switch (tb->format) {
++	case SCOLS_FMT_RAW:
+ 		fputs_nonblank(data, tb->out);
+-		if (!is_last_column(tb, cl))
++		if (!is_last)
+ 			fputs(colsep(tb), tb->out);
+ 		return 0;
+-	}
+ 
+-	/* NAME=value mode */
+-	if (scols_table_is_export(tb)) {
++	case SCOLS_FMT_EXPORT:
+ 		fprintf(tb->out, "%s=", scols_cell_get_data(&cl->header));
+ 		fputs_quoted(data, tb->out);
+-		if (!is_last_column(tb, cl))
++		if (!is_last)
+ 			fputs(colsep(tb), tb->out);
+ 		return 0;
+-	}
+ 
+-	if (tb->colors_wanted) {
+-		if (ce && !color)
+-			color = ce->color;
+-		if (ln && !color)
+-			color = ln->color;
+-		if (!color)
+-			color = cl->color;
++	case SCOLS_FMT_JSON:
++		fputs_quoted_json_lower(scols_cell_get_data(&cl->header), tb->out);
++		fputs(":", tb->out);
++		switch (cl->json_type) {
++			case SCOLS_JSON_STRING:
++				if (!*data)
++					fputs("null", tb->out);
++				else
++					fputs_quoted_json(data, tb->out);
++				break;
++			case SCOLS_JSON_NUMBER:
++				if (!*data)
++					fputs("null", tb->out);
++				else
++					fputs(data, tb->out);
++				break;
++			case SCOLS_JSON_BOOLEAN:
++				fputs(!*data ? "false" :
++				      *data == '0' ? "false" :
++				      *data == 'N' || *data == 'n' ? "false" : "true",
++				      tb->out);
++				break;
++		}
++		if (!is_last)
++			fputs(", ", tb->out);
++		return 0;
++
++	case SCOLS_FMT_HUMAN:
++		break;		/* continue below */
+ 	}
+ 
+-	/* encode, note that 'len' and 'width' are number of cells, not bytes */
+-	data = buffer_get_safe_data(buf, &len);
++	color = get_cell_color(tb, cl, ln, ce);
++
++	/* Encode. Note that 'len' and 'width' are number of cells, not bytes.
++	 */
++	data = buffer_get_safe_data(tb, buf, &len, scols_column_get_safechars(cl));
+ 	if (!data)
+ 		data = "";
+-	width = cl->width;
+ 	bytes = strlen(data);
++	width = cl->width;
++
++	/* custom multi-line cell based */
++	if (*data && scols_column_is_customwrap(cl)
++	    && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
++		set_pending_data(cl, nextchunk, bytes - (nextchunk - data));
++		bytes = nextchunk - data;
++		len = mbs_safe_nwidth(data, bytes, NULL);
++	}
+ 
+-	if (is_last_column(tb, cl) && len < width && !scols_table_is_maxout(tb))
++	if (is_last
++	    && len < width
++	    && !scols_table_is_maxout(tb)
++	    && !scols_column_is_right(cl))
+ 		width = len;
+ 
+ 	/* truncate data */
+ 	if (len > width && scols_column_is_trunc(cl)) {
+ 		len = width;
+ 		bytes = mbs_truncate(data, &len);	/* updates 'len' */
++	}
+ 
+-		if (!data || bytes == (size_t) -1) {
+-			bytes = len = 0;
+-			data = NULL;
+-		}
++	/* standard multi-line cell */
++	if (len > width && scols_column_is_wrap(cl)
++	    && !scols_column_is_customwrap(cl)) {
++		set_pending_data(cl, data, bytes);
++
++		len = width;
++		bytes = mbs_truncate(data, &len);
++		if (bytes  != (size_t) -1 && bytes > 0)
++			step_pending_data(cl, bytes);
++	}
++
++	if (bytes == (size_t) -1) {
++		bytes = len = 0;
++		data = NULL;
+ 	}
+ 
+ 	if (data) {
+ 		if (scols_column_is_right(cl)) {
+-			size_t xw = cl->width;
+ 			if (color)
+ 				fputs(color, tb->out);
+-			fprintf(tb->out, "%*s", (int) xw, data);
++			for (i = len; i < width; i++)
++				fputs(cellpadding_symbol(tb), tb->out);
++			fputs(data, tb->out);
+ 			if (color)
+ 				fputs(UL_COLOR_RESET, tb->out);
+-			if (len < xw)
+-				len = xw;
++			len = width;
++
+ 		} else if (color) {
+ 			char *p = data;
+ 			size_t art = buffer_get_safe_art_size(buf);
+@@ -252,46 +569,17 @@ static int print_data(struct libscols_table *tb,
+ 			fputs(data, tb->out);
+ 	}
+ 	for (i = len; i < width; i++)
+-		fputs(" ", tb->out);		/* padding */
+-
+-	if (!is_last_column(tb, cl)) {
+-		if (len > width && !scols_column_is_trunc(cl)) {
+-			fputs(linesep(tb), tb->out);
+-			for (i = 0; i <= (size_t) cl->seqnum; i++) {
+-				struct libscols_column *x = scols_table_get_column(tb, i);
+-				fprintf(tb->out, "%*s ", -((int)x->width), " ");
+-			}
+-		} else
+-			fputs(colsep(tb), tb->out);	/* columns separator */
+-	}
+-
+-	return 0;
+-}
+-
+-/* returns pointer to the end of used data */
+-static int line_ascii_art_to_buffer(struct libscols_table *tb,
+-				    struct libscols_line *ln,
+-				    struct libscols_buffer *buf)
+-{
+-	const char *art;
+-	int rc;
++		fputs(cellpadding_symbol(tb), tb->out);	/* padding */
+ 
+-	assert(ln);
+-	assert(buf);
+-
+-	if (!ln->parent)
++	if (is_last)
+ 		return 0;
+ 
+-	rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
+-	if (rc)
+-		return rc;
+-
+-	if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
+-		art = "  ";
++	if (len > width && !scols_column_is_trunc(cl))
++		print_newline_padding(tb, cl, ln, buf->bufsz);	/* next column starts on next line */
+ 	else
+-		art = tb->symbols->vert;
++		fputs(colsep(tb), tb->out);		/* columns separator */
+ 
+-	return buffer_append_data(buf, art);
++	return 0;
+ }
+ 
+ static int cell_to_buffer(struct libscols_table *tb,
+@@ -322,13 +610,13 @@ static int cell_to_buffer(struct libscols_table *tb,
+ 	/*
+ 	 * Tree stuff
+ 	 */
+-	if (ln->parent) {
++	if (ln->parent && !scols_table_is_json(tb)) {
+ 		rc = line_ascii_art_to_buffer(tb, ln->parent, buf);
+ 
+ 		if (!rc && list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
+-			rc = buffer_append_data(buf, tb->symbols->right);
++			rc = buffer_append_data(buf, right_symbol(tb));
+ 		else if (!rc)
+-			rc = buffer_append_data(buf, tb->symbols->branch);
++			rc = buffer_append_data(buf, branch_symbol(tb));
+ 		if (!rc)
+ 			buffer_set_art_index(buf);
+ 	}
+@@ -338,34 +626,252 @@ static int cell_to_buffer(struct libscols_table *tb,
+ 	return rc;
+ }
+ 
++static void fput_indent(struct libscols_table *tb)
++{
++	int i;
++
++	for (i = 0; i <= tb->indent; i++)
++		fputs("   ", tb->out);
++}
++
++static void fput_table_open(struct libscols_table *tb)
++{
++	tb->indent = 0;
++
++	if (scols_table_is_json(tb)) {
++		fputc('{', tb->out);
++		fputs(linesep(tb), tb->out);
++
++		fput_indent(tb);
++		fputs_quoted(tb->name, tb->out);
++		fputs(": [", tb->out);
++		fputs(linesep(tb), tb->out);
++
++		tb->indent++;
++		tb->indent_last_sep = 1;
++	}
++}
++
++static void fput_table_close(struct libscols_table *tb)
++{
++	tb->indent--;
++
++	if (scols_table_is_json(tb)) {
++		fput_indent(tb);
++		fputc(']', tb->out);
++		tb->indent--;
++		fputs(linesep(tb), tb->out);
++		fputc('}', tb->out);
++		tb->indent_last_sep = 1;
++	}
++}
++
++static void fput_children_open(struct libscols_table *tb)
++{
++	if (scols_table_is_json(tb)) {
++		fputc(',', tb->out);
++		fputs(linesep(tb), tb->out);
++		fput_indent(tb);
++		fputs("\"children\": [", tb->out);
++	}
++	/* between parent and child is separator */
++	fputs(linesep(tb), tb->out);
++	tb->indent_last_sep = 1;
++	tb->indent++;
++	tb->termlines_used++;
++}
++
++static void fput_children_close(struct libscols_table *tb)
++{
++	tb->indent--;
++
++	if (scols_table_is_json(tb)) {
++		fput_indent(tb);
++		fputc(']', tb->out);
++		fputs(linesep(tb), tb->out);
++		tb->indent_last_sep = 1;
++	}
++}
++
++static void fput_line_open(struct libscols_table *tb)
++{
++	if (scols_table_is_json(tb)) {
++		fput_indent(tb);
++		fputc('{', tb->out);
++		tb->indent_last_sep = 0;
++	}
++	tb->indent++;
++}
++
++static void fput_line_close(struct libscols_table *tb, int last, int last_in_table)
++{
++	tb->indent--;
++	if (scols_table_is_json(tb)) {
++		if (tb->indent_last_sep)
++			fput_indent(tb);
++		fputs(last ? "}" : "},", tb->out);
++		if (!tb->no_linesep)
++			fputs(linesep(tb), tb->out);
++
++	} else if (tb->no_linesep == 0 && last_in_table == 0) {
++		fputs(linesep(tb), tb->out);
++		tb->termlines_used++;
++	}
++
++	tb->indent_last_sep = 1;
++}
++
+ /*
+- * Prints data, data maybe be printed in more formats (raw, NAME=xxx pairs) and
+- * control and non-printable chars maybe encoded in \x?? hex encoding.
++ * Prints data. Data can be printed in more formats (raw, NAME=xxx pairs), and
++ * control and non-printable characters can be encoded in the \x?? encoding.
+  */
+ static int print_line(struct libscols_table *tb,
+ 		      struct libscols_line *ln,
+ 		      struct libscols_buffer *buf)
+ {
+-	int rc = 0;
++	int rc = 0, pending = 0;
+ 	struct libscols_column *cl;
+ 	struct libscols_iter itr;
+ 
+ 	assert(ln);
+ 
+-	DBG(TAB, ul_debugobj(tb, "printing line, line=%p, buff=%p", ln, buf));
++	DBG(TAB, ul_debugobj(tb, "printing line"));
+ 
++	/* regular line */
+ 	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 	while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
++		if (scols_column_is_hidden(cl))
++			continue;
+ 		rc = cell_to_buffer(tb, ln, cl, buf);
+-		if (!rc)
++		if (rc == 0)
+ 			rc = print_data(tb, cl, ln,
+ 					scols_line_get_cell(ln, cl->seqnum),
+ 					buf);
++		if (rc == 0 && cl->pending_data)
++			pending = 1;
++	}
++
++	/* extra lines of the multi-line cells */
++	while (rc == 0 && pending) {
++		pending = 0;
++		fputs(linesep(tb), tb->out);
++		tb->termlines_used++;
++		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++		while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
++			if (scols_column_is_hidden(cl))
++				continue;
++			if (cl->pending_data) {
++				rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum));
++				if (rc == 0 && cl->pending_data)
++					pending = 1;
++			} else
++				print_empty_cell(tb, cl, ln, buf->bufsz);
++		}
+ 	}
+ 
+-	if (rc == 0)
+-		fputs(linesep(tb), tb->out);
+-	return 0;
++	return 0;
++}
++
++static int print_title(struct libscols_table *tb)
++{
++	int rc, color = 0;
++	mbs_align_t align;
++	size_t width, len = 0, bufsz, titlesz;
++	char *title = NULL, *buf = NULL;
++
++	assert(tb);
++
++	if (!tb->title.data)
++		return 0;
++
++	DBG(TAB, ul_debugobj(tb, "printing title"));
++
++	/* encode data */
++	if (tb->no_encode) {
++		len = bufsz = strlen(tb->title.data) + 1;
++		buf = strdup(tb->title.data);
++		if (!buf) {
++			rc = -ENOMEM;
++			goto done;
++		}
++	} else {
++		bufsz = mbs_safe_encode_size(strlen(tb->title.data)) + 1;
++		if (bufsz == 1) {
++			DBG(TAB, ul_debugobj(tb, "title is empty string -- ignore"));
++			return 0;
++		}
++		buf = malloc(bufsz);
++		if (!buf) {
++			rc = -ENOMEM;
++			goto done;
++		}
++
++		if (!mbs_safe_encode_to_buffer(tb->title.data, &len, buf, NULL) ||
++		    !len || len == (size_t) -1) {
++			rc = -EINVAL;
++			goto done;
++		}
++	}
++
++	/* truncate and align */
++	width = tb->is_term ? tb->termwidth : 80;
++	titlesz = width + bufsz;
++
++	title = malloc(titlesz);
++	if (!title) {
++		rc = -EINVAL;
++		goto done;
++	}
++
++	switch (scols_cell_get_alignment(&tb->title)) {
++	case SCOLS_CELL_FL_RIGHT:
++		align = MBS_ALIGN_RIGHT;
++		break;
++	case SCOLS_CELL_FL_CENTER:
++		align = MBS_ALIGN_CENTER;
++		break;
++	case SCOLS_CELL_FL_LEFT:
++	default:
++		align = MBS_ALIGN_LEFT;
++		/*
++		 * Don't print extra blank chars after the title if on left
++		 * (that's same as we use for the last column in the table).
++		 */
++		if (len < width
++		    && !scols_table_is_maxout(tb)
++		    && isblank(*titlepadding_symbol(tb)))
++			width = len;
++		break;
++
++	}
++
++	/* copy from buf to title and align to width with title_padding */
++	rc = mbsalign_with_padding(buf, title, titlesz,
++			&width, align,
++			0, (int) *titlepadding_symbol(tb));
++
++	if (rc == -1) {
++		rc = -EINVAL;
++		goto done;
++	}
++
++	if (tb->colors_wanted && tb->title.color)
++		color = 1;
++	if (color)
++		fputs(tb->title.color, tb->out);
++
++	fputs(title, tb->out);
++
++	if (color)
++		fputs(UL_COLOR_RESET, tb->out);
++
++	fputc('\n', tb->out);
++	rc = 0;
++done:
++	free(buf);
++	free(title);
++	DBG(TAB, ul_debugobj(tb, "printing title done [rc=%d]", rc));
++	return rc;
+ }
+ 
+ static int print_header(struct libscols_table *tb, struct libscols_buffer *buf)
+@@ -376,86 +882,139 @@ static int print_header(struct libscols_table *tb, struct libscols_buffer *buf)
+ 
+ 	assert(tb);
+ 
+-	if (scols_table_is_noheadings(tb) ||
++	if ((tb->header_printed == 1 && tb->header_repeat == 0) ||
++	    scols_table_is_noheadings(tb) ||
+ 	    scols_table_is_export(tb) ||
++	    scols_table_is_json(tb) ||
+ 	    list_empty(&tb->tb_lines))
+ 		return 0;
+ 
+ 	DBG(TAB, ul_debugobj(tb, "printing header"));
+ 
+-	/* set width according to the size of data
+-	 */
++	/* set the width according to the size of the data */
+ 	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 	while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
++		if (scols_column_is_hidden(cl))
++			continue;
+ 		rc = buffer_set_data(buf, scols_cell_get_data(&cl->header));
+ 		if (!rc)
+ 			rc = print_data(tb, cl, NULL, &cl->header, buf);
+ 	}
+ 
+-	if (rc == 0)
++	if (rc == 0) {
+ 		fputs(linesep(tb), tb->out);
++		tb->termlines_used++;
++	}
++
++	tb->header_printed = 1;
++	tb->header_next = tb->termlines_used + tb->termheight;
++	if (tb->header_repeat)
++		DBG(TAB, ul_debugobj(tb, "\tnext header: %zu [current=%zu]",
++					tb->header_next, tb->termlines_used));
+ 	return rc;
+ }
+ 
+-static int print_table(struct libscols_table *tb, struct libscols_buffer *buf)
++
++static int print_range(	struct libscols_table *tb,
++			struct libscols_buffer *buf,
++			struct libscols_iter *itr,
++			struct libscols_line *end)
+ {
+-	int rc;
++	int rc = 0;
+ 	struct libscols_line *ln;
+-	struct libscols_iter itr;
+ 
+ 	assert(tb);
++	DBG(TAB, ul_debugobj(tb, "printing range"));
+ 
+-	rc = print_header(tb, buf);
++	while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) {
+ 
+-	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+-	while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0)
++		int last = scols_iter_is_last(itr);
++
++		fput_line_open(tb);
+ 		rc = print_line(tb, ln, buf);
++		fput_line_close(tb, last, last);
++
++		if (end && ln == end)
++			break;
++
++		if (!last && want_repeat_header(tb))
++			print_header(tb, buf);
++	}
+ 
+ 	return rc;
++
++}
++
++static int print_table(struct libscols_table *tb, struct libscols_buffer *buf)
++{
++	struct libscols_iter itr;
++
++	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++	return print_range(tb, buf, &itr, NULL);
+ }
+ 
++
+ static int print_tree_line(struct libscols_table *tb,
+ 			   struct libscols_line *ln,
+-			   struct libscols_buffer *buf)
++			   struct libscols_buffer *buf,
++			   int last,
++			   int last_in_table)
+ {
+ 	int rc;
+-	struct list_head *p;
+ 
++	/* print the line */
++	fput_line_open(tb);
+ 	rc = print_line(tb, ln, buf);
+ 	if (rc)
+-		return rc;
+-	if (list_empty(&ln->ln_branch))
+-		return 0;
++		goto done;
+ 
+-	/* print all children */
+-	list_for_each(p, &ln->ln_branch) {
+-		struct libscols_line *chld =
+-				list_entry(p, struct libscols_line, ln_children);
+-		rc = print_tree_line(tb, chld, buf);
+-		if (rc)
+-			break;
++	/* print children */
++	if (!list_empty(&ln->ln_branch)) {
++		struct list_head *p;
++
++		fput_children_open(tb);
++
++		/* print all children */
++		list_for_each(p, &ln->ln_branch) {
++			struct libscols_line *chld =
++					list_entry(p, struct libscols_line, ln_children);
++			int last_child = p->next == &ln->ln_branch;
++
++			rc = print_tree_line(tb, chld, buf, last_child, last_in_table && last_child);
++			if (rc)
++				goto done;
++		}
++
++		fput_children_close(tb);
+ 	}
+ 
++	if (list_empty(&ln->ln_branch) || scols_table_is_json(tb))
++		fput_line_close(tb, last, last_in_table);
++done:
+ 	return rc;
+ }
+ 
+ static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
+ {
+-	int rc;
+-	struct libscols_line *ln;
++	int rc = 0;
++	struct libscols_line *ln, *last = NULL;
+ 	struct libscols_iter itr;
+ 
+ 	assert(tb);
+ 
+ 	DBG(TAB, ul_debugobj(tb, "printing tree"));
+ 
+-	rc = print_header(tb, buf);
++	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++
++	while (scols_table_next_line(tb, &itr, &ln) == 0)
++		if (!last || !ln->parent)
++			last = ln;
+ 
+ 	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 	while (rc == 0 && scols_table_next_line(tb, &itr, &ln) == 0) {
+ 		if (ln->parent)
+ 			continue;
+-		rc = print_tree_line(tb, ln, buf);
++		rc = print_tree_line(tb, ln, buf, ln == last, ln == last);
+ 	}
+ 
+ 	return rc;
+@@ -463,9 +1022,14 @@ static int print_tree(struct libscols_table *tb, struct libscols_buffer *buf)
+ 
+ static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
+ {
++	if (scols_column_is_hidden(cl)) {
++		DBG(COL, ul_debugobj(cl, "%s (hidden) ignored", cl->header.data));
++		return;
++	}
++
+ 	DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, "
+ 				 "hint=%d, avg=%zu, max=%zu, min=%zu, "
+-				 "extreme=%s",
++				 "extreme=%s %s",
+ 
+ 		cl->header.data, cl->seqnum, cl->width,
+ 		cl->width_hint > 1 ? (int) cl->width_hint :
+@@ -473,7 +1037,8 @@ static void dbg_column(struct libscols_table *tb, struct libscols_column *cl)
+ 		cl->width_avg,
+ 		cl->width_max,
+ 		cl->width_min,
+-		cl->is_extreme ? "yes" : "not"));
++		cl->is_extreme ? "yes" : "not",
++		cl->flags & SCOLS_FL_TRUNC ? "trunc" : ""));
+ }
+ 
+ static void dbg_columns(struct libscols_table *tb)
+@@ -486,14 +1051,15 @@ static void dbg_columns(struct libscols_table *tb)
+ 		dbg_column(tb, cl);
+ }
+ 
++
+ /*
+  * This function counts column width.
+  *
+- * For the SCOLS_FL_NOEXTREMES columns is possible to call this function two
+- * times.  The first pass counts width and average width. If the column
+- * contains too large fields (width greater than 2 * average) then the column
+- * is marked as "extreme". In the second pass all extreme fields are ignored
+- * and column width is counted from non-extreme fields only.
++ * For the SCOLS_FL_NOEXTREMES columns it is possible to call this function
++ * two times. The first pass counts the width and average width. If the column
++ * contains fields that are too large (a width greater than 2 * average) then
++ * the column is marked as "extreme". In the second pass all extreme fields
++ * are ignored and the column width is counted from non-extreme fields only.
+  */
+ static int count_column_width(struct libscols_table *tb,
+ 			      struct libscols_column *cl,
+@@ -508,6 +1074,19 @@ static int count_column_width(struct libscols_table *tb,
+ 	assert(cl);
+ 
+ 	cl->width = 0;
++	if (!cl->width_min) {
++		if (cl->width_hint < 1 && scols_table_is_maxout(tb) && tb->is_term) {
++			cl->width_min = (size_t) (cl->width_hint * tb->termwidth);
++			if (cl->width_min && !is_last_column(cl))
++				cl->width_min--;
++		}
++		if (scols_cell_get_data(&cl->header)) {
++			size_t len = mbs_safe_width(scols_cell_get_data(&cl->header));
++			cl->width_min = max(cl->width_min, len);
++		}
++		if (!cl->width_min)
++			cl->width_min = 1;
++	}
+ 
+ 	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 	while (scols_table_next_line(tb, &itr, &ln) == 0) {
+@@ -516,15 +1095,20 @@ static int count_column_width(struct libscols_table *tb,
+ 
+ 		rc = cell_to_buffer(tb, ln, cl, buf);
+ 		if (rc)
+-			return rc;
++			goto done;
+ 
+ 		data = buffer_get_data(buf);
+-		len = data ? mbs_safe_width(data) : 0;
++
++		if (!data)
++			len = 0;
++		else if (scols_column_is_customwrap(cl))
++			len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data);
++		else
++			len = mbs_safe_width(data);
+ 
+ 		if (len == (size_t) -1)		/* ignore broken multibyte strings */
+ 			len = 0;
+-		if (len > cl->width_max)
+-			cl->width_max = len;
++		cl->width_max = max(len, cl->width_max);
+ 
+ 		if (cl->is_extreme && len > cl->width_avg * 2)
+ 			continue;
+@@ -532,81 +1116,105 @@ static int count_column_width(struct libscols_table *tb,
+ 			sum += len;
+ 			count++;
+ 		}
+-		if (len > cl->width)
+-			cl->width = len;
++		cl->width = max(len, cl->width);
++		if (scols_column_is_tree(cl)) {
++			size_t treewidth = buffer_get_safe_art_size(buf);
++			cl->width_treeart = max(cl->width_treeart, treewidth);
++		}
+ 	}
+ 
+ 	if (count && cl->width_avg == 0) {
+ 		cl->width_avg = sum / count;
+-
+ 		if (cl->width_max > cl->width_avg * 2)
+ 			cl->is_extreme = 1;
+ 	}
+ 
+-	/* check and set minimal column width */
+-	if (scols_cell_get_data(&cl->header))
+-		cl->width_min = mbs_safe_width(scols_cell_get_data(&cl->header));
+-
+ 	/* enlarge to minimal width */
+ 	if (cl->width < cl->width_min && !scols_column_is_strict_width(cl))
+ 		cl->width = cl->width_min;
+ 
+-	/* use relative size for large columns */
++	/* use absolute size for large columns */
+ 	else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint
+ 		 && cl->width_min < (size_t) cl->width_hint)
+ 
+ 		cl->width = (size_t) cl->width_hint;
+ 
++done:
+ 	ON_DBG(COL, dbg_column(tb, cl));
+ 	return rc;
+ }
+ 
+-
+ /*
+- * This is core of the scols_* voodo...
++ * This is core of the scols_* voodoo...
+  */
+ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf)
+ {
+ 	struct libscols_column *cl;
+ 	struct libscols_iter itr;
+-	size_t width = 0;		/* output width */
+-	int trunc_only, rc = 0;
++	size_t width = 0, width_min = 0;	/* output width */
++	int stage, rc = 0;
+ 	int extremes = 0;
++	size_t colsepsz;
+ 
+ 
+ 	DBG(TAB, ul_debugobj(tb, "recounting widths (termwidth=%zu)", tb->termwidth));
+ 
++	colsepsz = mbs_safe_width(colsep(tb));
++
+ 	/* set basic columns width
+ 	 */
+ 	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 	while (scols_table_next_column(tb, &itr, &cl) == 0) {
++		int is_last;
++
++		if (scols_column_is_hidden(cl))
++			continue;
+ 		rc = count_column_width(tb, cl, buf);
+ 		if (rc)
+-			return rc;
++			goto done;
++
++		is_last = is_last_column(cl);
+ 
+-		width += cl->width + (is_last_column(tb, cl) ? 0 : 1);
++		width += cl->width + (is_last ? 0 : colsepsz);		/* separator for non-last column */
++		width_min += cl->width_min + (is_last ? 0 : colsepsz);
+ 		extremes += cl->is_extreme;
+ 	}
+ 
+-	if (!tb->is_term)
+-		return 0;
++	if (!tb->is_term) {
++		DBG(TAB, ul_debugobj(tb, " non-terminal output"));
++		goto done;
++	}
+ 
+-	/* reduce columns with extreme fields
+-	 */
++	/* be paranoid */
++	if (width_min > tb->termwidth && scols_table_is_maxout(tb)) {
++		DBG(TAB, ul_debugobj(tb, " min width larger than terminal! [width=%zu, term=%zu]", width_min, tb->termwidth));
++
++		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++		while (width_min > tb->termwidth
++		       && scols_table_next_column(tb, &itr, &cl) == 0) {
++			if (scols_column_is_hidden(cl))
++				continue;
++			width_min--;
++			cl->width_min--;
++		}
++		DBG(TAB, ul_debugobj(tb, " min width reduced to %zu", width_min));
++	}
++
++	/* reduce columns with extreme fields */
+ 	if (width > tb->termwidth && extremes) {
+-		DBG(TAB, ul_debugobj(tb, "   reduce width (extreme columns)"));
++		DBG(TAB, ul_debugobj(tb, " reduce width (extreme columns)"));
+ 
+ 		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 		while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ 			size_t org_width;
+ 
+-			if (!cl->is_extreme)
++			if (!cl->is_extreme || scols_column_is_hidden(cl))
+ 				continue;
+ 
+ 			org_width = cl->width;
+ 			rc = count_column_width(tb, cl, buf);
+ 			if (rc)
+-				return rc;
++				goto done;
+ 
+ 			if (org_width > cl->width)
+ 				width -= org_width - cl->width;
+@@ -617,17 +1225,17 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf
+ 
+ 	if (width < tb->termwidth) {
+ 		if (extremes) {
+-			DBG(TAB, ul_debugobj(tb, "   enlarge width (extreme columns)"));
++			DBG(TAB, ul_debugobj(tb, " enlarge width (extreme columns)"));
+ 
+ 			/* enlarge the first extreme column */
+ 			scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 			while (scols_table_next_column(tb, &itr, &cl) == 0) {
+ 				size_t add;
+ 
+-				if (!cl->is_extreme)
++				if (!cl->is_extreme || scols_column_is_hidden(cl))
+ 					continue;
+ 
+-				/* this column is tooo large, ignore?
++				/* this column is too large, ignore?
+ 				if (cl->width_max - cl->width >
+ 						(tb->termwidth - width))
+ 					continue;
+@@ -646,12 +1254,14 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf
+ 		}
+ 
+ 		if (width < tb->termwidth && scols_table_is_maxout(tb)) {
+-			DBG(TAB, ul_debugobj(tb, "   enlarge width (max-out)"));
++			DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)"));
+ 
+-			/* try enlarge all columns */
++			/* try enlarging all columns */
+ 			while (width < tb->termwidth) {
+ 				scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 				while (scols_table_next_column(tb, &itr, &cl) == 0) {
++					if (scols_column_is_hidden(cl))
++						continue;
+ 					cl->width++;
+ 					width++;
+ 					if (width == tb->termwidth)
+@@ -660,67 +1270,133 @@ static int recount_widths(struct libscols_table *tb, struct libscols_buffer *buf
+ 			}
+ 		} else if (width < tb->termwidth) {
+ 			/* enlarge the last column */
+-			struct libscols_column *cl = list_entry(
++			struct libscols_column *col = list_entry(
+ 				tb->tb_columns.prev, struct libscols_column, cl_columns);
+ 
+-			DBG(TAB, ul_debugobj(tb, "   enlarge width (last column)"));
++			DBG(TAB, ul_debugobj(tb, " enlarge width (last column)"));
+ 
+-			if (!scols_column_is_right(cl) && tb->termwidth - width > 0) {
+-				cl->width += tb->termwidth - width;
++			if (!scols_column_is_right(col) && tb->termwidth - width > 0) {
++				col->width += tb->termwidth - width;
+ 				width = tb->termwidth;
+ 			}
+ 		}
+ 	}
+ 
+-	/* bad, we have to reduce output width, this is done in two steps:
+-	 * 1/ reduce columns with a relative width and with truncate flag
+-	 * 2) reduce columns with a relative width without truncate flag
++	/* bad, we have to reduce output width, this is done in three stages:
++	 *
++	 * 1) trunc relative with trunc flag if the column width is greater than
++	 *    expected column width (it means "width_hint * terminal_width").
++	 *
++	 * 2) trunc all with trunc flag
++	 *
++	 * 3) trunc relative without trunc flag
++	 *
++	 * Note that SCOLS_FL_WRAP (if no custom wrap function is specified) is
++	 * interpreted as SCOLS_FL_TRUNC.
+ 	 */
+-	trunc_only = 1;
+-	while (width > tb->termwidth) {
+-		size_t org = width;
++	for (stage = 1; width > tb->termwidth && stage <= 3; ) {
++		size_t org_width = width;
+ 
+-		DBG(TAB, ul_debugobj(tb, "   reduce width (current=%zu, "
+-					 "wanted=%zu, mode=%s)",
+-					width, tb->termwidth,
+-					trunc_only ? "trunc-only" : "all-relative"));
++		DBG(TAB, ul_debugobj(tb, " reduce width - #%d stage (current=%zu, wanted=%zu)",
++				stage, width, tb->termwidth));
+ 
+ 		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 		while (scols_table_next_column(tb, &itr, &cl) == 0) {
++
++			int trunc_flag = 0;
++
++			DBG(TAB, ul_debugobj(cl, "   checking %s (width=%zu, treeart=%zu)",
++						cl->header.data, cl->width, cl->width_treeart));
++			if (scols_column_is_hidden(cl))
++				continue;
+ 			if (width <= tb->termwidth)
+ 				break;
+-			if (cl->width_hint > 1 && !scols_column_is_trunc(cl))
+-				continue;	/* never truncate columns with absolute sizes */
+-			if (scols_column_is_tree(cl))
+-				continue;	/* never truncate the tree */
+-			if (trunc_only && !scols_column_is_trunc(cl))
+-				continue;
++
++			/* never truncate if already minimal width */
+ 			if (cl->width == cl->width_min)
+ 				continue;
+ 
+-			/* truncate column with relative sizes */
+-			if (cl->width_hint < 1 && cl->width > 0 && width > 0 &&
+-			    cl->width > cl->width_hint * tb->termwidth) {
++			/* never truncate the tree */
++			if (scols_column_is_tree(cl) && width <= cl->width_treeart)
++				continue;
++
++			/* nothing to truncate */
++			if (cl->width == 0 || width == 0)
++				continue;
++
++			trunc_flag = scols_column_is_trunc(cl)
++				    || (scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl));
++
++			switch (stage) {
++			/* #1 stage - trunc relative with TRUNC flag */
++			case 1:
++				if (!trunc_flag)		/* ignore: missing flag */
++					break;
++				if (cl->width_hint <= 0 || cl->width_hint >= 1)	/* ignore: no relative */
++					break;
++				if (cl->width < (size_t) (cl->width_hint * tb->termwidth)) /* ignore: smaller than expected width */
++					break;
++
++				DBG(TAB, ul_debugobj(tb, "     reducing (relative with flag)"));
+ 				cl->width--;
+ 				width--;
+-			}
+-			/* truncate column with absolute size */
+-			if (cl->width_hint > 1 && cl->width > 0 && width > 0 &&
+-			    !trunc_only) {
++				break;
++
++			/* #2 stage - trunc all with TRUNC flag */
++			case 2:
++				if (!trunc_flag)		/* ignore: missing flag */
++					break;
++
++				DBG(TAB, ul_debugobj(tb, "     reducing (all with flag)"));
+ 				cl->width--;
+ 				width--;
++				break;
++
++			/* #3 stage - trunc relative without flag */
++			case 3:
++				if (cl->width_hint <= 0 || cl->width_hint >= 1)	/* ignore: no relative */
++					break;
++
++				DBG(TAB, ul_debugobj(tb, "     reducing (relative without flag)"));
++				cl->width--;
++				width--;
++				break;
+ 			}
+ 
++			/* hide zero width columns */
++			if (cl->width == 0)
++				cl->flags |= SCOLS_FL_HIDDEN;
+ 		}
+-		if (org == width) {
+-			if (trunc_only)
+-				trunc_only = 0;
+-			else
++
++		/* the current stage is without effect, go to the next */
++		if (org_width == width)
++			stage++;
++	}
++
++	/* ignore last column(s) or force last column to be truncated if
++	 * nowrap mode enabled */
++	if (tb->no_wrap && width > tb->termwidth) {
++		scols_reset_iter(&itr, SCOLS_ITER_BACKWARD);
++		while (scols_table_next_column(tb, &itr, &cl) == 0) {
++
++			if (scols_column_is_hidden(cl))
++				continue;
++			if (width <= tb->termwidth)
+ 				break;
++			if (width - cl->width < tb->termwidth) {
++				size_t r =  width - tb->termwidth;
++
++				cl->flags |= SCOLS_FL_TRUNC;
++				cl->width -= r;
++				width -= r;
++			} else {
++				cl->flags |= SCOLS_FL_HIDDEN;
++				width -= cl->width + colsepsz;
++			}
+ 		}
+ 	}
+-
+-	DBG(TAB, ul_debugobj(tb, "  result: %zu", width));
++done:
++	DBG(TAB, ul_debugobj(tb, " final width: %zu (rc=%d)", width, rc));
+ 	ON_DBG(TAB, dbg_columns(tb));
+ 
+ 	return rc;
+@@ -742,64 +1418,281 @@ static size_t strlen_line(struct libscols_line *ln)
+ 	return sz;
+ }
+ 
++static void cleanup_printing(struct libscols_table *tb, struct libscols_buffer *buf)
++{
++	if (!tb)
++		return;
+ 
++	free_buffer(buf);
+ 
+-/**
+- * scols_print_table:
+- * @tb: table
+- *
+- * Prints the table to the output stream.
+- *
+- * Returns: 0, a negative value in case of an error.
+- */
+-int scols_print_table(struct libscols_table *tb)
++	if (tb->priv_symbols) {
++		scols_table_set_symbols(tb, NULL);
++		tb->priv_symbols = 0;
++	}
++}
++
++static int initialize_printing(struct libscols_table *tb, struct libscols_buffer **buf)
+ {
+-	int rc = 0;
+-	size_t bufsz;
++	size_t bufsz, extra_bufsz = 0;
+ 	struct libscols_line *ln;
+ 	struct libscols_iter itr;
+-	struct libscols_buffer *buf;
++	int rc;
+ 
+-	assert(tb);
+-	if (!tb)
+-		return -1;
++	DBG(TAB, ul_debugobj(tb, "initialize printing"));
++	*buf = NULL;
+ 
+-	DBG(TAB, ul_debugobj(tb, "printing"));
+-	if (!tb->symbols)
+-		scols_table_set_symbols(tb, NULL);	/* use default */
++	if (!tb->symbols) {
++		rc = scols_table_set_default_symbols(tb);
++		if (rc)
++			goto err;
++		tb->priv_symbols = 1;
++	} else
++		tb->priv_symbols = 0;
++
++	if (tb->format == SCOLS_FMT_HUMAN)
++		tb->is_term = tb->termforce == SCOLS_TERMFORCE_NEVER  ? 0 :
++			      tb->termforce == SCOLS_TERMFORCE_ALWAYS ? 1 :
++			      isatty(STDOUT_FILENO);
++
++	if (tb->is_term) {
++		size_t width = (size_t) scols_table_get_termwidth(tb);
++
++		if (tb->termreduce > 0 && tb->termreduce < width) {
++			width -= tb->termreduce;
++			scols_table_set_termwidth(tb, width);
++		}
++		bufsz = width;
++	} else
++		bufsz = BUFSIZ;
+ 
+-	tb->is_term = isatty(STDOUT_FILENO) ? 1 : 0;
+-	tb->termwidth = tb->is_term ? get_terminal_width() : 0;
+-	if (tb->termwidth <= 0)
+-		tb->termwidth = 80;
+-	tb->termwidth -= tb->termreduce;
++	if (!tb->is_term || tb->format != SCOLS_FMT_HUMAN || scols_table_is_tree(tb))
++		tb->header_repeat = 0;
+ 
+-	bufsz = tb->termwidth;
++	/*
++	 * Estimate extra space necessary for tree, JSON or another output
++	 * decoration.
++	 */
++	if (scols_table_is_tree(tb))
++		extra_bufsz += tb->nlines * strlen(vertical_symbol(tb));
++
++	switch (tb->format) {
++	case SCOLS_FMT_RAW:
++		extra_bufsz += tb->ncols;			/* separator between columns */
++		break;
++	case SCOLS_FMT_JSON:
++		if (tb->format == SCOLS_FMT_JSON)
++			extra_bufsz += tb->nlines * 3;		/* indention */
++		/* fallthrough */
++	case SCOLS_FMT_EXPORT:
++	{
++		struct libscols_column *cl;
++
++		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++
++		while (scols_table_next_column(tb, &itr, &cl) == 0) {
++			if (scols_column_is_hidden(cl))
++				continue;
++			extra_bufsz += strlen(scols_cell_get_data(&cl->header));	/* data */
++			extra_bufsz += 2;						/* separators */
++		}
++		break;
++	}
++	case SCOLS_FMT_HUMAN:
++		break;
++	}
+ 
++	/*
++	 * Enlarge buffer if necessary, the buffer should be large enough to
++	 * store line data and tree ascii art (or another decoration).
++	 */
+ 	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
+ 	while (scols_table_next_line(tb, &itr, &ln) == 0) {
+-		size_t sz = strlen_line(ln);
++		size_t sz;
++
++		sz = strlen_line(ln) + extra_bufsz;
+ 		if (sz > bufsz)
+ 			bufsz = sz;
+ 	}
+ 
+-	buf = new_buffer(bufsz + 1);	/* data + space for \0 */
+-	if (!buf)
+-		return -ENOMEM;
++	*buf = new_buffer(bufsz + 1);	/* data + space for \0 */
++	if (!*buf) {
++		rc = -ENOMEM;
++		goto err;
++	}
+ 
+-	if (!(scols_table_is_raw(tb) || scols_table_is_export(tb))) {
+-		rc = recount_widths(tb, buf);
++	if (tb->format == SCOLS_FMT_HUMAN) {
++		rc = recount_widths(tb, *buf);
+ 		if (rc != 0)
++			goto err;
++	}
++
++	return 0;
++err:
++	cleanup_printing(tb, *buf);
++	return rc;
++}
++
++/**
++ * scola_table_print_range:
++ * @tb: table
++ * @start: first printed line or NULL to print from the begin of the table
++ * @end: last printed line or NULL to print all from start.
++ *
++ * If the start is the first line in the table than prints table header too.
++ * The header is printed only once. This does not work for trees.
++ *
++ * Returns: 0, a negative value in case of an error.
++ */
++int scols_table_print_range(	struct libscols_table *tb,
++				struct libscols_line *start,
++				struct libscols_line *end)
++{
++	struct libscols_buffer *buf = NULL;
++	struct libscols_iter itr;
++	int rc;
++
++	if (scols_table_is_tree(tb))
++		return -EINVAL;
++
++	DBG(TAB, ul_debugobj(tb, "printing range from API"));
++
++	rc = initialize_printing(tb, &buf);
++	if (rc)
++		return rc;
++
++	if (start) {
++		itr.direction = SCOLS_ITER_FORWARD;
++		itr.head = &tb->tb_lines;
++		itr.p = &start->ln_lines;
++	} else
++		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
++
++	if (!start || itr.p == tb->tb_lines.next) {
++		rc = print_header(tb, buf);
++		if (rc)
+ 			goto done;
+ 	}
+ 
++	rc = print_range(tb, buf, &itr, end);
++done:
++	cleanup_printing(tb, buf);
++	return rc;
++}
++
++/**
++ * scols_table_print_range_to_string:
++ * @tb: table
++ * @start: first printed line or NULL to print from the beginning of the table
++ * @end: last printed line or NULL to print all from start.
++ * @data: pointer to the beginning of a memory area to print to
++ *
++ * The same as scols_table_print_range(), but prints to @data instead of
++ * stream.
++ *
++ * Returns: 0, a negative value in case of an error.
++ */
++#ifdef HAVE_OPEN_MEMSTREAM
++int scols_table_print_range_to_string(	struct libscols_table *tb,
++					struct libscols_line *start,
++					struct libscols_line *end,
++					char **data)
++{
++	FILE *stream, *old_stream;
++	size_t sz;
++	int rc;
++
++	if (!tb)
++		return -EINVAL;
++
++	DBG(TAB, ul_debugobj(tb, "printing range to string"));
++
++	/* create a stream for output */
++	stream = open_memstream(data, &sz);
++	if (!stream)
++		return -ENOMEM;
++
++	old_stream = scols_table_get_stream(tb);
++	scols_table_set_stream(tb, stream);
++	rc = scols_table_print_range(tb, start, end);
++	fclose(stream);
++	scols_table_set_stream(tb, old_stream);
++
++	return rc;
++}
++#else
++int scols_table_print_range_to_string(
++			struct libscols_table *tb __attribute__((__unused__)),
++			struct libscols_line *start __attribute__((__unused__)),
++			struct libscols_line *end __attribute__((__unused__)),
++			char **data __attribute__((__unused__)))
++{
++	return -ENOSYS;
++}
++#endif
++
++static int __scols_print_table(struct libscols_table *tb, int *is_empty)
++{
++	int rc = 0;
++	struct libscols_buffer *buf = NULL;
++
++	if (!tb)
++		return -EINVAL;
++
++	DBG(TAB, ul_debugobj(tb, "printing"));
++	if (is_empty)
++		*is_empty = 0;
++
++	if (list_empty(&tb->tb_columns)) {
++		DBG(TAB, ul_debugobj(tb, "error -- no columns"));
++		return -EINVAL;
++	}
++	if (list_empty(&tb->tb_lines)) {
++		DBG(TAB, ul_debugobj(tb, "ignore -- no lines"));
++		if (is_empty)
++			*is_empty = 1;
++		return 0;
++	}
++
++	tb->header_printed = 0;
++	rc = initialize_printing(tb, &buf);
++	if (rc)
++		return rc;
++
++	fput_table_open(tb);
++
++	if (tb->format == SCOLS_FMT_HUMAN)
++		print_title(tb);
++
++	rc = print_header(tb, buf);
++	if (rc)
++		goto done;
++
+ 	if (scols_table_is_tree(tb))
+ 		rc = print_tree(tb, buf);
+ 	else
+ 		rc = print_table(tb, buf);
+ 
++	fput_table_close(tb);
+ done:
+-	free_buffer(buf);
++	cleanup_printing(tb, buf);
++	return rc;
++}
++
++/**
++ * scols_print_table:
++ * @tb: table
++ *
++ * Prints the table to the output stream and terminate by \n.
++ *
++ * Returns: 0, a negative value in case of an error.
++ */
++int scols_print_table(struct libscols_table *tb)
++{
++	int empty = 0;
++	int rc = __scols_print_table(tb, &empty);
++
++	if (rc == 0 && !empty)
++		fputc('\n', tb->out);
+ 	return rc;
+ }
+ 
+@@ -812,10 +1705,10 @@ done:
+  *
+  * Returns: 0, a negative value in case of an error.
+  */
++#ifdef HAVE_OPEN_MEMSTREAM
+ int scols_print_table_to_string(struct libscols_table *tb, char **data)
+ {
+-#ifdef HAVE_OPEN_MEMSTREAM
+-	FILE *stream;
++	FILE *stream, *old_stream;
+ 	size_t sz;
+ 	int rc;
+ 
+@@ -829,13 +1722,20 @@ int scols_print_table_to_string(struct libscols_table *tb, char **data)
+ 	if (!stream)
+ 		return -ENOMEM;
+ 
++	old_stream = scols_table_get_stream(tb);
+ 	scols_table_set_stream(tb, stream);
+-	rc = scols_print_table(tb);
++	rc = __scols_print_table(tb, NULL);
+ 	fclose(stream);
++	scols_table_set_stream(tb, old_stream);
+ 
+ 	return rc;
++}
+ #else
++int scols_print_table_to_string(
++		struct libscols_table *tb __attribute__((__unused__)),
++		char **data  __attribute__((__unused__)))
++{
+ 	return -ENOSYS;
+-#endif
+ }
++#endif
+ 
+-- 
+2.14.4
+
diff --git a/SOURCES/0154-tests-backport-libsmartcols-tests.patch b/SOURCES/0154-tests-backport-libsmartcols-tests.patch
new file mode 100644
index 0000000..e59618d
--- /dev/null
+++ b/SOURCES/0154-tests-backport-libsmartcols-tests.patch
@@ -0,0 +1,990 @@
+From 83f79678dbb9cb48969968fa4df57d98c67a321d Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 31 May 2018 11:44:35 +0200
+Subject: [PATCH 154/173] tests: backport libsmartcols tests
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1561350
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ tests/commands.sh                                  |   2 +
+ tests/expected/libsmartcols/fromfile               |   1 +
+ .../libsmartcols/fromfile-column-separator         |  11 +
+ tests/expected/libsmartcols/fromfile-export        |  10 +
+ tests/expected/libsmartcols/fromfile-hidden        |  11 +
+ tests/expected/libsmartcols/fromfile-noextremes    |  12 ++
+ tests/expected/libsmartcols/fromfile-raw           |  11 +
+ tests/expected/libsmartcols/fromfile-right         |  11 +
+ tests/expected/libsmartcols/fromfile-right-maxout  |  11 +
+ tests/expected/libsmartcols/fromfile-strictwidth   |  11 +
+ tests/expected/libsmartcols/fromfile-tree          |  11 +
+ tests/expected/libsmartcols/fromfile-tree-end      |  11 +
+ tests/expected/libsmartcols/fromfile-tree-json     |  29 +++
+ tests/expected/libsmartcols/fromfile-tree-middle   |  11 +
+ tests/expected/libsmartcols/fromfile-trunc         |  11 +
+ tests/expected/libsmartcols/fromfile-wrap          |  17 ++
+ tests/expected/libsmartcols/fromfile-wrap-tree     |  18 ++
+ tests/expected/libsmartcols/fromfile-wrapnl        |  19 ++
+ tests/expected/libsmartcols/fromfile-wrapnl-tree   |  19 ++
+ tests/expected/libsmartcols/title                  |  16 ++
+ tests/ts/libsmartcols/files/col-hidden             |   3 +
+ tests/ts/libsmartcols/files/col-id                 |   3 +
+ tests/ts/libsmartcols/files/col-name               |   3 +
+ tests/ts/libsmartcols/files/col-noextremes         |   3 +
+ tests/ts/libsmartcols/files/col-number             |   3 +
+ tests/ts/libsmartcols/files/col-parent             |   3 +
+ tests/ts/libsmartcols/files/col-strict             |   3 +
+ tests/ts/libsmartcols/files/col-string             |   3 +
+ tests/ts/libsmartcols/files/col-tree               |   3 +
+ tests/ts/libsmartcols/files/col-trunc              |   3 +
+ tests/ts/libsmartcols/files/col-wrap               |   3 +
+ tests/ts/libsmartcols/files/col-wrapnl             |   3 +
+ tests/ts/libsmartcols/files/data-id                |  10 +
+ tests/ts/libsmartcols/files/data-number            |  10 +
+ tests/ts/libsmartcols/files/data-number-tiny       |  10 +
+ tests/ts/libsmartcols/files/data-parent            |  10 +
+ tests/ts/libsmartcols/files/data-string            |  10 +
+ tests/ts/libsmartcols/files/data-string-extreme    |  10 +
+ tests/ts/libsmartcols/files/data-string-long       |  10 +
+ tests/ts/libsmartcols/files/data-string-nl         |  10 +
+ tests/ts/libsmartcols/fromfile                     | 240 +++++++++++++++++++++
+ tests/ts/libsmartcols/title                        |  28 +++
+ 42 files changed, 637 insertions(+)
+ create mode 100644 tests/expected/libsmartcols/fromfile
+ create mode 100644 tests/expected/libsmartcols/fromfile-column-separator
+ create mode 100644 tests/expected/libsmartcols/fromfile-export
+ create mode 100644 tests/expected/libsmartcols/fromfile-hidden
+ create mode 100644 tests/expected/libsmartcols/fromfile-noextremes
+ create mode 100644 tests/expected/libsmartcols/fromfile-raw
+ create mode 100644 tests/expected/libsmartcols/fromfile-right
+ create mode 100644 tests/expected/libsmartcols/fromfile-right-maxout
+ create mode 100644 tests/expected/libsmartcols/fromfile-strictwidth
+ create mode 100644 tests/expected/libsmartcols/fromfile-tree
+ create mode 100644 tests/expected/libsmartcols/fromfile-tree-end
+ create mode 100644 tests/expected/libsmartcols/fromfile-tree-json
+ create mode 100644 tests/expected/libsmartcols/fromfile-tree-middle
+ create mode 100644 tests/expected/libsmartcols/fromfile-trunc
+ create mode 100644 tests/expected/libsmartcols/fromfile-wrap
+ create mode 100644 tests/expected/libsmartcols/fromfile-wrap-tree
+ create mode 100644 tests/expected/libsmartcols/fromfile-wrapnl
+ create mode 100644 tests/expected/libsmartcols/fromfile-wrapnl-tree
+ create mode 100644 tests/expected/libsmartcols/title
+ create mode 100644 tests/ts/libsmartcols/files/col-hidden
+ create mode 100644 tests/ts/libsmartcols/files/col-id
+ create mode 100644 tests/ts/libsmartcols/files/col-name
+ create mode 100644 tests/ts/libsmartcols/files/col-noextremes
+ create mode 100644 tests/ts/libsmartcols/files/col-number
+ create mode 100644 tests/ts/libsmartcols/files/col-parent
+ create mode 100644 tests/ts/libsmartcols/files/col-strict
+ create mode 100644 tests/ts/libsmartcols/files/col-string
+ create mode 100644 tests/ts/libsmartcols/files/col-tree
+ create mode 100644 tests/ts/libsmartcols/files/col-trunc
+ create mode 100644 tests/ts/libsmartcols/files/col-wrap
+ create mode 100644 tests/ts/libsmartcols/files/col-wrapnl
+ create mode 100644 tests/ts/libsmartcols/files/data-id
+ create mode 100644 tests/ts/libsmartcols/files/data-number
+ create mode 100644 tests/ts/libsmartcols/files/data-number-tiny
+ create mode 100644 tests/ts/libsmartcols/files/data-parent
+ create mode 100644 tests/ts/libsmartcols/files/data-string
+ create mode 100644 tests/ts/libsmartcols/files/data-string-extreme
+ create mode 100644 tests/ts/libsmartcols/files/data-string-long
+ create mode 100644 tests/ts/libsmartcols/files/data-string-nl
+ create mode 100755 tests/ts/libsmartcols/fromfile
+ create mode 100755 tests/ts/libsmartcols/title
+
+diff --git a/tests/commands.sh b/tests/commands.sh
+index e769b5dac..96b8dc97c 100644
+--- a/tests/commands.sh
++++ b/tests/commands.sh
+@@ -20,6 +20,8 @@ TS_HELPER_PARTITIONS="$top_builddir/sample-partitions"
+ TS_HELPER_PATHS="$top_builddir/test_pathnames"
+ TS_HELPER_STRUTILS="$top_builddir/test_strutils"
+ TS_HELPER_SYSINFO="$top_builddir/test_sysinfo"
++TS_HELPER_LIBSMARTCOLS_FROMFILE="$top_builddir/sample-scols-fromfile"
++TS_HELPER_LIBSMARTCOLS_TITLE="$top_builddir/sample-scols-title"
+ 
+ # paths to commands
+ TS_CMD_BLKID=${TS_CMD_BLKID-"$top_builddir/blkid"}
+diff --git a/tests/expected/libsmartcols/fromfile b/tests/expected/libsmartcols/fromfile
+new file mode 100644
+index 000000000..4155aa36f
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile
+@@ -0,0 +1 @@
++...done.
+diff --git a/tests/expected/libsmartcols/fromfile-column-separator b/tests/expected/libsmartcols/fromfile-column-separator
+new file mode 100644
+index 000000000..8c5aa2aff
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-column-separator
+@@ -0,0 +1,11 @@
++NAME  |      NUM|TRUNC
++aaaa  |        0|qqqqqqqqqqqqqqqqqX
++bbb   |      100|dddddddddddddX
++ccccc |       21|ffffffffffffffffffffffffffffffffffffffffX
++dddddd|        3|ssssssssssX
++ee    |      411|ddddddddddddddddddddddddddX
++ffff  |     5111|jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX
++gggggg|678993321|mmmmmmmmmmmmmmmmmmmX
++hhh   |  7666666|lllllllllllllllllllllllllllllllllllllX
++iiiiii|     8765|yyyyyyyyyyyyyyyyyyyyyyyyyyyyX
++jj    |   987456|pppppppppX
+diff --git a/tests/expected/libsmartcols/fromfile-export b/tests/expected/libsmartcols/fromfile-export
+new file mode 100644
+index 000000000..f63afd882
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-export
+@@ -0,0 +1,10 @@
++NAME="aaaa" NUM="0" TRUNC="qqqqqqqqqqqqqqqqqX"
++NAME="bbb" NUM="100" TRUNC="dddddddddddddX"
++NAME="ccccc" NUM="21" TRUNC="ffffffffffffffffffffffffffffffffffffffffX"
++NAME="dddddd" NUM="3" TRUNC="ssssssssssX"
++NAME="ee" NUM="411" TRUNC="ddddddddddddddddddddddddddX"
++NAME="ffff" NUM="5111" TRUNC="jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX"
++NAME="gggggg" NUM="678993321" TRUNC="mmmmmmmmmmmmmmmmmmmX"
++NAME="hhh" NUM="7666666" TRUNC="lllllllllllllllllllllllllllllllllllllX"
++NAME="iiiiii" NUM="8765" TRUNC="yyyyyyyyyyyyyyyyyyyyyyyyyyyyX"
++NAME="jj" NUM="987456" TRUNC="pppppppppX"
+diff --git a/tests/expected/libsmartcols/fromfile-hidden b/tests/expected/libsmartcols/fromfile-hidden
+new file mode 100644
+index 000000000..cf50bcb3e
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-hidden
+@@ -0,0 +1,11 @@
++NAME         NUM
++aaaa           0
++bbb          100
++ccccc         21
++dddddd         3
++ee           411
++ffff        5111
++gggggg 678993321
++hhh      7666666
++iiiiii      8765
++jj        987456
+diff --git a/tests/expected/libsmartcols/fromfile-noextremes b/tests/expected/libsmartcols/fromfile-noextremes
+new file mode 100644
+index 000000000..4bac08070
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-noextremes
+@@ -0,0 +1,12 @@
++NAME   NOEXTREME         NUM NAME         NUM
++aaaa   qqqqqqX             0 aaaa           0
++bbb    ddddddddX         100 bbb          100
++ccccc  ffffffffffffffffffffffffffffffffffX
++                          21 ccccc         21
++dddddd sssX                3 dddddd         3
++ee     ddX               411 ee           411
++ffff   jjjjjX           5111 ffff        5111
++gggggg mmmmmmmX    678993321 gggggg 678993321
++hhh    llllllllllX   7666666 hhh      7666666
++iiiiii yyyyyyX          8765 iiiiii      8765
++jj     pppppX         987456 jj        987456
+diff --git a/tests/expected/libsmartcols/fromfile-raw b/tests/expected/libsmartcols/fromfile-raw
+new file mode 100644
+index 000000000..cc188aaab
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-raw
+@@ -0,0 +1,11 @@
++NAME NUM TRUNC
++aaaa 0 qqqqqqqqqqqqqqqqqX
++bbb 100 dddddddddddddX
++ccccc 21 ffffffffffffffffffffffffffffffffffffffffX
++dddddd 3 ssssssssssX
++ee 411 ddddddddddddddddddddddddddX
++ffff 5111 jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX
++gggggg 678993321 mmmmmmmmmmmmmmmmmmmX
++hhh 7666666 lllllllllllllllllllllllllllllllllllllX
++iiiiii 8765 yyyyyyyyyyyyyyyyyyyyyyyyyyyyX
++jj 987456 pppppppppX
+diff --git a/tests/expected/libsmartcols/fromfile-right b/tests/expected/libsmartcols/fromfile-right
+new file mode 100644
+index 000000000..d78285a13
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-right
+@@ -0,0 +1,11 @@
++NAME         NUM STRINGS
++aaaa           0 qqqqqqqqqqqqqqqqqX
++bbb          100 dddddddddddddX
++ccccc         21 ffffffffffffffffffffffffffffffffffffffffX
++dddddd         3 ssssssssssX
++ee           411 ddddddddddddddddddddddddddX
++ffff        5111 jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX
++gggggg 678993321 mmmmmmmmmmmmmmmmmmmX
++hhh      7666666 lllllllllllllllllllllllllllllllllllllX
++iiiiii      8765 yyyyyyyyyyyyyyyyyyyyyyyyyyyyX
++jj        987456 pppppppppX
+diff --git a/tests/expected/libsmartcols/fromfile-right-maxout b/tests/expected/libsmartcols/fromfile-right-maxout
+new file mode 100644
+index 000000000..3bcb65abd
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-right-maxout
+@@ -0,0 +1,11 @@
++NAME                                                                         NUM
++aaaa                                                                           0
++bbb                                                                          100
++ccccc                                                                         21
++dddddd                                                                         3
++ee                                                                           411
++ffff                                                                        5111
++gggggg                                                                 678993321
++hhh                                                                      7666666
++iiiiii                                                                      8765
++jj                                                                        987456
+diff --git a/tests/expected/libsmartcols/fromfile-strictwidth b/tests/expected/libsmartcols/fromfile-strictwidth
+new file mode 100644
+index 000000000..41054b235
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-strictwidth
+@@ -0,0 +1,11 @@
++NAME                 STRICT       NUM
++aaaa                      0         0
++bbb                       1       100
++ccccc                     2        21
++dddddd                    3         3
++ee                        4       411
++ffff                      5      5111
++gggggg                    6 678993321
++hhh                       7   7666666
++iiiiii                    8      8765
++jj                        9    987456
+diff --git a/tests/expected/libsmartcols/fromfile-tree b/tests/expected/libsmartcols/fromfile-tree
+new file mode 100644
+index 000000000..9d345f8dc
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-tree
+@@ -0,0 +1,11 @@
++TREE           ID PARENT STRINGS
++aaaa            1      0 qqqqqqqqqqqqqqqqqX
++|-bbb           2      1 dddddddddddddX
++| |-ee          5      2 ddddddddddddddddddddddddddX
++| `-ffff        6      2 jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX
++|-ccccc         3      1 ffffffffffffffffffffffffffffffffffffffffX
++| `-gggggg      7      3 mmmmmmmmmmmmmmmmmmmX
++|   |-hhh       8      7 lllllllllllllllllllllllllllllllllllllX
++|   | `-iiiiii  9      8 yyyyyyyyyyyyyyyyyyyyyyyyyyyyX
++|   `-jj       10      7 pppppppppX
++`-dddddd        4      1 ssssssssssX
+diff --git a/tests/expected/libsmartcols/fromfile-tree-end b/tests/expected/libsmartcols/fromfile-tree-end
+new file mode 100644
+index 000000000..41aebff56
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-tree-end
+@@ -0,0 +1,11 @@
++ID PARENT STRINGS                                            TREE
++ 1      0 qqqqqqqqqqqqqqqqqX                                 aaaa
++ 2      1 dddddddddddddX                                     |-bbb
++ 5      2 ddddddddddddddddddddddddddX                        | |-ee
++ 6      2 jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX | `-ffff
++ 3      1 ffffffffffffffffffffffffffffffffffffffffX          |-ccccc
++ 7      3 mmmmmmmmmmmmmmmmmmmX                               | `-gggggg
++ 8      7 lllllllllllllllllllllllllllllllllllllX             |   |-hhh
++ 9      8 yyyyyyyyyyyyyyyyyyyyyyyyyyyyX                      |   | `-iiiiii
++10      7 pppppppppX                                         |   `-jj
++ 4      1 ssssssssssX                                        `-dddddd
+diff --git a/tests/expected/libsmartcols/fromfile-tree-json b/tests/expected/libsmartcols/fromfile-tree-json
+new file mode 100644
+index 000000000..5b3546d0a
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-tree-json
+@@ -0,0 +1,29 @@
++{
++   "testtable": [
++      {"tree":"aaaa", "id":"1", "parent":"0", "strings":"qqqqqqqqqqqqqqqqqX",
++         "children": [
++            {"tree":"bbb", "id":"2", "parent":"1", "strings":"dddddddddddddX",
++               "children": [
++                  {"tree":"ee", "id":"5", "parent":"2", "strings":"ddddddddddddddddddddddddddX"},
++                  {"tree":"ffff", "id":"6", "parent":"2", "strings":"jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX"}
++               ]
++            },
++            {"tree":"ccccc", "id":"3", "parent":"1", "strings":"ffffffffffffffffffffffffffffffffffffffffX",
++               "children": [
++                  {"tree":"gggggg", "id":"7", "parent":"3", "strings":"mmmmmmmmmmmmmmmmmmmX",
++                     "children": [
++                        {"tree":"hhh", "id":"8", "parent":"7", "strings":"lllllllllllllllllllllllllllllllllllllX",
++                           "children": [
++                              {"tree":"iiiiii", "id":"9", "parent":"8", "strings":"yyyyyyyyyyyyyyyyyyyyyyyyyyyyX"}
++                           ]
++                        },
++                        {"tree":"jj", "id":"10", "parent":"7", "strings":"pppppppppX"}
++                     ]
++                  }
++               ]
++            },
++            {"tree":"dddddd", "id":"4", "parent":"1", "strings":"ssssssssssX"}
++         ]
++      }
++   ]
++}
+diff --git a/tests/expected/libsmartcols/fromfile-tree-middle b/tests/expected/libsmartcols/fromfile-tree-middle
+new file mode 100644
+index 000000000..b2183313c
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-tree-middle
+@@ -0,0 +1,11 @@
++ID PARENT TREE           STRINGS
++ 1      0 aaaa           qqqqqqqqqqqqqqqqqX
++ 2      1 |-bbb          dddddddddddddX
++ 5      2 | |-ee         ddddddddddddddddddddddddddX
++ 6      2 | `-ffff       jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX
++ 3      1 |-ccccc        ffffffffffffffffffffffffffffffffffffffffX
++ 7      3 | `-gggggg     mmmmmmmmmmmmmmmmmmmX
++ 8      7 |   |-hhh      lllllllllllllllllllllllllllllllllllllX
++ 9      8 |   | `-iiiiii yyyyyyyyyyyyyyyyyyyyyyyyyyyyX
++10      7 |   `-jj       pppppppppX
++ 4      1 `-dddddd       ssssssssssX
+diff --git a/tests/expected/libsmartcols/fromfile-trunc b/tests/expected/libsmartcols/fromfile-trunc
+new file mode 100644
+index 000000000..6a095dc88
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-trunc
+@@ -0,0 +1,11 @@
++NAME         NUM TRUNC
++aaaa           0 qqqqqqqqqqqqqqqqqX
++bbb          100 dddddddddddddX
++ccccc         21 fffffffffffffffffffffff
++dddddd         3 ssssssssssX
++ee           411 ddddddddddddddddddddddd
++ffff        5111 jjjjjjjjjjjjjjjjjjjjjjj
++gggggg 678993321 mmmmmmmmmmmmmmmmmmmX
++hhh      7666666 lllllllllllllllllllllll
++iiiiii      8765 yyyyyyyyyyyyyyyyyyyyyyy
++jj        987456 pppppppppX
+diff --git a/tests/expected/libsmartcols/fromfile-wrap b/tests/expected/libsmartcols/fromfile-wrap
+new file mode 100644
+index 000000000..bb94f4973
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-wrap
+@@ -0,0 +1,17 @@
++NAME         NUM WRAP
++aaaa           0 qqqqqqqqqqqqqqqqqX
++bbb          100 dddddddddddddX
++ccccc         21 fffffffffffffffffffffff
++                 fffffffffffffffffX
++dddddd         3 ssssssssssX
++ee           411 ddddddddddddddddddddddd
++                 dddX
++ffff        5111 jjjjjjjjjjjjjjjjjjjjjjj
++                 jjjjjjjjjjjjjjjjjjjjjjj
++                 jjjX
++gggggg 678993321 mmmmmmmmmmmmmmmmmmmX
++hhh      7666666 lllllllllllllllllllllll
++                 llllllllllllllX
++iiiiii      8765 yyyyyyyyyyyyyyyyyyyyyyy
++                 yyyyyX
++jj        987456 pppppppppX
+diff --git a/tests/expected/libsmartcols/fromfile-wrap-tree b/tests/expected/libsmartcols/fromfile-wrap-tree
+new file mode 100644
+index 000000000..a2db7a4ac
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-wrap-tree
+@@ -0,0 +1,18 @@
++TREE           ID PARENT WRAP
++aaaa            1      0 qqqqqqqqqqqqqqqqqX
++|-bbb           2      1 dddddddddddddX
++| |-ee          5      2 dddddddddddddddddddd
++| |                      ddddddX
++| `-ffff        6      2 jjjjjjjjjjjjjjjjjjjj
++|                        jjjjjjjjjjjjjjjjjjjj
++|                        jjjjjjjjjX
++|-ccccc         3      1 ffffffffffffffffffff
++| |                      ffffffffffffffffffff
++| |                      X
++| `-gggggg      7      3 mmmmmmmmmmmmmmmmmmmX
++|   |-hhh       8      7 llllllllllllllllllll
++|   | |                  lllllllllllllllllX
++|   | `-iiiiii  9      8 yyyyyyyyyyyyyyyyyyyy
++|   |                    yyyyyyyyX
++|   `-jj       10      7 pppppppppX
++`-dddddd        4      1 ssssssssssX
+diff --git a/tests/expected/libsmartcols/fromfile-wrapnl b/tests/expected/libsmartcols/fromfile-wrapnl
+new file mode 100644
+index 000000000..c747ebb08
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-wrapnl
+@@ -0,0 +1,19 @@
++NAME         NUM WRAPNL
++aaaa           0 aaa
++bbb          100 bbbbb
++ccccc         21 cccc
++                 CCCC
++dddddd         3 dddddddd
++                 DDDD
++                 DD
++ee           411 hello
++                 baby
++ffff        5111 aaa
++                 bbb
++                 ccc
++                 ddd
++gggggg 678993321 eee
++hhh      7666666 fffff
++iiiiii      8765 g
++                 hhhhh
++jj        987456 ppppppppp
+diff --git a/tests/expected/libsmartcols/fromfile-wrapnl-tree b/tests/expected/libsmartcols/fromfile-wrapnl-tree
+new file mode 100644
+index 000000000..3862cf16a
+--- /dev/null
++++ b/tests/expected/libsmartcols/fromfile-wrapnl-tree
+@@ -0,0 +1,19 @@
++TREE           ID PARENT WRAPNL
++aaaa            1      0 aaa
++|-bbb           2      1 bbbbb
++| |-ee          5      2 hello
++| |                      baby
++| `-ffff        6      2 aaa
++|                        bbb
++|                        ccc
++|                        ddd
++|-ccccc         3      1 cccc
++| |                      CCCC
++| `-gggggg      7      3 eee
++|   |-hhh       8      7 fffff
++|   | `-iiiiii  9      8 g
++|   |                    hhhhh
++|   `-jj       10      7 ppppppppp
++`-dddddd        4      1 dddddddd
++                         DDDD
++                         DD
+diff --git a/tests/expected/libsmartcols/title b/tests/expected/libsmartcols/title
+new file mode 100644
+index 000000000..bb5d7225a
+--- /dev/null
++++ b/tests/expected/libsmartcols/title
+@@ -0,0 +1,16 @@
++                                                             This is right title
++NAME DATA
++foo  bla bla bla
++bar  alb alb alb
++This is left title (without padding)
++NAME DATA
++foo  bla bla bla
++bar  alb alb alb
++=======================This is center title (with padding)======================
++NAME DATA
++foo  bla bla bla
++bar  alb alb alb
++This is left title (with padding)-----------------------------------------------
++NAME DATA
++foo  bla bla bla
++bar  alb alb alb
+diff --git a/tests/ts/libsmartcols/files/col-hidden b/tests/ts/libsmartcols/files/col-hidden
+new file mode 100644
+index 000000000..83182a8ee
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-hidden
+@@ -0,0 +1,3 @@
++FOO
++0
++hidden
+diff --git a/tests/ts/libsmartcols/files/col-id b/tests/ts/libsmartcols/files/col-id
+new file mode 100644
+index 000000000..0188f42a0
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-id
+@@ -0,0 +1,3 @@
++ID
++0
++right
+diff --git a/tests/ts/libsmartcols/files/col-name b/tests/ts/libsmartcols/files/col-name
+new file mode 100644
+index 000000000..0a98f29cf
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-name
+@@ -0,0 +1,3 @@
++NAME
++0
++none
+diff --git a/tests/ts/libsmartcols/files/col-noextremes b/tests/ts/libsmartcols/files/col-noextremes
+new file mode 100644
+index 000000000..715edce7e
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-noextremes
+@@ -0,0 +1,3 @@
++NOEXTREME
++0
++noextremes
+diff --git a/tests/ts/libsmartcols/files/col-number b/tests/ts/libsmartcols/files/col-number
+new file mode 100644
+index 000000000..34a70e4a4
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-number
+@@ -0,0 +1,3 @@
++NUM
++0
++right
+diff --git a/tests/ts/libsmartcols/files/col-parent b/tests/ts/libsmartcols/files/col-parent
+new file mode 100644
+index 000000000..86fe08ced
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-parent
+@@ -0,0 +1,3 @@
++PARENT
++0
++right
+diff --git a/tests/ts/libsmartcols/files/col-strict b/tests/ts/libsmartcols/files/col-strict
+new file mode 100644
+index 000000000..62bb96b9a
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-strict
+@@ -0,0 +1,3 @@
++STRICT
++20
++strictwidth,right
+diff --git a/tests/ts/libsmartcols/files/col-string b/tests/ts/libsmartcols/files/col-string
+new file mode 100644
+index 000000000..7e2904b9f
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-string
+@@ -0,0 +1,3 @@
++STRINGS
++0
++none
+diff --git a/tests/ts/libsmartcols/files/col-tree b/tests/ts/libsmartcols/files/col-tree
+new file mode 100644
+index 000000000..507688000
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-tree
+@@ -0,0 +1,3 @@
++TREE
++0
++tree
+diff --git a/tests/ts/libsmartcols/files/col-trunc b/tests/ts/libsmartcols/files/col-trunc
+new file mode 100644
+index 000000000..2887b4314
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-trunc
+@@ -0,0 +1,3 @@
++TRUNC
++0
++trunc
+diff --git a/tests/ts/libsmartcols/files/col-wrap b/tests/ts/libsmartcols/files/col-wrap
+new file mode 100644
+index 000000000..dc4ca340e
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-wrap
+@@ -0,0 +1,3 @@
++WRAP
++0
++wrap
+diff --git a/tests/ts/libsmartcols/files/col-wrapnl b/tests/ts/libsmartcols/files/col-wrapnl
+new file mode 100644
+index 000000000..0a18fd146
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/col-wrapnl
+@@ -0,0 +1,3 @@
++WRAPNL
++0
++wrapnl
+diff --git a/tests/ts/libsmartcols/files/data-id b/tests/ts/libsmartcols/files/data-id
+new file mode 100644
+index 000000000..f00c965d8
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/data-id
+@@ -0,0 +1,10 @@
++1
++2
++3
++4
++5
++6
++7
++8
++9
++10
+diff --git a/tests/ts/libsmartcols/files/data-number b/tests/ts/libsmartcols/files/data-number
+new file mode 100644
+index 000000000..562d75061
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/data-number
+@@ -0,0 +1,10 @@
++0
++100
++21
++3
++411
++5111
++678993321
++7666666
++8765
++987456
+diff --git a/tests/ts/libsmartcols/files/data-number-tiny b/tests/ts/libsmartcols/files/data-number-tiny
+new file mode 100644
+index 000000000..8b1acc12b
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/data-number-tiny
+@@ -0,0 +1,10 @@
++0
++1
++2
++3
++4
++5
++6
++7
++8
++9
+diff --git a/tests/ts/libsmartcols/files/data-parent b/tests/ts/libsmartcols/files/data-parent
+new file mode 100644
+index 000000000..aa5071608
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/data-parent
+@@ -0,0 +1,10 @@
++0
++1
++1
++1
++2
++2
++3
++7
++8
++7
+diff --git a/tests/ts/libsmartcols/files/data-string b/tests/ts/libsmartcols/files/data-string
+new file mode 100644
+index 000000000..dff6e9c80
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/data-string
+@@ -0,0 +1,10 @@
++aaaa
++bbb
++ccccc
++dddddd
++ee
++ffff
++gggggg
++hhh
++iiiiii
++jj
+diff --git a/tests/ts/libsmartcols/files/data-string-extreme b/tests/ts/libsmartcols/files/data-string-extreme
+new file mode 100644
+index 000000000..6fb395d7a
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/data-string-extreme
+@@ -0,0 +1,10 @@
++qqqqqqX
++ddddddddX
++ffffffffffffffffffffffffffffffffffX
++sssX
++ddX
++jjjjjX
++mmmmmmmX
++llllllllllX
++yyyyyyX
++pppppX
+diff --git a/tests/ts/libsmartcols/files/data-string-long b/tests/ts/libsmartcols/files/data-string-long
+new file mode 100644
+index 000000000..1b5683aa4
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/data-string-long
+@@ -0,0 +1,10 @@
++qqqqqqqqqqqqqqqqqX
++dddddddddddddX
++ffffffffffffffffffffffffffffffffffffffffX
++ssssssssssX
++ddddddddddddddddddddddddddX
++jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjX
++mmmmmmmmmmmmmmmmmmmX
++lllllllllllllllllllllllllllllllllllllX
++yyyyyyyyyyyyyyyyyyyyyyyyyyyyX
++pppppppppX
+diff --git a/tests/ts/libsmartcols/files/data-string-nl b/tests/ts/libsmartcols/files/data-string-nl
+new file mode 100644
+index 000000000..7822e57bc
+--- /dev/null
++++ b/tests/ts/libsmartcols/files/data-string-nl
+@@ -0,0 +1,10 @@
++aaa
++bbbbb
++cccc\nCCCC
++dddddddd\nDDDD\nDD
++hello\nbaby
++aaa\nbbb\nccc\nddd
++eee
++fffff
++g\nhhhhh
++ppppppppp
+diff --git a/tests/ts/libsmartcols/fromfile b/tests/ts/libsmartcols/fromfile
+new file mode 100755
+index 000000000..311dd7e1e
+--- /dev/null
++++ b/tests/ts/libsmartcols/fromfile
+@@ -0,0 +1,240 @@
++#!/bin/bash
++#
++# This file is part of util-linux.
++#
++# This file is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This file is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++#
++
++TS_TOPDIR="${0%/*}/../.."
++TS_DESC="fromfile"
++
++. $TS_TOPDIR/functions.sh
++ts_init "$*"
++
++TESTPROG="$TS_HELPER_LIBSMARTCOLS_FROMFILE"
++ts_check_test_command "$TESTPROG"
++
++ts_init_subtest "tree"
++$TESTPROG --nlines 10 \
++	--tree-id-column 1 \
++	--tree-parent-column 2 \
++	--column $TS_SELF/files/col-tree \
++	--column $TS_SELF/files/col-id \
++	--column $TS_SELF/files/col-parent \
++	--column $TS_SELF/files/col-string \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-id \
++	$TS_SELF/files/data-parent \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "tree-json"
++$TESTPROG --nlines 10 --json \
++	--tree-id-column 1 \
++	--tree-parent-column 2 \
++	--column $TS_SELF/files/col-tree \
++	--column $TS_SELF/files/col-id \
++	--column $TS_SELF/files/col-parent \
++	--column $TS_SELF/files/col-string \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-id \
++	$TS_SELF/files/data-parent \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "tree-middle"
++$TESTPROG --nlines 10 \
++	--tree-id-column 0 \
++	--tree-parent-column 1 \
++	--column $TS_SELF/files/col-id \
++	--column $TS_SELF/files/col-parent \
++	--column $TS_SELF/files/col-tree \
++	--column $TS_SELF/files/col-string \
++	$TS_SELF/files/data-id \
++	$TS_SELF/files/data-parent \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "tree-end"
++$TESTPROG --nlines 10 \
++	--tree-id-column 0 \
++	--tree-parent-column 1 \
++	--column $TS_SELF/files/col-id \
++	--column $TS_SELF/files/col-parent \
++	--column $TS_SELF/files/col-string \
++	--column $TS_SELF/files/col-tree \
++	$TS_SELF/files/data-id \
++	$TS_SELF/files/data-parent \
++	$TS_SELF/files/data-string-long \
++	$TS_SELF/files/data-string \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "trunc"
++$TESTPROG --nlines 10 --width 40 \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	--column $TS_SELF/files/col-trunc \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "right"
++$TESTPROG --nlines 10 \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	--column $TS_SELF/files/col-string \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "right-maxout"
++$TESTPROG --nlines 10 --maxout --width 80\
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "strictwidth"
++$TESTPROG --nlines 10 \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-strict \
++	--column $TS_SELF/files/col-number \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number-tiny \
++	$TS_SELF/files/data-number \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "noextremes"
++$TESTPROG --nlines 10 --width 45 \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-noextremes \
++	--column $TS_SELF/files/col-number \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-string-extreme \
++	$TS_SELF/files/data-number \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "hidden"
++$TESTPROG --nlines 10 \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-hidden \
++	--column $TS_SELF/files/col-number \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-string-long \
++	$TS_SELF/files/data-number \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "wrap"
++$TESTPROG --nlines 10 --width 40 \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	--column $TS_SELF/files/col-wrap \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "wrap-tree"
++$TESTPROG --nlines 10 --width 45 \
++	--tree-id-column 1 \
++	--tree-parent-column 2 \
++	--column $TS_SELF/files/col-tree \
++	--column $TS_SELF/files/col-id \
++	--column $TS_SELF/files/col-parent \
++	--column $TS_SELF/files/col-wrap \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-id \
++	$TS_SELF/files/data-parent \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "wrapnl"
++$TESTPROG --nlines 10 \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	--column $TS_SELF/files/col-wrapnl \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	$TS_SELF/files/data-string-nl \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "wrapnl-tree"
++$TESTPROG --nlines 10 \
++	--tree-id-column 1 \
++	--tree-parent-column 2 \
++	--column $TS_SELF/files/col-tree \
++	--column $TS_SELF/files/col-id \
++	--column $TS_SELF/files/col-parent \
++	--column $TS_SELF/files/col-wrapnl \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-id \
++	$TS_SELF/files/data-parent \
++	$TS_SELF/files/data-string-nl \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "raw"
++$TESTPROG --nlines 10 --raw \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	--column $TS_SELF/files/col-trunc \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "export"
++$TESTPROG --nlines 10 --export \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	--column $TS_SELF/files/col-trunc \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_init_subtest "column-separator"
++$TESTPROG --nlines 10 --colsep \| \
++	--column $TS_SELF/files/col-name \
++	--column $TS_SELF/files/col-number \
++	--column $TS_SELF/files/col-trunc \
++	$TS_SELF/files/data-string \
++	$TS_SELF/files/data-number \
++	$TS_SELF/files/data-string-long \
++	>> $TS_OUTPUT 2>&1
++ts_finalize_subtest
++
++ts_log "...done."
++ts_finalize
+diff --git a/tests/ts/libsmartcols/title b/tests/ts/libsmartcols/title
+new file mode 100755
+index 000000000..a9ba6a4fd
+--- /dev/null
++++ b/tests/ts/libsmartcols/title
+@@ -0,0 +1,28 @@
++#!/bin/bash
++#
++# This file is part of util-linux.
++#
++# This file is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This file is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++# GNU General Public License for more details.
++#
++#
++
++TS_TOPDIR="${0%/*}/../.."
++TS_DESC="title"
++
++. $TS_TOPDIR/functions.sh
++ts_init "$*"
++
++TESTPROG="$TS_HELPER_LIBSMARTCOLS_TITLE"
++ts_check_test_command "$TESTPROG"
++
++$TESTPROG --width 80 >> $TS_OUTPUT 2>&1
++
++ts_finalize
+-- 
+2.14.4
+
diff --git a/SOURCES/0155-lslogins-fix-password-verification.patch b/SOURCES/0155-lslogins-fix-password-verification.patch
new file mode 100644
index 0000000..b508526
--- /dev/null
+++ b/SOURCES/0155-lslogins-fix-password-verification.patch
@@ -0,0 +1,114 @@
+From f769cb435c4db2e7f6d11e14fe87a1c81e0912fe Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 23 May 2018 12:43:26 +0200
+Subject: [PATCH 155/173] lslogins: fix password verification
+
+Let's follow the standard $id$salt$encrypted password format in
+verification code.
+
+The current code is useless and for example PWD-LOCK column is always
+FALSE.
+
+Upstream: http://github.com/karelzak/util-linux/commit/214fbec40abf0432b8e7968f05024ee76d11b3c7
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1581611
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ login-utils/lslogins.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 74 insertions(+), 4 deletions(-)
+
+diff --git a/login-utils/lslogins.c b/login-utils/lslogins.c
+index d7a24b1fb..041053625 100644
+--- a/login-utils/lslogins.c
++++ b/login-utils/lslogins.c
+@@ -541,14 +541,84 @@ static int get_nprocs(const uid_t uid)
+ 	return nprocs;
+ }
+ 
++static const char *get_pwd_method(const char *str, const char **next, unsigned int *sz)
++{
++	const char *p = str;
++	const char *res = NULL;
++
++	if (!p || *p++ != '$')
++		return NULL;
++
++	if (sz)
++		*sz = 0;
++
++	switch (*p) {
++	case '1':
++		res = "MD5";
++		if (sz)
++			*sz = 22;
++		break;
++	case '2':
++		p++;
++		if (*p == 'a' || *p == 'y')
++			res = "Blowfish";
++		break;
++	case '5':
++		res = "SHA-256";
++		if (sz)
++			*sz = 43;
++		break;
++	case '6':
++		res = "SHA-512";
++		if (sz)
++			*sz = 86;
++		break;
++	default:
++		return NULL;
++	}
++	p++;
++
++	if (!*p || *p != '$')
++		return NULL;
++	if (next)
++		*next = ++p;
++	return res;
++}
++
++#define is_valid_pwd_char(x)	(isalnum((unsigned char) (x)) || (x) ==  '.' || (x) == '/')
++
+ static int valid_pwd(const char *str)
+ {
+-	const char *p;
++	const char *p = str;
++	unsigned int sz = 0, n;
++
++	/* $id$ */
++	if (get_pwd_method(str, &p, &sz) == NULL)
++		return 0;
++	if (!*p)
++		return 0;
+ 
+-	for (p = str; p && *p; p++)
+-		if (!isalnum((unsigned int) *p))
++	/* salt$ */
++	for (; p && *p; p++) {
++		if (*p == '$') {
++			p++;
++			break;
++		}
++		if (!is_valid_pwd_char(*p))
+ 			return 0;
+-	return p > str ? 1 : 0;
++	}
++	if (!*p)
++		return 0;
++
++	/* encrypted */
++	for (n = 0; p && *p; p++, n++) {
++		if (!is_valid_pwd_char(*p))
++			return 0;
++	}
++
++	if (sz && n != sz)
++		return 0;
++	return 1;
+ }
+ 
+ static struct lslogins_user *get_user_info(struct lslogins_control *ctl, const char *username)
+-- 
+2.14.4
+
diff --git a/SOURCES/0156-umount-add-note-about-lazy.patch b/SOURCES/0156-umount-add-note-about-lazy.patch
new file mode 100644
index 0000000..fc01a26
--- /dev/null
+++ b/SOURCES/0156-umount-add-note-about-lazy.patch
@@ -0,0 +1,53 @@
+From 0ae2aa6a43a852cb0326faade1532c02aa7617a4 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Fri, 20 Apr 2018 09:50:04 +0200
+Subject: [PATCH 156/173] umount: add note about --lazy
+
+Unfortunately, it's pretty common that users on production systems use
+lazy umount to fix some FS issues. The usual result is unwanted system
+reboot, because -l is not the right way how to fix unreachable NFS or
+mess with local FS with submounts.
+
+Note that after lazy umount /proc/self/mountinfo does not contain the
+FS entry, but kernel still references the FS. It makes it very
+difficult to debug.
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1566674
+Suggested-by:  Steve Dickson <steved@redhat.com>
+Signed-off-by: Karel Zak <kzak@redhat.com>
+Upstream: http://github.com/karelzak/util-linux/commit/031800ff6c66b4d62229cfd116195950a718a21c
+---
+ sys-utils/umount.8 | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/sys-utils/umount.8 b/sys-utils/umount.8
+index f0a712b06..71c2155b4 100644
+--- a/sys-utils/umount.8
++++ b/sys-utils/umount.8
+@@ -54,7 +54,8 @@ working directory there, or when a swap file on it is in use.  The
+ offending process could even be
+ .B umount
+ itself - it opens libc, and libc in its turn may open for example locale
+-files.  A lazy unmount avoids this problem.
++files.  A lazy unmount avoids this problem, but it may introduce another
++issues. See \fB\-\-lazy\fR description bellow.
+ .SH OPTIONS
+ .TP
+ \fB\-a\fR, \fB\-\-all\fR
+@@ -107,6 +108,13 @@ Unmount without writing in
+ Lazy unmount.  Detach the filesystem from the filesystem hierarchy now,
+ and cleanup all references to the filesystem as soon as it is not busy
+ anymore.  (Requires kernel 2.4.11 or later.)
++
++A system reboot would be expected in near future if you're going to use this
++option for network filesystem or local filesystem with submounts.  The
++recommended use-case for \fBumount -l\fR is to prevent hangs on shutdown due to
++an unreachable network share where a normal umount will hang due to a downed
++server or a network partition. Remounts of the share will not be possible.
++
+ .TP
+ \fB\-O\fR, \fB\-\-test\-opts\fR \fIoptions,list\fR
+ Indicate that the actions should only be taken on file systems with the
+-- 
+2.14.4
+
diff --git a/SOURCES/0157-losetup-add-info-about-lazy-detach-to-manpage.patch b/SOURCES/0157-losetup-add-info-about-lazy-detach-to-manpage.patch
new file mode 100644
index 0000000..cd95dfe
--- /dev/null
+++ b/SOURCES/0157-losetup-add-info-about-lazy-detach-to-manpage.patch
@@ -0,0 +1,31 @@
+From 0258872e2b45b589481e6796aa983f8749be9ada Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 10 Aug 2017 12:37:34 +0200
+Subject: [PATCH 157/173] losetup: add info about lazy detach to manpage
+
+Upstream: http://github.com/karelzak/util-linux/commit/60eedb0a53f14aa1169713a72325421db8e1c647
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1566390
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/losetup.8 | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/sys-utils/losetup.8 b/sys-utils/losetup.8
+index 9a8c1d597..45c220187 100644
+--- a/sys-utils/losetup.8
++++ b/sys-utils/losetup.8
+@@ -80,7 +80,10 @@ without --list) is deprecated.
+ .IP "\fB\-c, \-\-set-capacity\fP \fIloopdev\fP
+ force loop driver to reread size of the file associated with the specified loop device
+ .IP "\fB\-d, \-\-detach\fP \fIloopdev\fP..."
+-detach the file or device associated with the specified loop device(s)
++detach the file or device associated with the specified loop device(s). Note
++that since Linux v3.7 kernel uses "lazy device destruction".  The detach
++operation does not return EBUSY error anymore if device is actively used by
++system, but it is marked by autoclear flag and destroyed later.
+ .IP "\fB\-D, \-\-detach-all\fP"
+ detach all associated loop devices
+ .IP "\fB\-f, \-\-find\fP"
+-- 
+2.14.4
+
diff --git a/SOURCES/0158-setarch-Fix-ppc64le-architectures.patch b/SOURCES/0158-setarch-Fix-ppc64le-architectures.patch
new file mode 100644
index 0000000..6e0b02d
--- /dev/null
+++ b/SOURCES/0158-setarch-Fix-ppc64le-architectures.patch
@@ -0,0 +1,44 @@
+From e10a3a201b2b4dc6ff9957600c9bf67a2f9de38e Mon Sep 17 00:00:00 2001
+From: Anton Blanchard <anton@samba.org>
+Date: Mon, 7 Apr 2014 09:18:13 +1000
+Subject: [PATCH 158/173] setarch: Fix ppc64le architectures
+
+setarch currently fails on ppc64le because it tries to
+use big endian architecture names. Fix it.
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1562125
+Upstream: http://github.com/karelzak/util-linux/commit/95bf26fd68ec7f0b2dde1f022dc79d04d1a6e620
+Upstream: http://github.com/karelzak/util-linux/commit/e60b6df54e27cdb68a75ea8a056a70f60df8f025
+Signed-off-by: Anton Blanchard <anton@samba.org>
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/setarch.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/sys-utils/setarch.c b/sys-utils/setarch.c
+index 051cbefcd..b03406dd3 100644
+--- a/sys-utils/setarch.c
++++ b/sys-utils/setarch.c
+@@ -149,11 +149,19 @@ set_arch(const char *pers, unsigned long options, int list)
+     {PER_LINUX32, "linux32", NULL},
+     {PER_LINUX, "linux64", NULL},
+ #if defined(__powerpc__) || defined(__powerpc64__)
++#ifdef __BIG_ENDIAN__
+     {PER_LINUX32, "ppc32", "ppc"},
+     {PER_LINUX32, "ppc", "ppc"},
+     {PER_LINUX, "ppc64", "ppc64"},
+     {PER_LINUX, "ppc64pseries", "ppc64"},
+     {PER_LINUX, "ppc64iseries", "ppc64"},
++#else
++    {PER_LINUX32, "ppc32",  "ppcle"},
++    {PER_LINUX32, "ppc",    "ppcle"},
++    {PER_LINUX32, "ppc32le", "ppcle"},
++    {PER_LINUX32, "ppcle", "ppcle"},
++    {PER_LINUX, "ppc64le", "ppc64le"},
++#endif
+ #endif
+ #if defined(__x86_64__) || defined(__i386__) || defined(__ia64__)
+     {PER_LINUX32, "i386", "i386"},
+-- 
+2.14.4
+
diff --git a/SOURCES/0159-fallocate-backport-v2.32-164-g641af90dc.patch b/SOURCES/0159-fallocate-backport-v2.32-164-g641af90dc.patch
new file mode 100644
index 0000000..615638d
--- /dev/null
+++ b/SOURCES/0159-fallocate-backport-v2.32-164-g641af90dc.patch
@@ -0,0 +1,717 @@
+From 9f2a32a8fd08bb1b48f29c88bfa398fa4eb5f2a4 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 6 Jun 2018 11:59:16 +0200
+Subject: [PATCH 159/173] fallocate: backport v2.32-164-g641af90dc
+
+* add --dig-holes
+* add --collapse-range
+* add --insert-range
+* add --zero-range
+
+For backward compatibility with previous RHEL7 versions we keep
+O_CREAT for open(). The current upstream uses O_CREAT only when
+necessary.
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1528567
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/fallocate.1 | 195 ++++++++++++++++++++++------
+ sys-utils/fallocate.c | 349 +++++++++++++++++++++++++++++++++++++++-----------
+ 2 files changed, 428 insertions(+), 116 deletions(-)
+
+diff --git a/sys-utils/fallocate.1 b/sys-utils/fallocate.1
+index 376353013..d4821dcd1 100644
+--- a/sys-utils/fallocate.1
++++ b/sys-utils/fallocate.1
+@@ -1,72 +1,185 @@
+-.\" -*- nroff -*-
+-.TH FALLOCATE 1 "September 2011" "util-linux" "User Commands"
++.TH FALLOCATE 1 "April 2014" "util-linux" "User Commands"
+ .SH NAME
+-fallocate \- preallocate space to a file
++fallocate \- preallocate or deallocate space to a file
+ .SH SYNOPSIS
+ .B fallocate
+-.RB [ \-n ]
+-.RB [ \-p ]
++.RB [ \-c | \-p | \-z ]
+ .RB [ \-o
+ .IR offset ]
+ .B \-l
+-.IR length
++.I length
++.RB [ \-n ]
++.I filename
++.PP
++.B fallocate \-d
++.RB [ \-o
++.IR offset ]
++.RB [ \-l
++.IR length ]
+ .I filename
+ .PP
+ .B fallocate \-x
+ .RB [ \-o
+ .IR offset ]
+-.RB \-l
+-.IR length
++.B \-l
++.I length
+ .I filename
+ .SH DESCRIPTION
+ .B fallocate
+-is used to preallocate blocks to a file.  For filesystems which support the
+-fallocate system call, this is done quickly by allocating blocks and marking
+-them as uninitialized, requiring no IO to the data blocks.  This is much faster
+-than creating a file by filling it with zeros.
+-.PP
+-As of the Linux Kernel v2.6.31, the fallocate system call is supported by the
+-btrfs, ext4, ocfs2, and xfs filesystems.
++is used to manipulate the allocated disk space for a file,
++either to deallocate or preallocate it.
++For filesystems which support the fallocate system call,
++preallocation is done quickly by allocating blocks and marking them as
++uninitialized, requiring no IO to the data blocks.
++This is much faster than creating a file by filling it with zeroes.
+ .PP
+ The exit code returned by
+ .B fallocate
+ is 0 on success and 1 on failure.
+-.PP
+ .SH OPTIONS
+-The \fIlength\fR and \fIoffset\fR arguments may be followed by the multiplicative
+-suffixes KiB=1024, MiB=1024*1024, and so on for GiB, TiB, PiB, EiB, ZiB and YiB
+-(the "iB" is optional, e.g. "K" has the same meaning as "KiB") or the suffixes
+-KB=1000, MB=1000*1000, and so on for GB, TB, PB, EB, ZB and YB.
+-.IP "\fB\-n, \-\-keep-size\fP"
++The
++.I length
++and
++.I offset
++arguments may be followed by the multiplicative suffixes KiB (=1024),
++MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB, and YiB (the "iB" is
++optional, e.g., "K" has the same meaning as "KiB") or the suffixes
++KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB, and YB.
++.PP
++The options
++.BR \-\-collapse\-range ", " \-\-dig\-holes ", " \-\-punch\-hole ,
++and
++.B \-\-zero\-range
++are mutually exclusive.
++.TP
++.BR \-c ", " \-\-collapse\-range
++Removes a byte range from a file, without leaving a hole.
++The byte range to be collapsed starts at
++.I offset
++and continues for
++.I length
++bytes.
++At the completion of the operation,
++the contents of the file starting at the location
++.IR offset + length
++will be appended at the location
++.IR offset ,
++and the file will be
++.I length
++bytes smaller.
++The option
++.B \-\-keep\-size
++may not be specified for the collapse-range operation.
++.sp
++Available since Linux 3.15 for ext4 (only for extent-based files) and XFS.
++.TP
++.BR \-d ", " \-\-dig\-holes
++Detect and dig holes.
++This makes the file sparse in-place, without using extra disk space.
++The minimum size of the hole depends on filesystem I/O block size
++(usually 4096 bytes).
++Also, when using this option,
++.B \-\-keep\-size
++is implied.  If no range is specified by
++.B \-\-offset
++and
++.BR \-\-length ,
++then the entire file is analyzed for holes.
++.sp
++You can think of this option as doing a
++.RB """" "cp \-\-sparse" """"
++and then renaming the destination file to the original,
++without the need for extra disk space.
++.sp
++See \fB\-\-punch\-hole\fP for a list of supported filesystems.
++.TP
++.BR \-i ", " \-\-insert\-range
++Insert a hole of
++.I length
++bytes from
++.IR offset ,
++shifting existing data.
++.TP
++.BR \-l ", " "\-\-length " \fIlength
++Specifies the length of the range, in bytes.
++.TP
++.BR \-n ", " \-\-keep\-size
+ Do not modify the apparent length of the file.  This may effectively allocate
+ blocks past EOF, which can be removed with a truncate.
+-.IP "\fB\-p, \-\-punch-hole\fP"
+-Punch holes in the file, the range should not exceed the length of the file.
+-.IP "\fB\-o, \-\-offset\fP \fIoffset\fP
+-Specifies the beginning offset of the allocation, in bytes.
+-.IP "\fB\-l, \-\-length\fP \fIlength\fP
+-Specifies the length of the allocation, in bytes.
+-.IP "\fB\-x , \-\-posix\fP
+-Enable POSIX operation mode. In that mode allocation operation always completes,
+-but it may take longer time when fast allocation is not supported by the underlying filesystem.
+-.IP "\fB\-h, \-\-help\fP"
+-Print help and exit.
+-.IP "\fB-V, \-\-version"
+-Print version and exit.
++.TP
++.BR \-o ", " "\-\-offset " \fIoffset
++Specifies the beginning offset of the range, in bytes.
++.TP
++.BR \-p ", " \-\-punch\-hole
++Deallocates space (i.e., creates a hole) in the byte range starting at
++.I offset
++and continuing for
++.I length
++bytes.
++Within the specified range, partial filesystem blocks are zeroed,
++and whole filesystem blocks are removed from the file.
++After a successful call,
++subsequent reads from this range will return zeroes.
++This option may not be specified at the same time as the
++.B \-\-zero\-range
++option.
++Also, when using this option,
++.B \-\-keep\-size
++is implied.
++.sp
++Supported for XFS (since Linux 2.6.38), ext4 (since Linux 3.0),
++Btrfs (since Linux 3.7) and tmpfs (since Linux 3.5).
++.TP
++.BR \-v ", " \-\-verbose
++Enable verbose mode.
++.TP
++.BR \-x ", " \-\-posix
++Enable POSIX operation mode.
++In that mode allocation operation always completes,
++but it may take longer time when fast allocation is not supported by
++the underlying filesystem.
++.TP
++.BR \-z ", " \-\-zero\-range
++Zeroes space in the byte range starting at
++.I offset
++and continuing for
++.I length
++bytes.
++Within the specified range, blocks are preallocated for the regions
++that span the holes in the file.
++After a successful call,
++subsequent reads from this range will return zeroes.
++.sp
++Zeroing is done within the filesystem preferably by converting the
++range into unwritten extents.  This approach means that the specified
++range will not be physically zeroed out on the device (except for
++partial blocks at the either end of the range), and I/O is
++(otherwise) required only to update metadata.
++.sp
++Option \fB\-\-keep\-size\fP can be specified to prevent file length
++modification.
++.sp
++Available since Linux 3.14 for ext4 (only for extent-based files) and XFS.
++.TP
++.BR \-V ", " \-\-version
++Display version information and exit.
++.TP
++.BR \-h ", " \-\-help
++Display help text and exit.
+ .SH AUTHORS
+-.UR sandeen@redhat.com
++.MT sandeen@redhat.com
+ Eric Sandeen
+-.UE
++.ME
+ .br
+-.UR kzak@redhat.com
++.MT kzak@redhat.com
+ Karel Zak
+-.UE
++.ME
+ .SH SEE ALSO
++.BR truncate (1),
+ .BR fallocate (2),
+-.BR posix_fallocate (3),
+-.BR truncate (1)
++.BR posix_fallocate (3)
+ .SH AVAILABILITY
+ The fallocate command is part of the util-linux package and is available from
+-.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
++.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+ Linux Kernel Archive
+ .UE .
+diff --git a/sys-utils/fallocate.c b/sys-utils/fallocate.c
+index 17ae5fe69..75d89a7a9 100644
+--- a/sys-utils/fallocate.c
++++ b/sys-utils/fallocate.c
+@@ -23,6 +23,7 @@
+  */
+ #include <sys/stat.h>
+ #include <sys/types.h>
++#include <sys/mman.h>
+ #include <ctype.h>
+ #include <errno.h>
+ #include <fcntl.h>
+@@ -31,50 +32,110 @@
+ #include <unistd.h>
+ #include <getopt.h>
+ #include <limits.h>
++#include <string.h>
+ 
+ #ifndef HAVE_FALLOCATE
+ # include <sys/syscall.h>
+ #endif
+ 
+-#ifdef HAVE_LINUX_FALLOC_H
+-# include <linux/falloc.h>	/* for FALLOC_FL_* flags */
++#if defined(HAVE_LINUX_FALLOC_H) && \
++    (!defined(FALLOC_FL_KEEP_SIZE) || !defined(FALLOC_FL_PUNCH_HOLE) || \
++     !defined(FALLOC_FL_COLLAPSE_RANGE) || !defined(FALLOC_FL_ZERO_RANGE) || \
++     !defined(FALLOC_FL_INSERT_RANGE))
++# include <linux/falloc.h>	/* non-libc fallback for FALLOC_FL_* flags */
+ #endif
+ 
++
+ #ifndef FALLOC_FL_KEEP_SIZE
+-# define FALLOC_FL_KEEP_SIZE 1
++# define FALLOC_FL_KEEP_SIZE		0x1
+ #endif
+ 
+ #ifndef FALLOC_FL_PUNCH_HOLE
+-# define FALLOC_FL_PUNCH_HOLE 2
++# define FALLOC_FL_PUNCH_HOLE		0x2
++#endif
++
++#ifndef FALLOC_FL_COLLAPSE_RANGE
++# define FALLOC_FL_COLLAPSE_RANGE	0x8
++#endif
++
++#ifndef FALLOC_FL_ZERO_RANGE
++# define FALLOC_FL_ZERO_RANGE		0x10
++#endif
++
++#ifndef FALLOC_FL_INSERT_RANGE
++# define FALLOC_FL_INSERT_RANGE		0x20
+ #endif
+ 
+ #include "nls.h"
+ #include "strutils.h"
+ #include "c.h"
+ #include "closestream.h"
++#include "xalloc.h"
+ #include "optutils.h"
+ 
+-static void __attribute__((__noreturn__)) usage(FILE *out)
++static int verbose;
++static char *filename;
++
++static void __attribute__((__noreturn__)) usage(void)
+ {
++	FILE *out = stdout;
+ 	fputs(USAGE_HEADER, out);
+ 	fprintf(out,
+ 	      _(" %s [options] <filename>\n"), program_invocation_short_name);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Preallocate space to, or deallocate space from a file.\n"), out);
++
+ 	fputs(USAGE_OPTIONS, out);
+-	fputs(_(" -n, --keep-size     don't modify the length of the file\n"
+-		" -p, --punch-hole    punch holes in the file\n"
+-		" -o, --offset <num>  offset of the allocation, in bytes\n"
+-		" -l, --length <num>  length of the allocation, in bytes\n"), out);
++	fputs(_(" -c, --collapse-range remove a range from the file\n"), out);
++	fputs(_(" -d, --dig-holes      detect zeroes and replace with holes\n"), out);
++	fputs(_(" -i, --insert-range   insert a hole at range, shifting existing data\n"), out);
++	fputs(_(" -l, --length <num>   length for range operations, in bytes\n"), out);
++	fputs(_(" -n, --keep-size      maintain the apparent size of the file\n"), out);
++	fputs(_(" -o, --offset <num>   offset for range operations, in bytes\n"), out);
++	fputs(_(" -p, --punch-hole     replace a range with a hole (implies -n)\n"), out);
++	fputs(_(" -z, --zero-range     zero and ensure allocation of a range\n"), out);
+ #ifdef HAVE_POSIX_FALLOCATE
+-	fputs(_(" -x, --posix         use posix_fallocate(3) instead of fallocate(2)\n"), out);
++	fputs(_(" -x, --posix          use posix_fallocate(3) instead of fallocate(2)\n"), out);
+ #endif
++	fputs(_(" -v, --verbose        verbose mode\n"), out);
++
+ 	fputs(USAGE_SEPARATOR, out);
+-	fputs(USAGE_HELP, out);
+-	fputs(USAGE_VERSION, out);
+-	fprintf(out, USAGE_MAN_TAIL("fallocate(1)"));
++	printf(USAGE_HELP_OPTIONS(22));
++
++	printf(USAGE_MAN_TAIL("fallocate(1)"));
+ 
+-	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
++	exit(EXIT_SUCCESS);
+ }
+ 
++static loff_t cvtnum(char *s)
++{
++	uintmax_t x;
++
++	if (strtosize(s, &x))
++		return -1LL;
++
++	return x;
++}
++
++static void xfallocate(int fd, int mode, off_t offset, off_t length)
++{
++	int error;
++#ifdef HAVE_FALLOCATE
++	error = fallocate(fd, mode, offset, length);
++#else
++	error = syscall(SYS_fallocate, fd, mode, offset, length);
++#endif
++	/*
++	 * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported
++	 * ENOSYS: The filesystem does not support sys_fallocate
++	 */
++	if (error < 0) {
++		if ((mode & FALLOC_FL_KEEP_SIZE) && errno == EOPNOTSUPP)
++			errx(EXIT_FAILURE, _("fallocate failed: keep size mode is unsupported"));
++		err(EXIT_FAILURE, _("fallocate failed"));
++	}
++}
+ 
+ #ifdef HAVE_POSIX_FALLOCATE
+ static void xposix_fallocate(int fd, off_t offset, off_t length)
+@@ -86,41 +147,163 @@ static void xposix_fallocate(int fd, off_t offset, off_t length)
+ }
+ #endif
+ 
++/* The real buffer size has to be bufsize + sizeof(uintptr_t) */
++static int is_nul(void *buf, size_t bufsize)
++{
++	typedef uintptr_t word;
++	void const *vp;
++	char const *cbuf = buf, *cp;
++	word const *wp = buf;
+ 
+-static loff_t cvtnum(char *s)
++	/* set sentinel */
++	memset((char *) buf + bufsize, '\1', sizeof(word));
++
++	/* Find first nonzero *word*, or the word with the sentinel.  */
++	while (*wp++ == 0)
++		continue;
++
++	/* Find the first nonzero *byte*, or the sentinel.  */
++	vp = wp - 1;
++	cp = vp;
++
++	while (*cp++ == 0)
++		continue;
++
++	return cbuf + bufsize < cp;
++}
++
++static void dig_holes(int fd, off_t file_off, off_t len)
+ {
+-	uintmax_t x;
++	off_t file_end = len ? file_off + len : 0;
++	off_t hole_start = 0, hole_sz = 0;
++	uintmax_t ct = 0;
++	size_t  bufsz;
++	char *buf;
++	struct stat st;
++#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
++	off_t cache_start = file_off;
++	/*
++	 * We don't want to call POSIX_FADV_DONTNEED to discard cached
++	 * data in PAGE_SIZE steps. IMHO it's overkill (too many syscalls).
++	 *
++	 * Let's assume that 1MiB (on system with 4K page size) is just
++	 * a good compromise.
++	 *					    -- kzak Feb-2014
++	 */
++	const size_t cachesz = getpagesize() * 256;
++#endif
+ 
+-	if (strtosize(s, &x))
+-		return -1LL;
++	if (fstat(fd, &st) != 0)
++		err(EXIT_FAILURE, _("stat of %s failed"), filename);
+ 
+-	return x;
++	bufsz = st.st_blksize;
++
++	if (lseek(fd, file_off, SEEK_SET) < 0)
++		err(EXIT_FAILURE, _("seek on %s failed"), filename);
++
++	/* buffer + extra space for is_nul() sentinel */
++	buf = xmalloc(bufsz + sizeof(uintptr_t));
++	while (file_end == 0 || file_off < file_end) {
++		/*
++		 * Detect data area (skip holes)
++		 */
++		off_t end, off;
++
++		off = lseek(fd, file_off, SEEK_DATA);
++		if ((off == -1 && errno == ENXIO) ||
++		    (file_end && off >= file_end))
++			break;
++
++		end = lseek(fd, off, SEEK_HOLE);
++		if (file_end && end > file_end)
++			end = file_end;
++
++#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
++		posix_fadvise(fd, off, end, POSIX_FADV_SEQUENTIAL);
++#endif
++		/*
++		 * Dig holes in the area
++		 */
++		while (off < end) {
++			ssize_t rsz = pread(fd, buf, bufsz, off);
++			if (rsz < 0 && errno)
++				err(EXIT_FAILURE, _("%s: read failed"), filename);
++			if (end && rsz > 0 && off > end - rsz)
++				rsz = end - off;
++			if (rsz <= 0)
++				break;
++
++			if (is_nul(buf, rsz)) {
++				if (!hole_sz)				/* new hole detected */
++					hole_start = off;
++				hole_sz += rsz;
++			 } else if (hole_sz) {
++				xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
++					   hole_start, hole_sz);
++				ct += hole_sz;
++				hole_sz = hole_start = 0;
++			}
++
++#if defined(POSIX_FADV_DONTNEED) && defined(HAVE_POSIX_FADVISE)
++			/* discard cached data */
++			if (off - cache_start > (off_t) cachesz) {
++				size_t clen = off - cache_start;
++
++				clen = (clen / cachesz) * cachesz;
++				posix_fadvise(fd, cache_start, clen, POSIX_FADV_DONTNEED);
++				cache_start = cache_start + clen;
++			}
++#endif
++			off += rsz;
++		}
++		if (hole_sz) {
++			xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
++					hole_start, hole_sz);
++			ct += hole_sz;
++		}
++		file_off = off;
++	}
++
++	free(buf);
++
++	if (verbose) {
++		char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE, ct);
++		fprintf(stdout, _("%s: %s (%ju bytes) converted to sparse holes.\n"),
++				filename, str, ct);
++		free(str);
++	}
+ }
+ 
+ int main(int argc, char **argv)
+ {
+-	char	*fname;
+ 	int	c;
+-	int	error = 0;
+ 	int	fd;
+ 	int	mode = 0;
+-	int	posix = 0;
++	int	dig = 0;
++	int posix = 0;
+ 	loff_t	length = -2LL;
+ 	loff_t	offset = 0;
+ 
+ 	static const struct option longopts[] = {
+-	    { "help",      0, 0, 'h' },
+-	    { "version",   0, 0, 'V' },
+-	    { "keep-size", 0, 0, 'n' },
+-	    { "punch-hole", 0, 0, 'p' },
+-	    { "offset",    1, 0, 'o' },
+-	    { "length",    1, 0, 'l' },
+-	    { "posix",     0, 0, 'x' },
+-	    { NULL,        0, 0, 0 }
++	    { "help",           no_argument,       NULL, 'h' },
++	    { "version",        no_argument,       NULL, 'V' },
++	    { "keep-size",      no_argument,       NULL, 'n' },
++	    { "punch-hole",     no_argument,       NULL, 'p' },
++	    { "collapse-range", no_argument,       NULL, 'c' },
++	    { "dig-holes",      no_argument,       NULL, 'd' },
++	    { "insert-range",   no_argument,       NULL, 'i' },
++	    { "zero-range",     no_argument,       NULL, 'z' },
++	    { "offset",         required_argument, NULL, 'o' },
++	    { "length",         required_argument, NULL, 'l' },
++	    { "posix",          no_argument,       NULL, 'x' },
++	    { "verbose",        no_argument,       NULL, 'v' },
++	    { NULL, 0, NULL, 0 }
+ 	};
+ 
+-	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
+-		{ 'x', 'n', 'p' },
++	static const ul_excl_t excl[] = {	/* rows and cols in ASCII order */
++		{ 'c', 'd', 'p', 'z' },
++		{ 'c', 'n' },
++		{ 'x', 'c', 'd', 'i', 'n', 'p', 'z'},
+ 		{ 0 }
+ 	};
+ 	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+@@ -130,29 +313,39 @@ int main(int argc, char **argv)
+ 	textdomain(PACKAGE);
+ 	atexit(close_stdout);
+ 
+-	while ((c = getopt_long(argc, argv, "hVnpl:o:x", longopts, NULL)) != -1) {
++	while ((c = getopt_long(argc, argv, "hvVncpdizxl:o:", longopts, NULL))
++			!= -1) {
+ 
+ 		err_exclusive_options(c, longopts, excl, excl_st);
+ 
+ 		switch(c) {
+ 		case 'h':
+-			usage(stdout);
++			usage();
+ 			break;
+-		case 'V':
+-			printf(UTIL_LINUX_VERSION);
+-			return EXIT_SUCCESS;
+-		case 'p':
+-			mode |= FALLOC_FL_PUNCH_HOLE;
+-			/* fall through */
+-		case 'n':
+-			mode |= FALLOC_FL_KEEP_SIZE;
++		case 'c':
++			mode |= FALLOC_FL_COLLAPSE_RANGE;
++			break;
++		case 'd':
++			dig = 1;
++			break;
++		case 'i':
++			mode |= FALLOC_FL_INSERT_RANGE;
+ 			break;
+ 		case 'l':
+ 			length = cvtnum(optarg);
+ 			break;
++		case 'n':
++			mode |= FALLOC_FL_KEEP_SIZE;
++			break;
+ 		case 'o':
+ 			offset = cvtnum(optarg);
+ 			break;
++		case 'p':
++			mode |= FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
++			break;
++		case 'z':
++			mode |= FALLOC_FL_ZERO_RANGE;
++			break;
+ 		case 'x':
+ #ifdef HAVE_POSIX_FALLOCATE
+ 			posix = 1;
+@@ -160,53 +353,59 @@ int main(int argc, char **argv)
+ #else
+ 			errx(EXIT_FAILURE, _("posix_fallocate support is not compiled"))
+ #endif
+-		default:
+-			usage(stderr);
++		case 'v':
++			verbose++;
+ 			break;
++		case 'V':
++			printf(UTIL_LINUX_VERSION);
++			return EXIT_SUCCESS;
++		default:
++			errtryhelp(EXIT_FAILURE);
+ 		}
+ 	}
+ 
+-	if (length == -2LL)
+-		errx(EXIT_FAILURE, _("no length argument specified"));
+-	if (length <= 0)
+-		errx(EXIT_FAILURE, _("invalid length value specified"));
+-	if (offset < 0)
+-		errx(EXIT_FAILURE, _("invalid offset value specified"));
+ 	if (optind == argc)
+-		errx(EXIT_FAILURE, _("no filename specified."));
++		errx(EXIT_FAILURE, _("no filename specified"));
++
++	filename = argv[optind++];
+ 
+-	fname = argv[optind++];
++	if (optind != argc)
++		errx(EXIT_FAILURE, _("unexpected number of arguments"));
+ 
+-	if (optind != argc) {
+-		warnx(_("unexpected number of arguments"));
+-		usage(stderr);
++	if (dig) {
++		/* for --dig-holes the default is analyze all file */
++		if (length == -2LL)
++			length = 0;
++		if (length < 0)
++			errx(EXIT_FAILURE, _("invalid length value specified"));
++	} else {
++		/* it's safer to require the range specification (--length --offset) */
++		if (length == -2LL)
++			errx(EXIT_FAILURE, _("no length argument specified"));
++		if (length <= 0)
++			errx(EXIT_FAILURE, _("invalid length value specified"));
+ 	}
++	if (offset < 0)
++		errx(EXIT_FAILURE, _("invalid offset value specified"));
+ 
+-	fd = open(fname, O_WRONLY|O_CREAT, 0644);
++	/* O_CREAT makes sense only for the default fallocate(2) behavior
++	 * when mode is no specified and new space is allocated
++	 *
++	 * RHEL7.6: for backward compatibility in RHEL7 we keep O_CREAT there.
++	 */
++	fd = open(filename, O_RDWR | O_CREAT,
++		  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ 	if (fd < 0)
+-		err(EXIT_FAILURE, _("cannot open %s"), fname);
++		err(EXIT_FAILURE, _("cannot open %s"), filename);
+ 
++	if (dig)
++		dig_holes(fd, offset, length);
+ #ifdef HAVE_POSIX_FALLOCATE
+-	if (posix)
++	else if (posix)
+ 		xposix_fallocate(fd, offset, length);
+-	else
+ #endif
+-
+-#ifdef HAVE_FALLOCATE
+-	error = fallocate(fd, mode, offset, length);
+-#else
+-	error = syscall(SYS_fallocate, fd, mode, offset, length);
+-#endif
+-	/*
+-	 * EOPNOTSUPP: The FALLOC_FL_KEEP_SIZE is unsupported
+-	 * ENOSYS: The filesystem does not support sys_fallocate
+-	 */
+-	if (error < 0) {
+-		if ((mode & FALLOC_FL_KEEP_SIZE) && errno == EOPNOTSUPP)
+-			errx(EXIT_FAILURE,
+-				_("keep size mode (-n option) unsupported"));
+-		err(EXIT_FAILURE, _("%s: fallocate failed"), fname);
+-	}
++	else
++		xfallocate(fd, mode, offset, length);
+ 
+ 	close(fd);
+ 	return EXIT_SUCCESS;
+-- 
+2.14.4
+
diff --git a/SOURCES/0160-libmount-fix-mnt_table_is_fs_mounted-for-rbind.patch b/SOURCES/0160-libmount-fix-mnt_table_is_fs_mounted-for-rbind.patch
new file mode 100644
index 0000000..8a66eac
--- /dev/null
+++ b/SOURCES/0160-libmount-fix-mnt_table_is_fs_mounted-for-rbind.patch
@@ -0,0 +1,54 @@
+From 7f5f61824b3b7ffa81d34481aec5a7ecca213a5a Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 3 Jan 2018 13:59:59 +0100
+Subject: [PATCH 160/173] libmount: fix mnt_table_is_fs_mounted() for rbind
+
+There is no difference between "bind" and "rbind" if we want to FS
+root to search for the FS in mountinfo file.
+
+fstab:
+
+ /dev/sdc1       /mnt/foo        xfs     defaults        0 0
+ /mnt/foo        /mnt/test       none    rw,rbind        0 0
+
+use -a more than once:
+
+  mount -a
+  mount -a
+
+/proc/mounts (the current result):
+
+ /dev/sdc1 /mnt/foo xfs rw,relatime,attr2,inode64,noquota 0 0
+ /dev/sdc1 /mnt/test xfs rw,relatime,attr2,inode64,noquota 0 0
+ /dev/sdc1 /mnt/test xfs rw,relatime,attr2,inode64,noquota 0 0
+ /dev/sdc1 /mnt/foo xfs rw,relatime,attr2,inode64,noquota 0 0
+
+expected (fixed version) result:
+
+ /dev/sdc1 /mnt/foo xfs rw,relatime,attr2,inode64,noquota 0 0
+ /dev/sdc1 /mnt/test xfs rw,relatime,attr2,inode64,noquota 0 0
+
+Upstream: http://github.com/karelzak/util-linux/commit/b5cc232362a60b2a7738ee250609202bd6195e49
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1528959
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libmount/src/tab.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/libmount/src/tab.c b/libmount/src/tab.c
+index 5628da6e5..dfa1da822 100644
+--- a/libmount/src/tab.c
++++ b/libmount/src/tab.c
+@@ -1099,7 +1099,8 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs)
+ 		struct libmnt_fs *rootfs;
+ 		int flags = 0;
+ 
+-		if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0)
++		if (mnt_fs_get_option(fstab_fs, "bind", NULL, NULL) == 0 ||
++		    mnt_fs_get_option(fstab_fs, "rbind", NULL, NULL) == 0)
+ 			flags = MS_BIND;
+ 
+ 		rootfs = mnt_table_get_fs_root(tb, fstab_fs, flags, &root);
+-- 
+2.14.4
+
diff --git a/SOURCES/0161-mount-add-ext4-to-some-places-in-man-page.patch b/SOURCES/0161-mount-add-ext4-to-some-places-in-man-page.patch
new file mode 100644
index 0000000..00d2d5d
--- /dev/null
+++ b/SOURCES/0161-mount-add-ext4-to-some-places-in-man-page.patch
@@ -0,0 +1,54 @@
+From 7492cf62b7334792df931469ca783878b868d0d0 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 6 Jun 2018 12:10:42 +0200
+Subject: [PATCH 161/173] mount: add ext4 to some places in man page
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1538721
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/mount.8 | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/sys-utils/mount.8 b/sys-utils/mount.8
+index 49cb2818f..35d782f0e 100644
+--- a/sys-utils/mount.8
++++ b/sys-utils/mount.8
+@@ -874,7 +874,7 @@ for example tune2fs -l output for extN filesystems).
+ The following options apply to any filesystem that is being
+ mounted (but not every filesystem actually honors them - e.g., the
+ .B sync
+-option today has effect only for ext2, ext3, fat, vfat and ufs):
++option today has effect only for ext2, ext3, ext4, fat, vfat and ufs):
+ 
+ .TP
+ .B async
+@@ -909,7 +909,7 @@ The
+ .BR context=
+ option is useful when mounting filesystems that do not support
+ extended attributes, such as a floppy or hard disk formatted with VFAT, or
+-systems that are not normally running under SELinux, such as an ext3 formatted
++systems that are not normally running under SELinux, such as an ext3 or ext4 formatted
+ disk from a non-SELinux workstation. You can also use
+ .BR context=
+ on filesystems you do not trust, such as a floppy. It also helps in compatibility with
+@@ -2833,7 +2833,7 @@ not specified or the filesystem is known for libblkid, for example:
+ .sp
+ .B "mount /tmp/disk.img /mnt"
+ .sp
+-.B "mount -t ext3 /tmp/disk.img /mnt"
++.B "mount -t ext4 /tmp/disk.img /mnt"
+ .sp
+ .RE
+ This type of mount knows about four options, namely
+@@ -2948,7 +2948,7 @@ It is possible for a corrupted filesystem to cause a crash.
+ .PP
+ Some Linux filesystems don't support
+ .B "\-o sync and \-o dirsync"
+-(the ext2, ext3, fat and vfat filesystems
++(the ext2, ext3, ext4, fat and vfat filesystems
+ .I do
+ support synchronous updates (a la BSD) when mounted with the
+ .B sync
+-- 
+2.14.4
+
diff --git a/SOURCES/0162-agetty-keep-c_iflags-unmodified-on-autologin.patch b/SOURCES/0162-agetty-keep-c_iflags-unmodified-on-autologin.patch
new file mode 100644
index 0000000..a14f4c4
--- /dev/null
+++ b/SOURCES/0162-agetty-keep-c_iflags-unmodified-on-autologin.patch
@@ -0,0 +1,72 @@
+From 7974045302fe53629e70501f0180f30cbaa25b1e Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 6 Jun 2018 15:57:24 +0200
+Subject: [PATCH 162/173] agetty: keep c_iflags unmodified on --autologin
+
+agetty sets c_iflags according to interaction with serial line in
+get_logname(). For --autologin it does not read from the line, so we
+have no clue how to set the flags.
+
+The current behavior is to zeroize the flags.  Unfortunately, it seems
+like bad idea, because the line may be already properly initialized by
+kernel (or systemd, etc.).
+
+The new behavior is not touch the flags on --autologin.
+
+Upstream: http://github.com/karelzak/util-linux/commit/2c4d86abdadab19be76abecb156da7f7dc284d81
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1252764
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ term-utils/agetty.8 |  4 ++++
+ term-utils/agetty.c | 17 +++++++++++++----
+ 2 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/term-utils/agetty.8 b/term-utils/agetty.8
+index a42cdf158..fe4bfd427 100644
+--- a/term-utils/agetty.8
++++ b/term-utils/agetty.8
+@@ -94,6 +94,10 @@ password. The \-f \fIusername\fP option is added to the \fB/bin/login\fP
+ command line by default. The \-\-login-options option changes this default
+ behaviour and then only \\u is replaced by the \fIusername\fP and no other
+ option is added to the login command line.
++
++Note that \fB\-\-autologin\fP may affect the way how agetty initializes the
++serial line, because on auto-login agetty does not read from the line and it
++has no opportunity optimize the line setting.
+ .TP
+ \-c, \-\-noreset
+ Don't reset terminal cflags (control modes). See \fItermios(3)\fP for more
+diff --git a/term-utils/agetty.c b/term-utils/agetty.c
+index b626cdbeb..948d77246 100644
+--- a/term-utils/agetty.c
++++ b/term-utils/agetty.c
+@@ -1100,13 +1100,22 @@ static void termio_init(struct options *op, struct termios *tp)
+ 	 /* Flush input and output queues, important for modems! */
+ 	tcflush(STDIN_FILENO, TCIOFLUSH);
+ 
++	/* The defaul is set c_iflag in termio_final() according to chardata.
++	 * Unfortunately, the chardata are not set according to the serial line
++	 * if --autolog is enabled. In this case we do not read from the line
++	 * at all. The best what we can do in this case is to keep c_iflag
++	 * unmodified for --autolog.
++	 */
++	if (!op->autolog) {
+ #ifdef IUTF8
+-	tp->c_iflag = tp->c_iflag & IUTF8;
+-	if (tp->c_iflag & IUTF8)
+-		op->flags |= F_UTF8;
++		tp->c_iflag = tp->c_iflag & IUTF8;
++		if (tp->c_iflag & IUTF8)
++			op->flags |= F_UTF8;
+ #else
+-	tp->c_iflag = 0;
++		tp->c_iflag = 0;
+ #endif
++	}
++
+ 	tp->c_lflag = 0;
+ 	tp->c_oflag &= OPOST | ONLCR;
+ 
+-- 
+2.14.4
+
diff --git a/SOURCES/0163-sulogin-don-t-use-strcpy-enlarge-pwd-line-buffer.patch b/SOURCES/0163-sulogin-don-t-use-strcpy-enlarge-pwd-line-buffer.patch
new file mode 100644
index 0000000..d3b7610
--- /dev/null
+++ b/SOURCES/0163-sulogin-don-t-use-strcpy-enlarge-pwd-line-buffer.patch
@@ -0,0 +1,73 @@
+From 94f380e223e7496804dcd68e204fba0a15df8bd7 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Mon, 25 May 2015 15:24:13 +0200
+Subject: [PATCH 163/173] sulogin: don't use strcpy(), enlarge pwd line buffer
+
+* according to "man getpwnam" 16384 bytes is enough to store one
+  passwd entry (let's use 2*BUFSIZE to avoid magic numbers in code)
+
+* don't use strcpy() to set empty password
+
+Upstream: http://github.com/karelzak/util-linux/commit/d681e0956cdca1a016346424939fe1b9c6a0a549
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ login-utils/sulogin.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
+index bbd67b3ee..6d03bc5ae 100644
+--- a/login-utils/sulogin.c
++++ b/login-utils/sulogin.c
+@@ -373,8 +373,8 @@ static struct passwd *getrootpwent(int try_manually)
+ 	struct passwd *pw;
+ 	struct spwd *spw;
+ 	FILE *fp;
+-	static char line[256];
+-	static char sline[256];
++	static char line[2 * BUFSIZ];
++	static char sline[2 * BUFSIZ];
+ 	char *p;
+ 
+ 	/*
+@@ -410,7 +410,7 @@ static struct passwd *getrootpwent(int try_manually)
+ 	/*
+ 	 * Find root in the password file.
+ 	 */
+-	while ((p = fgets(line, 256, fp)) != NULL) {
++	while ((p = fgets(line, sizeof(line), fp)) != NULL) {
+ 		if (strncmp(line, "root:", 5) != 0)
+ 			continue;
+ 		p += 5;
+@@ -439,12 +439,12 @@ static struct passwd *getrootpwent(int try_manually)
+ 	/*
+ 	 * The password is invalid. If there is a shadow password, try it.
+ 	 */
+-	strcpy(pwd.pw_passwd, "");
++	*pwd.pw_passwd = '\0';
+ 	if ((fp = fopen(_PATH_SHADOW_PASSWD, "r")) == NULL) {
+ 		warn(_("cannot open %s"), _PATH_PASSWD);
+ 		return &pwd;
+ 	}
+-	while ((p = fgets(sline, 256, fp)) != NULL) {
++	while ((p = fgets(sline, sizeof(sline), fp)) != NULL) {
+ 		if (strncmp(sline, "root:", 5) != 0)
+ 			continue;
+ 		p += 5;
+@@ -458,11 +458,11 @@ static struct passwd *getrootpwent(int try_manually)
+ 	 */
+ 	if (p == NULL) {
+ 		warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD);
+-		strcpy(pwd.pw_passwd, "");
++		*pwd.pw_passwd = '\0';
+ 	}
+ 	if (!valid(pwd.pw_passwd)) {
+ 		warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD);
+-		strcpy(pwd.pw_passwd, "");
++		*pwd.pw_passwd = '\0';
+ 	}
+ 	return &pwd;
+ }
+-- 
+2.14.4
+
diff --git a/SOURCES/0164-sulogin-improve-support-for-locked-root-account.patch b/SOURCES/0164-sulogin-improve-support-for-locked-root-account.patch
new file mode 100644
index 0000000..9aa2443
--- /dev/null
+++ b/SOURCES/0164-sulogin-improve-support-for-locked-root-account.patch
@@ -0,0 +1,175 @@
+From a08c1a6aadaa27e4070fe58242d55464d6c7a3e7 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Mon, 25 May 2015 15:30:52 +0200
+Subject: [PATCH 164/173] sulogin: improve support for locked root account
+
+Some installations and distributions don't use a root account password
+for security reasons and use sudo instead. In that case, asking for the
+password makes no sense, and it is not even considered as valid as it's just
+"*" or "!".
+
+In these cases --force is required to just start a root shell and no
+ask for password.
+
+I don't think it's a good idea to automatically start root shell when
+locked account is detected. It's possible that the machine is on
+public place and for example Ubuntu uses root account disabled by
+default (and also Fedora when installed by yum/dnf without anaconda).
+
+The --force option forces admins to think about it...
+
+The distro maintainers can also use --force in their initscripts or
+systemd emergency.service if they believe that promiscuous setting is
+the right thing for the distro.
+
+Addresses: https://bugs.debian.org/326678
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200
+Upstream: http://github.com/karelzak/util-linux/commit/7ff1162e67164cb4ece19dd809c26272461aa254
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ login-utils/sulogin.8 | 13 +++++++++----
+ login-utils/sulogin.c | 48 ++++++++++++++++++++++++++++++++++--------------
+ 2 files changed, 43 insertions(+), 18 deletions(-)
+
+diff --git a/login-utils/sulogin.8 b/login-utils/sulogin.8
+index b9dec165c..702487985 100644
+--- a/login-utils/sulogin.8
++++ b/login-utils/sulogin.8
+@@ -35,8 +35,10 @@ Give root password for system maintenance
+ .br
+ (or type Control\-D for normal startup):
+ .PP
+-.I sulogin
+-will be connected to the current terminal, or to the optional tty device that
++If the root account is locked and --force is specified, no password is required.
++.PP
++.B sulogin
++will be connected to the current terminal, or to the optional \fItty\fR device that
+ can be specified on the command line (typically
+ .BR /dev/console ).
+ .PP
+@@ -50,8 +52,11 @@ from the system fails, manually examine
+ .I /etc/passwd
+ and
+ .I /etc/shadow
+-to get the password.  If they are damaged or nonexistent, sulogin will start
+-a root shell without asking for a password.
++to get the password.  If these files are damaged or nonexistent, or when
++root account is locked by '!' or '*' at the begin of the password then
++.B sulogin
++will \fBstart a root shell without asking for a password\fP.
++.PP
+ .IP
+ Only use the
+ .B \-e
+diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
+index 6d03bc5ae..a17b91d71 100644
+--- a/login-utils/sulogin.c
++++ b/login-utils/sulogin.c
+@@ -81,6 +81,13 @@ static volatile sig_atomic_t sigchild;
+ # define IUCLC		0
+ #endif
+ 
++static int locked_account_password(const char *passwd)
++{
++	if (passwd && (*passwd == '*' || *passwd == '!'))
++		return 1;
++	return 0;
++}
++
+ /*
+  * Fix the tty modes and set reasonable defaults.
+  */
+@@ -423,7 +430,6 @@ static struct passwd *getrootpwent(int try_manually)
+ 		p = line;
+ 		break;
+ 	}
+-
+ 	fclose(fp);
+ 
+ 	/*
+@@ -460,7 +466,8 @@ static struct passwd *getrootpwent(int try_manually)
+ 		warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD);
+ 		*pwd.pw_passwd = '\0';
+ 	}
+-	if (!valid(pwd.pw_passwd)) {
++	/* locked accont passwords are valid too */
++	if (!locked_account_password(pwd.pw_passwd) && !valid(pwd.pw_passwd)) {
+ 		warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD);
+ 		*pwd.pw_passwd = '\0';
+ 	}
+@@ -470,7 +477,7 @@ static struct passwd *getrootpwent(int try_manually)
+ /*
+  * Ask by prompt for the password.
+  */
+-static void doprompt(const char *crypted, struct console *con)
++static void doprompt(const char *crypted, struct console *con, int deny)
+ {
+ 	struct termios tty;
+ 
+@@ -487,18 +494,25 @@ static void doprompt(const char *crypted, struct console *con)
+ 		if  ((con->file = fdopen(con->fd, "r+")) == (FILE*)0)
+ 			goto err;
+ 	}
++
++	if (deny)
++		fprintf(con->file, _("\nCannot open access to console, the root account is locked.\n"
++				     "See sulogin(8) man page for more details.\n\n"
++				     "Press Enter to continue.\n"));
++	else {
+ #if defined(USE_ONELINE)
+-	if (crypted[0])
+-		fprintf(con->file, _("Give root password for login: "));
+-	else
+-		fprintf(con->file, _("Press enter for login: "));
++		if (crypted[0] && !locked_account_password(crypted))
++			fprintf(con->file, _("Give root password for login: "));
++		else
++			fprintf(con->file, _("Press Enter for login: "));
+ #else
+-	if (crypted[0])
+-		fprintf(con->file, _("Give root password for maintenance\n"));
+-	else
+-		fprintf(con->file, _("Press enter for maintenance"));
+-	fprintf(con->file, _("(or type Control-D to continue): "));
++		if (crypted[0] && !locked_account_password(crypted))
++			fprintf(con->file, _("Give root password for maintenance\n"));
++		else
++			fprintf(con->file, _("Press Enter for maintenance\n"));
++		fprintf(con->file, _("(or press Control-D to continue): "));
+ #endif
++	}
+ 	fflush(con->file);
+ err:
+ 	if (con->flags & CON_SERIAL)
+@@ -914,6 +928,7 @@ int main(int argc, char **argv)
+ 		goto nofork;
+ 	}
+ 
++
+ 	mask_signal(SIGCHLD, chld_handler, &saved_sigchld);
+ 	do {
+ 		con = list_entry(ptr, struct console, entry);
+@@ -930,12 +945,17 @@ int main(int argc, char **argv)
+ 				const char *passwd = pwd->pw_passwd;
+ 				const char *answer;
+ 				int failed = 0, doshell = 0;
++				int deny = !opt_e && locked_account_password(pwd->pw_passwd);
++
++				doprompt(passwd, con, deny);
+ 
+-				doprompt(passwd, con);
+ 				if ((answer = getpasswd(con)) == NULL)
+ 					break;
++				if (deny)
++					exit(EXIT_FAILURE);
+ 
+-				if (passwd[0] == '\0')
++				/* no password or locked account */
++				if (!passwd[0] || locked_account_password(passwd))
+ 					doshell++;
+ 				else {
+ 					const char *cryptbuf;
+-- 
+2.14.4
+
diff --git a/SOURCES/0165-sulogin-Always-make-echo-work-after-performing-getpa.patch b/SOURCES/0165-sulogin-Always-make-echo-work-after-performing-getpa.patch
new file mode 100644
index 0000000..abc0cb9
--- /dev/null
+++ b/SOURCES/0165-sulogin-Always-make-echo-work-after-performing-getpa.patch
@@ -0,0 +1,34 @@
+From 6bb8e6d4c38f260109852ef8b43876ea1aff3810 Mon Sep 17 00:00:00 2001
+From: Werner Fink <werner@suse.de>
+Date: Tue, 22 Mar 2016 10:38:59 +0100
+Subject: [PATCH 165/173] sulogin: Always make echo work after performing
+ getpasswd even if root account is locked
+
+If the root account is locked and no password was provided then the terminal
+line is not set back to do echo of the input. This correct a small overlook
+in commit 7ff1162e67164cb4ece19dd809c26272461aa254
+
+Upstream: http://github.com/karelzak/util-linux/commit/6988998b66b4b95c494d60599f8e3de2ffbaeece
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200
+Signed-off-by: Werner Fink <werner@suse.de>
+---
+ login-utils/sulogin.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
+index a17b91d71..a6918282e 100644
+--- a/login-utils/sulogin.c
++++ b/login-utils/sulogin.c
+@@ -676,8 +676,7 @@ quit:
+ 	alarm(0);
+ 	if (tc)
+ 		tcsetattr(fd, TCSAFLUSH, &con->tio);
+-	if (ret && *ret != '\0')
+-		tcfinal(con);
++	tcfinal(con);
+ 	printf("\r\n");
+ out:
+ 	return ret;
+-- 
+2.14.4
+
diff --git a/SOURCES/0166-sulogin-make-getpasswd-.-return-NULL-on-D.patch b/SOURCES/0166-sulogin-make-getpasswd-.-return-NULL-on-D.patch
new file mode 100644
index 0000000..fb8e29d
--- /dev/null
+++ b/SOURCES/0166-sulogin-make-getpasswd-.-return-NULL-on-D.patch
@@ -0,0 +1,35 @@
+From c19feb058ff5883ab7255241d13732ab66bf44f0 Mon Sep 17 00:00:00 2001
+From: Andreas Henriksson <andreas@fatal.se>
+Date: Mon, 28 Nov 2016 17:24:49 +0100
+Subject: [PATCH 166/173] sulogin: make getpasswd(...) return NULL on ^D
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This makes the caller bail out early instead of evaluating the
+input as a password.
+
+Reported-by: Bjørn Mork <bjorn@mork.no>
+Addresses: http://bugs.debian.org/846112
+Upstream: http://github.com/karelzak/util-linux/commit/60dea9d187caa700e42f37c7955116f71be912d5
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200
+Signed-off-by: Andreas Henriksson <andreas@fatal.se>
+---
+ login-utils/sulogin.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
+index a6918282e..7ec349953 100644
+--- a/login-utils/sulogin.c
++++ b/login-utils/sulogin.c
+@@ -661,6 +661,7 @@ static char *getpasswd(struct console *con)
+ 				ptr--;
+ 			break;
+ 		case CEOF:
++			ret = NULL;
+ 			goto quit;
+ 		default:
+ 			if ((size_t)(ptr - &pass[0]) >= (sizeof(pass) -1 )) {
+-- 
+2.14.4
+
diff --git a/SOURCES/0167-sulogin-bail-out-from-getpasswd-.-on-timeout.patch b/SOURCES/0167-sulogin-bail-out-from-getpasswd-.-on-timeout.patch
new file mode 100644
index 0000000..e0f72a7
--- /dev/null
+++ b/SOURCES/0167-sulogin-bail-out-from-getpasswd-.-on-timeout.patch
@@ -0,0 +1,38 @@
+From 971b2ecede4d2040e879d9cfb56332a2e210bed8 Mon Sep 17 00:00:00 2001
+From: Andreas Henriksson <andreas@fatal.se>
+Date: Mon, 28 Nov 2016 17:24:50 +0100
+Subject: [PATCH 167/173] sulogin: bail out from getpasswd(...) on timeout
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If timeout happens while waiting in prompt, bail out instead
+of retrying.
+
+Reported-by: Bjørn Mork <bjorn@mork.no>
+Addresses: http://bugs.debian.org/846107
+Upstream: http://github.com/karelzak/util-linux/commit/1c4b2d43926e2a7032310cd18b411d8d872cb4ed
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1561200
+Signed-off-by: Andreas Henriksson <andreas@fatal.se>
+---
+ login-utils/sulogin.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
+index 7ec349953..dd73a1c50 100644
+--- a/login-utils/sulogin.c
++++ b/login-utils/sulogin.c
+@@ -611,6 +611,10 @@ static char *getpasswd(struct console *con)
+ 	while (cp->eol == '\0') {
+ 		if (read(fd, &c, 1) < 1) {
+ 			if (errno == EINTR || errno == EAGAIN) {
++				if (alarm_rised) {
++					ret = NULL;
++					goto quit;
++				}
+ 				usleep(1000);
+ 				continue;
+ 			}
+-- 
+2.14.4
+
diff --git a/SOURCES/0168-losetup-keep-f-and-devname-mutually-exclusive.patch b/SOURCES/0168-losetup-keep-f-and-devname-mutually-exclusive.patch
new file mode 100644
index 0000000..28278f3
--- /dev/null
+++ b/SOURCES/0168-losetup-keep-f-and-devname-mutually-exclusive.patch
@@ -0,0 +1,40 @@
+From 14a4f41a94cc4bd227940c4bd976e39d9f13e8c5 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 7 Jun 2018 12:05:08 +0200
+Subject: [PATCH 168/173] losetup: keep -f and <devname> mutually exclusive
+
+losetup tries to blindly use specified device as well as search for
+the first free device, the result is:
+
+ # losetup /dev/loop1 -f /tmp/tfile_loop1
+ losetup: /dev/loop1: failed to set up loop device: Invalid argument
+
+fixed version:
+
+ # losetup /dev/loop10 -f img
+ losetup: unexpected arguments
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1566432
+Upstream: http://github.com/karelzak/util-linux/commit/c3f5a0f1d47dbc47f6d21da232d4eb1cfb7905db
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/losetup.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c
+index d9b0c9b61..8df831b8f 100644
+--- a/sys-utils/losetup.c
++++ b/sys-utils/losetup.c
+@@ -545,6 +545,9 @@ int main(int argc, char **argv)
+ 		 */
+ 		act = A_CREATE;
+ 		file = argv[optind++];
++
++		if (optind < argc)
++			errx(EXIT_FAILURE, _("unexpected arguments"));
+ 	}
+ 
+ 	if (list && !act && optind == argc)
+-- 
+2.14.4
+
diff --git a/SOURCES/0169-lscpu-fix-mzx-min-MHz-reporting.patch b/SOURCES/0169-lscpu-fix-mzx-min-MHz-reporting.patch
new file mode 100644
index 0000000..dd53555
--- /dev/null
+++ b/SOURCES/0169-lscpu-fix-mzx-min-MHz-reporting.patch
@@ -0,0 +1,83 @@
+From 79866c4b187e9ccf37fe333e9865a8575f4a3a16 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 6 Jun 2018 10:10:20 +0200
+Subject: [PATCH 169/173] lscpu: fix mzx/min MHz reporting
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/lscpu.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 48 insertions(+), 2 deletions(-)
+
+diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c
+index 1ee73f34a..4c15de1d4 100644
+--- a/sys-utils/lscpu.c
++++ b/sys-utils/lscpu.c
+@@ -1251,6 +1251,52 @@ read_configured(struct lscpu_desc *desc, int idx)
+ 	desc->configured[idx] = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", num);
+ }
+ 
++/* Read overall maximum frequency of cpu */
++static char *
++cpu_max_mhz(struct lscpu_desc *desc, char *buf, size_t bufsz)
++{
++	int i;
++	float cpu_freq = 0.0;
++	size_t setsize = CPU_ALLOC_SIZE(maxcpus);
++
++	if (desc->present) {
++		for (i = 0; i < desc->ncpuspos; i++) {
++			if (CPU_ISSET_S(real_cpu_num(desc, i), setsize, desc->present)
++			    && desc->maxmhz[i]) {
++				float freq = atof(desc->maxmhz[i]);
++
++				if (freq > cpu_freq)
++					cpu_freq = freq;
++			}
++		}
++	}
++	snprintf(buf, bufsz, "%.4f", cpu_freq);
++	return buf;
++}
++
++/* Read overall minimum frequency of cpu */
++static char *
++cpu_min_mhz(struct lscpu_desc *desc, char *buf, size_t bufsz)
++{
++	int i;
++	float cpu_freq = -1.0;
++	size_t setsize = CPU_ALLOC_SIZE(maxcpus);
++
++	if (desc->present) {
++		for (i = 0; i < desc->ncpuspos; i++) {
++			if (CPU_ISSET_S(real_cpu_num(desc, i), setsize, desc->present)
++			    && desc->minmhz[i]) {
++				float freq = atof(desc->minmhz[i]);
++
++				if (cpu_freq < 0.0 || freq < cpu_freq)
++					cpu_freq = freq;
++			}
++		}
++	}
++        snprintf(buf, bufsz, "%.4f", cpu_freq);
++	return buf;
++}
++
+ static void
+ read_max_mhz(struct lscpu_desc *desc, int idx)
+ {
+@@ -1898,9 +1944,9 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
+ 	if (desc->static_mhz)
+ 		print_s(_("CPU static MHz:"), desc->static_mhz);
+ 	if (desc->maxmhz && desc->maxmhz[0])
+-		print_s(_("CPU max MHz:"), desc->maxmhz[0]);
++		print_s(_("CPU max MHz:"), cpu_max_mhz(desc, buf, sizeof(buf)));
+ 	if (desc->minmhz && desc->minmhz[0])
+-		print_s(_("CPU min MHz:"), desc->minmhz[0]);
++		print_s(_("CPU min MHz:"), cpu_min_mhz(desc, buf, sizeof(buf)));
+ 	if (desc->bogomips)
+ 		print_s(_("BogoMIPS:"), desc->bogomips);
+ 	if (desc->virtflag) {
+-- 
+2.14.4
+
diff --git a/SOURCES/0170-chcpu-cleanup-return-codes.patch b/SOURCES/0170-chcpu-cleanup-return-codes.patch
new file mode 100644
index 0000000..147d201
--- /dev/null
+++ b/SOURCES/0170-chcpu-cleanup-return-codes.patch
@@ -0,0 +1,253 @@
+From 8f57ecb12194a10f3dbe6eca9f4bf0b18c4be757 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 5 Mar 2014 11:06:59 +0100
+Subject: [PATCH 170/173] chcpu: cleanup return codes
+
+The code currently always return EXIT_SUCCESS, that's strange. It
+seems better to return 0 on success, 1 on complete failure and 64 on
+partial success.
+
+Signed-off-by: Karel Zak <kzak@redhat.com>
+Upstream: http://github.com/karelzak/util-linux/commit/48fc00c1c70f3dbbd8ad6ef423bbba27dd3efb69
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439
+---
+ sys-utils/chcpu.8 | 14 ++++++++++
+ sys-utils/chcpu.c | 82 ++++++++++++++++++++++++++++++++++++++++---------------
+ 2 files changed, 74 insertions(+), 22 deletions(-)
+
+diff --git a/sys-utils/chcpu.8 b/sys-utils/chcpu.8
+index d016b86f2..125d9d2ad 100644
+--- a/sys-utils/chcpu.8
++++ b/sys-utils/chcpu.8
+@@ -80,6 +80,20 @@ Display help information and exit.
+ .TP
+ .BR \-V , " \-\-version"
+ Display version information and exit.
++
++.SH RETURN CODES
++.B chcpu
++has the following return codes:
++.TP
++.BR 0
++success
++.TP
++.BR 1
++failure
++.TP
++.BR 64
++partial success
++.RE
+ .SH AUTHOR
+ .MT heiko.carstens@de.ibm.com
+ Heiko Carstens
+diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c
+index 1162888d5..304b80d7a 100644
+--- a/sys-utils/chcpu.c
++++ b/sys-utils/chcpu.c
+@@ -45,6 +45,9 @@
+ 
+ #define EXCL_ERROR "--{configure,deconfigure,disable,dispatch,enable}"
+ 
++/* partial success, otherwise we return regular EXIT_{SUCCESS,FAILURE} */
++#define CHCPU_EXIT_SOMEOK	64
++
+ #define _PATH_SYS_CPU		"/sys/devices/system/cpu"
+ #define _PATH_SYS_CPU_ONLINE	_PATH_SYS_CPU "/online"
+ #define _PATH_SYS_CPU_RESCAN	_PATH_SYS_CPU "/rescan"
+@@ -66,21 +69,28 @@ enum {
+ 	CMD_CPU_DISPATCH_VERTICAL,
+ };
+ 
++/* returns:   0 = success
++ *          < 0 = failure
++ *          > 0 = partial success
++ */
+ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ {
+ 	unsigned int cpu;
+ 	int online, rc;
+ 	int configured = -1;
++	size_t fails = 0;
+ 
+ 	for (cpu = 0; cpu < setsize; cpu++) {
+ 		if (!CPU_ISSET(cpu, cpu_set))
+ 			continue;
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+ 			printf(_("CPU %d does not exist\n"), cpu);
++			fails++;
+ 			continue;
+ 		}
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d/online", cpu)) {
+ 			printf(_("CPU %d is not hot pluggable\n"), cpu);
++			fails++;
+ 			continue;
+ 		}
+ 		online = path_read_s32(_PATH_SYS_CPU "/cpu%d/online", cpu);
+@@ -96,30 +106,35 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ 			configured = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", cpu);
+ 		if (enable) {
+ 			rc = path_write_str("1", _PATH_SYS_CPU "/cpu%d/online", cpu);
+-			if ((rc == -1) && (configured == 0))
++			if ((rc == -1) && (configured == 0)) {
+ 				warnx(_("CPU %d enable failed "
+ 					 "(CPU is deconfigured)"), cpu);
+-			else if (rc == -1)
++				fails++;
++			} else if (rc == -1) {
+ 				warn(_("CPU %d enable failed"), cpu);
+-			else
++				fails++;
++			} else
+ 				printf(_("CPU %d enabled\n"), cpu);
+ 		} else {
+ 			if (onlinecpus && num_online_cpus() == 1) {
+ 				printf(_("CPU %d disable failed "
+ 					 "(last enabled CPU)\n"), cpu);
++				fails++;
+ 				continue;
+ 			}
+ 			rc = path_write_str("0", _PATH_SYS_CPU "/cpu%d/online", cpu);
+-			if (rc == -1)
++			if (rc == -1) {
+ 				warn(_("CPU %d disable failed"), cpu);
+-			else {
++				fails++;
++			} else {
+ 				printf(_("CPU %d disabled\n"), cpu);
+ 				if (onlinecpus)
+ 					CPU_CLR(cpu, onlinecpus);
+ 			}
+ 		}
+ 	}
+-	return EXIT_SUCCESS;
++
++	return fails == 0 ? 0 : fails == setsize ? -1 : 1;
+ }
+ 
+ static int cpu_rescan(void)
+@@ -129,7 +144,7 @@ static int cpu_rescan(void)
+ 	if (path_write_str("1", _PATH_SYS_CPU_RESCAN) == -1)
+ 		err(EXIT_FAILURE, _("Failed to trigger rescan of CPUs"));
+ 	printf(_("Triggered rescan of CPUs\n"));
+-	return EXIT_SUCCESS;
++	return 0;
+ }
+ 
+ static int cpu_set_dispatch(int mode)
+@@ -146,23 +161,30 @@ static int cpu_set_dispatch(int mode)
+ 			err(EXIT_FAILURE, _("Failed to set vertical dispatch mode"));
+ 		printf(_("Successfully set vertical dispatching mode\n"));
+ 	}
+-	return EXIT_SUCCESS;
++	return 0;
+ }
+ 
++/* returns:   0 = success
++ *          < 0 = failure
++ *          > 0 = partial success
++ */
+ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+ {
+ 	unsigned int cpu;
+ 	int rc, current;
++	size_t fails = 0;
+ 
+ 	for (cpu = 0; cpu < setsize; cpu++) {
+ 		if (!CPU_ISSET(cpu, cpu_set))
+ 			continue;
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+ 			printf(_("CPU %d does not exist\n"), cpu);
++			fails++;
+ 			continue;
+ 		}
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", cpu)) {
+ 			printf(_("CPU %d is not configurable\n"), cpu);
++			fails++;
+ 			continue;
+ 		}
+ 		current = path_read_s32(_PATH_SYS_CPU "/cpu%d/configure", cpu);
+@@ -178,23 +200,27 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+ 		    is_cpu_online(cpu)) {
+ 			printf(_("CPU %d deconfigure failed "
+ 				 "(CPU is enabled)\n"), cpu);
++			fails++;
+ 			continue;
+ 		}
+ 		if (configure) {
+ 			rc = path_write_str("1", _PATH_SYS_CPU "/cpu%d/configure", cpu);
+-			if (rc == -1)
++			if (rc == -1) {
+ 				warn(_("CPU %d configure failed"), cpu);
+-			else
++				fails++;
++			} else
+ 				printf(_("CPU %d configured\n"), cpu);
+ 		} else {
+ 			rc = path_write_str("0", _PATH_SYS_CPU "/cpu%d/configure", cpu);
+-			if (rc == -1)
++			if (rc == -1) {
+ 				warn(_("CPU %d deconfigure failed"), cpu);
+-			else
++				fails++;
++			} else
+ 				printf(_("CPU %d deconfigured\n"), cpu);
+ 		}
+ 	}
+-	return EXIT_SUCCESS;
++
++	return fails == 0 ? 0 : fails == setsize ? -1 : 1;
+ }
+ 
+ static void cpu_parse(char *cpu_string, cpu_set_t *cpu_set, size_t setsize)
+@@ -233,7 +259,7 @@ int main(int argc, char *argv[])
+ 	cpu_set_t *cpu_set;
+ 	size_t setsize;
+ 	int cmd = -1;
+-	int c;
++	int c, rc;
+ 
+ 	static const struct option longopts[] = {
+ 		{ "configure",	required_argument, 0, 'c' },
+@@ -317,19 +343,31 @@ int main(int argc, char *argv[])
+ 
+ 	switch (cmd) {
+ 	case CMD_CPU_ENABLE:
+-		return cpu_enable(cpu_set, maxcpus, 1);
++		rc = cpu_enable(cpu_set, maxcpus, 1);
++		break;
+ 	case CMD_CPU_DISABLE:
+-		return cpu_enable(cpu_set, maxcpus, 0);
++		rc = cpu_enable(cpu_set, maxcpus, 0);
++		break;
+ 	case CMD_CPU_CONFIGURE:
+-		return cpu_configure(cpu_set, maxcpus, 1);
++		rc = cpu_configure(cpu_set, maxcpus, 1);
++		break;
+ 	case CMD_CPU_DECONFIGURE:
+-		return cpu_configure(cpu_set, maxcpus, 0);
++		rc = cpu_configure(cpu_set, maxcpus, 0);
++		break;
+ 	case CMD_CPU_RESCAN:
+-		return cpu_rescan();
++		rc = cpu_rescan();
++		break;
+ 	case CMD_CPU_DISPATCH_HORIZONTAL:
+-		return cpu_set_dispatch(0);
++		rc = cpu_set_dispatch(0);
++		break;
+ 	case CMD_CPU_DISPATCH_VERTICAL:
+-		return cpu_set_dispatch(1);
++		rc = cpu_set_dispatch(1);
++		break;
++	default:
++		rc = -EINVAL;
++		break;
+ 	}
+-	return EXIT_SUCCESS;
++
++	return rc == 0 ? EXIT_SUCCESS :
++	        rc < 0 ? EXIT_FAILURE : CHCPU_EXIT_SOMEOK;
+ }
+-- 
+2.14.4
+
diff --git a/SOURCES/0171-chcpu-cleanup-stdout-stderr-usage.patch b/SOURCES/0171-chcpu-cleanup-stdout-stderr-usage.patch
new file mode 100644
index 0000000..8f500a8
--- /dev/null
+++ b/SOURCES/0171-chcpu-cleanup-stdout-stderr-usage.patch
@@ -0,0 +1,79 @@
+From 26c75797103da70f85a5fdd646625edb3eaa300c Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 5 Mar 2014 11:23:16 +0100
+Subject: [PATCH 171/173] chcpu: cleanup stdout/stderr usage
+
+Signed-off-by: Karel Zak <kzak@redhat.com>
+Upstream: http://github.com/karelzak/util-linux/commit/69e74525bb3212944feebc8c2848317e04d95ec0
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439
+---
+ sys-utils/chcpu.c | 17 +++++++----------
+ 1 file changed, 7 insertions(+), 10 deletions(-)
+
+diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c
+index 304b80d7a..ada0eaacc 100644
+--- a/sys-utils/chcpu.c
++++ b/sys-utils/chcpu.c
+@@ -84,12 +84,12 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ 		if (!CPU_ISSET(cpu, cpu_set))
+ 			continue;
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+-			printf(_("CPU %d does not exist\n"), cpu);
++			warnx(_("CPU %d does not exist"), cpu);
+ 			fails++;
+ 			continue;
+ 		}
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d/online", cpu)) {
+-			printf(_("CPU %d is not hot pluggable\n"), cpu);
++			warnx(_("CPU %d is not hot pluggable"), cpu);
+ 			fails++;
+ 			continue;
+ 		}
+@@ -107,8 +107,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ 		if (enable) {
+ 			rc = path_write_str("1", _PATH_SYS_CPU "/cpu%d/online", cpu);
+ 			if ((rc == -1) && (configured == 0)) {
+-				warnx(_("CPU %d enable failed "
+-					 "(CPU is deconfigured)"), cpu);
++				warn(_("CPU %d enable failed (CPU is deconfigured)"), cpu);
+ 				fails++;
+ 			} else if (rc == -1) {
+ 				warn(_("CPU %d enable failed"), cpu);
+@@ -117,8 +116,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ 				printf(_("CPU %d enabled\n"), cpu);
+ 		} else {
+ 			if (onlinecpus && num_online_cpus() == 1) {
+-				printf(_("CPU %d disable failed "
+-					 "(last enabled CPU)\n"), cpu);
++				warnx(_("CPU %d disable failed (last enabled CPU)"), cpu);
+ 				fails++;
+ 				continue;
+ 			}
+@@ -178,12 +176,12 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+ 		if (!CPU_ISSET(cpu, cpu_set))
+ 			continue;
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+-			printf(_("CPU %d does not exist\n"), cpu);
++			warnx(_("CPU %d does not exist"), cpu);
+ 			fails++;
+ 			continue;
+ 		}
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", cpu)) {
+-			printf(_("CPU %d is not configurable\n"), cpu);
++			warnx(_("CPU %d is not configurable"), cpu);
+ 			fails++;
+ 			continue;
+ 		}
+@@ -198,8 +196,7 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+ 		}
+ 		if ((current == 1) && (configure == 0) && onlinecpus &&
+ 		    is_cpu_online(cpu)) {
+-			printf(_("CPU %d deconfigure failed "
+-				 "(CPU is enabled)\n"), cpu);
++			warnx(_("CPU %d deconfigure failed (CPU is enabled)"), cpu);
+ 			fails++;
+ 			continue;
+ 		}
+-- 
+2.14.4
+
diff --git a/SOURCES/0172-lscpu-chcpu-Avoid-use-of-the-old-CPU-macros.patch b/SOURCES/0172-lscpu-chcpu-Avoid-use-of-the-old-CPU-macros.patch
new file mode 100644
index 0000000..30822d1
--- /dev/null
+++ b/SOURCES/0172-lscpu-chcpu-Avoid-use-of-the-old-CPU-macros.patch
@@ -0,0 +1,86 @@
+From 255a3b8bb8cbcb3f689cc3332983983bfbf9eca0 Mon Sep 17 00:00:00 2001
+From: Stanislav Brabec <sbrabec@suse.cz>
+Date: Wed, 9 May 2018 18:08:32 +0200
+Subject: [PATCH 172/173] lscpu, chcpu: Avoid use of the old CPU macros
+
+The old CPU macros are limited to 1024 cores. As a result, lscpu cannot
+count sockets on large systems. Use new scalable macros.
+
+Signed-off-by: Stanislav Brabec <sbrabec@suse.cz>
+Cc: Michael Matz <matz@suse.de>
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439
+Upstream: http://github.com/karelzak/util-linux/commit/538b50cb0a4aac56b6b3b6e4d1e8ce886854c6d8
+---
+ sys-utils/chcpu.c | 6 +++---
+ sys-utils/lscpu.c | 7 +++++--
+ 2 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c
+index ada0eaacc..7843dfb22 100644
+--- a/sys-utils/chcpu.c
++++ b/sys-utils/chcpu.c
+@@ -81,7 +81,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ 	size_t fails = 0;
+ 
+ 	for (cpu = 0; cpu < setsize; cpu++) {
+-		if (!CPU_ISSET(cpu, cpu_set))
++		if (!CPU_ISSET_S(cpu, setsize, cpu_set))
+ 			continue;
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+ 			warnx(_("CPU %d does not exist"), cpu);
+@@ -127,7 +127,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ 			} else {
+ 				printf(_("CPU %d disabled\n"), cpu);
+ 				if (onlinecpus)
+-					CPU_CLR(cpu, onlinecpus);
++					CPU_CLR_S(cpu, setsize, onlinecpus);
+ 			}
+ 		}
+ 	}
+@@ -173,7 +173,7 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+ 	size_t fails = 0;
+ 
+ 	for (cpu = 0; cpu < setsize; cpu++) {
+-		if (!CPU_ISSET(cpu, cpu_set))
++		if (!CPU_ISSET_S(cpu, setsize, cpu_set))
+ 			continue;
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+ 			warnx(_("CPU %d does not exist"), cpu);
+diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c
+index 4c15de1d4..002ad0d1c 100644
+--- a/sys-utils/lscpu.c
++++ b/sys-utils/lscpu.c
+@@ -626,7 +626,7 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
+ 		desc->idx2cpunum = xcalloc(desc->ncpuspos, sizeof(int));
+ 
+ 		for (num = 0, idx = 0; num < maxcpus; num++) {
+-			if (CPU_ISSET(num, tmp))
++			if (CPU_ISSET_S(num, setsize, tmp))
+ 				desc->idx2cpunum[idx++] = num;
+ 		}
+ 		cpuset_free(tmp);
+@@ -2038,6 +2038,7 @@ int main(int argc, char *argv[])
+ 	int c, i;
+ 	int columns[ARRAY_SIZE(coldescs)], ncolumns = 0;
+ 	int cpu_modifier_specified = 0;
++	size_t setsize;
+ 
+ 	static const struct option longopts[] = {
+ 		{ "all",        no_argument,       NULL, 'a' },
+@@ -2134,10 +2135,12 @@ int main(int argc, char *argv[])
+ 
+ 	read_basicinfo(desc, mod);
+ 
++	setsize = CPU_ALLOC_SIZE(maxcpus);
++
+ 	for (i = 0; i < desc->ncpuspos; i++) {
+ 		/* only consider present CPUs */
+ 		if (desc->present &&
+-		    !CPU_ISSET(real_cpu_num(desc, i), desc->present))
++		    !CPU_ISSET_S(real_cpu_num(desc, i), setsize, desc->present))
+ 			continue;
+ 		read_topology(desc, i);
+ 		read_cache(desc, i);
+-- 
+2.14.4
+
diff --git a/SOURCES/0173-chcpu-Fix-maximal-number-of-CPUs.patch b/SOURCES/0173-chcpu-Fix-maximal-number-of-CPUs.patch
new file mode 100644
index 0000000..3d27014
--- /dev/null
+++ b/SOURCES/0173-chcpu-Fix-maximal-number-of-CPUs.patch
@@ -0,0 +1,74 @@
+From 72e09b02e7b4cf3e9e77ef02d5073d9a11f3b314 Mon Sep 17 00:00:00 2001
+From: Stanislav Brabec <sbrabec@suse.cz>
+Date: Wed, 9 May 2018 22:13:07 +0200
+Subject: [PATCH 173/173] chcpu: Fix maximal number of CPUs
+
+chcpu.c mixed maxcpus (number of cpus) and setsize (size of CPU bit
+mask). It effectively limits number of CPUs to 1/8 of the supported
+amount.
+
+Signed-off-by: Stanislav Brabec <sbrabec@suse.cz>
+Cc: Michael Matz <matz@suse.de>
+Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
+Upstream: http://github.com/karelzak/util-linux/commit/607274943bfd3d4856b872bc4278b36903fb2182
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1579439
+---
+ sys-utils/chcpu.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c
+index 7843dfb22..1a0157360 100644
+--- a/sys-utils/chcpu.c
++++ b/sys-utils/chcpu.c
+@@ -75,12 +75,12 @@ enum {
+  */
+ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ {
+-	unsigned int cpu;
++	int cpu;
+ 	int online, rc;
+ 	int configured = -1;
+-	size_t fails = 0;
++	int fails = 0;
+ 
+-	for (cpu = 0; cpu < setsize; cpu++) {
++	for (cpu = 0; cpu < maxcpus; cpu++) {
+ 		if (!CPU_ISSET_S(cpu, setsize, cpu_set))
+ 			continue;
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+@@ -132,7 +132,7 @@ static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+ 		}
+ 	}
+ 
+-	return fails == 0 ? 0 : fails == setsize ? -1 : 1;
++	return fails == 0 ? 0 : fails == maxcpus ? -1 : 1;
+ }
+ 
+ static int cpu_rescan(void)
+@@ -168,11 +168,11 @@ static int cpu_set_dispatch(int mode)
+  */
+ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+ {
+-	unsigned int cpu;
++	int cpu;
+ 	int rc, current;
+-	size_t fails = 0;
++	int fails = 0;
+ 
+-	for (cpu = 0; cpu < setsize; cpu++) {
++	for (cpu = 0; cpu < maxcpus; cpu++) {
+ 		if (!CPU_ISSET_S(cpu, setsize, cpu_set))
+ 			continue;
+ 		if (!path_exist(_PATH_SYS_CPU "/cpu%d", cpu)) {
+@@ -217,7 +217,7 @@ static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+ 		}
+ 	}
+ 
+-	return fails == 0 ? 0 : fails == setsize ? -1 : 1;
++	return fails == 0 ? 0 : fails == maxcpus ? -1 : 1;
+ }
+ 
+ static void cpu_parse(char *cpu_string, cpu_set_t *cpu_set, size_t setsize)
+-- 
+2.14.4
+
diff --git a/SOURCES/0174-libblkid-minix-Match-minix-superblock-types.patch b/SOURCES/0174-libblkid-minix-Match-minix-superblock-types.patch
new file mode 100644
index 0000000..5e42e3b
--- /dev/null
+++ b/SOURCES/0174-libblkid-minix-Match-minix-superblock-types.patch
@@ -0,0 +1,44 @@
+From b4d5cf4819023ce07ecd39729cdd4cde8b59ca35 Mon Sep 17 00:00:00 2001
+From: Nate Clark <nate@neworld.us>
+Date: Wed, 4 Jan 2017 15:24:22 -0500
+Subject: [PATCH 174/176] libblkid/minix: Match minix superblock types
+
+All of the types in the minix super block are unsigned but in
+probe_minix they were being treated as signed. This would cause some of
+the extra sanity checks to pass on a non minix device. The types were
+updated to match the return types of the helper functions in
+disk-utils/minix_programs.h
+
+This can be checked by creating a swap partition with one of these UUIDs
+35f1f264-2468-471a-bc85-acc9f4bc04a3
+35f1f264-6824-471a-bc85-acc9f4bc04a3
+35f1f264-2478-471a-bc85-acc9f4bc04a3
+35f1f264-7824-471a-bc85-acc9f4bc04a3
+
+Prior to this change they would all be considered minix and swap by
+blkid.
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681
+Upstream: http://github.com/karelzak/util-linux/commit/a9975c1072c4975ec2df958188a80d89cabc6171
+Signed-off-by: Nate Clark <nate@neworld.us>
+---
+ libblkid/src/superblocks/minix.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
+index 3e80e5b22..a20d51f2c 100644
+--- a/libblkid/src/superblocks/minix.c
++++ b/libblkid/src/superblocks/minix.c
+@@ -87,7 +87,8 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+ 
+ 	if (version <= 2) {
+ 		struct minix_super_block *sb = (struct minix_super_block *) data;
+-		int zones, ninodes, imaps, zmaps, firstz;
++		unsigned long zones, ninodes, imaps, zmaps;
++		off_t firstz;
+ 
+ 		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+ 			return 1;
+-- 
+2.14.4
+
diff --git a/SOURCES/0175-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch b/SOURCES/0175-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch
new file mode 100644
index 0000000..58dfa33
--- /dev/null
+++ b/SOURCES/0175-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch
@@ -0,0 +1,44 @@
+From 6eb3a6579768458c13c26958e1cc3fbffe2ada70 Mon Sep 17 00:00:00 2001
+From: Nate Clark <nate@neworld.us>
+Date: Wed, 4 Jan 2017 15:21:17 -0500
+Subject: [PATCH 175/176] libblkid/minix: Sanity check superblock s_state for v
+ 1 and 2
+
+Swap devices with specific values in the uuid can look like minix
+devices to blkid. Add an extra check to make sure the state of the
+filesystem has valid state flags.
+
+A couple of offending swap uuids include:
+35f1f264-137f-471a-bc85-acc9f4bc04a3
+35f1f264-7f13-471a-bc85-acc9f4bc04a3
+35f1f264-138f-471a-bc85-acc9f4bc04a3
+35f1f264-8f13-471a-bc85-acc9f4bc04a3
+
+Without this change a swap device with any of those uuids would be
+detected as minix and swap by blkid.
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681
+Upstream: http://github.com/karelzak/util-linux/commit/892553b1a41b449f58462f123eca2bf2c6c56b33
+Signed-off-by: Nate Clark <nate@neworld.us>
+---
+ libblkid/src/superblocks/minix.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
+index a20d51f2c..4e70fda8f 100644
+--- a/libblkid/src/superblocks/minix.c
++++ b/libblkid/src/superblocks/minix.c
+@@ -93,6 +93,10 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+ 		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+ 			return 1;
+ 
++		uint16_t state = minix_swab16(swabme, sb->s_state);
++		if ((state & (MINIX_VALID_FS | MINIX_ERROR_FS)) != state)
++			return 1;
++
+ 		zones = version == 2 ? minix_swab32(swabme, sb->s_zones) :
+ 				       minix_swab16(swabme, sb->s_nzones);
+ 		ninodes = minix_swab16(swabme, sb->s_ninodes);
+-- 
+2.14.4
+
diff --git a/SOURCES/0176-libblkid-minix-Use-same-checks-for-version-3.patch b/SOURCES/0176-libblkid-minix-Use-same-checks-for-version-3.patch
new file mode 100644
index 0000000..577af59
--- /dev/null
+++ b/SOURCES/0176-libblkid-minix-Use-same-checks-for-version-3.patch
@@ -0,0 +1,90 @@
+From b5d9bfe34e62cf1c57646efa0bd72ecad7d98258 Mon Sep 17 00:00:00 2001
+From: Nate Clark <nate@neworld.us>
+Date: Wed, 4 Jan 2017 15:24:32 -0500
+Subject: [PATCH 176/176] libblkid/minix: Use same checks for version 3
+
+fsck.minix performs the same sanity checks on all versions of the
+superblock. Update the probe to perform the same sanity checks so it is
+less likely a different type of filesystem will be identified as minix.
+
+Upstream: http://github.com/karelzak/util-linux/commit/f82c804869bb8613fa0924e3111b7eb55bb04fcd
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1594681
+Signed-off-by: Nate Clark <nate@neworld.us>
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libblkid/src/superblocks/minix.c | 38 +++++++++++++++++++++++---------------
+ 1 file changed, 23 insertions(+), 15 deletions(-)
+
+diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
+index 4e70fda8f..21b3bf8bb 100644
+--- a/libblkid/src/superblocks/minix.c
++++ b/libblkid/src/superblocks/minix.c
+@@ -75,6 +75,9 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+ 	unsigned char *ext;
+ 	const unsigned char *data;
+ 	int version = 0, swabme = 0;
++	unsigned long zones, ninodes, imaps, zmaps;
++	off_t firstz;
++	size_t zone_size;
+ 
+ 	data = blkid_probe_get_buffer(pr, 1024,
+ 			max(sizeof(struct minix_super_block),
+@@ -85,14 +88,9 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+ 	if (version < 1)
+ 		return 1;
+ 
++
+ 	if (version <= 2) {
+ 		struct minix_super_block *sb = (struct minix_super_block *) data;
+-		unsigned long zones, ninodes, imaps, zmaps;
+-		off_t firstz;
+-
+-		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+-			return 1;
+-
+ 		uint16_t state = minix_swab16(swabme, sb->s_state);
+ 		if ((state & (MINIX_VALID_FS | MINIX_ERROR_FS)) != state)
+ 			return 1;
+@@ -103,20 +101,30 @@ static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+ 		imaps   = minix_swab16(swabme, sb->s_imap_blocks);
+ 		zmaps   = minix_swab16(swabme, sb->s_zmap_blocks);
+ 		firstz  = minix_swab16(swabme, sb->s_firstdatazone);
+-
+-		/* sanity checks to be sure that the FS is really minix */
+-		if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1)
+-			return 1;
+-		if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1)
+-			return 1;
+-
++		zone_size = sb->s_log_zone_size;
+ 	} else if (version == 3) {
+ 		struct minix3_super_block *sb = (struct minix3_super_block *) data;
+ 
+-		if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+-			return 1;
++		zones = minix_swab32(swabme, sb->s_zones);
++		ninodes = minix_swab32(swabme, sb->s_ninodes);
++		imaps   = minix_swab16(swabme, sb->s_imap_blocks);
++		zmaps   = minix_swab16(swabme, sb->s_zmap_blocks);
++		firstz  = minix_swab16(swabme, sb->s_firstdatazone);
++		zone_size = sb->s_log_zone_size;
+ 	}
+ 
++	/* sanity checks to be sure that the FS is really minix.
++	 * see disk-utils/fsck.minix.c read_superblock
++	 */
++	if (zone_size != 0 || ninodes == 0 || ninodes == UINT32_MAX)
++		return 1;
++	if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1)
++		return 1;
++	if (firstz > (off_t) zones)
++		return 1;
++	if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1)
++		return 1;
++
+ 	/* unfortunately, some parts of ext3 is sometimes possible to
+ 	 * interpreted as minix superblock. So check for extN magic
+ 	 * string. (For extN magic string and offsets see ext.c.)
+-- 
+2.14.4
+
diff --git a/SOURCES/0177-mount-append-inverting-options-for-mount.-type-on-us.patch b/SOURCES/0177-mount-append-inverting-options-for-mount.-type-on-us.patch
new file mode 100644
index 0000000..cf3ba6d
--- /dev/null
+++ b/SOURCES/0177-mount-append-inverting-options-for-mount.-type-on-us.patch
@@ -0,0 +1,41 @@
+From 4b376d1ad1b46408bc59d949c277991a9b5035eb Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 27 Oct 2016 15:30:20 +0200
+Subject: [PATCH 177/178] mount: append inverting options for mount.<type> on
+ "users"
+
+If you call mount(8) as root, then we need to append inverting options
+(if specified by fstab) for "user" and "users" to /sbin/mount.<type>
+command line, because for UID=0 mount.nfs follows command line rather
+than the fstab setting.
+
+This has been originally implemented by commit
+a4c0cc75ff9744299f108c259efab1bd30c8007a for the old mount(8). The
+same feature is supported by libmount, unfortunately for "user" only.
+We need the same also for "users" to be backwardly compatible.
+
+Addresses: https://github.com/karelzak/util-linux/issues/368
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1618711
+Upstream: http://github.com/karelzak/util-linux/commit/3c4a3de0fcb8f21bffacfd8bdc3d6fbd683c71f5
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libmount/src/context_mount.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
+index 4df2646b0..0f4485592 100644
+--- a/libmount/src/context_mount.c
++++ b/libmount/src/context_mount.c
+@@ -321,7 +321,8 @@ static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
+ 	if (!*optstr)
+ 		return -ENOMEM;
+ 
+-	if (cxt->user_mountflags & MNT_MS_USER) {
++	if ((cxt->user_mountflags & MNT_MS_USER) ||
++	    (cxt->user_mountflags & MNT_MS_USERS)) {
+ 		/*
+ 		 * This is unnecessary for real user-mounts as mount.<type>
+ 		 * helpers have to always follow fstab rather than mount
+-- 
+2.14.4
+
diff --git a/SOURCES/0178-sulogin-backport-RHEL-8-version.patch b/SOURCES/0178-sulogin-backport-RHEL-8-version.patch
new file mode 100644
index 0000000..f33b245
--- /dev/null
+++ b/SOURCES/0178-sulogin-backport-RHEL-8-version.patch
@@ -0,0 +1,839 @@
+From f720781671c9f5421fb8101dd3bcf7b56efca131 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Mon, 20 Aug 2018 14:56:08 +0200
+Subject: [PATCH 178/178] sulogin: backport RHEL-8 version
+
+The new version is more robust and it does not use mmap()-ed shared
+memory between sulogins instances. It also provides some fallbacks for
+s390 machines.
+
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1616264
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ configure.ac                   |   1 +
+ include/c.h                    |   4 +
+ login-utils/sulogin-consoles.c | 116 +++++++++++++------
+ login-utils/sulogin.c          | 253 +++++++++++++++++++++++++++--------------
+ 4 files changed, 252 insertions(+), 122 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index d561e01d0..3f07df495 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -218,6 +218,7 @@ AC_CHECK_HEADERS([ \
+ 	sys/prctl.h \
+ 	sys/queue.h \
+ 	sys/resource.h \
++	sys/sysmacros.h \
+ 	sys/socket.h \
+ 	sys/sockio.h \
+ 	sys/stat.h \
+diff --git a/include/c.h b/include/c.h
+index 124035ea5..51d439297 100644
+--- a/include/c.h
++++ b/include/c.h
+@@ -19,6 +19,10 @@
+ # include <err.h>
+ #endif
+ 
++#ifdef HAVE_SYS_SYSMACROS_H
++# include <sys/sysmacros.h>     /* for major, minor */
++#endif
++
+ #ifndef HAVE_USLEEP
+ # include <time.h>
+ #endif
+diff --git a/login-utils/sulogin-consoles.c b/login-utils/sulogin-consoles.c
+index 07af33a6d..2c0eed3a4 100644
+--- a/login-utils/sulogin-consoles.c
++++ b/login-utils/sulogin-consoles.c
+@@ -36,8 +36,9 @@
+ # include <linux/serial.h>
+ # include <linux/major.h>
+ #endif
+-#include <fcntl.h>
+ #include <dirent.h>
++#include <errno.h>
++#include <fcntl.h>
+ #include <unistd.h>
+ 
+ #ifdef USE_SULOGIN_EMERGENCY_MOUNT
+@@ -74,7 +75,7 @@ static int consoles_debug;
+ 		} while (0)
+ 
+ static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+-dbgprint(const char *mesg, ...)
++dbgprint(const char * const mesg, ...)
+ {
+ 	va_list ap;
+ 	va_start(ap, mesg);
+@@ -112,7 +113,7 @@ void emergency_do_mounts(void)
+ 	}
+ 
+ 	if (stat("/", &rt) != 0) {
+-		warn("can not get file status of root file system\n");
++		warn("cannot get file status of root file system\n");
+ 		return;
+ 	}
+ 
+@@ -150,17 +151,19 @@ void emergency_do_mounts(void) { }
+  * the caller has to free the result
+  */
+ static __attribute__((__nonnull__))
+-char *oneline(const char *file)
++char *oneline(const char * const file)
+ {
+ 	FILE *fp;
+ 	char *ret = NULL;
+-	size_t len = 0;
++	size_t dummy = 0;
++	ssize_t len;
+ 
+ 	DBG(dbgprint("reading %s", file));
+ 
+-	if (!(fp = fopen(file, "re")))
++	if (!(fp = fopen(file, "r" UL_CLOEXECSTR)))
+ 		return NULL;
+-	if (getline(&ret, &len, fp) >= 0) {
++	len = getline(&ret, &dummy, fp);
++	if (len >= 0) {
+ 		char *nl;
+ 
+ 		if (len)
+@@ -179,7 +182,7 @@ char *oneline(const char *file)
+  *  /sys/class/tty, the caller has to free the result.
+  */
+ static __attribute__((__malloc__))
+-char *actattr(const char *tty)
++char *actattr(const char * const tty)
+ {
+ 	char *ret, *path;
+ 
+@@ -198,7 +201,7 @@ char *actattr(const char *tty)
+  * /sys/class/tty.
+  */
+ static
+-dev_t devattr(const char *tty)
++dev_t devattr(const char * const tty)
+ {
+ 	dev_t dev = 0;
+ 	char *path, *value;
+@@ -223,42 +226,77 @@ dev_t devattr(const char *tty)
+ #endif /* __linux__ */
+ 
+ /*
+- * Search below /dev for the characer device in `dev_t comparedev' variable.
++ * Search below /dev for the character device in `dev_t comparedev' variable.
++ * Note that realpath(3) is used here to avoid not existent devices due the
++ * strdup(3) used in our canonicalize_path()!
+  */
+ static
+ #ifdef __GNUC__
+ __attribute__((__nonnull__,__malloc__,__hot__))
+ #endif
+-char* scandev(DIR *dir, dev_t comparedev)
++char* scandev(DIR *dir, const dev_t comparedev)
+ {
++	char path[PATH_MAX];
+ 	char *name = NULL;
+-	struct dirent *dent;
+-	int fd;
++	const struct dirent *dent;
++	int len, fd;
+ 
+ 	DBG(dbgprint("scanning /dev for %u:%u", major(comparedev), minor(comparedev)));
+ 
++	/*
++	 * Try udev links on character devices first.
++	 */
++	if ((len = snprintf(path, sizeof(path),
++			    "/dev/char/%u:%u", major(comparedev), minor(comparedev))) > 0 &&
++	    (size_t)len < sizeof(path)) {
++
++	    name = realpath(path, NULL);
++	    if (name)
++		    goto out;
++	}
++
+ 	fd = dirfd(dir);
+ 	rewinddir(dir);
+ 	while ((dent = readdir(dir))) {
+-		char path[PATH_MAX];
+ 		struct stat st;
++
++#ifdef _DIRENT_HAVE_D_TYPE
++		if (dent->d_type != DT_UNKNOWN && dent->d_type != DT_CHR)
++			continue;
++#endif
+ 		if (fstatat(fd, dent->d_name, &st, 0) < 0)
+ 			continue;
+ 		if (!S_ISCHR(st.st_mode))
+ 			continue;
+ 		if (comparedev != st.st_rdev)
+ 			continue;
+-		if ((size_t)snprintf(path, sizeof(path), "/dev/%s", dent->d_name) >= sizeof(path))
++		if ((len = snprintf(path, sizeof(path), "/dev/%s", dent->d_name)) < 0 ||
++		    (size_t)len >= sizeof(path))
+ 			continue;
+-#ifdef USE_SULOGIN_EMERGENCY_MOUNT
+-		if (emergency_flags & MNT_DEVTMPFS)
+-			mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev);
+-#endif
+ 
+-		name = canonicalize_path(path);
+-		break;
++		name = realpath(path, NULL);
++		if (name)
++			goto out;
+ 	}
+ 
++#ifdef USE_SULOGIN_EMERGENCY_MOUNT
++	/*
++	 * There was no /dev mounted hence and no device was found hence we create our own.
++	 */
++	if (!name && (emergency_flags & MNT_DEVTMPFS)) {
++
++		if ((len = snprintf(path, sizeof(path),
++				    "/dev/tmp-%u:%u", major(comparedev), minor(comparedev))) < 0 ||
++		    (size_t)len >= sizeof(path))
++			goto out;
++
++		if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev) < 0 && errno != EEXIST)
++			goto out;
++
++		name = realpath(path, NULL);
++	}
++#endif
++out:
+ 	return name;
+ }
+ 
+@@ -273,12 +311,12 @@ char* scandev(DIR *dir, dev_t comparedev)
+  */
+ static
+ #ifdef __GNUC__
+-__attribute__((__nonnull__,__hot__))
++__attribute__((__hot__))
+ #endif
+-int append_console(struct list_head *consoles, const char *name)
++int append_console(struct list_head *consoles, const char * const name)
+ {
+ 	struct console *restrict tail;
+-	struct console *last = NULL;
++	const struct console *last = NULL;
+ 
+ 	DBG(dbgprint("appenging %s", name));
+ 
+@@ -300,7 +338,7 @@ int append_console(struct list_head *consoles, const char *name)
+ 	tail->flags = 0;
+ 	tail->fd = -1;
+ 	tail->id = last ? last->id + 1 : 0;
+-	tail->pid = 0;
++	tail->pid = -1;
+ 	memset(&tail->tio, 0, sizeof(tail->tio));
+ 
+ 	return 0;
+@@ -319,11 +357,11 @@ static int detect_consoles_from_proc(struct list_head *consoles)
+ 	char fbuf[16 + 1];
+ 	DIR *dir = NULL;
+ 	FILE *fc = NULL;
+-	int maj, min, rc = 1;
++	int maj, min, rc = 1, matches;
+ 
+ 	DBG(dbgprint("trying /proc"));
+ 
+-	fc = fopen("/proc/consoles", "re");
++	fc = fopen("/proc/consoles", "r" UL_CLOEXECSTR);
+ 	if (!fc) {
+ 		rc = 2;
+ 		goto done;
+@@ -332,10 +370,12 @@ static int detect_consoles_from_proc(struct list_head *consoles)
+ 	if (!dir)
+ 		goto done;
+ 
+-	while (fscanf(fc, "%*s %*s (%16[^)]) %d:%d", fbuf, &maj, &min) == 3) {
++	while ((matches = fscanf(fc, "%*s %*s (%16[^)]) %d:%d", fbuf, &maj, &min)) >= 1) {
+ 		char *name;
+ 		dev_t comparedev;
+ 
++		if (matches != 3)
++			continue;
+ 		if (!strchr(fbuf, 'E'))
+ 			continue;
+ 		comparedev = makedev(maj, min);
+@@ -509,7 +549,7 @@ done:
+ 
+ #ifdef TIOCGDEV
+ static int detect_consoles_from_tiocgdev(struct list_head *consoles,
+-					int fallback,
++					const int fallback,
+ 					const char *device)
+ {
+ 	unsigned int devnum;
+@@ -579,7 +619,7 @@ done:
+  * Returns 1 if stdout and stderr should be reconnected and 0
+  * otherwise or less than zero on error.
+  */
+-int detect_consoles(const char *device, int fallback, struct list_head *consoles)
++int detect_consoles(const char *device, const int fallback, struct list_head *consoles)
+ {
+ 	int fd, reconnect = 0, rc;
+ 	dev_t comparedev = 0;
+@@ -587,7 +627,7 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles
+ 	consoles_debug = getenv("CONSOLES_DEBUG") ? 1 : 0;
+ 
+ 	if (!device || !*device)
+-		fd = dup(fallback);
++		fd = fallback >= 0 ? dup(fallback) : - 1;
+ 	else {
+ 		fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
+ 		reconnect = 1;
+@@ -602,6 +642,14 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles
+ 		struct stat st;
+ #ifdef TIOCGDEV
+ 		unsigned int devnum;
++#endif
++#ifdef __GNU__
++		/*
++		 * The Hurd always gives st_rdev as 0, which causes this
++		 * method to select the first terminal it finds.
++		 */
++		close(fd);
++		goto fallback;
+ #endif
+ 		DBG(dbgprint("trying device/fallback file descriptor"));
+ 
+@@ -670,7 +718,7 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles
+ #ifdef __linux__
+ console:
+ 	/*
+-	 * Detection of devices used for Linux system consolei using
++	 * Detection of devices used for Linux system console using
+ 	 * the /proc/consoles API with kernel 2.6.38 and higher.
+ 	 */
+ 	rc = detect_consoles_from_proc(consoles);
+@@ -754,8 +802,7 @@ int main(int argc, char *argv[])
+ {
+ 	char *name = NULL;
+ 	int fd, re;
+-	LIST_HEAD(consoles);
+-	struct list_head *p;
++	struct list_head *p, consoles;
+ 
+ 	if (argc == 2) {
+ 		name = argv[1];
+@@ -768,6 +815,7 @@ int main(int argc, char *argv[])
+ 	if (!name)
+ 		errx(EXIT_FAILURE, "usage: %s [<tty>]\n", program_invocation_short_name);
+ 
++	INIT_LIST_HEAD(&consoles);
+ 	re = detect_consoles(name, fd, &consoles);
+ 
+ 	list_for_each(p, &consoles) {
+diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
+index dd73a1c50..4620ed2da 100644
+--- a/login-utils/sulogin.c
++++ b/login-utils/sulogin.c
+@@ -56,8 +56,12 @@
+ 
+ #include "c.h"
+ #include "closestream.h"
++#include "env.h"
+ #include "nls.h"
+ #include "pathnames.h"
++#ifdef USE_PLYMOUTH_SUPPORT
++# include "plymouth-ctrl.h"
++#endif
+ #include "strutils.h"
+ #include "ttyutils.h"
+ #include "sulogin-consoles.h"
+@@ -66,13 +70,12 @@
+ static unsigned int timeout;
+ static int profile;
+ static volatile uint32_t openfd;		/* Remember higher file descriptors */
+-static volatile uint32_t *usemask;
+ 
+-struct sigaction saved_sigint;
+-struct sigaction saved_sigtstp;
+-struct sigaction saved_sigquit;
+-struct sigaction saved_sighup;
+-struct sigaction saved_sigchld;
++static struct sigaction saved_sigint;
++static struct sigaction saved_sigtstp;
++static struct sigaction saved_sigquit;
++static struct sigaction saved_sighup;
++static struct sigaction saved_sigchld;
+ 
+ static volatile sig_atomic_t alarm_rised;
+ static volatile sig_atomic_t sigchild;
+@@ -81,7 +84,12 @@ static volatile sig_atomic_t sigchild;
+ # define IUCLC		0
+ #endif
+ 
+-static int locked_account_password(const char *passwd)
++#ifndef WEXITED
++# warning "WEXITED is missing, sulogin may not work as expected"
++# define WEXITED 0
++#endif
++
++static int locked_account_password(const char * const passwd)
+ {
+ 	if (passwd && (*passwd == '*' || *passwd == '!'))
+ 		return 1;
+@@ -93,10 +101,29 @@ static int locked_account_password(const char *passwd)
+  */
+ static void tcinit(struct console *con)
+ {
+-	int mode = 0, flags = 0;
++	int flags = 0, mode = 0;
+ 	struct termios *tio = &con->tio;
+-	int fd = con->fd;
+-
++	const int fd = con->fd;
++#ifdef USE_PLYMOUTH_SUPPORT
++	struct termios lock;
++	int i = (plymouth_command(MAGIC_PING)) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0;
++	if (i)
++		plymouth_command(MAGIC_QUIT);
++	while (i-- > 0) {
++		/*
++		 * With plymouth the termios flags become changed after this
++		 * function had changed the termios.
++		 */
++		memset(&lock, 0, sizeof(struct termios));
++		if (ioctl(fd, TIOCGLCKTRMIOS, &lock) < 0)
++			break;
++		if (!lock.c_iflag && !lock.c_oflag && !lock.c_cflag && !lock.c_lflag)
++			break;
++		sleep(1);
++	}
++	memset(&lock, 0, sizeof(struct termios));
++	ioctl(fd, TIOCSLCKTRMIOS, &lock);
++#endif
+ 	errno = 0;
+ 
+ 	if (tcgetattr(fd, tio) < 0) {
+@@ -189,20 +216,23 @@ setattr:
+  */
+ static void tcfinal(struct console *con)
+ {
+-	struct termios *tio;
+-	int fd;
++	struct termios *tio = &con->tio;
++	const int fd = con->fd;
+ 
+ 	if ((con->flags & CON_SERIAL) == 0) {
+-		setenv("TERM", "linux", 1);
++		xsetenv("TERM", "linux", 1);
+ 		return;
+ 	}
+-	if (con->flags & CON_NOTTY)
++	if (con->flags & CON_NOTTY) {
++		xsetenv("TERM", "dumb", 1);
+ 		return;
++	}
+ 
+-	setenv("TERM", "vt102", 1);
+-	tio = &con->tio;
+-	fd = con->fd;
+-
++#if defined (__s390__) || defined (__s390x__)
++	xsetenv("TERM", "dumb", 1);
++#else
++	xsetenv("TERM", "vt102", 1);
++#endif
+ 	tio->c_iflag |= (IXON | IXOFF);
+ 	tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE);
+ 	tio->c_oflag |= OPOST;
+@@ -237,11 +267,11 @@ static void tcfinal(struct console *con)
+ 		break;
+ 	case 1:				/* odd parity */
+ 		tio->c_cflag |= PARODD;
+-		/* fall through */
++		/* fallthrough */
+ 	case 2:				/* even parity */
+ 		tio->c_cflag |= PARENB;
+ 		tio->c_iflag |= (INPCK | ISTRIP);
+-		/* fall through */
++		/* fallthrough */
+ 	case (1 | 2):			/* no parity bit */
+ 		tio->c_cflag &= ~CSIZE;
+ 		tio->c_cflag |= CS7;
+@@ -466,7 +496,7 @@ static struct passwd *getrootpwent(int try_manually)
+ 		warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD);
+ 		*pwd.pw_passwd = '\0';
+ 	}
+-	/* locked accont passwords are valid too */
++	/* locked account passwords are valid too */
+ 	if (!locked_account_password(pwd.pw_passwd) && !valid(pwd.pw_passwd)) {
+ 		warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD);
+ 		*pwd.pw_passwd = '\0';
+@@ -524,22 +554,17 @@ err:
+  */
+ static void setup(struct console *con)
+ {
+-	pid_t pid, pgrp, ppgrp, ttypgrp;
+-	int fd;
++	int fd = con->fd;
++	const pid_t pid = getpid(), pgrp = getpgid(0), ppgrp =
++	    getpgid(getppid()), ttypgrp = tcgetpgrp(fd);
+ 
+ 	if (con->flags & CON_NOTTY)
+ 		return;
+-	fd = con->fd;
+ 
+ 	/*
+ 	 * Only go through this trouble if the new
+ 	 * tty doesn't fall in this process group.
+ 	 */
+-	pid = getpid();
+-	pgrp = getpgid(0);
+-	ppgrp = getpgid(getppid());
+-	ttypgrp = tcgetpgrp(fd);
+-
+ 	if (pgrp != ttypgrp && ppgrp != ttypgrp) {
+ 		if (pid != getsid(0)) {
+ 			if (pid == getpgid(0))
+@@ -575,21 +600,20 @@ static void setup(struct console *con)
+  * Ask for the password. Note that there is no default timeout as we normally
+  * skip this during boot.
+  */
+-static char *getpasswd(struct console *con)
++static const char *getpasswd(struct console *con)
+ {
+ 	struct sigaction sa;
+ 	struct termios tty;
+ 	static char pass[128], *ptr;
+ 	struct chardata *cp;
+-	char *ret = pass;
++	const char *ret = pass;
+ 	unsigned char tc;
+ 	char c, ascval;
+ 	int eightbit;
+-	int fd;
++	const int fd = con->fd;
+ 
+ 	if (con->flags & CON_NOTTY)
+ 		goto out;
+-	fd = con->fd;
+ 	cp = &con->cp;
+ 	tty = con->tio;
+ 
+@@ -597,6 +621,7 @@ static char *getpasswd(struct console *con)
+ 	tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG);
+ 	tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0);
+ 
++	sigemptyset(&sa.sa_mask);
+ 	sa.sa_handler = alrm_handler;
+ 	sa.sa_flags = 0;
+ 	sigaction(SIGALRM, &sa, NULL);
+@@ -615,7 +640,7 @@ static char *getpasswd(struct console *con)
+ 					ret = NULL;
+ 					goto quit;
+ 				}
+-				usleep(1000);
++				usleep(250000);
+ 				continue;
+ 			}
+ 			ret = (char*)0;
+@@ -627,7 +652,7 @@ static char *getpasswd(struct console *con)
+ 			case ENOENT:
+ 				break;
+ 			default:
+-				warn(_("%s: read failed"), con->tty);
++				warn(_("cannot read %s"), con->tty);
+ 				break;
+ 			}
+ 			goto quit;
+@@ -694,8 +719,8 @@ static void sushell(struct passwd *pwd)
+ {
+ 	char shell[PATH_MAX];
+ 	char home[PATH_MAX];
+-	char *p;
+-	char *su_shell;
++	char const *p;
++	char const *su_shell;
+ 
+ 	/*
+ 	 * Set directory and shell.
+@@ -731,16 +756,16 @@ static void sushell(struct passwd *pwd)
+ 	if (getcwd(home, sizeof(home)) == NULL)
+ 		strcpy(home, "/");
+ 
+-	setenv("HOME", home, 1);
+-	setenv("LOGNAME", "root", 1);
+-	setenv("USER", "root", 1);
++	xsetenv("HOME", home, 1);
++	xsetenv("LOGNAME", "root", 1);
++	xsetenv("USER", "root", 1);
+ 	if (!profile)
+-		setenv("SHLVL","0",1);
++		xsetenv("SHLVL","0",1);
+ 
+ 	/*
+ 	 * Try to execute a shell.
+ 	 */
+-	setenv("SHELL", su_shell, 1);
++	xsetenv("SHELL", su_shell, 1);
+ 	unmask_signal(SIGINT, &saved_sigint);
+ 	unmask_signal(SIGTSTP, &saved_sigtstp);
+ 	unmask_signal(SIGQUIT, &saved_sigquit);
+@@ -765,17 +790,21 @@ static void sushell(struct passwd *pwd)
+ 	execl(su_shell, shell, NULL);
+ 	warn(_("failed to execute %s"), su_shell);
+ 
+-	setenv("SHELL", "/bin/sh", 1);
++	xsetenv("SHELL", "/bin/sh", 1);
+ 	execl("/bin/sh", profile ? "-sh" : "sh", NULL);
+ 	warn(_("failed to execute %s"), "/bin/sh");
+ }
+ 
+-static void usage(FILE *out)
++static void usage(void)
+ {
++	FILE *out = stdout;
+ 	fputs(USAGE_HEADER, out);
+ 	fprintf(out, _(
+ 		" %s [options] [tty device]\n"), program_invocation_short_name);
+ 
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Single-user login.\n"), out);
++
+ 	fputs(USAGE_OPTIONS, out);
+ 	fputs(_(" -p, --login-shell        start a login shell\n"
+ 		" -t, --timeout <seconds>  max time to wait for a password (default: no limit)\n"
+@@ -783,32 +812,35 @@ static void usage(FILE *out)
+ 		out);
+ 
+ 	fputs(USAGE_SEPARATOR, out);
+-	fputs(USAGE_HELP, out);
+-	fputs(USAGE_VERSION, out);
+-	fprintf(out, USAGE_MAN_TAIL("sulogin(8)"));
++	printf(USAGE_HELP_OPTIONS(26));
++	printf(USAGE_MAN_TAIL("sulogin(8)"));
+ }
+ 
+ int main(int argc, char **argv)
+ {
+-	LIST_HEAD(consoles);
+-	struct list_head *ptr;
++	struct list_head *ptr, consoles;
+ 	struct console *con;
+ 	char *tty = NULL;
+ 	struct passwd *pwd;
+-	int c, status = 0;
+-	int reconnect = 0;
++	const struct timespec sigwait = { .tv_sec = 0, .tv_nsec = 50000000 };
++	siginfo_t status = { 0 };
++	sigset_t set;
++	int c, reconnect = 0;
+ 	int opt_e = 0;
++	int wait = 0;
+ 	pid_t pid;
+ 
+ 	static const struct option longopts[] = {
+-		{ "login-shell",  0, 0, 'p' },
+-		{ "timeout",      1, 0, 't' },
+-		{ "force",        0, 0, 'e' },
+-		{ "help",         0, 0, 'h' },
+-		{ "version",      0, 0, 'V' },
+-		{ NULL,           0, 0, 0 }
++		{ "login-shell",  no_argument,       NULL, 'p' },
++		{ "timeout",      required_argument, NULL, 't' },
++		{ "force",        no_argument,       NULL, 'e' },
++		{ "help",         no_argument,       NULL, 'h' },
++		{ "version",      no_argument,       NULL, 'V' },
++		{ NULL, 0, NULL, 0 }
+ 	};
+ 
++	INIT_LIST_HEAD(&consoles);
++
+ 	/*
+ 	 * If we are init we need to set up a own session.
+ 	 */
+@@ -840,17 +872,16 @@ int main(int argc, char **argv)
+ 			printf(UTIL_LINUX_VERSION);
+ 			return EXIT_SUCCESS;
+ 		case 'h':
+-			usage(stdout);
++			usage();
+ 			return EXIT_SUCCESS;
+ 		default:
+-			usage(stderr);
+-			/* Do not exit! */
++			/* Do not exit! getopt prints a warning. */
+ 			break;
+ 		}
+ 	}
+ 
+ 	if (geteuid() != 0)
+-		errx(EXIT_FAILURE, _("only root can run this program."));
++		errx(EXIT_FAILURE, _("only superuser can run this program"));
+ 
+ 	mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
+ 	mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
+@@ -877,7 +908,7 @@ int main(int argc, char **argv)
+ 	reconnect = detect_consoles(tty, STDIN_FILENO, &consoles);
+ 
+ 	/*
+-	 * If previous stdin was not the speified tty and therefore reconnected
++	 * If previous stdin was not the specified tty and therefore reconnected
+ 	 * to the specified tty also reconnect stdout and stderr.
+ 	 */
+ 	if (reconnect) {
+@@ -900,7 +931,7 @@ int main(int argc, char **argv)
+ 	 * Get the root password.
+ 	 */
+ 	if ((pwd = getrootpwent(opt_e)) == NULL) {
+-		warnx(_("cannot open password database."));
++		warnx(_("cannot open password database"));
+ 		sleep(2);
+ 		return EXIT_FAILURE;
+ 	}
+@@ -923,9 +954,6 @@ int main(int argc, char **argv)
+ 		tcinit(con);
+ 	}
+ 	ptr = (&consoles)->next;
+-	usemask = (uint32_t*) mmap(NULL, sizeof(uint32_t),
+-					PROT_READ|PROT_WRITE,
+-					MAP_ANONYMOUS|MAP_SHARED, -1, 0);
+ 
+ 	if (ptr->next == &consoles) {
+ 		con = list_entry(ptr, struct console, entry);
+@@ -942,7 +970,6 @@ int main(int argc, char **argv)
+ 		switch ((con->pid = fork())) {
+ 		case 0:
+ 			mask_signal(SIGCHLD, SIG_DFL, NULL);
+-			/* fall through */
+ 		nofork:
+ 			setup(con);
+ 			while (1) {
+@@ -971,9 +998,7 @@ int main(int argc, char **argv)
+ 				}
+ 
+ 				if (doshell) {
+-					*usemask |= (1<<con->id);
+ 					sushell(pwd);
+-					*usemask &= ~(1<<con->id);
+ 					failed++;
+ 				}
+ 
+@@ -982,7 +1007,7 @@ int main(int argc, char **argv)
+ 				mask_signal(SIGINT,  SIG_IGN, &saved_sigint);
+ 
+ 				if (failed) {
+-					fprintf(stderr, _("Can not execute su shell\n\n"));
++					fprintf(stderr, _("cannot execute su shell\n\n"));
+ 					break;
+ 				}
+ 				fprintf(stderr, _("Login incorrect\n\n"));
+@@ -997,7 +1022,7 @@ int main(int argc, char **argv)
+ 			exit(0);
+ 		case -1:
+ 			warn(_("fork failed"));
+-			/* fall through */
++			/* fallthrough */
+ 		default:
+ 			break;
+ 		}
+@@ -1006,28 +1031,80 @@ int main(int argc, char **argv)
+ 
+ 	} while (ptr != &consoles);
+ 
+-	while ((pid = wait(&status))) {
+-		if (errno == ECHILD)
++	do {
++		int ret;
++
++		status.si_pid = 0;
++		ret = waitid(P_ALL, 0, &status, WEXITED);
++
++		if (ret == 0)
+ 			break;
+-		if (pid < 0)
+-			continue;
+-		list_for_each(ptr, &consoles) {
+-			con = list_entry(ptr, struct console, entry);
+-			if (con->pid == pid) {
+-				*usemask &= ~(1<<con->id);
++		if (ret < 0) {
++			if (errno == ECHILD)
++				break;
++			if (errno == EINTR)
+ 				continue;
+-			}
+-			if (kill(con->pid, 0) < 0) {
+-				*usemask &= ~(1<<con->id);
++		}
++
++		errx(EXIT_FAILURE, _("cannot wait on su shell\n\n"));
++
++	} while (1);
++
++	list_for_each(ptr, &consoles) {
++		con = list_entry(ptr, struct console, entry);
++
++		if (con->fd < 0)
++			continue;
++		if (con->pid < 0)
++			continue;
++		if (con->pid == status.si_pid)
++			con->pid = -1;
++		else {
++			kill(con->pid, SIGTERM);
++			wait++;
++		}
++	}
++
++	sigemptyset(&set);
++	sigaddset(&set, SIGCHLD);
++
++	do {
++		int signum, ret;
++
++		if (!wait)
++			break;
++
++		status.si_pid = 0;
++		ret = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
++
++		if (ret < 0) {
++			if (errno == ECHILD)
++				break;
++			if (errno == EINTR)
+ 				continue;
++		}
++
++		if (!ret && status.si_pid > 0) {
++			list_for_each(ptr, &consoles) {
++				con = list_entry(ptr, struct console, entry);
++
++				if (con->fd < 0)
++					continue;
++				if (con->pid < 0)
++					continue;
++				if (con->pid == status.si_pid) {
++					con->pid = -1;
++					wait--;
++				}
+ 			}
+-			if (*usemask & (1<<con->id))
+-				continue;
+-			kill(con->pid, SIGHUP);
+-			usleep(5000);
+-			kill(con->pid, SIGKILL);
++			continue;
+ 		}
+-	}
++
++		signum = sigtimedwait(&set, NULL, &sigwait);
++		if (signum != SIGCHLD && signum < 0 && errno == EAGAIN)
++			break;
++
++	} while (1);
+ 
+ 	mask_signal(SIGCHLD, SIG_DFL, NULL);
+ 	return EXIT_SUCCESS;
+-- 
+2.14.4
+
diff --git a/SPECS/util-linux.spec b/SPECS/util-linux.spec
index 904ef70..a7ec932 100644
--- a/SPECS/util-linux.spec
+++ b/SPECS/util-linux.spec
@@ -2,7 +2,7 @@
 Summary: A collection of basic system utilities
 Name: util-linux
 Version: 2.23.2
-Release: 52%{?dist}.1
+Release: 59%{?dist}
 License: GPLv2 and GPLv2+ and LGPLv2+ and BSD with advertising and Public Domain
 Group: System Environment/Base
 URL: http://en.wikipedia.org/wiki/Util-linux
@@ -77,6 +77,7 @@ Requires: audit-libs >= 1.0.6
 Requires: libuuid = %{version}-%{release}
 Requires: libblkid = %{version}-%{release}
 Requires: libmount = %{version}-%{release}
+Requires: libsmartcols = %{version}-%{release}
 
 ### Ready for upstream?
 ###
@@ -387,13 +388,53 @@ Patch148: 0148-mkswap-tolerate-ENOTSUP-when-failing-to-relabel.patch
 Patch149: 0149-libmount-fix-debug-message.patch
 
 #
-# RHEL7.5.Z
+# RHEL7.6
 #
+# 1543428 - update lsns
+Patch150: 0150-lsns-missing-ns-name-is-not-error.patch
+Patch151: 0151-lsns-Fix-parser-for-proc-pid-stat-which-is-including.patch
+# 1561350 - provide libsmartcols and libsmartcols-devel libraries
+Patch152: 0152-libsmartcols-add-basic-tools-necessary-for-new-versi.patch
+Patch153: 0153-libsmartcols-backport-upstream-version-v2.32-158-gc0.patch
+Patch154: 0154-tests-backport-libsmartcols-tests.patch
+# 1581611 - lslogins never shows lock passwords as PWD-LOCK
+Patch155: 0155-lslogins-fix-password-verification.patch
+# 1566674 - [RFE] Add warnings that have to be acknowledged to the -l flag in umount or remove the option
+Patch156: 0156-umount-add-note-about-lazy.patch
+# 1566390 - [RHEL-7.5] losetup can not detach all loop devices
+Patch157: 0157-losetup-add-info-about-lazy-detach-to-manpage.patch
+# 1562125 - "setarch ppc<*>" fails with "Unrecognized architecture"
+Patch158: 0158-setarch-Fix-ppc64le-architectures.patch
+# 1528567 - [RFE] Provide 'dig-holes' functionality for fallocate
+Patch159: 0159-fallocate-backport-v2.32-164-g641af90dc.patch
+# 1528959 - rbind creates duplicate entries in /proc/mounts
+Patch160: 0160-libmount-fix-mnt_table_is_fs_mounted-for-rbind.patch
+# 1538721 - mount man page says sync option has no effect for ext4
+Patch161: 0161-mount-add-ext4-to-some-places-in-man-page.patch
+# 1252764 - icrnl is not set when autologin is used
+Patch162: 0162-agetty-keep-c_iflags-unmodified-on-autologin.patch
+# 1561200 - [RFE] - sulogin: improve support for locked root account
+Patch163: 0163-sulogin-don-t-use-strcpy-enlarge-pwd-line-buffer.patch
+Patch164: 0164-sulogin-improve-support-for-locked-root-account.patch
+Patch165: 0165-sulogin-Always-make-echo-work-after-performing-getpa.patch
+Patch166: 0166-sulogin-make-getpasswd-.-return-NULL-on-D.patch
+Patch167: 0167-sulogin-bail-out-from-getpasswd-.-on-timeout.patch
+# 1566432 - [RHEL-7.5] losetup: /dev/loop1: failed to set up loop device
+Patch168: 0168-losetup-keep-f-and-devname-mutually-exclusive.patch
+# 1579439 - RHEL 7.5 - lscpu shows wrong sockets, threads on max config (32TB) POWER8 machine (util-linux)
+Patch169: 0169-lscpu-fix-mzx-min-MHz-reporting.patch
+Patch170: 0170-chcpu-cleanup-return-codes.patch
+Patch171: 0171-chcpu-cleanup-stdout-stderr-usage.patch
+Patch172: 0172-lscpu-chcpu-Avoid-use-of-the-old-CPU-macros.patch
+Patch173: 0173-chcpu-Fix-maximal-number-of-CPUs.patch
 # 1594681 - [RHEL7.2] blkid does not output swap area
-Patch150: 0150-libblkid-minix-Match-minix-superblock-types.patch
-Patch151: 0151-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch
-Patch152: 0152-libblkid-minix-Use-same-checks-for-version-3.patch
-
+Patch174: 0174-libblkid-minix-Match-minix-superblock-types.patch
+Patch175: 0175-libblkid-minix-Sanity-check-superblock-s_state-for-v.patch
+Patch176: 0176-libblkid-minix-Use-same-checks-for-version-3.patch
+# 1618711 - exec option in mount unable to override the implicit noexec in user option
+Patch177: 0177-mount-append-inverting-options-for-mount.-type-on-us.patch
+# 1616264 - Login to emergency shell failed
+Patch178: 0178-sulogin-backport-RHEL-8-version.patch
 
 %description
 The util-linux package contains a large variety of low-level system
@@ -401,6 +442,27 @@ utilities that are necessary for a Linux system to function. Among
 others, Util-linux contains the fdisk configuration tool and the login
 program.
 
+%package -n libsmartcols
+Summary: Formatting library for ls-like programs.
+Group: Development/Libraries
+License: LGPLv2+
+
+%description -n libsmartcols
+This is library for ls-like terminal programs, part of util-linux.
+
+
+%package -n libsmartcols-devel
+Summary: Formatting library for ls-like programs.
+Group: Development/Libraries
+License: LGPLv2+
+Requires: libsmartcols = %{version}-%{release}
+Requires: pkgconfig
+
+%description -n libsmartcols-devel
+This is development library and headers for ls-like terminal programs,
+part of util-linux.
+
+
 %package -n libmount
 Summary: Device mounting library
 Group: Development/Libraries
@@ -653,7 +715,7 @@ rmdir ${RPM_BUILD_ROOT}%{_datadir}/getopt
 ln -sf ../proc/self/mounts %{buildroot}/etc/mtab
 
 # remove static libs
-rm -f $RPM_BUILD_ROOT%{_libdir}/lib{uuid,blkid,mount}.a
+rm -f $RPM_BUILD_ROOT%{_libdir}/lib{uuid,blkid,mount,smartcols}.a
 
 # find MO files
 %find_lang %name
@@ -712,6 +774,9 @@ done
 %post -n libmount -p /sbin/ldconfig
 %postun -n libmount -p /sbin/ldconfig
 
+%post -n libsmartcols -p /sbin/ldconfig
+%postun -n libsmartcols -p /sbin/ldconfig
+
 %pre -n uuidd
 getent group uuidd >/dev/null || groupadd -r uuidd
 getent passwd uuidd >/dev/null || \
@@ -1075,6 +1140,18 @@ fi
 %{compldir}/uuidd
 
 
+%files -n libsmartcols
+%defattr(-,root,root)
+%doc libsmartcols/COPYING
+%{_libdir}/libsmartcols.so.*
+
+%files -n libsmartcols-devel
+%defattr(-,root,root)
+%{_libdir}/libsmartcols.so
+%{_includedir}/libsmartcols
+%{_libdir}/pkgconfig/smartcols.pc
+
+
 %files -n libmount
 %defattr(-,root,root)
 %doc libmount/COPYING
@@ -1127,8 +1204,38 @@ fi
 %{_libdir}/pkgconfig/uuid.pc
 
 %changelog
-* Thu Jul 12 2018 Karel Zak <kzak@redhat.com> 2.23.2-52.el7_5.1
-- fix #1594681 - blkid does not output swap area
+* Mon Aug 20 2018 Karel Zak <kzak@redhat.com> 2.23.2-59
+- fix #1618711 - exec option in mount unable to override the implicit noexec in user option
+- fix #1616264 - Login to emergency shell failed
+
+* Wed Jul 04 2018 Karel Zak <kzak@redhat.com> 2.23.2-58
+- fix #1594681 - [RHEL7.2] blkid does not output swap area
+
+* Thu Jun 14 2018 Karel Zak <kzak@redhat.com> 2.23.2-57
+- add another ppc aliases to setarch (#1562125)
+
+* Wed Jun 13 2018 Karel Zak <kzak@redhat.com> 2.23.2-56
+- backport another chcpu patches for #1579439
+
+* Fri Jun 08 2018 Karel Zak <kzak@redhat.com> 2.23.2-55
+- improve fallocate patch backward compatibility (#1528567)
+
+* Thu Jun 07 2018 Karel Zak <kzak@redhat.com> 2.23.2-54
+- fix #1581611 - lslogins never shows lock passwords as PWD-LOCK
+- fix #1579439 - RHEL 7.5 - lscpu shows wrong sockets, threads on max config (32TB) POWER8 machine (util-linux)
+- fix #1566674 - [RFE] Add warnings that have to be acknowledged to the -l flag in umount or remove the option
+- fix #1566390 - [RHEL-7.5] losetup can not detach all loop devices
+- fix #1562125 - "setarch ppc<*>" fails with "Unrecognized architecture"
+- fix #1528567 - [RFE] Provide 'dig-holes' functionality for fallocate
+- fix #1528959 - rbind creates duplicate entries in /proc/mounts
+- fix #1538721 - mount man page says sync option has no effect for ext4
+- fix #1252764 - icrnl is not set when autologin is used
+- fix #1561200 - [RFE] - sulogin: improve support for locked root account
+- fix #1566432 - [RHEL-7.5] losetup: /dev/loop1: failed to set up loop device
+
+* Thu May 31 2018 Karel Zak <kzak@redhat.com> 2.23.2-53
+- fix #1543428 - update lsns
+- fix #1561350 - provide libsmartcols and libsmartcols-devel libraries
 
 * Fri Feb 02 2018 Karel Zak <kzak@redhat.com> 2.23.2-52
 - fix #1534893 - RHEL7: util-linux: mount/unmount ASLR bypass via environment variable in libmount