From 0cb991fe767b54e8d305dc2feca2cebc022c3887 Mon Sep 17 00:00:00 2001
From: Petr Uzel <petr.uzel@suse.cz>
Date: Wed, 31 Oct 2012 17:49:38 +0100
Subject: [PATCH] lscpu,chcpu: backport from latest upstream (v2.22)


Signed-off-by: Petr Uzel <petr.uzel@suse.cz>
---
 include/all-io.h      |   74 ++++
 include/c.h           |    4 +
 include/closestream.h |   51 +++
 include/cpuset.h      |   22 +-
 include/optutils.h    |   95 +++++
 include/path.h        |   17 +
 include/strutils.h    |    3 +
 include/xalloc.h      |   13 +
 lib/cpuset.c          |   30 +-
 lib/path.c            |  221 +++++++++++
 lib/strutils.c        |   50 +++
 schedutils/taskset.c  |    2 +-
 sys-utils/Makefile.am |    6 +-
 sys-utils/chcpu.8     |   96 +++++
 sys-utils/chcpu.c     |  335 ++++++++++++++++
 sys-utils/lscpu.1     |  139 ++++++-
 sys-utils/lscpu.c     | 1010 ++++++++++++++++++++++++++++++++++---------------
 17 files changed, 1830 insertions(+), 338 deletions(-)
 create mode 100644 include/all-io.h
 create mode 100644 include/closestream.h
 create mode 100644 include/optutils.h
 create mode 100644 include/path.h
 create mode 100644 lib/path.c
 create mode 100644 sys-utils/chcpu.8
 create mode 100644 sys-utils/chcpu.c

diff --git a/include/all-io.h b/include/all-io.h
new file mode 100644
index 0000000..2487599
--- /dev/null
+++ b/include/all-io.h
@@ -0,0 +1,74 @@
+#ifndef UTIL_LINUX_ALL_IO_H
+#define UTIL_LINUX_ALL_IO_H
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "c.h"
+
+static inline int write_all(int fd, const void *buf, size_t count)
+{
+	while (count) {
+		ssize_t tmp;
+
+		errno = 0;
+		tmp = write(fd, buf, count);
+		if (tmp > 0) {
+			count -= tmp;
+			if (count)
+				buf = (void *) ((char *) buf + tmp);
+		} else if (errno != EINTR && errno != EAGAIN)
+			return -1;
+		if (errno == EAGAIN)	/* Try later, *sigh* */
+			usleep(10000);
+	}
+	return 0;
+}
+
+static inline int fwrite_all(const void *ptr, size_t size,
+			     size_t nmemb, FILE *stream)
+{
+	while (nmemb) {
+		size_t tmp;
+
+		errno = 0;
+		tmp = fwrite(ptr, size, nmemb, stream);
+		if (tmp > 0) {
+			nmemb -= tmp;
+			if (nmemb)
+				ptr = (void *) ((char *) ptr + (tmp * size));
+		} else if (errno != EINTR && errno != EAGAIN)
+			return -1;
+		if (errno == EAGAIN)	/* Try later, *sigh* */
+			usleep(10000);
+	}
+	return 0;
+}
+
+static inline ssize_t read_all(int fd, char *buf, size_t count)
+{
+	ssize_t ret;
+	ssize_t c = 0;
+	int tries = 0;
+
+	memset(buf, 0, count);
+	while (count > 0) {
+		ret = read(fd, buf, count);
+		if (ret <= 0) {
+			if ((errno == EAGAIN || errno == EINTR || ret == 0) &&
+			    (tries++ < 5))
+				continue;
+			return c ? c : -1;
+		}
+		if (ret > 0)
+			tries = 0;
+		count -= ret;
+		buf += ret;
+		c += ret;
+	}
+	return c;
+}
+
+
+#endif /* UTIL_LINUX_ALL_IO_H */
diff --git a/include/c.h b/include/c.h
index 599539f..e4500a8 100644
--- a/include/c.h
+++ b/include/c.h
@@ -6,6 +6,7 @@
 #define UTIL_LINUX_C_H
 
 #include <limits.h>
+#include <stdarg.h>
 
 /*
  * Compiler specific stuff
@@ -162,6 +163,9 @@ prog_inv_sh_nm_from_file(char *f, char stripext)
 
 #ifndef IUTF8
 #define IUTF8 0040000
+
+#define USAGE_HEADER     _("\nUsage:\n")
+#define USAGE_OPTIONS    _("\nOptions:\n")
 #endif
 
 #endif /* UTIL_LINUX_C_H */
diff --git a/include/closestream.h b/include/closestream.h
new file mode 100644
index 0000000..d61b83b
--- /dev/null
+++ b/include/closestream.h
@@ -0,0 +1,51 @@
+#ifndef UTIL_LINUX_CLOSESTREAM_H
+#define UTIL_LINUX_CLOSESTREAM_H
+
+#include <stdio.h>
+#ifdef HAVE_STDIO_EXT_H
+#include <stdio_ext.h>
+#endif
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+
+#ifndef HAVE___FPENDING
+static inline int
+__fpending(FILE *stream __attribute__((__unused__)))
+{
+	return 0;
+}
+#endif
+
+static inline int
+close_stream(FILE * stream)
+{
+	const int some_pending = (__fpending(stream) != 0);
+	const int prev_fail = (ferror(stream) != 0);
+	const int fclose_fail = (fclose(stream) != 0);
+	if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) {
+		if (!fclose_fail)
+			errno = 0;
+		return EOF;
+	}
+	return 0;
+}
+
+/* Meant to be used atexit(close_stdout); */
+static inline void
+close_stdout(void)
+{
+	if (close_stream(stdout) != 0 && !(errno == EPIPE)) {
+		if (errno)
+			warn(_("write error"));
+		else
+			warnx(_("write error"));
+		_exit(EXIT_FAILURE);
+	}
+
+	if (close_stream(stderr) != 0)
+		_exit(EXIT_FAILURE);
+}
+
+#endif /* UTIL_LINUX_CLOSESTREAM_H */
diff --git a/include/cpuset.h b/include/cpuset.h
index 9364660..47ddfe2 100644
--- a/include/cpuset.h
+++ b/include/cpuset.h
@@ -61,13 +61,33 @@ extern int __cpuset_count_s(size_t setsize, const cpu_set_t *set);
 
 #define cpuset_nbits(setsize)	(8 * (setsize))
 
+/*
+ * The @idx parametr returns an index of the first mask from @ary array where
+ * the @cpu is set.
+ *
+ * Returns: 0 if found, otherwise 1.
+ */
+static inline int cpuset_ary_isset(size_t cpu, cpu_set_t **ary, size_t nmemb,
+				   size_t setsize, size_t *idx)
+{
+	size_t i;
+
+	for (i = 0; i < nmemb; i++) {
+		if (CPU_ISSET_S(cpu, setsize, ary[i])) {
+			*idx = i;
+			return 0;
+		}
+	}
+	return 1;
+}
+
 extern int get_max_number_of_cpus(void);
 
 extern cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits);
 extern void cpuset_free(cpu_set_t *set);
 
 extern char *cpulist_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
-extern int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize);
+extern int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail);
 
 extern char *cpumask_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
 extern int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize);
diff --git a/include/optutils.h b/include/optutils.h
new file mode 100644
index 0000000..28a54b2
--- /dev/null
+++ b/include/optutils.h
@@ -0,0 +1,95 @@
+#ifndef UTIL_LINUX_OPTUTILS_H
+#define UTIL_LINUX_OPTUTILS_H
+
+#include "c.h"
+#include "nls.h"
+
+static inline const char *option_to_longopt(int c, const struct option *opts)
+{
+	const struct option *o;
+
+	for (o = opts; o->name; o++)
+		if (o->val == c)
+			return o->name;
+	return NULL;
+}
+
+#ifndef OPTUTILS_EXIT_CODE
+# define OPTUTILS_EXIT_CODE EXIT_FAILURE
+#endif
+
+/*
+ * Check collisions between options.
+ *
+ * The conflicts between options are described in ul_excl_t array. The
+ * array contains groups of mutually exclusive options. For example
+ *
+ *	static const ul_excl_t excl[] = {
+ *		{ 'Z','b','c' },		// first group
+ *		{ 'b','x' },			// second group
+ *		{ 0 }
+ *	};
+ *
+ *	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+ *
+ *	while ((c = getopt_long(argc, argv, "Zbcx", longopts, NULL)) != -1) {
+ *
+ *		err_exclusive_options(c, longopts, excl, excl_st);
+ *
+ *		switch (c) {
+ *		case 'Z':
+ *		   ....
+ *		}
+ *	}
+ *
+ * The array excl[] defines two groups of the mutually exclusive options. The
+ * option '-b' is in the both groups.
+ *
+ * Note that the options in the group have to be in ASCII order (ABC..abc..) and
+ * groups have to be also in ASCII order.
+ *
+ * The current status of options is stored in excl_st array. The size of the array
+ * must be the same as number of the groups in the ul_excl_t array.
+ *
+ * If you're unsure then see sys-utils/mount.c or misc-utils/findmnt.c.
+ */
+#define UL_EXCL_STATUS_INIT	{ 0 }
+typedef int ul_excl_t[16];
+
+static inline void err_exclusive_options(
+			int c,
+			const struct option *opts,
+			const ul_excl_t *excl,
+			int *status)
+{
+	int e;
+
+	for (e = 0; excl[e][0] && excl[e][0] <= c; e++) {
+		const int *op = excl[e];
+
+		for (; *op && *op <= c; op++) {
+			if (*op != c)
+				continue;
+			if (status[e] == 0)
+				status[e] = c;
+			else if (status[e] != c) {
+				fprintf(stderr, _("%s: options "),
+						program_invocation_short_name);
+				for (op = excl[e]; *op; op++) {
+					if (opts)
+						fprintf(stderr, "--%s ",
+							option_to_longopt(*op, opts));
+					else
+						fprintf(stderr, "-%c ", *op);
+				}
+				fprintf(stderr, _("are mutually exclusive."));
+				fputc('\n', stderr);
+				exit(OPTUTILS_EXIT_CODE);
+			}
+			break;
+		}
+	}
+}
+
+#endif
+
diff --git a/include/path.h b/include/path.h
new file mode 100644
index 0000000..8e79a85
--- /dev/null
+++ b/include/path.h
@@ -0,0 +1,17 @@
+#include <stdio.h>
+
+extern FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...)
+			__attribute__ ((__format__ (__printf__, 3, 4)));
+extern void path_getstr(char *result, size_t len, const char *path, ...)
+			__attribute__ ((__format__ (__printf__, 3, 4)));
+extern int path_writestr(const char *str, const char *path, ...)
+			 __attribute__ ((__format__ (__printf__, 2, 3)));
+extern int path_getnum(const char *path, ...)
+		       __attribute__ ((__format__ (__printf__, 1, 2)));
+extern int path_exist(const char *path, ...)
+		      __attribute__ ((__format__ (__printf__, 1, 2)));
+extern cpu_set_t *path_cpuset(int, const char *path, ...)
+			      __attribute__ ((__format__ (__printf__, 2, 3)));
+extern cpu_set_t *path_cpulist(int, const char *path, ...)
+			       __attribute__ ((__format__ (__printf__, 2, 3)));
+extern void path_setprefix(const char *);
diff --git a/include/strutils.h b/include/strutils.h
index 462332d..8439c82 100644
--- a/include/strutils.h
+++ b/include/strutils.h
@@ -28,4 +28,7 @@ static inline void xstrncpy(char *dest, const char *src, size_t n)
 extern void strmode(mode_t mode, char *str);
 extern char *size_to_human_string(uint64_t bytes);
 
+extern int string_to_idarray(const char *list, int ary[], size_t arysz,
+			   int (name2id)(const char *, size_t));
+
 #endif
diff --git a/include/xalloc.h b/include/xalloc.h
index 841333c..3bc7a10 100644
--- a/include/xalloc.h
+++ b/include/xalloc.h
@@ -59,4 +59,17 @@ static inline char *xstrdup(const char *str)
 	return ret;
 }
 
+static inline int __attribute__ ((__format__(printf, 2, 3)))
+    xasprintf(char **strp, const char *fmt, ...)
+{
+	int ret;
+	va_list args;
+	va_start(args, fmt);
+	ret = vasprintf(&(*strp), fmt, args);
+	va_end(args);
+	if (ret < 0)
+		err(XALLOC_EXIT_CODE, "cannot allocate string");
+	return ret;
+}
+
 #endif
diff --git a/lib/cpuset.c b/lib/cpuset.c
index 0c483fa..4f053e5 100644
--- a/lib/cpuset.c
+++ b/lib/cpuset.c
@@ -258,13 +258,20 @@ int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize)
 }
 
 /*
- * Parses string with CPUs mask.
+ * Parses string with list of CPU ranges.
+ * Returns 0 on success.
+ * Returns 1 on error.
+ * Returns 2 if fail is set and a cpu number passed in the list doesn't fit
+ * into the cpu_set. If fail is not set cpu numbers that do not fit are
+ * ignored and 0 is returned instead.
  */
-int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize)
+int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
 {
+	size_t max = cpuset_nbits(setsize);
 	const char *p, *q;
-	q = str;
+	int r = 0;
 
+	q = str;
 	CPU_ZERO_S(setsize, set);
 
 	while (p = q, q = nexttoken(q, ','), p) {
@@ -272,8 +279,9 @@ int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize)
 		unsigned int b;	/* end of range */
 		unsigned int s;	/* stride */
 		const char *c1, *c2;
+		char c;
 
-		if (sscanf(p, "%u", &a) < 1)
+		if ((r = sscanf(p, "%u%c", &a, &c)) < 1)
 			return 1;
 		b = a;
 		s = 1;
@@ -281,11 +289,13 @@ int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize)
 		c1 = nexttoken(p, '-');
 		c2 = nexttoken(p, ',');
 		if (c1 != NULL && (c2 == NULL || c1 < c2)) {
-			if (sscanf(c1, "%u", &b) < 1)
+			if ((r = sscanf(c1, "%u%c", &b, &c)) < 1)
 				return 1;
 			c1 = nexttoken(c1, ':');
-			if (c1 != NULL && (c2 == NULL || c1 < c2))
-				if (sscanf(c1, "%u", &s) < 1) {
+			if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+				if ((r = sscanf(c1, "%u%c", &s, &c)) < 1)
+					return 1;
+				if (s == 0)
 					return 1;
 			}
 		}
@@ -293,11 +303,15 @@ int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize)
 		if (!(a <= b))
 			return 1;
 		while (a <= b) {
+			if (fail && (a >= max))
+				return 2;
 			CPU_SET_S(a, setsize, set);
 			a += s;
 		}
 	}
 
+	if (r == 2)
+		return 1;
 	return 0;
 }
 
@@ -356,7 +370,7 @@ int main(int argc, char *argv[])
 	if (mask)
 		rc = cpumask_parse(mask, set, setsize);
 	else
-		rc = cpulist_parse(range, set, setsize);
+		rc = cpulist_parse(range, set, setsize, 0);
 
 	if (rc)
 		errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range);
diff --git a/lib/path.c b/lib/path.c
new file mode 100644
index 0000000..0f5b0b3
--- /dev/null
+++ b/lib/path.c
@@ -0,0 +1,221 @@
+/*
+ * Simple functions to access files.
+ *
+ * Taken from lscpu.c
+ *
+ * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This program 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 program is distributed in the hope that it would 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <err.h>
+#include <fcntl.h>
+
+#include "all-io.h"
+#include "cpuset.h"
+#include "path.h"
+#include "nls.h"
+#include "c.h"
+
+static size_t prefixlen;
+static char pathbuf[PATH_MAX];
+
+static const char *
+path_vcreate(const char *path, va_list ap)
+{
+	if (prefixlen)
+		vsnprintf(pathbuf + prefixlen,
+			  sizeof(pathbuf) - prefixlen, path, ap);
+	else
+		vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
+	return pathbuf;
+}
+
+static FILE *
+path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap)
+{
+	FILE *f;
+	const char *p = path_vcreate(path, ap);
+
+	f = fopen(p, mode);
+	if (!f && exit_on_error)
+		err(EXIT_FAILURE, _("cannot open %s"), p);
+	return f;
+}
+
+static int
+path_vopen(int flags, const char *path, va_list ap)
+{
+	int fd;
+	const char *p = path_vcreate(path, ap);
+
+	fd = open(p, flags);
+	if (fd == -1)
+		err(EXIT_FAILURE, _("cannot open %s"), p);
+	return fd;
+}
+
+FILE *
+path_fopen(const char *mode, int exit_on_error, const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vfopen(mode, exit_on_error, path, ap);
+	va_end(ap);
+
+	return fd;
+}
+
+void
+path_getstr(char *result, size_t len, const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vfopen("r", 1, path, ap);
+	va_end(ap);
+
+	if (!fgets(result, len, fd))
+		err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
+	fclose(fd);
+
+	len = strlen(result);
+	if (result[len - 1] == '\n')
+		result[len - 1] = '\0';
+}
+
+int
+path_getnum(const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+	int result;
+
+	va_start(ap, path);
+	fd = path_vfopen("r", 1, path, ap);
+	va_end(ap);
+
+	if (fscanf(fd, "%d", &result) != 1) {
+		if (ferror(fd))
+			err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
+		else
+			errx(EXIT_FAILURE, _("parse error: %s"), pathbuf);
+	}
+	fclose(fd);
+	return result;
+}
+
+int
+path_writestr(const char *str, const char *path, ...)
+{
+	int fd, result;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vopen(O_WRONLY, path, ap);
+	va_end(ap);
+	result = write_all(fd, str, strlen(str));
+	close(fd);
+	return result;
+}
+
+int
+path_exist(const char *path, ...)
+{
+	va_list ap;
+	const char *p;
+
+	va_start(ap, path);
+	p = path_vcreate(path, ap);
+	va_end(ap);
+
+	return access(p, F_OK) == 0;
+}
+
+static cpu_set_t *
+path_cpuparse(int maxcpus, int islist, const char *path, va_list ap)
+{
+	FILE *fd;
+	cpu_set_t *set;
+	size_t setsize, len = maxcpus * 7;
+	char buf[len];
+
+	fd = path_vfopen("r", 1, path, ap);
+
+	if (!fgets(buf, len, fd))
+		err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
+	fclose(fd);
+
+	len = strlen(buf);
+	if (buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+
+	set = cpuset_alloc(maxcpus, &setsize, NULL);
+	if (!set)
+		err(EXIT_FAILURE, _("failed to callocate cpu set"));
+
+	if (islist) {
+		if (cpulist_parse(buf, set, setsize, 0))
+			errx(EXIT_FAILURE, _("failed to parse CPU list %s"), buf);
+	} else {
+		if (cpumask_parse(buf, set, setsize))
+			errx(EXIT_FAILURE, _("failed to parse CPU mask %s"), buf);
+	}
+	return set;
+}
+
+cpu_set_t *
+path_cpuset(int maxcpus, const char *path, ...)
+{
+	va_list ap;
+	cpu_set_t *set;
+
+	va_start(ap, path);
+	set = path_cpuparse(maxcpus, 0, path, ap);
+	va_end(ap);
+
+	return set;
+}
+
+cpu_set_t *
+path_cpulist(int maxcpus, const char *path, ...)
+{
+	va_list ap;
+	cpu_set_t *set;
+
+	va_start(ap, path);
+	set = path_cpuparse(maxcpus, 1, path, ap);
+	va_end(ap);
+
+	return set;
+}
+
+void
+path_setprefix(const char *prefix)
+{
+	prefixlen = strlen(prefix);
+	strncpy(pathbuf, prefix, sizeof(pathbuf));
+	pathbuf[sizeof(pathbuf) - 1] = '\0';
+}
diff --git a/lib/strutils.c b/lib/strutils.c
index a610548..28d42da 100644
--- a/lib/strutils.c
+++ b/lib/strutils.c
@@ -279,6 +279,56 @@ char *size_to_human_string(uint64_t bytes)
 	return strdup(buf);
 }
 
+/*
+ * Parses comma delimited list to array with IDs, for example:
+ *
+ * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
+ *                   ary[1] = FOO_BBB;
+ *                   ary[3] = FOO_CCC;
+ *
+ * The function name2id() provides conversion from string to ID.
+ *
+ * Returns: >= 0  : number of items added to ary[]
+ *            -1  : parse error or unknown item
+ *            -2  : arysz reached
+ */
+int string_to_idarray(const char *list, int ary[], size_t arysz,
+			int (name2id)(const char *, size_t))
+{
+	const char *begin = NULL, *p;
+	size_t n = 0;
+
+	if (!list || !*list || !ary || !arysz || !name2id)
+		return -1;
+
+	for (p = list; p && *p; p++) {
+		const char *end = NULL;
+		int id;
+
+		if (n >= arysz)
+			return -2;
+		if (!begin)
+			begin = p;		/* begin of the column name */
+		if (*p == ',')
+			end = p;		/* terminate the name */
+		if (*(p + 1) == '\0')
+			end = p + 1;		/* end of string */
+		if (!begin || !end)
+			continue;
+		if (end <= begin)
+			return -1;
+
+		id = name2id(begin, end - begin);
+		if (id == -1)
+			return -1;
+		ary[ n++ ] = id;
+		begin = NULL;
+		if (end && !*end)
+			break;
+	}
+	return n;
+}
+
 
 #ifdef TEST_PROGRAM
 
diff --git a/schedutils/taskset.c b/schedutils/taskset.c
index fa16647..9951807 100644
--- a/schedutils/taskset.c
+++ b/schedutils/taskset.c
@@ -149,7 +149,7 @@ int main(int argc, char *argv[])
 			return EXIT_SUCCESS;
 	}
 
-	rc = c_opt ? cpulist_parse(argv[optind], new_set, new_setsize) :
+	rc = c_opt ? cpulist_parse(argv[optind], new_set, new_setsize, 0) :
 		     cpumask_parse(argv[optind], new_set, new_setsize);
 
 	if (rc)
diff --git a/sys-utils/Makefile.am b/sys-utils/Makefile.am
index d916d3d..56eda59 100644
--- a/sys-utils/Makefile.am
+++ b/sys-utils/Makefile.am
@@ -19,8 +19,12 @@ dist_man_MANS += dmesg.1 ctrlaltdel.8 cytune.8 setarch.8 \
 
 if HAVE_CPU_SET_T
 usrbin_exec_PROGRAMS += lscpu
-lscpu_SOURCES = lscpu.c $(top_srcdir)/lib/cpuset.c
+lscpu_SOURCES = lscpu.c $(top_srcdir)/lib/cpuset.c $(top_srcdir)/lib/path.c \
+			$(top_srcdir)/lib/tt.c $(top_srcdir)/lib/strutils.c
 dist_man_MANS += lscpu.1
+sbin_PROGRAMS += chcpu
+chcpu_SOURCES = chcpu.c $(top_srcdir)/lib/cpuset.c $(top_srcdir)/lib/path.c 
+dist_man_MANS += chcpu.8
 endif
 
 endif
diff --git a/sys-utils/chcpu.8 b/sys-utils/chcpu.8
new file mode 100644
index 0000000..d016b86
--- /dev/null
+++ b/sys-utils/chcpu.8
@@ -0,0 +1,96 @@
+.TH CHCPU "8" "June 2012" Linux "User Manuals"
+.SH NAME
+chcpu \- configure CPUs
+.SH SYNOPSIS
+chcpu
+[\-e] [\-d] [\-c] [\-g]
+cpu-list
+.br
+chcpu [\-p] mode
+.br
+chcpu
+\-r
+.br
+chcpu [\-h] [\-V]
+.br
+.SH DESCRIPTION
+.B chcpu
+can modify the state of CPUs.  It can enable or disable CPUs, scan for new
+CPUs, change the CPU dispatching
+.I mode
+of the underlying hypervisor, and request CPUs from the hypervisor
+(configure) or return CPUs to the hypervisor (deconfigure).
+.PP
+Some options have a
+.I cpu-list
+argument.  Use this argument to specify a comma-separated list of CPUs.  The
+list can contain individual CPU addresses or ranges of addresses.  For
+example,
+.B 0,5,7,9-11
+makes the command applicable to the CPUs with the addresses 0, 5, 7, 9, 10,
+and 11.
+.SH OPTIONS
+.TP
+.BR \-r , " \-\-rescan"
+Trigger a rescan of CPUs.  Use this option on systems that do not
+automatically detect newly attached CPUs.  The Linux kernel then recognizes
+the new CPUs.
+.TP
+.BR \-c , " \-\-configure " \fIcpu-list\fP
+Configure all specified CPUs.  Configuring a CPU means that the hypervisor
+takes a CPU from the CPU pool and assigns it to the virtual hardware on which
+your kernel runs.
+.TP
+.BR \-e , " \-\-enable " \fIcpu-list\fP
+Enable all specified CPUs.  Enabling a CPU means that the kernel sets it
+online.  A CPU must be configured, see
+.BR -c ,
+before it can be enabled.
+.TP
+.BR \-p , " \-\-dispatch " \fImode\fP
+Set the CPU dispatching
+.I mode
+(polarization).  This option has an effect only if your hardware architecture
+and hypervisor support CPU polarization.  Available
+.I modes
+are:
+.RS 14
+.TP 12
+.PD 0
+.B horizontal
+The workload is spread across all available CPUs.
+.TP 12
+.B vertical
+The workload is concentrated on few CPUs.
+.RE
+.PD 1
+.TP
+.BR \-d , " \-\-disable " \fIcpu-list\fP
+Disable all specified CPUs.  Disabling a CPU means that the kernel sets it
+offline.
+.TP
+.BR \-g , " \-\-deconfigure " \fIcpu-list\fP
+Deconfigure all specified CPUs.  Deconfiguring a CPU means that the
+hypervisor removes the CPU from the virtual hardware on which the Linux
+instance runs and returns it to the CPU pool.  A CPU must be offline, see
+\-d, before it can be deconfigured.
+.TP
+.BR \-h , " \-\-help"
+Display help information and exit.
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
+.SH AUTHOR
+.MT heiko.carstens@de.ibm.com
+Heiko Carstens
+.ME
+.SH COPYRIGHT
+Copyright IBM Corp. 2011
+.br
+.SH "SEE ALSO"
+.BR lscpu (1)
+.SH AVAILABILITY
+The chcpu command is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/sys-utils/chcpu.c b/sys-utils/chcpu.c
new file mode 100644
index 0000000..0209fe9
--- /dev/null
+++ b/sys-utils/chcpu.c
@@ -0,0 +1,335 @@
+/*
+ * chcpu - CPU configuration tool
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ *
+ * This program 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 program is distributed in the hope that it would 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "cpuset.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "c.h"
+#include "strutils.h"
+#include "bitops.h"
+#include "path.h"
+#include "closestream.h"
+#include "optutils.h"
+
+#define EXCL_ERROR "--{configure,deconfigure,disable,dispatch,enable}"
+
+#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"
+#define _PATH_SYS_CPU_DISPATCH	_PATH_SYS_CPU "/dispatching"
+
+static cpu_set_t *onlinecpus;
+static int maxcpus;
+
+#define is_cpu_online(cpu) (CPU_ISSET_S((cpu), CPU_ALLOC_SIZE(maxcpus), onlinecpus))
+#define num_online_cpus()  (CPU_COUNT_S(CPU_ALLOC_SIZE(maxcpus), onlinecpus))
+
+enum {
+	CMD_CPU_ENABLE	= 0,
+	CMD_CPU_DISABLE,
+	CMD_CPU_CONFIGURE,
+	CMD_CPU_DECONFIGURE,
+	CMD_CPU_RESCAN,
+	CMD_CPU_DISPATCH_HORIZONTAL,
+	CMD_CPU_DISPATCH_VERTICAL,
+};
+
+static int cpu_enable(cpu_set_t *cpu_set, size_t setsize, int enable)
+{
+	unsigned int cpu;
+	int online, rc;
+	int configured = -1;
+
+	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);
+			continue;
+		}
+		if (!path_exist(_PATH_SYS_CPU "/cpu%d/online", cpu)) {
+			printf(_("CPU %d is not hot pluggable\n"), cpu);
+			continue;
+		}
+		online = path_getnum(_PATH_SYS_CPU "/cpu%d/online", cpu);
+		if ((online == 1) && (enable == 1)) {
+			printf(_("CPU %d is already enabled\n"), cpu);
+			continue;
+		}
+		if ((online == 0) && (enable == 0)) {
+			printf(_("CPU %d is already disabled\n"), cpu);
+			continue;
+		}
+		if (path_exist(_PATH_SYS_CPU "/cpu%d/configure", cpu))
+			configured = path_getnum(_PATH_SYS_CPU "/cpu%d/configure", cpu);
+		if (enable) {
+			rc = path_writestr("1", _PATH_SYS_CPU "/cpu%d/online", cpu);
+			if ((rc == -1) && (configured == 0))
+				printf(_("CPU %d enable failed "
+					 "(CPU is deconfigured)\n"), cpu);
+			else if (rc == -1)
+				printf(_("CPU %d enable failed (%m)\n"), cpu);
+			else
+				printf(_("CPU %d enabled\n"), cpu);
+		} else {
+			if (onlinecpus && num_online_cpus() == 1) {
+				printf(_("CPU %d disable failed "
+					 "(last enabled CPU)\n"), cpu);
+				continue;
+			}
+			rc = path_writestr("0", _PATH_SYS_CPU "/cpu%d/online", cpu);
+			if (rc == -1)
+				printf(_("CPU %d disable failed (%m)\n"), cpu);
+			else {
+				printf(_("CPU %d disabled\n"), cpu);
+				if (onlinecpus)
+					CPU_CLR(cpu, onlinecpus);
+			}
+		}
+	}
+	return EXIT_SUCCESS;
+}
+
+static int cpu_rescan(void)
+{
+	if (!path_exist(_PATH_SYS_CPU_RESCAN))
+		errx(EXIT_FAILURE, _("This system does not support rescanning of CPUs"));
+	if (path_writestr("1", _PATH_SYS_CPU_RESCAN) == -1)
+		err(EXIT_FAILURE, _("Failed to trigger rescan of CPUs"));
+	printf(_("Triggered rescan of CPUs\n"));
+	return EXIT_SUCCESS;
+}
+
+static int cpu_set_dispatch(int mode)
+{
+	if (!path_exist(_PATH_SYS_CPU_DISPATCH))
+		errx(EXIT_FAILURE, _("This system does not support setting "
+				     "the dispatching mode of CPUs"));
+	if (mode == 0) {
+		if (path_writestr("0", _PATH_SYS_CPU_DISPATCH) == -1)
+			err(EXIT_FAILURE, _("Failed to set horizontal dispatch mode"));
+		printf(_("Successfully set horizontal dispatching mode\n"));
+	} else {
+		if (path_writestr("1", _PATH_SYS_CPU_DISPATCH) == -1)
+			err(EXIT_FAILURE, _("Failed to set vertical dispatch mode"));
+		printf(_("Successfully set vertical dispatching mode\n"));
+	}
+	return EXIT_SUCCESS;
+}
+
+static int cpu_configure(cpu_set_t *cpu_set, size_t setsize, int configure)
+{
+	unsigned int cpu;
+	int rc, current;
+
+	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);
+			continue;
+		}
+		if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", cpu)) {
+			printf(_("CPU %d is not configurable\n"), cpu);
+			continue;
+		}
+		current = path_getnum(_PATH_SYS_CPU "/cpu%d/configure", cpu);
+		if ((current == 1) && (configure == 1)) {
+			printf(_("CPU %d is already configured\n"), cpu);
+			continue;
+		}
+		if ((current == 0) && (configure == 0)) {
+			printf(_("CPU %d is already deconfigured\n"), cpu);
+			continue;
+		}
+		if ((current == 1) && (configure == 0) && onlinecpus &&
+		    is_cpu_online(cpu)) {
+			printf(_("CPU %d deconfigure failed "
+				 "(CPU is enabled)\n"), cpu);
+			continue;
+		}
+		if (configure) {
+			rc = path_writestr("1", _PATH_SYS_CPU "/cpu%d/configure", cpu);
+			if (rc == -1)
+				printf(_("CPU %d configure failed (%m)\n"), cpu);
+			else
+				printf(_("CPU %d configured\n"), cpu);
+		} else {
+			rc = path_writestr("0", _PATH_SYS_CPU "/cpu%d/configure", cpu);
+			if (rc == -1)
+				printf(_("CPU %d deconfigure failed (%m)\n"), cpu);
+			else
+				printf(_("CPU %d deconfigured\n"), cpu);
+		}
+	}
+	return EXIT_SUCCESS;
+}
+
+static void cpu_parse(char *cpu_string, cpu_set_t *cpu_set, size_t setsize)
+{
+	int rc;
+
+	rc = cpulist_parse(cpu_string, cpu_set, setsize, 1);
+	if (rc == 0)
+		return;
+	if (rc == 2)
+		errx(EXIT_FAILURE, _("invalid CPU number in CPU list: %s"), cpu_string);
+	errx(EXIT_FAILURE, _("failed to parse CPU list: %s"), cpu_string);
+}
+
+static void __attribute__((__noreturn__)) usage(FILE *out)
+{
+	fprintf(out, _(
+		"\nUsage:\n"
+		" %s [options]\n"), program_invocation_short_name);
+
+	puts(_(	"\nOptions:\n"
+		"  -h, --help                    print this help\n"
+		"  -e, --enable <cpu-list>       enable cpus\n"
+		"  -d, --disable <cpu-list>      disable cpus\n"
+		"  -c, --configure <cpu-list>    configure cpus\n"
+		"  -g, --deconfigure <cpu-list>  deconfigure cpus\n"
+		"  -p, --dispatch <mode>         set dispatching mode\n"
+		"  -r, --rescan                  trigger rescan of cpus\n"
+		"  -V, --version                 output version information and exit\n"));
+
+	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+	cpu_set_t *cpu_set;
+	size_t setsize;
+	int cmd = -1;
+	int c;
+
+	static const struct option longopts[] = {
+		{ "configure",	required_argument, 0, 'c' },
+		{ "deconfigure",required_argument, 0, 'g' },
+		{ "disable",	required_argument, 0, 'd' },
+		{ "dispatch",	required_argument, 0, 'p' },
+		{ "enable",	required_argument, 0, 'e' },
+		{ "help",	no_argument,       0, 'h' },
+		{ "rescan",	no_argument,       0, 'r' },
+		{ "version",	no_argument,       0, 'V' },
+		{ NULL,		0, 0, 0 }
+	};
+
+	static const ul_excl_t excl[] = {       /* rows and cols in in ASCII order */
+		{ 'c','d','e','g','p' },
+		{ 0 }
+	};
+	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+	atexit(close_stdout);
+
+	maxcpus = get_max_number_of_cpus();
+	if (maxcpus < 1)
+		errx(EXIT_FAILURE, _("cannot determine NR_CPUS; aborting"));
+	if (path_exist(_PATH_SYS_CPU_ONLINE))
+		onlinecpus = path_cpulist(maxcpus, _PATH_SYS_CPU_ONLINE);
+	setsize = CPU_ALLOC_SIZE(maxcpus);
+	cpu_set = CPU_ALLOC(maxcpus);
+	if (!cpu_set)
+		err(EXIT_FAILURE, _("cpuset_alloc failed"));
+
+	while ((c = getopt_long(argc, argv, "c:d:e:g:hp:rV", longopts, NULL)) != -1) {
+
+		err_exclusive_options(c, longopts, excl, excl_st);
+
+		switch (c) {
+		case 'c':
+			cmd = CMD_CPU_CONFIGURE;
+			cpu_parse(argv[optind - 1], cpu_set, setsize);
+			break;
+		case 'd':
+			cmd = CMD_CPU_DISABLE;
+			cpu_parse(argv[optind - 1], cpu_set, setsize);
+			break;
+		case 'e':
+			cmd = CMD_CPU_ENABLE;
+			cpu_parse(argv[optind - 1], cpu_set, setsize);
+			break;
+		case 'g':
+			cmd = CMD_CPU_DECONFIGURE;
+			cpu_parse(argv[optind - 1], cpu_set, setsize);
+			break;
+		case 'h':
+			usage(stdout);
+		case 'p':
+			if (strcmp("horizontal", argv[optind - 1]) == 0)
+				cmd = CMD_CPU_DISPATCH_HORIZONTAL;
+			else if (strcmp("vertical", argv[optind - 1]) == 0)
+				cmd = CMD_CPU_DISPATCH_VERTICAL;
+			else
+				errx(EXIT_FAILURE, _("unsupported argument: %s"),
+				     argv[optind -1 ]);
+			break;
+		case 'r':
+			cmd = CMD_CPU_RESCAN;
+			break;
+		case 'V':
+			printf(_("%s from %s\n"), program_invocation_short_name,
+			       PACKAGE_STRING);
+			return EXIT_SUCCESS;
+		default:
+			usage(stderr);
+		}
+	}
+
+	if ((argc == 1) || (argc != optind))
+		usage(stderr);
+
+	switch (cmd) {
+	case CMD_CPU_ENABLE:
+		return cpu_enable(cpu_set, maxcpus, 1);
+	case CMD_CPU_DISABLE:
+		return cpu_enable(cpu_set, maxcpus, 0);
+	case CMD_CPU_CONFIGURE:
+		return cpu_configure(cpu_set, maxcpus, 1);
+	case CMD_CPU_DECONFIGURE:
+		return cpu_configure(cpu_set, maxcpus, 0);
+	case CMD_CPU_RESCAN:
+		return cpu_rescan();
+	case CMD_CPU_DISPATCH_HORIZONTAL:
+		return cpu_set_dispatch(0);
+	case CMD_CPU_DISPATCH_VERTICAL:
+		return cpu_set_dispatch(1);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/sys-utils/lscpu.1 b/sys-utils/lscpu.1
index 7a0a972..f7e34b9 100644
--- a/sys-utils/lscpu.1
+++ b/sys-utils/lscpu.1
@@ -1,48 +1,149 @@
 .\" Process this file with
 .\" groff -man -Tascii lscpu.1
 .\"
-.TH LSCPU 1 "February 2011" Linux "User Manuals"
+.TH LSCPU 1 "February 2011" "util-linux" "User Commands"
 .SH NAME
-lscpu \- CPU architecture information helper
+lscpu \- display information about the CPU architecture
 .SH SYNOPSIS
 .B lscpu
-.RB [ \-hpx ]
-.RB [ \-s
-.IR directory ]
+.RB [ \-a | \-b | \-c "] [" \-x "] [" \-s " \fIdirectory\fP] [" \-e " [\fI=list\fP]|" \-p " [\fI=list\fP]]"
+.br
+.B lscpu
+.BR \-h | \-V
 .SH DESCRIPTION
 .B lscpu
-gathers CPU architecture information like number of CPUs, threads,
-cores, sockets, NUMA nodes, information about CPU caches, CPU family,
-model, bogoMIPS, byte order and stepping from sysfs and /proc/cpuinfo, and prints it in
-a human-readable format.  It supports both online and offline CPUs.
-It can also print out in a parsable format,
-including how different caches are shared by different CPUs,
-which can be fed to other programs.
+gathers CPU architecture information from sysfs and /proc/cpuinfo. The
+command output can be optimized for parsing or for easy readability by humans.
+.br
+The information includes, for example, the number of CPUs, threads, cores,
+sockets,  and Non-Uniform Memory Access (NUMA) nodes. There is also information
+about the CPU caches and cache sharing, family, model, bogoMIPS, byte order,
+and stepping.
+
+Options that result in an output table have a \fIlist\fP argument. Use this
+argument to customize the command output. Specify a comma-separated list of
+column labels to limit the output table to only the specified columns, arranged
+in the specified order. See \fBCOLUMNS\fP for a list of valid column labels. The
+column labels are not case sensitive.
+
+Not all columns are supported on all architectures. If an unsupported column is
+specified, \fBlscpu\fP prints the column but does not provide any data for it.
+
+.SS COLUMNS
+.TP
+.B CPU
+The logical CPU number of a CPU as used by the Linux kernel.
+.TP
+.B CORE
+The logical core number. A core can contain several CPUs.
+.TP
+.B SOCKET
+The logical socket number. A socket can contain several cores.
+.TP
+.B BOOK
+The logical book number. A book can contain several sockets.
+.TP
+.B NODE
+The logical NUMA node number. A node may contain several books.
+.TP
+.B CACHE
+Information about how caches are shared between CPUs.
+.TP
+.B ADDRESS
+The physical address of a CPU.
+.TP
+.B ONLINE
+Indicator that shows whether the Linux instance currently makes use of the CPU.
+.TP
+.B CONFIGURED
+Indicator that shows if the hypervisor has allocated the CPU to the virtual
+hardware on which the Linux instance runs. CPUs that are configured can be set
+online by the Linux instance.
+This column contains data only if your hardware system and hypervisor support
+dynamic CPU resource allocation.
+.TP
+.B POLARIZATION
+This column contains data for Linux instances that run on virtual hardware with
+a hypervisor that can switch the CPU dispatching mode (polarization). The
+polarization can be:
+.RS
+.TP 12
+.B horizontal\fP
+The workload is spread across all available CPUs.
+.TP 12
+.B vertical
+The workload is concentrated on few CPUs.
+.P
+For vertical polarization, the column also shows the degree of concentration,
+high, medium, or low. This column contains data only if your hardware system
+and hypervisor support CPU polarization.
+.RE
 .SH OPTIONS
 .TP
+.BR \-a , " \-\-all"
+Include lines for online and offline CPUs in the output (default for -e). This
+option may only specified together with option -e or -p.
+.TP
+.BR \-b , " \-\-online"
+Limit the output to online CPUs (default for -p). This option may only
+be specified together with option -e or -p.
+.TP
+.BR \-c , " \-\-offline"
+Limit the output to offline CPUs. This option may only be specified together
+with option -e or -p.
+.TP
+.BR \-e , " \-\-extended " \fI[=list]\fP
+Display the CPU information in human readable format.
+
+If the \fIlist\fP argument is omitted, all columns for which data is available
+are included in the command output.
+
+When specifying the \fIlist\fP argument, the string of option, equal sign (=), and
+\fIlist\fP must not contain any blanks or other white space.
+Examples: '\fB-e=cpu,node\fP' or '\fB--extended=cpu,node\fP'.
+.TP
 .BR \-h , " \-\-help"
-Print a help message.
+Display help information and exit.
 .TP
-.BR \-p , " \-\-parse"
-Print out in parsable instead of human-readable format.
+.BR \-p , " \-\-parse " \fI[=list]\fP
+Optimize the command output for easy parsing.
+
+If the \fIlist\fP argument is omitted, the command output is compatible with earlier
+versions of \fBlscpu\fP. In this compatible format, two commas are used to separate
+CPU cache columns. If no CPU caches are identified the cache column is omitted.
+.br
+If the \fIlist\fP argument is used, cache columns are separated with a colon (:).
+
+When specifying the \fIlist\fP argument, the string of option, equal sign (=), and
+\fIlist\fP must not contain any blanks or other white space.
+Examples: '\fB-p=cpu,node\fP' or '\fB--parse=cpu,node\fP'.
 .TP
 .BR \-s , " \-\-sysroot " \fIdirectory\fP
-Use the specified \fIdirectory\fP as system root.  This allows you to inspect
-a snapshot from a different system.
+Gather CPU data for a Linux instance other than the instance from which the
+\fBlscpu\fP command is issued. The specified \fIdirectory\fP is the system root
+of the Linux instance to be inspected.
 .TP
 .BR \-x , " \-\-hex"
-Use hexadecimal masks for CPU sets (e.g. 0x3).  The default is to print the sets
-in list format (e.g. 0,1).
+Use hexadecimal masks for CPU sets (for example 0x3).  The default is to print
+the sets in list format (for example 0,1).
+.TP
+.BR \-V , " \-\-version"
+Display version information and exit.
 .SH BUGS
 The basic overview about CPU family, model, etc. is always based on the first
 CPU only.
 
 Sometimes in Xen Dom0 the kernel reports wrong data.
+
+On virtual hardware the number of cores per socket, etc. can be wrong.
 .SH AUTHOR
 .nf
 Cai Qian <qcai@redhat.com>
 Karel Zak <kzak@redhat.com>
+Heiko Carstens <heiko.carstens@de.ibm.com>
 .fi
+.SH "SEE ALSO"
+.BR chcpu (8)
 .SH AVAILABILITY
 The lscpu command is part of the util-linux package and is available from
 ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c
index 42707be..bb0b042 100644
--- a/sys-utils/lscpu.c
+++ b/sys-utils/lscpu.c
@@ -14,14 +14,13 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #include <ctype.h>
 #include <dirent.h>
-#include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -31,12 +30,20 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 #include <stdarg.h>
-#include <bitops.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#include "strutils.h"
 #include "cpuset.h"
 #include "nls.h"
+#include "xalloc.h"
+#include "c.h"
+#include "strutils.h"
+#include "bitops.h"
+#include "tt.h"
+#include "path.h"
+#include "closestream.h"
+#include "optutils.h"
 
 #define CACHE_MAX 100
 
@@ -47,6 +54,7 @@
 #define _PATH_PROC_XENCAP	_PATH_PROC_XEN "/capabilities"
 #define _PATH_PROC_CPUINFO	"/proc/cpuinfo"
 #define _PATH_PROC_PCIDEVS	"/proc/bus/pci/devices"
+#define _PATH_PROC_SYSINFO	"/proc/sysinfo"
 
 /* virtualization types */
 enum {
@@ -65,13 +73,17 @@ enum {
 	HYPER_NONE	= 0,
 	HYPER_XEN,
 	HYPER_KVM,
-	HYPER_MSHV
+	HYPER_MSHV,
+	HYPER_VMWARE,
+	HYPER_IBM
 };
 const char *hv_vendors[] = {
 	[HYPER_NONE]	= NULL,
 	[HYPER_XEN]	= "Xen",
 	[HYPER_KVM]	= "KVM",
-	[HYPER_MSHV]	= "Microsoft"
+	[HYPER_MSHV]	= "Microsoft",
+	[HYPER_VMWARE]  = "VMware",
+	[HYPER_IBM]	= "IBM"
 };
 
 /* CPU modes */
@@ -89,6 +101,39 @@ struct cpu_cache {
 	cpu_set_t	**sharedmaps;
 };
 
+/* dispatching modes */
+enum {
+	DISP_HORIZONTAL = 0,
+	DISP_VERTICAL	= 1
+};
+
+const char *disp_modes[] = {
+	[DISP_HORIZONTAL]	= N_("horizontal"),
+	[DISP_VERTICAL]		= N_("vertical")
+};
+
+/* cpu polarization */
+enum {
+	POLAR_UNKNOWN	= 0,
+	POLAR_VLOW,
+	POLAR_VMEDIUM,
+	POLAR_VHIGH,
+	POLAR_HORIZONTAL
+};
+
+struct polarization_modes {
+	char *parsable;
+	char *readable;
+};
+
+struct polarization_modes polar_modes[] = {
+	[POLAR_UNKNOWN]	   = {"U",  "-"},
+	[POLAR_VLOW]	   = {"VL", "vert-low"},
+	[POLAR_VMEDIUM]	   = {"VM", "vert-medium"},
+	[POLAR_VHIGH]	   = {"VH", "vert-high"},
+	[POLAR_HORIZONTAL] = {"H",  "horizontal"},
+};
+
 /* global description */
 struct lscpu_desc {
 	char	*arch;
@@ -96,12 +141,14 @@ struct lscpu_desc {
 	char	*family;
 	char	*model;
 	char	*virtflag;	/* virtualization flag (vmx, svm) */
+	char	*hypervisor;	/* hypervisor software */
 	int	hyper;		/* hypervisor vendor ID */
 	int	virtype;	/* VIRT_PARA|FULL|NONE ? */
 	char	*mhz;
 	char	*stepping;
 	char    *bogomips;
 	char	*flags;
+	int	dispatching;	/* none, horizontal or vertical */
 	int	mode;		/* rm, lm or/and tm */
 
 	int		ncpus;		/* number of CPUs */
@@ -110,6 +157,11 @@ struct lscpu_desc {
 	int		nnodes;		/* number of NUMA modes */
 	cpu_set_t	**nodemaps;	/* array with NUMA nodes */
 
+	/* books -- based on book_siblings (internal kernel map of cpuX's
+	 * hardware threads within the same book */
+	int		nbooks;		/* number of all online books */
+	cpu_set_t	**bookmaps;	/* unique book_siblings */
+
 	/* sockets -- based on core_siblings (internal kernel map of cpuX's
 	 * hardware threads within the same physical_package_id (socket)) */
 	int		nsockets;	/* number of all online sockets */
@@ -124,181 +176,90 @@ struct lscpu_desc {
 
 	int		ncaches;
 	struct cpu_cache *caches;
+
+	int		*polarization;	/* cpu polarization */
+	int		*addresses;	/* physical cpu addresses */
+	int		*configured;	/* cpu configured */
+};
+
+enum {
+	OUTPUT_SUMMARY	= 0,	/* default */
+	OUTPUT_PARSABLE,	/* -p */
+	OUTPUT_READABLE,	/* -e */
+};
+
+enum {
+	SYSTEM_LIVE = 0,	/* analyzing a live system */
+	SYSTEM_SNAPSHOT,	/* analyzing a snapshot of a different system */
+};
+
+struct lscpu_modifier {
+	int		mode;		/* OUTPUT_* */
+	int		system;		/* SYSTEM_* */
+	unsigned int	hex:1,		/* print CPU masks rather than CPU lists */
+			compat:1,	/* use backwardly compatible format */
+			online:1,	/* print online CPUs */
+			offline:1;	/* print offline CPUs */
 };
 
-static size_t sysrootlen;
-static char pathbuf[PATH_MAX];
 static int maxcpus;		/* size in bits of kernel cpu mask */
 
 #define is_cpu_online(_d, _cpu) \
 		((_d) && (_d)->online ? \
 			CPU_ISSET_S((_cpu), CPU_ALLOC_SIZE(maxcpus), (_d)->online) : 0)
 
-static FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...)
-		__attribute__ ((__format__ (__printf__, 3, 4)));
-static void path_getstr(char *result, size_t len, const char *path, ...)
-		__attribute__ ((__format__ (__printf__, 3, 4)));
-static int path_getnum(const char *path, ...)
-		__attribute__ ((__format__ (__printf__, 1, 2)));
-static int path_exist(const char *path, ...)
-		__attribute__ ((__format__ (__printf__, 1, 2)));
-static cpu_set_t *path_cpuset(const char *path, ...)
-		__attribute__ ((__format__ (__printf__, 1, 2)));
-
-static const char *
-path_vcreate(const char *path, va_list ap)
-{
-	if (sysrootlen)
-		vsnprintf(pathbuf + sysrootlen,
-			  sizeof(pathbuf) - sysrootlen, path, ap);
-	else
-		vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
-	return pathbuf;
-}
-
-static FILE *
-path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap)
-{
-	FILE *f;
-	const char *p = path_vcreate(path, ap);
-
-	f = fopen(p, mode);
-	if (!f && exit_on_error)
-		err(EXIT_FAILURE, _("error: cannot open %s"), p);
-	return f;
-}
-
-static FILE *
-path_fopen(const char *mode, int exit_on_error, const char *path, ...)
-{
-	FILE *fd;
-	va_list ap;
-
-	va_start(ap, path);
-	fd = path_vfopen(mode, exit_on_error, path, ap);
-	va_end(ap);
-
-	return fd;
-}
-
-static void
-path_getstr(char *result, size_t len, const char *path, ...)
-{
-	FILE *fd;
-	va_list ap;
-
-	va_start(ap, path);
-	fd = path_vfopen("r", 1, path, ap);
-	va_end(ap);
+/*
+ * IDs
+ */
+enum {
+	COL_CPU,
+	COL_CORE,
+	COL_SOCKET,
+	COL_NODE,
+	COL_BOOK,
+	COL_CACHE,
+	COL_POLARIZATION,
+	COL_ADDRESS,
+	COL_CONFIGURED,
+	COL_ONLINE,
+};
 
-	if (!fgets(result, len, fd))
-		err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
-	fclose(fd);
+/* column description
+ */
+struct lscpu_coldesc {
+	const char *name;
+	const char *help;
 
-	len = strlen(result);
-	if (result[len - 1] == '\n')
-		result[len - 1] = '\0';
-}
+	unsigned int  is_abbr:1;	/* name is abbreviation */
+};
 
-static int
-path_getnum(const char *path, ...)
+static struct lscpu_coldesc coldescs[] =
 {
-	FILE *fd;
-	va_list ap;
-	int result;
-
-	va_start(ap, path);
-	fd = path_vfopen("r", 1, path, ap);
-	va_end(ap);
-
-	if (fscanf(fd, "%d", &result) != 1) {
-		if (ferror(fd))
-			err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
-		else
-			errx(EXIT_FAILURE, _("parse error: %s"), pathbuf);
-	}
-	fclose(fd);
-	return result;
-}
+	[COL_CPU]          = { "CPU", N_("logical CPU number"), 1 },
+	[COL_CORE]         = { "CORE", N_("logical core number") },
+	[COL_SOCKET]       = { "SOCKET", N_("logical socket number") },
+	[COL_NODE]         = { "NODE", N_("logical NUMA node number") },
+	[COL_BOOK]         = { "BOOK", N_("logical book number") },
+	[COL_CACHE]        = { "CACHE", N_("shows how caches are shared between CPUs") },
+	[COL_POLARIZATION] = { "POLARIZATION", N_("CPU dispatching mode on virtual hardware") },
+	[COL_ADDRESS]      = { "ADDRESS", N_("physical address of a CPU") },
+	[COL_CONFIGURED]   = { "CONFIGURED", N_("shows if the hypervisor has allocated the CPU") },
+	[COL_ONLINE]       = { "ONLINE", N_("shows if Linux currently makes use of the CPU") }
+};
 
 static int
-path_exist(const char *path, ...)
-{
-	va_list ap;
-	const char *p;
-
-	va_start(ap, path);
-	p = path_vcreate(path, ap);
-	va_end(ap);
-
-	return access(p, F_OK) == 0;
-}
-
-static char *
-xstrdup(const char *str)
-{
-	char *s = strdup(str);
-	if (!s)
-		err(EXIT_FAILURE, _("error: strdup failed"));
-	return s;
-}
-
-static cpu_set_t *
-path_cpuparse(int islist, const char *path, va_list ap)
+column_name_to_id(const char *name, size_t namesz)
 {
-	FILE *fd;
-	cpu_set_t *set;
-	size_t setsize, len = maxcpus * 7;
-	char buf[len];
-
-	fd = path_vfopen("r", 1, path, ap);
-
-	if (!fgets(buf, len, fd))
-		err(EXIT_FAILURE, _("failed to read: %s"), pathbuf);
-	fclose(fd);
+	size_t i;
 
-	len = strlen(buf);
-	if (buf[len - 1] == '\n')
-		buf[len - 1] = '\0';
+	for (i = 0; i < ARRAY_SIZE(coldescs); i++) {
+		const char *cn = coldescs[i].name;
 
-	set = cpuset_alloc(maxcpus, &setsize, NULL);
-	if (!set)
-		err(EXIT_FAILURE, _("failed to callocate cpu set"));
-
-	if (islist) {
-		if (cpulist_parse(buf, set, setsize))
-			errx(EXIT_FAILURE, _("failed to parse CPU list %s"), buf);
-	} else {
-		if (cpumask_parse(buf, set, setsize))
-			errx(EXIT_FAILURE, _("failed to parse CPU mask %s"), buf);
+		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+			return i;
 	}
-	return set;
-}
-
-static cpu_set_t *
-path_cpuset(const char *path, ...)
-{
-	va_list ap;
-	cpu_set_t *set;
-
-	va_start(ap, path);
-	set = path_cpuparse(0, path, ap);
-	va_end(ap);
-
-	return set;
-}
-
-static cpu_set_t *
-path_cpulist(const char *path, ...)
-{
-	va_list ap;
-	cpu_set_t *set;
-
-	va_start(ap, path);
-	set = path_cpuparse(1, path, ap);
-	va_end(ap);
-
-	return set;
+	warnx(_("unknown column: %s"), name);
+	return -1;
 }
 
 /* Lookup a pattern and get the value from cpuinfo.
@@ -306,7 +267,8 @@ path_cpulist(const char *path, ...)
  *
  *	"<pattern>   : <key>"
  */
-int lookup(char *line, char *pattern, char **value)
+static int
+lookup(char *line, char *pattern, char **value)
 {
 	char *p, *v;
 	int len = strlen(pattern);
@@ -346,11 +308,11 @@ int lookup(char *line, char *pattern, char **value)
  * detect that CPU supports 64-bit mode.
  */
 static int
-init_mode(void)
+init_mode(struct lscpu_modifier *mod)
 {
 	int m = 0;
 
-	if (sysrootlen)
+	if (mod->system == SYSTEM_SNAPSHOT)
 		/* reading info from any /{sys,proc} dump, don't mix it with
 		 * information about our real CPU */
 		return 0;
@@ -368,7 +330,7 @@ init_mode(void)
 }
 
 static void
-read_basicinfo(struct lscpu_desc *desc)
+read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
 {
 	FILE *fp = path_fopen("r", 1, _PATH_PROC_CPUINFO);
 	char buf[BUFSIZ];
@@ -396,18 +358,19 @@ read_basicinfo(struct lscpu_desc *desc)
 		else if (lookup(buf, "features", &desc->flags)) ;	/* s390 */
 		else if (lookup(buf, "type", &desc->flags)) ;		/* sparc64 */
 		else if (lookup(buf, "bogomips", &desc->bogomips)) ;
+		else if (lookup(buf, "bogomips per cpu", &desc->bogomips)) ; /* s390 */
 		else
 			continue;
 	}
 
-	desc->mode = init_mode();
+	desc->mode = init_mode(mod);
 
 	if (desc->flags) {
 		snprintf(buf, sizeof(buf), " %s ", desc->flags);
 		if (strstr(buf, " svm "))
-			desc->virtflag = strdup("svm");
+			desc->virtflag = xstrdup("svm");
 		else if (strstr(buf, " vmx "))
-			desc->virtflag = strdup("vmx");
+			desc->virtflag = xstrdup("vmx");
 		if (strstr(buf, " lm "))
 			desc->mode |= MODE_32BIT | MODE_64BIT;		/* x86_64 */
 		if (strstr(buf, " zarch "))
@@ -422,27 +385,34 @@ read_basicinfo(struct lscpu_desc *desc)
 		/* note that kernel_max is maximum index [NR_CPUS-1] */
 		maxcpus = path_getnum(_PATH_SYS_SYSTEM "/cpu/kernel_max") + 1;
 
-	else if (!sysrootlen)
+	else if (mod->system == SYSTEM_LIVE)
 		/* the root is '/' so we are working with data from the current kernel */
 		maxcpus = get_max_number_of_cpus();
-	else
-		/* we are reading some /sys snapshot instead of the real /sys,
-		 * let's use any crazy number... */
+
+	if (maxcpus <= 0)
+		/* error or we are reading some /sys snapshot instead of the
+		 * real /sys, let's use any crazy number... */
 		maxcpus = desc->ncpus > 2048 ? desc->ncpus : 2048;
 
 	/* get mask for online CPUs */
 	if (path_exist(_PATH_SYS_SYSTEM "/cpu/online")) {
 		size_t setsize = CPU_ALLOC_SIZE(maxcpus);
-		desc->online = path_cpulist(_PATH_SYS_SYSTEM "/cpu/online");
+		desc->online = path_cpulist(maxcpus, _PATH_SYS_SYSTEM "/cpu/online");
 		desc->nthreads = CPU_COUNT_S(setsize, desc->online);
 	}
+
+	/* get dispatching mode */
+	if (path_exist(_PATH_SYS_SYSTEM "/cpu/dispatching"))
+		desc->dispatching = path_getnum(_PATH_SYS_SYSTEM "/cpu/dispatching");
+	else
+		desc->dispatching = -1;
 }
 
 static int
-has_pci_device(int vendor, int device)
+has_pci_device(unsigned int vendor, unsigned int device)
 {
 	FILE *f;
-	int num, fn, ven, dev;
+	unsigned int num, fn, ven, dev;
 	int res = 1;
 
 	f = path_fopen("r", 0, _PATH_PROC_PCIDEVS);
@@ -519,6 +489,8 @@ read_hypervisor_cpuid(struct lscpu_desc *desc)
 		desc->hyper = HYPER_KVM;
 	else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
 		desc->hyper = HYPER_MSHV;
+	else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
+		desc->hyper = HYPER_VMWARE;
 }
 
 #else	/* ! __x86_64__ */
@@ -557,10 +529,42 @@ read_hypervisor(struct lscpu_desc *desc)
 		/* Xen full-virt on non-x86_64 */
 		desc->hyper = HYPER_XEN;
 		desc->virtype = VIRT_FULL;
+	} else if (path_exist(_PATH_PROC_SYSINFO)) {
+		FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
+		char buf[BUFSIZ];
+
+		desc->hyper = HYPER_IBM;
+		desc->hypervisor = "PR/SM";
+		desc->virtype = VIRT_FULL;
+		while (fgets(buf, sizeof(buf), fd) != NULL) {
+			char *str;
+
+			if (!strstr(buf, "Control Program:"))
+				continue;
+			if (!strstr(buf, "KVM"))
+				desc->hyper = HYPER_IBM;
+			else
+				desc->hyper = HYPER_KVM;
+			str = strchr(buf, ':');
+			if (!str)
+				continue;
+			if (xasprintf(&str, "%s", str + 1) == -1)
+				errx(EXIT_FAILURE, _("failed to allocate memory"));
+			/* remove leading, trailing and repeating whitespace */
+			while (*str == ' ')
+				str++;
+			desc->hypervisor = str;
+			str += strlen(str) - 1;
+			while ((*str == '\n') || (*str == ' '))
+				*(str--) = '\0';
+			while ((str = strstr(desc->hypervisor, "  ")))
+				memmove(str, str + 1, strlen(str));
+		}
+		fclose(fd);
 	}
 }
 
-/* add @set to the @ary, unnecesary set is deallocated. */
+/* add @set to the @ary, unnecessary set is deallocated. */
 static int add_cpuset_to_array(cpu_set_t **ary, int *items, cpu_set_t *set)
 {
 	int i;
@@ -585,44 +589,103 @@ static int add_cpuset_to_array(cpu_set_t **ary, int *items, cpu_set_t *set)
 static void
 read_topology(struct lscpu_desc *desc, int num)
 {
-	cpu_set_t *thread_siblings, *core_siblings;
+	cpu_set_t *thread_siblings, *core_siblings, *book_siblings;
 
 	if (!path_exist(_PATH_SYS_CPU "/cpu%d/topology/thread_siblings", num))
 		return;
 
-	thread_siblings = path_cpuset(_PATH_SYS_CPU
+	thread_siblings = path_cpuset(maxcpus, _PATH_SYS_CPU
 					"/cpu%d/topology/thread_siblings", num);
-	core_siblings = path_cpuset(_PATH_SYS_CPU
+	core_siblings = path_cpuset(maxcpus, _PATH_SYS_CPU
 					"/cpu%d/topology/core_siblings", num);
+	book_siblings = NULL;
+	if (path_exist(_PATH_SYS_CPU "/cpu%d/topology/book_siblings", num)) {
+		book_siblings = path_cpuset(maxcpus, _PATH_SYS_CPU
+					    "/cpu%d/topology/book_siblings", num);
+	}
 
 	if (!desc->coremaps) {
-		int ncores, nsockets, nthreads;
+		int nbooks, nsockets, ncores, nthreads;
 		size_t setsize = CPU_ALLOC_SIZE(maxcpus);
 
 		/* threads within one core */
 		nthreads = CPU_COUNT_S(setsize, thread_siblings);
 		/* cores within one socket */
 		ncores = CPU_COUNT_S(setsize, core_siblings) / nthreads;
-		/* number of sockets */
-		nsockets = desc->ncpus / nthreads / ncores;
+		/* number of sockets within one book.
+		 * Because of odd / non-present cpu maps and to keep
+		 * calculation easy we make sure that nsockets and
+		 * nbooks is at least 1.
+		 */
+		nsockets = desc->ncpus / nthreads / ncores ?: 1;
+		/* number of books */
+		nbooks = desc->ncpus / nthreads / ncores / nsockets ?: 1;
 
 		/* all threads, see also read_basicinfo()
-		 * -- this is fallback for kernels where is not
+		 * -- fallback for kernels without
 		 *    /sys/devices/system/cpu/online.
 		 */
 		if (!desc->nthreads)
-			desc->nthreads = nsockets * ncores * nthreads;
-
-		desc->socketmaps = calloc(nsockets, sizeof(cpu_set_t *));
-		if (!desc->socketmaps)
-			err(EXIT_FAILURE, _("error: calloc failed"));
-		desc->coremaps = calloc(ncores * nsockets, sizeof(cpu_set_t *));
-		if (!desc->coremaps)
-			err(EXIT_FAILURE, _("error: calloc failed"));
+			desc->nthreads = nbooks * nsockets * ncores * nthreads;
+		/* For each map we make sure that it can have up to ncpus
+		 * entries. This is because we cannot reliably calculate the
+		 * number of cores, sockets and books on all architectures.
+		 * E.g. completely virtualized architectures like s390 may
+		 * have multiple sockets of different sizes.
+		 */
+		desc->coremaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+		desc->socketmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
+		if (book_siblings)
+			desc->bookmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
 	}
 
 	add_cpuset_to_array(desc->socketmaps, &desc->nsockets, core_siblings);
 	add_cpuset_to_array(desc->coremaps, &desc->ncores, thread_siblings);
+	if (book_siblings)
+		add_cpuset_to_array(desc->bookmaps, &desc->nbooks, book_siblings);
+}
+static void
+read_polarization(struct lscpu_desc *desc, int num)
+{
+	char mode[64];
+
+	if (desc->dispatching < 0)
+		return;
+	if (!path_exist(_PATH_SYS_CPU "/cpu%d/polarization", num))
+		return;
+	if (!desc->polarization)
+		desc->polarization = xcalloc(desc->ncpus, sizeof(int));
+	path_getstr(mode, sizeof(mode), _PATH_SYS_CPU "/cpu%d/polarization", num);
+	if (strncmp(mode, "vertical:low", sizeof(mode)) == 0)
+		desc->polarization[num] = POLAR_VLOW;
+	else if (strncmp(mode, "vertical:medium", sizeof(mode)) == 0)
+		desc->polarization[num] = POLAR_VMEDIUM;
+	else if (strncmp(mode, "vertical:high", sizeof(mode)) == 0)
+		desc->polarization[num] = POLAR_VHIGH;
+	else if (strncmp(mode, "horizontal", sizeof(mode)) == 0)
+		desc->polarization[num] = POLAR_HORIZONTAL;
+	else
+		desc->polarization[num] = POLAR_UNKNOWN;
+}
+
+static void
+read_address(struct lscpu_desc *desc, int num)
+{
+	if (!path_exist(_PATH_SYS_CPU "/cpu%d/address", num))
+		return;
+	if (!desc->addresses)
+		desc->addresses = xcalloc(desc->ncpus, sizeof(int));
+	desc->addresses[num] = path_getnum(_PATH_SYS_CPU "/cpu%d/address", num);
+}
+
+static void
+read_configured(struct lscpu_desc *desc, int num)
+{
+	if (!path_exist(_PATH_SYS_CPU "/cpu%d/configure", num))
+		return;
+	if (!desc->configured)
+		desc->configured = xcalloc(desc->ncpus, sizeof(int));
+	desc->configured[num] = path_getnum(_PATH_SYS_CPU "/cpu%d/configure", num);
 }
 
 static int
@@ -648,14 +711,15 @@ read_cache(struct lscpu_desc *desc, int num)
 		if (!desc->ncaches)
 			return;
 
-		desc->caches = calloc(desc->ncaches, sizeof(*desc->caches));
-		if (!desc->caches)
-			err(EXIT_FAILURE, _("calloc failed"));
+		desc->caches = xcalloc(desc->ncaches, sizeof(*desc->caches));
 	}
 	for (i = 0; i < desc->ncaches; i++) {
 		struct cpu_cache *ca = &desc->caches[i];
 		cpu_set_t *map;
 
+		if (!path_exist(_PATH_SYS_SYSTEM "/cpu/cpu%d/cache/index%d",
+				num, i))
+			continue;
 		if (!ca->name) {
 			int type, level;
 
@@ -688,15 +752,12 @@ read_cache(struct lscpu_desc *desc, int num)
 		}
 
 		/* information about how CPUs share different caches */
-		map = path_cpuset(_PATH_SYS_CPU "/cpu%d/cache/index%d/shared_cpu_map",
-				num, i);
-
-		if (!ca->sharedmaps) {
-			ca->sharedmaps = calloc(desc->ncpus, sizeof(cpu_set_t *));
-			if (!ca->sharedmaps)
-				err(EXIT_FAILURE, _("error: calloc failed"));
-		}
+		map = path_cpuset(maxcpus,
+				  _PATH_SYS_CPU "/cpu%d/cache/index%d/shared_cpu_map",
+				  num, i);
 
+		if (!ca->sharedmaps)
+			ca->sharedmaps = xcalloc(desc->ncpus, sizeof(cpu_set_t *));
 		add_cpuset_to_array(ca->sharedmaps, &ca->nsharedmaps, map);
 	}
 }
@@ -713,97 +774,279 @@ read_nodes(struct lscpu_desc *desc)
 	if (!desc->nnodes)
 		return;
 
-	desc->nodemaps = calloc(desc->nnodes, sizeof(cpu_set_t *));
-	if (!desc->nodemaps)
-		err(EXIT_FAILURE, _("error: calloc failed"));
+	desc->nodemaps = xcalloc(desc->nnodes, sizeof(cpu_set_t *));
 
 	/* information about how nodes share different CPUs */
 	for (i = 0; i < desc->nnodes; i++)
-		desc->nodemaps[i] = path_cpuset(
+		desc->nodemaps[i] = path_cpuset(maxcpus,
 					_PATH_SYS_SYSTEM "/node/node%d/cpumap",
 					i);
 }
 
-static void
-print_parsable(struct lscpu_desc *desc)
+static char *
+get_cell_data(struct lscpu_desc *desc, int cpu, int col,
+	      struct lscpu_modifier *mod,
+	      char *buf, size_t bufsz)
 {
-	int i, j;
 	size_t setsize = CPU_ALLOC_SIZE(maxcpus);
+	size_t idx;
 
-	printf(_(
-	"# The following is the parsable format, which can be fed to other\n"
-	"# programs. Each different item in every column has an unique ID\n"
-	"# starting from zero.\n"
-	"# CPU,Core,Socket,Node"));
+	*buf = '\0';
 
-	if (desc->ncaches) {
-		/* separator between CPU topology and cache information */
-		putchar(',');
+	switch (col) {
+	case COL_CPU:
+		snprintf(buf, bufsz, "%d", cpu);
+		break;
+	case COL_CORE:
+		if (cpuset_ary_isset(cpu, desc->coremaps,
+				     desc->ncores, setsize, &idx) == 0)
+			snprintf(buf, bufsz, "%zd", idx);
+		break;
+	case COL_SOCKET:
+		if (cpuset_ary_isset(cpu, desc->socketmaps,
+				     desc->nsockets, setsize, &idx) == 0)
+			snprintf(buf, bufsz, "%zd", idx);
+		break;
+	case COL_NODE:
+		if (cpuset_ary_isset(cpu, desc->nodemaps,
+				     desc->nnodes, setsize, &idx) == 0)
+			snprintf(buf, bufsz, "%zd", idx);
+		break;
+	case COL_BOOK:
+		if (cpuset_ary_isset(cpu, desc->bookmaps,
+				     desc->nbooks, setsize, &idx) == 0)
+			snprintf(buf, bufsz, "%zd", idx);
+		break;
+	case COL_CACHE:
+	{
+		char *p = buf;
+		size_t sz = bufsz;
+		int j;
+
+		for (j = desc->ncaches - 1; j >= 0; j--) {
+			struct cpu_cache *ca = &desc->caches[j];
 
-		for (i = desc->ncaches - 1; i >= 0; i--)
-			printf(",%s", desc->caches[i].name);
+			if (cpuset_ary_isset(cpu, ca->sharedmaps,
+					     ca->nsharedmaps, setsize, &idx) == 0) {
+				int x = snprintf(p, sz, "%zd", idx);
+				if (x <= 0 || (size_t) x + 2 >= sz)
+					return NULL;
+				p += x;
+				sz -= x;
+			}
+			if (j != 0) {
+				*p++ = mod->compat ? ',' : ':';
+				*p = '\0';
+				sz++;
+			}
+		}
+		break;
 	}
-	putchar('\n');
+	case COL_POLARIZATION:
+		if (desc->polarization) {
+			int x = desc->polarization[cpu];
 
-	for (i = 0; i < desc->ncpus; i++) {
+			snprintf(buf, bufsz, "%s",
+				 mod->mode == OUTPUT_PARSABLE ?
+						polar_modes[x].parsable :
+						polar_modes[x].readable);
+		}
+		break;
+	case COL_ADDRESS:
+		if (desc->addresses)
+			snprintf(buf, bufsz, "%d", desc->addresses[cpu]);
+		break;
+	case COL_CONFIGURED:
+		if (!desc->configured)
+			break;
+		if (mod->mode == OUTPUT_PARSABLE)
+			snprintf(buf, bufsz,
+				 desc->configured[cpu] ? _("Y") : _("N"));
+		else
+			snprintf(buf, bufsz,
+				 desc->configured[cpu] ? _("yes") : _("no"));
+		break;
+	case COL_ONLINE:
+		if (!desc->online)
+			break;
+		if (mod->mode == OUTPUT_PARSABLE)
+			snprintf(buf, bufsz,
+				 is_cpu_online(desc, cpu) ? _("Y") : _("N"));
+		else
+			snprintf(buf, bufsz,
+				 is_cpu_online(desc, cpu) ? _("yes") : _("no"));
+		break;
+	}
+	return buf;
+}
 
-		if (desc->online && !is_cpu_online(desc, i))
-			continue;
+static char *
+get_cell_header(struct lscpu_desc *desc, int col,
+		struct lscpu_modifier *mod,
+	        char *buf, size_t bufsz)
+{
+	*buf = '\0';
 
-		/* #CPU */
-		printf("%d", i);
+	if (col == COL_CACHE) {
+		char *p = buf;
+		size_t sz = bufsz;
+		int i;
 
-		/* Core */
-		for (j = 0; j < desc->ncores; j++) {
-			if (CPU_ISSET_S(i, setsize, desc->coremaps[j])) {
-				printf(",%d", j);
-				break;
+		for (i = desc->ncaches - 1; i >= 0; i--) {
+			int x = snprintf(p, sz, "%s", desc->caches[i].name);
+			if (x <= 0 || (size_t) x + 2 > sz)
+				return NULL;
+			sz -= x;
+			p += x;
+			if (i > 0) {
+				*p++ = mod->compat ? ',' : ':';
+				*p = '\0';
+				sz++;
 			}
 		}
-		if (j == desc->ncores)
-			putchar(',');
+		if (desc->ncaches)
+			return buf;
+	}
+	snprintf(buf, bufsz, "%s", coldescs[col].name);
+	return buf;
+}
 
-		/* Socket */
-		for (j = 0; j < desc->nsockets; j++) {
-			if (CPU_ISSET_S(i, setsize, desc->socketmaps[j])) {
-				printf(",%d", j);
-				break;
-			}
+/*
+ * [-p] backend, we support two parsable formats:
+ *
+ * 1) "compatible" -- this format is compatible with the original lscpu(1)
+ * output and it contains fixed set of the columns. The CACHE columns are at
+ * the end of the line and the CACHE is not printed if the number of the caches
+ * is zero. The CACHE columns are separated by two commas, for example:
+ *
+ *    $ lscpu --parse
+ *    # CPU,Core,Socket,Node,,L1d,L1i,L2
+ *    0,0,0,0,,0,0,0
+ *    1,1,0,0,,1,1,0
+ *
+ * 2) "user defined output" -- this format prints always all columns without
+ * special prefix for CACHE column. If there are not CACHEs then the column is
+ * empty and the header "Cache" is printed rather than a real name of the cache.
+ * The CACHE columns are separated by ':'.
+ *
+ *	$ lscpu --parse=CPU,CORE,SOCKET,NODE,CACHE
+ *	# CPU,Core,Socket,Node,L1d:L1i:L2
+ *	0,0,0,0,0:0:0
+ *	1,1,0,0,1:1:0
+ */
+static void
+print_parsable(struct lscpu_desc *desc, int cols[], int ncols,
+	       struct lscpu_modifier *mod)
+{
+	char buf[BUFSIZ], *data;
+	int i;
+
+	/*
+	 * Header
+	 */
+	printf(_(
+	"# The following is the parsable format, which can be fed to other\n"
+	"# programs. Each different item in every column has an unique ID\n"
+	"# starting from zero.\n"));
+
+	fputs("# ", stdout);
+	for (i = 0; i < ncols; i++) {
+		int col = cols[i];
+
+		if (col == COL_CACHE) {
+			if (mod->compat && !desc->ncaches)
+				continue;
+			if (mod->compat && i != 0)
+				putchar(',');
 		}
-		if (j == desc->nsockets)
+		if (i > 0)
 			putchar(',');
 
-		/* Nodes */
-		for (j = 0; j < desc->nnodes; j++) {
-			if (CPU_ISSET_S(i, setsize, desc->nodemaps[j])) {
-				printf(",%d", j);
-				break;
+		data = get_cell_header(desc, col, mod, buf, sizeof(buf));
+
+		if (data && * data && col != COL_CACHE &&
+		    !coldescs[col].is_abbr) {
+			/*
+			 * For normal column names use mixed case (e.g. "Socket")
+			 */
+			char *p = data + 1;
+
+			while (p && *p != '\0') {
+				*p = tolower((unsigned int) *p);
+				p++;
 			}
 		}
-		if (j == desc->nnodes)
-			putchar(',');
-
-		if (desc->ncaches)
-			putchar(',');
+		fputs(data && *data ? data : "", stdout);
+	}
+	putchar('\n');
 
-		/* Caches */
-		for (j = desc->ncaches - 1; j >= 0; j--) {
-			struct cpu_cache *ca = &desc->caches[j];
-			int x;
+	/*
+	 * Data
+	 */
+	for (i = 0; i < desc->ncpus; i++) {
+		int c;
 
-			for (x = 0; x < ca->nsharedmaps; x++) {
-				if (CPU_ISSET_S(i, setsize, ca->sharedmaps[x])) {
-					printf(",%d", x);
-					break;
-				}
+		if (!mod->offline && desc->online && !is_cpu_online(desc, i))
+			continue;
+		if (!mod->online && desc->online && is_cpu_online(desc, i))
+			continue;
+		for (c = 0; c < ncols; c++) {
+			if (mod->compat && cols[c] == COL_CACHE) {
+				if (!desc->ncaches)
+					continue;
+				if (c > 0)
+					putchar(',');
 			}
-			if (x == ca->nsharedmaps)
+			if (c > 0)
 				putchar(',');
+
+			data = get_cell_data(desc, i, cols[c], mod,
+					     buf, sizeof(buf));
+			fputs(data && *data ? data : "", stdout);
 		}
 		putchar('\n');
 	}
 }
 
+/*
+ * [-e] backend
+ */
+static void
+print_readable(struct lscpu_desc *desc, int cols[], int ncols,
+	       struct lscpu_modifier *mod)
+{
+	int i;
+	char buf[BUFSIZ], *data;
+	struct tt *tt = tt_new_table(0);
+
+	if (!tt)
+		 err(EXIT_FAILURE, _("failed to initialize output table"));
+
+	for (i = 0; i < ncols; i++) {
+		data = get_cell_header(desc, cols[i], mod, buf, sizeof(buf));
+		tt_define_column(tt, xstrdup(data), 0, 0);
+	}
+
+	for (i = 0; i < desc->ncpus; i++) {
+		int c;
+		struct tt_line *line;
+
+		if (!mod->offline && desc->online && !is_cpu_online(desc, i))
+			continue;
+		if (!mod->online && desc->online && is_cpu_online(desc, i))
+			continue;
+
+		line = tt_add_line(tt, NULL);
+
+		for (c = 0; c < ncols; c++) {
+			data = get_cell_data(desc, i, cols[c], mod,
+					     buf, sizeof(buf));
+			tt_line_set_data(line, c, data && *data ? xstrdup(data) : "-");
+		}
+	}
+
+	tt_print_table(tt);
+}
 
 /* output formats "<key>  <value>"*/
 #define print_s(_key, _val)	printf("%-23s%s\n", _key, _val)
@@ -826,8 +1069,11 @@ print_cpuset(const char *key, cpu_set_t *set, int hex)
 
 }
 
+/*
+ * default output
+ */
 static void
-print_readable(struct lscpu_desc *desc, int hex)
+print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
 {
 	char buf[512];
 	int i;
@@ -836,7 +1082,7 @@ print_readable(struct lscpu_desc *desc, int hex)
 	print_s(_("Architecture:"), desc->arch);
 
 	if (desc->mode) {
-		char buf[64], *p = buf;
+		char mbuf[64], *p = mbuf;
 
 		if (desc->mode & MODE_32BIT) {
 			strcpy(p, "32-bit, ");
@@ -847,7 +1093,7 @@ print_readable(struct lscpu_desc *desc, int hex)
 			p += 8;
 		}
 		*(p - 2) = '\0';
-		print_s(_("CPU op-mode(s):"), buf);
+		print_s(_("CPU op-mode(s):"), mbuf);
 	}
 #if !defined(WORDS_BIGENDIAN)
 	print_s(_("Byte Order:"), "Little Endian");
@@ -857,9 +1103,9 @@ print_readable(struct lscpu_desc *desc, int hex)
 	print_n(_("CPU(s):"), desc->ncpus);
 
 	if (desc->online)
-		print_cpuset(hex ? _("On-line CPU(s) mask:") :
-				   _("On-line CPU(s) list:"),
-				desc->online, hex);
+		print_cpuset(mod->hex ? _("On-line CPU(s) mask:") :
+					_("On-line CPU(s) list:"),
+				desc->online, mod->hex);
 
 	if (desc->online && CPU_COUNT_S(setsize, desc->online) != desc->ncpus) {
 		cpu_set_t *set;
@@ -876,18 +1122,49 @@ print_readable(struct lscpu_desc *desc, int hex)
 			if (!is_cpu_online(desc, i))
 				CPU_SET_S(i, setsize, set);
 		}
-		print_cpuset(hex ? _("Off-line CPU(s) mask:") :
-				   _("Off-line CPU(s) list:"),
-			     set, hex);
+		print_cpuset(mod->hex ? _("Off-line CPU(s) mask:") :
+					_("Off-line CPU(s) list:"),
+			     set, mod->hex);
 		cpuset_free(set);
 	}
 
 	if (desc->nsockets) {
+		int cores_per_socket, sockets_per_book, books;
+
+		cores_per_socket = sockets_per_book = books = 0;
+		/* s390 detects its cpu topology via /proc/sysinfo, if present.
+		 * Using simply the cpu topology masks in sysfs will not give
+		 * usable results since everything is virtualized. E.g.
+		 * virtual core 0 may have only 1 cpu, but virtual core 2 may
+		 * five cpus.
+		 * If the cpu topology is not exported (e.g. 2nd level guest)
+		 * fall back to old calculation scheme.
+		 */
+		if (path_exist(_PATH_PROC_SYSINFO)) {
+			FILE *fd = path_fopen("r", 0, _PATH_PROC_SYSINFO);
+			char pbuf[BUFSIZ];
+			int t0, t1, t2;
+
+			while (fd && fgets(pbuf, sizeof(pbuf), fd) != NULL) {
+				if (sscanf(pbuf, "CPU Topology SW:%d%d%d%d%d%d",
+					   &t0, &t1, &t2, &books, &sockets_per_book,
+					   &cores_per_socket) == 6)
+					break;
+			}
+			if (fd)
+				fclose(fd);
+		}
 		print_n(_("Thread(s) per core:"), desc->nthreads / desc->ncores);
-		print_n(_("Core(s) per socket:"), desc->ncores / desc->nsockets);
-		print_n(_("CPU socket(s):"), desc->nsockets);
+		print_n(_("Core(s) per socket:"),
+			cores_per_socket ?: desc->ncores / desc->nsockets);
+		if (desc->nbooks) {
+			print_n(_("Socket(s) per book:"),
+				sockets_per_book ?: desc->nsockets / desc->nbooks);
+			print_n(_("Book(s):"), books ?: desc->nbooks);
+		} else {
+			print_n(_("Socket(s):"), sockets_per_book ?: desc->nsockets);
+		}
 	}
-
 	if (desc->nnodes)
 		print_n(_("NUMA node(s):"), desc->nnodes);
 	if (desc->vendor)
@@ -908,100 +1185,217 @@ print_readable(struct lscpu_desc *desc, int hex)
 		else if (!strcmp(desc->virtflag, "vmx"))
 			print_s(_("Virtualization:"), "VT-x");
 	}
+	if (desc->hypervisor)
+		print_s(_("Hypervisor:"), desc->hypervisor);
 	if (desc->hyper) {
 		print_s(_("Hypervisor vendor:"), hv_vendors[desc->hyper]);
-		print_s(_("Virtualization type:"), virt_types[desc->virtype]);
+		print_s(_("Virtualization type:"), _(virt_types[desc->virtype]));
 	}
+	if (desc->dispatching >= 0)
+		print_s(_("Dispatching mode:"), _(disp_modes[desc->dispatching]));
 	if (desc->ncaches) {
-		char buf[512];
-		int i;
+		char cbuf[512];
 
 		for (i = desc->ncaches - 1; i >= 0; i--) {
-			snprintf(buf, sizeof(buf),
+			snprintf(cbuf, sizeof(cbuf),
 					_("%s cache:"), desc->caches[i].name);
-			print_s(buf, desc->caches[i].size);
+			print_s(cbuf, desc->caches[i].size);
 		}
 	}
 
 	for (i = 0; i < desc->nnodes; i++) {
 		snprintf(buf, sizeof(buf), _("NUMA node%d CPU(s):"), i);
-		print_cpuset(buf, desc->nodemaps[i], hex);
+		print_cpuset(buf, desc->nodemaps[i], mod->hex);
 	}
 }
 
 static void __attribute__((__noreturn__)) usage(FILE *out)
 {
-	fprintf(out, _("Usage: %s [option]\n"),
-			program_invocation_short_name);
+	size_t i;
+
+	fputs(USAGE_HEADER, out);
+	fprintf(out,
+	      _(" %s [options]\n"), program_invocation_short_name);
+
+	fputs(USAGE_OPTIONS, out);
+	fputs(_(" -a, --all               print online and offline CPUs (default for -e)\n"
+		" -b, --online            print online CPUs only (default for -p)\n"
+		" -c, --offline           print offline CPUs only\n"
+		" -e, --extended[=<list>] print out an extended readable format\n"
+		" -h, --help              print this help\n"
+		" -p, --parse[=<list>]    print out a parsable format\n"
+		" -s, --sysroot <dir>     use directory DIR as system root\n"
+		" -V, --version           print version information and exit\n"
+		" -x, --hex               print hexadecimal masks rather than lists of CPUs\n"), out);
 
-	fprintf(out,_("CPU architecture information helper\n\n"
-		"  -h, --help     usage information\n"
-		"  -p, --parse    print out in parsable instead of printable format.\n"
-		"  -s, --sysroot  use the directory as a new system root.\n"
-		"  -x, --hex      print haxadecimal masks rather than lists of CPU(s)\n"));
+	fprintf(out, _("\nAvailable columns:\n"));
+
+	for (i = 0; i < ARRAY_SIZE(coldescs); i++)
+		fprintf(out, " %13s  %s\n", coldescs[i].name, _(coldescs[i].help));
+
+	fprintf(out, _("\nFor more details see lscpu(1).\n"));
 
 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
 }
 
 int main(int argc, char *argv[])
 {
-	struct lscpu_desc _desc, *desc = &_desc;
-	int parsable = 0, c, i, hex = 0;
+	struct lscpu_modifier _mod = { .mode = OUTPUT_SUMMARY }, *mod = &_mod;
+	struct lscpu_desc _desc = { .flags = 0 }, *desc = &_desc;
+	int c, i;
+	int columns[ARRAY_SIZE(coldescs)], ncolumns = 0;
+	int cpu_modifier_specified = 0;
 
-	struct option longopts[] = {
+	static const struct option longopts[] = {
+		{ "all",        no_argument,       0, 'a' },
+		{ "online",     no_argument,       0, 'b' },
+		{ "offline",    no_argument,       0, 'c' },
 		{ "help",	no_argument,       0, 'h' },
-		{ "parse",	no_argument,       0, 'p' },
+		{ "extended",	optional_argument, 0, 'e' },
+		{ "parse",	optional_argument, 0, 'p' },
 		{ "sysroot",	required_argument, 0, 's' },
 		{ "hex",	no_argument,	   0, 'x' },
+		{ "version",	no_argument,	   0, 'V' },
 		{ NULL,		0, 0, 0 }
 	};
 
+	static const ul_excl_t excl[] = {	/* rows and cols in ASCII order */
+		{ 'a','b','c' },
+		{ 'e','p' },
+		{ 0 }
+	};
+	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
 	setlocale(LC_ALL, "");
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	textdomain(PACKAGE);
+	atexit(close_stdout);
+
+	while ((c = getopt_long(argc, argv, "abce::hp::s:xV", longopts, NULL)) != -1) {
+
+		err_exclusive_options(c, longopts, excl, excl_st);
 
-	while((c = getopt_long(argc, argv, "hps:x", longopts, NULL)) != -1) {
 		switch (c) {
+		case 'a':
+			mod->online = mod->offline = 1;
+			cpu_modifier_specified = 1;
+			break;
+		case 'b':
+			mod->online = 1;
+			cpu_modifier_specified = 1;
+			break;
+		case 'c':
+			mod->offline = 1;
+			cpu_modifier_specified = 1;
+			break;
 		case 'h':
 			usage(stdout);
 		case 'p':
-			parsable = 1;
+			goto hop_over;
+		case 'e':
+			hop_over:
+			if (optarg) {
+				if (*optarg == '=')
+					optarg++;
+				ncolumns = string_to_idarray(optarg,
+						columns, ARRAY_SIZE(columns),
+						column_name_to_id);
+				if (ncolumns < 0)
+					return EXIT_FAILURE;
+			}
+			mod->mode = c == 'p' ? OUTPUT_PARSABLE : OUTPUT_READABLE;
 			break;
 		case 's':
-			sysrootlen = strlen(optarg);
-			strncpy(pathbuf, optarg, sizeof(pathbuf));
-			pathbuf[sizeof(pathbuf) - 1] = '\0';
+			path_setprefix(optarg);
+			mod->system = SYSTEM_SNAPSHOT;
 			break;
 		case 'x':
-			hex = 1;
+			mod->hex = 1;
 			break;
+		case 'V':
+			printf(_("%s from %s\n"), program_invocation_short_name,
+			       PACKAGE_STRING);
+			return EXIT_SUCCESS;
 		default:
 			usage(stderr);
 		}
 	}
 
-	memset(desc, 0, sizeof(*desc));
+	if (cpu_modifier_specified && mod->mode == OUTPUT_SUMMARY) {
+		fprintf(stderr,
+			_("%s: options --all, --online and --offline may only "
+			  "be used with options --extended or --parsable.\n"),
+			program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	if (argc != optind)
+		usage(stderr);
+
+	/* set default cpu display mode if none was specified */
+	if (!mod->online && !mod->offline) {
+		mod->online = 1;
+		mod->offline = mod->mode == OUTPUT_READABLE ? 1 : 0;
+	}
 
-	read_basicinfo(desc);
+	read_basicinfo(desc, mod);
 
 	for (i = 0; i < desc->ncpus; i++) {
-		if (desc->online && !is_cpu_online(desc, i))
-			continue;
 		read_topology(desc, i);
 		read_cache(desc, i);
+		read_polarization(desc, i);
+		read_address(desc, i);
+		read_configured(desc, i);
 	}
 
-	qsort(desc->caches, desc->ncaches, sizeof(struct cpu_cache), cachecmp);
+	if (desc->caches)
+		qsort(desc->caches, desc->ncaches,
+				sizeof(struct cpu_cache), cachecmp);
 
 	read_nodes(desc);
-
 	read_hypervisor(desc);
 
-	/* Show time! */
-	if (parsable)
-		print_parsable(desc);
-	else
-		print_readable(desc, hex);
+	switch(mod->mode) {
+	case OUTPUT_SUMMARY:
+		print_summary(desc, mod);
+		break;
+	case OUTPUT_PARSABLE:
+		if (!ncolumns) {
+			columns[ncolumns++] = COL_CPU;
+			columns[ncolumns++] = COL_CORE;
+			columns[ncolumns++] = COL_SOCKET;
+			columns[ncolumns++] = COL_NODE;
+			columns[ncolumns++] = COL_CACHE;
+			mod->compat = 1;
+		}
+		print_parsable(desc, columns, ncolumns, mod);
+		break;
+	case OUTPUT_READABLE:
+		if (!ncolumns) {
+			/* No list was given. Just print whatever is there. */
+			columns[ncolumns++] = COL_CPU;
+			if (desc->nodemaps)
+				columns[ncolumns++] = COL_NODE;
+			if (desc->bookmaps)
+				columns[ncolumns++] = COL_BOOK;
+			if (desc->socketmaps)
+				columns[ncolumns++] = COL_SOCKET;
+			if (desc->coremaps)
+				columns[ncolumns++] = COL_CORE;
+			if (desc->caches)
+				columns[ncolumns++] = COL_CACHE;
+			if (desc->online)
+				columns[ncolumns++] = COL_ONLINE;
+			if (desc->configured)
+				columns[ncolumns++] = COL_CONFIGURED;
+			if (desc->polarization)
+				columns[ncolumns++] = COL_POLARIZATION;
+			if (desc->addresses)
+				columns[ncolumns++] = COL_ADDRESS;
+		}
+		print_readable(desc, columns, ncolumns, mod);
+		break;
+	}
 
 	return EXIT_SUCCESS;
 }
-- 
1.7.7

