dm-devel.redhat.com archive mirror
 help / color / mirror / Atom feed
From: mwilck@suse.com
To: Benjamin Marzinski <bmarzins@redhat.com>,
	Christophe Varoqui <christophe.varoqui@opensvc.com>
Cc: dm-devel@redhat.com, Martin Wilck <mwilck@suse.com>
Subject: [dm-devel] [PATCH v2 5/9] libmultipath: use strbuf in print.c
Date: Wed, 11 Aug 2021 17:41:46 +0200	[thread overview]
Message-ID: <20210811154150.24714-6-mwilck@suse.com> (raw)
In-Reply-To: <20210811154150.24714-1-mwilck@suse.com>

From: Martin Wilck <mwilck@suse.com>

Use the strbuf API in print.c, wherever growing string buffers are
appropriate. This requires a large amount of changes in print.c itself,
and in other files that use functions from print.c. It makes no sense
to separate this into smaller patches though, as the various snprint_xyz()
functions depend on each other, and need to use similar prototypes.

libmultipath version must be bumped, as all the printing functions have
changed prototypes. Also, add the required strbuf functions to the
libmultipath API.

Change the libmultipath calls in prioritizers, foreign libs,
and in multipathd according to the strbuf-related API changes.

While working on print.c, I made a couple of other other minor changes:
 - all snprint_xyz() functions return a negative error in case of failure
   (most likely is -ENOMEM). In case of success, the number of printed
   characters is returned.
 - snprint_progress(): use fill_strbuf() rather than repeated iterations
 - _snprint_multipath(), _snprint_path(), snprint_multipath_header(),
   snprint_path_header(), _snprint_pathgroup(): use fill_strbuf()
   instead of the PAD/NOPAD logic.
 - _snprint_multipath_topology(): simplified the ASCII art generation for
   the topology tree somewhat.
 - snprint_json_xyz(): use fill_strbuf() rather than printing the indent
   repeatedly.
 - snprint_blacklist_group(), snprint_blacklist_devgroup(): combined two
   print statements into one.

In cli_handlers.c, fixed the returned values for the "len" parameter in
some functions (it's supposed to be the strlen of the reply + 1).

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/blacklist.c                 |   13 +-
 libmultipath/discovery.c                 |   13 +-
 libmultipath/foreign.c                   |   78 +-
 libmultipath/foreign.h                   |   13 +-
 libmultipath/foreign/nvme.c              |  100 +-
 libmultipath/generic.c                   |   26 +-
 libmultipath/generic.h                   |   17 +-
 libmultipath/libmultipath.version        |   14 +-
 libmultipath/parser.c                    |    5 +-
 libmultipath/parser.h                    |    2 +-
 libmultipath/print.c                     | 1696 +++++++++-------------
 libmultipath/print.h                     |   67 +-
 libmultipath/prioritizers/weightedpath.c |   71 +-
 multipathd/cli_handlers.c                |  345 ++---
 14 files changed, 1000 insertions(+), 1460 deletions(-)

diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
index 6c6a597..4e315c9 100644
--- a/libmultipath/blacklist.c
+++ b/libmultipath/blacklist.c
@@ -14,6 +14,7 @@
 #include "blacklist.h"
 #include "structs_vec.h"
 #include "print.h"
+#include "strbuf.h"
 
 char *check_invert(char *str, bool *invert)
 {
@@ -341,19 +342,21 @@ int
 filter_protocol(const struct _vector *blist, const struct _vector *elist,
 		const struct path *pp)
 {
-	char buf[PROTOCOL_BUF_SIZE];
+	STRBUF_ON_STACK(buf);
+	const char *prot;
 	int r = MATCH_NOTHING;
 
 	if (pp) {
-		snprint_path_protocol(buf, sizeof(buf), pp);
+		snprint_path_protocol(&buf, pp);
+		prot = get_strbuf_str(&buf);
 
-		if (match_reglist(elist, buf))
+		if (match_reglist(elist, prot))
 			r = MATCH_PROTOCOL_BLIST_EXCEPT;
-		else if (match_reglist(blist, buf))
+		else if (match_reglist(blist, prot))
 			r = MATCH_PROTOCOL_BLIST;
+		log_filter(pp->dev, NULL, NULL, NULL, NULL, prot, r, 3);
 	}
 
-	log_filter(pp->dev, NULL, NULL, NULL, NULL, buf, r, 3);
 	return r;
 }
 
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index e9f5703..f25fe9e 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -35,6 +35,7 @@
 #include "foreign.h"
 #include "configure.h"
 #include "print.h"
+#include "strbuf.h"
 
 struct vpd_vendor_page vpd_vendor_pages[VPD_VP_ARRAY_SIZE] = {
 	[VPD_VP_UNDEF]	= { 0x00, "undef" },
@@ -895,11 +896,11 @@ sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
 	}
 
 	if (err_path) {
-		char proto_buf[32];
+		STRBUF_ON_STACK(proto_buf);
 
-		snprint_path_protocol(proto_buf, sizeof(proto_buf), err_path);
+		snprint_path_protocol(&proto_buf, err_path);
 		condlog(2, "%s: setting dev_loss_tmo is unsupported for protocol %s",
-			mpp->alias, proto_buf);
+			mpp->alias, get_strbuf_str(&proto_buf));
 	}
 	return 0;
 }
@@ -2380,11 +2381,11 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
 				 * It's likely that this path is not fit for
 				 * multipath use.
 				 */
-				char buf[16];
+				STRBUF_ON_STACK(buf);
 
-				snprint_path(buf, sizeof(buf), "%T", pp, 0);
+				snprint_path(&buf, "%T", pp, 0);
 				condlog(1, "%s: no WWID in state \"%s\", giving up",
-					pp->dev, buf);
+					pp->dev, get_strbuf_str(&buf));
 				return PATHINFO_SKIPPED;
 			}
 			return PATHINFO_OK;
diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c
index fce1934..e091a1d 100644
--- a/libmultipath/foreign.c
+++ b/libmultipath/foreign.c
@@ -34,6 +34,7 @@
 #include "structs.h"
 #include "structs_vec.h"
 #include "print.h"
+#include "strbuf.h"
 
 static vector foreigns;
 
@@ -497,11 +498,11 @@ void foreign_multipath_layout(void)
 	pthread_cleanup_pop(1);
 }
 
-int snprint_foreign_topology(char *buf, int len, int verbosity)
+int snprint_foreign_topology(struct strbuf *buf, int verbosity)
 {
 	struct foreign *fgn;
 	int i;
-	char *c = buf;
+	size_t initial_len = get_strbuf_len(buf);
 
 	rdlock_foreigns();
 	if (foreigns == NULL) {
@@ -521,58 +522,32 @@ int snprint_foreign_topology(char *buf, int len, int verbosity)
 		vec = fgn->get_multipaths(fgn->context);
 		if (vec != NULL) {
 			vector_foreach_slot(vec, gm, j) {
-
-				c += _snprint_multipath_topology(gm, c,
-								 buf + len - c,
-								 verbosity);
-				if (c >= buf + len - 1)
+				if (_snprint_multipath_topology(
+					    gm, buf, verbosity) < 0)
 					break;
 			}
-			if (c >= buf + len - 1)
-				break;
 		}
 		fgn->release_multipaths(fgn->context, vec);
 		pthread_cleanup_pop(1);
 	}
 
 	pthread_cleanup_pop(1);
-	return c - buf;
+	return get_strbuf_len(buf) - initial_len;
 }
 
 void print_foreign_topology(int verbosity)
 {
-	int buflen = MAX_LINE_LEN * MAX_LINES;
-	char *buf = NULL, *tmp = NULL;
-
-	buf = calloc(1, buflen);
-
-	while (buf != NULL) {
-		char *c = buf;
-
-		c += snprint_foreign_topology(buf, buflen,
-						   verbosity);
-		if (c < buf + buflen - 1)
-			break;
-
-		buflen *= 2;
-		tmp = buf;
-		buf = realloc(buf, buflen);
-	}
+	STRBUF_ON_STACK(buf);
 
-	if (buf == NULL && tmp != NULL)
-		buf = tmp;
-
-	if (buf != NULL) {
-		printf("%s", buf);
-		free(buf);
-	}
+	snprint_foreign_topology(&buf, verbosity);
+	printf("%s", get_strbuf_str(&buf));
 }
 
-int snprint_foreign_paths(char *buf, int len, const char *style, int pretty)
+int snprint_foreign_paths(struct strbuf *buf, const char *style, int pretty)
 {
 	struct foreign *fgn;
 	int i;
-	char *c = buf;
+	size_t initial_len = get_strbuf_len(buf);
 
 	rdlock_foreigns();
 	if (foreigns == NULL) {
@@ -584,7 +559,7 @@ int snprint_foreign_paths(char *buf, int len, const char *style, int pretty)
 	vector_foreach_slot(foreigns, fgn, i) {
 		const struct _vector *vec;
 		const struct gen_path *gp;
-		int j;
+		int j, ret = 0;
 
 		fgn->lock(fgn->context);
 		pthread_cleanup_push(fgn->unlock, fgn->context);
@@ -592,28 +567,27 @@ int snprint_foreign_paths(char *buf, int len, const char *style, int pretty)
 		vec = fgn->get_paths(fgn->context);
 		if (vec != NULL) {
 			vector_foreach_slot(vec, gp, j) {
-				c += _snprint_path(gp, c, buf + len - c,
-						   style, pretty);
-				if (c >= buf + len - 1)
+				ret = _snprint_path(gp, buf, style, pretty);
+				if (ret < 0)
 					break;
 			}
-			if (c >= buf + len - 1)
-				break;
 		}
 		fgn->release_paths(fgn->context, vec);
 		pthread_cleanup_pop(1);
+		if (ret < 0)
+			break;
 	}
 
 	pthread_cleanup_pop(1);
-	return c - buf;
+	return get_strbuf_len(buf) - initial_len;
 }
 
-int snprint_foreign_multipaths(char *buf, int len,
+int snprint_foreign_multipaths(struct strbuf *buf,
 			       const char *style, int pretty)
 {
 	struct foreign *fgn;
 	int i;
-	char *c = buf;
+	size_t initial_len = get_strbuf_len(buf);
 
 	rdlock_foreigns();
 	if (foreigns == NULL) {
@@ -625,7 +599,7 @@ int snprint_foreign_multipaths(char *buf, int len,
 	vector_foreach_slot(foreigns, fgn, i) {
 		const struct _vector *vec;
 		const struct gen_multipath *gm;
-		int j;
+		int j, ret = 0;
 
 		fgn->lock(fgn->context);
 		pthread_cleanup_push(fgn->unlock, fgn->context);
@@ -633,18 +607,18 @@ int snprint_foreign_multipaths(char *buf, int len,
 		vec = fgn->get_multipaths(fgn->context);
 		if (vec != NULL) {
 			vector_foreach_slot(vec, gm, j) {
-				c += _snprint_multipath(gm, c, buf + len - c,
-							style, pretty);
-				if (c >= buf + len - 1)
+				ret = _snprint_multipath(gm, buf,
+							 style, pretty);
+				if (ret < 0)
 					break;
 			}
-			if (c >= buf + len - 1)
-				break;
 		}
 		fgn->release_multipaths(fgn->context, vec);
 		pthread_cleanup_pop(1);
+		if (ret < 0)
+			break;
 	}
 
 	pthread_cleanup_pop(1);
-	return c - buf;
+	return get_strbuf_len(buf) - initial_len;
 }
diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h
index acd3360..77fc485 100644
--- a/libmultipath/foreign.h
+++ b/libmultipath/foreign.h
@@ -18,9 +18,9 @@
 #define _FOREIGN_H
 #include <stdbool.h>
 #include <libudev.h>
+#define LIBMP_FOREIGN_API ((1 << 8) | 1)
 
-#define LIBMP_FOREIGN_API ((1 << 8) | 0)
-
+struct strbuf;
 struct context;
 
 /* return codes of functions below returning "int" */
@@ -267,35 +267,32 @@ void foreign_multipath_layout(void);
  * prints topology information from foreign libraries into buffer,
  * '\0' - terminated.
  * @param buf: output buffer
- * @param len: size of output buffer
  * @param verbosity: verbosity level
  * @returns: number of printed characters excluding trailing '\0'.
  */
-int snprint_foreign_topology(char *buf, int len, int verbosity);
+int snprint_foreign_topology(struct strbuf *buf, int verbosity);
 
 /**
  * snprint_foreign_paths(buf, len, style, pad);
  * prints formatted path information from foreign libraries into buffer,
  * '\0' - terminated.
  * @param buf: output buffer
- * @param len: size of output buffer
  * @param style: format string
  * @param pad: whether to pad field width
  * @returns: number of printed characters excluding trailing '\0'.
  */
-int snprint_foreign_paths(char *buf, int len, const char *style, int pad);
+int snprint_foreign_paths(struct strbuf *buf, const char *style, int pad);
 
 /**
  * snprint_foreign_multipaths(buf, len, style, pad);
  * prints formatted map information from foreign libraries into buffer,
  * '\0' - terminated.
  * @param buf: output buffer
- * @param len: size of output buffer
  * @param style: format string
  * @param pad: whether to pad field width
  * @returns: number of printed characters excluding trailing '\0'.
  */
-int snprint_foreign_multipaths(char *buf, int len,
+int snprint_foreign_multipaths(struct strbuf *buf,
 			       const char *style, int pretty);
 
 /**
diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c
index b726be2..d40c086 100644
--- a/libmultipath/foreign/nvme.c
+++ b/libmultipath/foreign/nvme.c
@@ -37,6 +37,7 @@
 #include "debug.h"
 #include "structs.h"
 #include "sysfs.h"
+#include "strbuf.h"
 
 static const char nvme_vendor[] = "NVMe";
 static const char N_A[] = "n/a";
@@ -138,7 +139,7 @@ static void rstrip(char *str)
 }
 
 static int snprint_nvme_map(const struct gen_multipath *gmp,
-			    char *buff, int len, char wildcard)
+			    struct strbuf *buff, char wildcard)
 {
 	const struct nvme_map *nvm = const_gen_mp_to_nvme(gmp);
 	char fld[NAME_LEN];
@@ -146,26 +147,26 @@ static int snprint_nvme_map(const struct gen_multipath *gmp,
 
 	switch (wildcard) {
 	case 'd':
-		return snprintf(buff, len, "%s",
+		return append_strbuf_str(buff,
 				udev_device_get_sysname(nvm->udev));
 	case 'n':
-		return snprintf(buff, len, "%s:nsid.%s",
+		return print_strbuf(buff, "%s:nsid.%s",
 				udev_device_get_sysattr_value(nvm->subsys,
 							      "subsysnqn"),
 				udev_device_get_sysattr_value(nvm->udev,
 							      "nsid"));
 	case 'w':
-		return snprintf(buff, len, "%s",
+		return append_strbuf_str(buff,
 				udev_device_get_sysattr_value(nvm->udev,
 							      "wwid"));
 	case 'N':
-		return snprintf(buff, len, "%u", nvm->nr_live);
+		return print_strbuf(buff, "%u", nvm->nr_live);
 	case 'S':
-		return snprintf(buff, len, "%s",
+		return append_strbuf_str(buff,
 				udev_device_get_sysattr_value(nvm->udev,
 							      "size"));
 	case 'v':
-		return snprintf(buff, len, "%s", nvme_vendor);
+		return append_strbuf_str(buff, nvme_vendor);
 	case 's':
 	case 'p':
 		snprintf(fld, sizeof(fld), "%s",
@@ -173,30 +174,30 @@ static int snprint_nvme_map(const struct gen_multipath *gmp,
 						      "model"));
 		rstrip(fld);
 		if (wildcard == 'p')
-			return snprintf(buff, len, "%s", fld);
-		return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
+			return append_strbuf_str(buff, fld);
+		return print_strbuf(buff, "%s,%s,%s", nvme_vendor, fld,
 				udev_device_get_sysattr_value(nvm->subsys,
 							      "firmware_rev"));
 	case 'e':
-		return snprintf(buff, len, "%s",
+		return append_strbuf_str(buff,
 				udev_device_get_sysattr_value(nvm->subsys,
 							      "firmware_rev"));
 	case 'r':
 		val = udev_device_get_sysattr_value(nvm->udev, "ro");
 		if (val[0] == 1)
-			return snprintf(buff, len, "%s", "ro");
+			return append_strbuf_str(buff, "ro");
 		else
-			return snprintf(buff, len, "%s", "rw");
+			return append_strbuf_str(buff, "rw");
 	case 'G':
-		return snprintf(buff, len, "%s", THIS);
+		return append_strbuf_str(buff, THIS);
 	case 'h':
 		if (nvm->ana_supported == YNU_YES)
-			return snprintf(buff, len, "ANA");
+			return append_strbuf_str(buff, "ANA");
 	default:
 		break;
 	}
 
-	return snprintf(buff, len, N_A);
+	return append_strbuf_str(buff, N_A);
 }
 
 static const struct _vector*
@@ -214,7 +215,7 @@ nvme_pg_rel_paths(__attribute__((unused)) const struct gen_pathgroup *gpg,
 	/* empty */
 }
 
-static int snprint_hcil(const struct nvme_path *np, char *buf, int len)
+static int snprint_hcil(const struct nvme_path *np, struct strbuf *buf)
 {
 	unsigned int nvmeid, ctlid, nsid;
 	int rc;
@@ -223,14 +224,13 @@ static int snprint_hcil(const struct nvme_path *np, char *buf, int len)
 	rc = sscanf(sysname, "nvme%uc%un%u", &nvmeid, &ctlid, &nsid);
 	if (rc != 3) {
 		condlog(1, "%s: failed to scan %s", __func__, sysname);
-		rc = snprintf(buf, len, "(ERR:%s)", sysname);
+		return print_strbuf(buf, "(ERR:%s)", sysname);
 	} else
-		rc = snprintf(buf, len, "%u:%u:%u", nvmeid, ctlid, nsid);
-	return (rc < len ? rc : len);
+		return print_strbuf(buf, "%u:%u:%u", nvmeid, ctlid, nsid);
 }
 
 static int snprint_nvme_path(const struct gen_path *gp,
-			     char *buff, int len, char wildcard)
+			     struct strbuf *buff, char wildcard)
 {
 	const struct nvme_path *np = const_gen_path_to_nvme(gp);
 	dev_t devt;
@@ -239,37 +239,37 @@ static int snprint_nvme_path(const struct gen_path *gp,
 
 	switch (wildcard) {
 	case 'w':
-		return snprintf(buff, len, "%s",
-				udev_device_get_sysattr_value(np->udev,
-							      "wwid"));
+		return print_strbuf(buff, "%s",
+				    udev_device_get_sysattr_value(np->udev,
+								  "wwid"));
 	case 'd':
-		return snprintf(buff, len, "%s",
-				udev_device_get_sysname(np->udev));
+		return print_strbuf(buff, "%s",
+				    udev_device_get_sysname(np->udev));
 	case 'i':
-		return snprint_hcil(np, buff, len);
+		return snprint_hcil(np, buff);
 	case 'D':
 		devt = udev_device_get_devnum(np->udev);
-		return snprintf(buff, len, "%u:%u", major(devt), minor(devt));
+		return print_strbuf(buff, "%u:%u", major(devt), minor(devt));
 	case 'o':
 		if (sysfs_attr_get_value(np->ctl, "state",
 					 fld, sizeof(fld)) > 0)
-			return snprintf(buff, len, "%s", fld);
+			return append_strbuf_str(buff, fld);
 		break;
 	case 'T':
 		if (sysfs_attr_get_value(np->udev, "ana_state", fld,
 					 sizeof(fld)) > 0)
-			return snprintf(buff, len, "%s", fld);
+			return append_strbuf_str(buff, fld);
 		break;
 	case 'p':
 		if (sysfs_attr_get_value(np->udev, "ana_state", fld,
 					 sizeof(fld)) > 0) {
 			rstrip(fld);
 			if (!strcmp(fld, "optimized"))
-				return snprintf(buff, len, "%d", 50);
+				return print_strbuf(buff, "%d", 50);
 			else if (!strcmp(fld, "non-optimized"))
-				return snprintf(buff, len, "%d", 10);
+				return print_strbuf(buff, "%d", 10);
 			else
-				return snprintf(buff, len, "%d", 0);
+				return print_strbuf(buff, "%d", 0);
 		}
 		break;
 	case 's':
@@ -277,46 +277,45 @@ static int snprint_nvme_path(const struct gen_path *gp,
 			 udev_device_get_sysattr_value(np->ctl,
 						      "model"));
 		rstrip(fld);
-		return snprintf(buff, len, "%s,%s,%s", nvme_vendor, fld,
-				udev_device_get_sysattr_value(np->ctl,
+		return print_strbuf(buff, "%s,%s,%s", nvme_vendor, fld,
+				    udev_device_get_sysattr_value(np->ctl,
 							      "firmware_rev"));
 	case 'S':
-		return snprintf(buff, len, "%s",
+		return append_strbuf_str(buff,
 			udev_device_get_sysattr_value(np->udev,
 						      "size"));
 	case 'z':
-		return snprintf(buff, len, "%s",
+		return append_strbuf_str(buff,
 				udev_device_get_sysattr_value(np->ctl,
 							      "serial"));
 	case 'm':
-		return snprintf(buff, len, "%s",
+		return append_strbuf_str(buff,
 				udev_device_get_sysname(np->map->udev));
 	case 'N':
 	case 'R':
-		return snprintf(buff, len, "%s:%s",
+		return print_strbuf(buff, "%s:%s",
 			udev_device_get_sysattr_value(np->ctl,
 						      "transport"),
 			udev_device_get_sysattr_value(np->ctl,
 						      "address"));
 	case 'G':
-		return snprintf(buff, len, "[%s]", THIS);
+		return print_strbuf(buff, "[%s]", THIS);
 	case 'a':
 		pci = udev_device_get_parent_with_subsystem_devtype(np->ctl,
 								    "pci",
 								    NULL);
 		if (pci != NULL)
-			return snprintf(buff, len, "PCI:%s",
-					udev_device_get_sysname(pci));
+			return print_strbuf(buff, "PCI:%s",
+					    udev_device_get_sysname(pci));
 		/* fall through */
 	default:
 		break;
 	}
-	return snprintf(buff, len, "%s", N_A);
-	return 0;
+	return append_strbuf_str(buff, N_A);
 }
 
 static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
-			   char *buff, int len, char wildcard)
+			   struct strbuf *buff, char wildcard)
 {
 	const struct nvme_pathgroup *pg = const_gen_pg_to_nvme(gmp);
 	const struct nvme_path *path = nvme_pg_to_path(pg);
@@ -324,22 +323,19 @@ static int snprint_nvme_pg(const struct gen_pathgroup *gmp,
 	switch (wildcard) {
 	case 't':
 		return snprint_nvme_path(nvme_path_to_gen(path),
-					 buff, len, 'T');
+					 buff, 'T');
 	case 'p':
 		return snprint_nvme_path(nvme_path_to_gen(path),
-					 buff, len, 'p');
+					 buff, 'p');
 	default:
-		return snprintf(buff, len, N_A);
+		return append_strbuf_str(buff, N_A);
 	}
 }
 
 static int nvme_style(__attribute__((unused)) const struct gen_multipath* gm,
-		      char *buf, int len,
-		      __attribute__((unused)) int verbosity)
+		      struct strbuf *buf, __attribute__((unused)) int verbosity)
 {
-	int n = snprintf(buf, len, "%%w [%%G]:%%d %%s");
-
-	return (n < len ? n : len - 1);
+	return append_strbuf_str(buf, "%%w [%%G]:%%d %%s");
 }
 
 static const struct gen_multipath_ops nvme_map_ops = {
diff --git a/libmultipath/generic.c b/libmultipath/generic.c
index 5f03b9e..e7cf297 100644
--- a/libmultipath/generic.c
+++ b/libmultipath/generic.c
@@ -15,23 +15,23 @@
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-
-#include <string.h>
 #include "generic.h"
 #include "structs.h"
+#include "util.h"
+#include "strbuf.h"
 
-int generic_style(const struct gen_multipath* gm,
-		  char *buf, int len, __attribute__((unused)) int verbosity)
+int generic_style(const struct gen_multipath* gm, struct strbuf *buf,
+		  __attribute__((unused)) int verbosity)
 {
-	char alias_buf[WWID_SIZE];
-	char wwid_buf[WWID_SIZE];
-	int n = 0;
-
-	gm->ops->snprint(gm, alias_buf, sizeof(alias_buf), 'n');
-	gm->ops->snprint(gm, wwid_buf, sizeof(wwid_buf), 'w');
+	STRBUF_ON_STACK(tmp);
+	char *alias_buf __attribute__((cleanup(cleanup_charp)));
+	const char *wwid_buf;
 
-	n += snprintf(buf, len, "%%n %s[%%G]:%%d %%s",
-		      strcmp(alias_buf, wwid_buf) ? "(%w) " : "");
+	gm->ops->snprint(gm, &tmp, 'n');
+	alias_buf = steal_strbuf_str(&tmp);
+	gm->ops->snprint(gm, &tmp, 'w');
+	wwid_buf = get_strbuf_str(&tmp);
 
-	return (n < len ? n : len - 1);
+	return print_strbuf(buf, "%%n %s[%%G]:%%d %%s",
+			    strcmp(alias_buf, wwid_buf) ? "(%w) " : "");
 }
diff --git a/libmultipath/generic.h b/libmultipath/generic.h
index 6346ffe..57c123c 100644
--- a/libmultipath/generic.h
+++ b/libmultipath/generic.h
@@ -18,6 +18,7 @@
 #define _GENERIC_H
 #include "vector.h"
 
+struct strbuf;
 struct gen_multipath;
 struct gen_pathgroup;
 struct gen_path;
@@ -50,26 +51,24 @@ struct gen_multipath_ops {
 	 * 0-terminated, no more than "len" characters including trailing '\0'.
 	 *
 	 * @param gmp: generic multipath object to act on
-	 * @param buf: output buffer
-	 * @param buflen: buffer size
+	 * @param buf: output struct strbuf
 	 * @param wildcard: the multipath wildcard (see print.c)
 	 * @returns the number of characters printed (without trailing '\0').
 	 */
 	int (*snprint)(const struct gen_multipath*,
-		       char *buf, int len, char wildcard);
+		       struct strbuf *buf, char wildcard);
 	/**
 	 * method: style(gmp, buf, len, verbosity)
 	 * returns the format string to be used for the multipath object,
 	 * defined with the wildcards as defined in print.c
 	 * generic_style() should work well in most cases.
 	 * @param gmp: generic multipath object to act on
-	 * @param buf: output buffer
-	 * @param buflen: buffer size
+	 * @param buf: output strbuf
 	 * @param verbosity: verbosity level
 	 * @returns number of format chars printed
 	 */
 	int (*style)(const struct gen_multipath*,
-		     char *buf, int len, int verbosity);
+		     struct strbuf *buf, int verbosity);
 };
 
 /**
@@ -95,7 +94,7 @@ struct gen_pathgroup_ops {
 	 * see gen_multipath_ops->snprint() above
 	 */
 	int (*snprint)(const struct gen_pathgroup*,
-		       char *buf, int len, char wildcard);
+		       struct strbuf *buf, char wildcard);
 };
 
 struct gen_path_ops {
@@ -104,7 +103,7 @@ struct gen_path_ops {
 	 * see gen_multipath_ops->snprint() above
 	 */
 	int (*snprint)(const struct gen_path*,
-		       char *buf, int len, char wildcard);
+		       struct strbuf *buf, char wildcard);
 };
 
 struct gen_multipath {
@@ -129,6 +128,6 @@ struct gen_path {
  * foreign libraries.
  */
 int generic_style(const struct gen_multipath*,
-		  char *buf, int len, int verbosity);
+		  struct strbuf *buf, int verbosity);
 
 #endif /* _GENERIC_H */
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 6dd52c2..1d84d97 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -31,7 +31,7 @@
  *   The new version inherits the previous ones.
  */
 
-LIBMULTIPATH_7.0.0 {
+LIBMULTIPATH_8.0.0 {
 global:
 	/* symbols referenced by multipath and multipathd */
 	add_foreign;
@@ -273,3 +273,15 @@ global:
 local:
 	*;
 };
+
+LIBMULTIPATH_8.1.0 {
+global:
+	reset_strbuf;
+	append_strbuf_str;
+	get_strbuf_len;
+	get_strbuf_str;
+	steal_strbuf_str;
+	fill_strbuf;
+	print_strbuf;
+	truncate_strbuf;
+} LIBMULTIPATH_8.0.0;
diff --git a/libmultipath/parser.c b/libmultipath/parser.c
index 88c7b59..8ca91bf 100644
--- a/libmultipath/parser.c
+++ b/libmultipath/parser.c
@@ -150,7 +150,7 @@ find_keyword(vector keywords, vector v, char * name)
 }
 
 int
-snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
+snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
 		const void *data)
 {
 	int r;
@@ -191,7 +191,8 @@ snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
 		}
 	} while (*fmt++);
 out:
-	return snprintf(buff, len, "%s", get_strbuf_str(&sbuf));
+	return __append_strbuf_str(buff, get_strbuf_str(&sbuf),
+				   get_strbuf_len(&sbuf));
 }
 
 static const char quote_marker[] = { '\0', '"', '\0' };
diff --git a/libmultipath/parser.h b/libmultipath/parser.h
index e8b6eb2..b43d46f 100644
--- a/libmultipath/parser.h
+++ b/libmultipath/parser.h
@@ -82,7 +82,7 @@ extern vector alloc_strvec(char *string);
 extern void *set_value(vector strvec);
 extern int process_file(struct config *conf, const char *conf_file);
 extern struct keyword * find_keyword(vector keywords, vector v, char * name);
-int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
+int snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
 		    const void *data);
 bool is_quote(const char* token);
 
diff --git a/libmultipath/print.c b/libmultipath/print.c
index 29ce499..dd04dea 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -1,4 +1,4 @@
- /*
+/*
  * Copyright (c) 2005 Christophe Varoqui
  */
 #include <stdio.h>
@@ -31,59 +31,33 @@
 #include "discovery.h"
 #include "util.h"
 #include "foreign.h"
+#include "strbuf.h"
 
 #define MAX(x,y) (((x) > (y)) ? (x) : (y))
 #define MIN(x,y) (((x) > (y)) ? (y) : (x))
-#define TAIL     (line + len - 1 - c)
-#define NOPAD    s = c
-#define PAD(x) \
-do { \
-	while (c < (s + x) && (c < (line + len - 1))) \
-		*c++ = ' '; \
-	s = c; \
-} while (0)
-
-static char *
-__endline(char *line, size_t len, char *c)
-{
-	if (c > line) {
-		if (c >= line + len)
-			c = line + len - 1;
-		*(c - 1) = '\n';
-		*c = '\0';
-	}
-	return c;
-}
-
-#define PRINT(var, size, format, args...) \
-do { \
-	fwd = snprintf(var, size, format, ##args); \
-	c += (fwd >= size) ? size : fwd; \
-} while (0)
-
 /*
  * information printing helpers
  */
 static int
-snprint_str (char * buff, size_t len, const char * str)
+snprint_str(struct strbuf *buff, const char *str)
 {
-	return snprintf(buff, len, "%s", str);
+	return append_strbuf_str(buff, str);
 }
 
 static int
-snprint_int (char * buff, size_t len, int val)
+snprint_int (struct strbuf *buff, int val)
 {
-	return snprintf(buff, len, "%i", val);
+	return print_strbuf(buff, "%i", val);
 }
 
 static int
-snprint_uint (char * buff, size_t len, unsigned int val)
+snprint_uint (struct strbuf *buff, unsigned int val)
 {
-	return snprintf(buff, len, "%u", val);
+	return print_strbuf(buff, "%u", val);
 }
 
 static int
-snprint_size (char * buff, size_t len, unsigned long long size)
+snprint_size (struct strbuf *buff, unsigned long long size)
 {
 	float s = (float)(size >> 1); /* start with KB */
 	char units[] = {'K','M','G','T','P'};
@@ -94,184 +68,177 @@ snprint_size (char * buff, size_t len, unsigned long long size)
 		u++;
 	}
 
-	return snprintf(buff, len, "%.*f%c", s < 10, s, *u);
+	return print_strbuf(buff, "%.*f%c", s < 10, s, *u);
 }
 
 /*
  * multipath info printing functions
  */
 static int
-snprint_name (char * buff, size_t len, const struct multipath * mpp)
+snprint_name (struct strbuf *buff, const struct multipath * mpp)
 {
 	if (mpp->alias)
-		return snprintf(buff, len, "%s", mpp->alias);
+		return append_strbuf_str(buff, mpp->alias);
 	else
-		return snprintf(buff, len, "%s", mpp->wwid);
+		return append_strbuf_str(buff, mpp->wwid);
 }
 
 static int
-snprint_sysfs (char * buff, size_t len, const struct multipath * mpp)
+snprint_sysfs (struct strbuf *buff, const struct multipath * mpp)
 {
 	if (mpp->dmi)
-		return snprintf(buff, len, "dm-%i", mpp->dmi->minor);
+		return print_strbuf(buff, "dm-%i", mpp->dmi->minor);
 	else
-		return snprintf(buff, len, "undef");
+		return append_strbuf_str(buff, "undef");
 }
 
 static int
-snprint_ro (char * buff, size_t len, const struct multipath * mpp)
+snprint_ro (struct strbuf *buff, const struct multipath * mpp)
 {
 	if (!mpp->dmi)
-		return snprintf(buff, len, "undef");
+		return append_strbuf_str(buff, "undef");
 	if (mpp->dmi->read_only)
-		return snprintf(buff, len, "ro");
+		return append_strbuf_str(buff, "ro");
 	else
-		return snprintf(buff, len, "rw");
+		return append_strbuf_str(buff, "rw");
 }
 
 static int
-snprint_progress (char * buff, size_t len, int cur, int total)
+snprint_progress (struct strbuf *buff, int cur, int total)
 {
-	char * c = buff;
-	char * end = buff + len;
+	size_t initial_len = get_strbuf_len(buff);
+	int rc;
 
 	if (total > 0) {
 		int i = PROGRESS_LEN * cur / total;
 		int j = PROGRESS_LEN - i;
 
-		while (i-- > 0) {
-			c += snprintf(c, len, "X");
-			if ((len = (end - c)) <= 1) goto out;
-		}
-
-		while (j-- > 0) {
-			c += snprintf(c, len,  ".");
-			if ((len = (end - c)) <= 1) goto out;
+		if ((rc = fill_strbuf(buff, 'X', i)) < 0 ||
+		    (rc = fill_strbuf(buff, '.', j) < 0)) {
+			truncate_strbuf(buff, initial_len);
+			return rc;
 		}
 	}
 
-	c += snprintf(c, len, " %i/%i", cur, total);
-
-out:
-	buff[c - buff + 1] = '\0';
-	return (c - buff + 1);
+	if ((rc = print_strbuf(buff, " %i/%i", cur, total)) < 0)
+		return rc;
+	return get_strbuf_len(buff) - initial_len;
 }
 
 static int
-snprint_failback (char * buff, size_t len, const struct multipath * mpp)
+snprint_failback (struct strbuf *buff, const struct multipath * mpp)
 {
 	if (mpp->pgfailback == -FAILBACK_IMMEDIATE)
-		return snprintf(buff, len, "immediate");
+		return append_strbuf_str(buff, "immediate");
 	if (mpp->pgfailback == -FAILBACK_FOLLOWOVER)
-		return snprintf(buff, len, "followover");
+		return append_strbuf_str(buff, "followover");
 
 	if (!mpp->failback_tick)
-		return snprintf(buff, len, "-");
+		return append_strbuf_str(buff, "-");
 	else
-		return snprint_progress(buff, len, mpp->failback_tick,
+		return snprint_progress(buff, mpp->failback_tick,
 					mpp->pgfailback);
 }
 
 static int
-snprint_queueing (char * buff, size_t len, const struct multipath * mpp)
+snprint_queueing (struct strbuf *buff, const struct multipath * mpp)
 {
 	if (mpp->no_path_retry == NO_PATH_RETRY_FAIL)
-		return snprintf(buff, len, "off");
+		return append_strbuf_str(buff, "off");
 	else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE)
-		return snprintf(buff, len, "on");
+		return append_strbuf_str(buff, "on");
 	else if (mpp->no_path_retry == NO_PATH_RETRY_UNDEF)
-		return snprintf(buff, len, "-");
+		return append_strbuf_str(buff, "-");
 	else if (mpp->no_path_retry > 0) {
 		if (mpp->retry_tick > 0)
 
-			return snprintf(buff, len, "%i sec",
-					mpp->retry_tick);
+			return print_strbuf(buff, "%i sec", mpp->retry_tick);
 		else if (mpp->retry_tick == 0 && count_active_paths(mpp) > 0)
-			return snprintf(buff, len, "%i chk",
-					mpp->no_path_retry);
+			return print_strbuf(buff, "%i chk",
+					    mpp->no_path_retry);
 		else
-			return snprintf(buff, len, "off");
+			return append_strbuf_str(buff, "off");
 	}
 	return 0;
 }
 
 static int
-snprint_nb_paths (char * buff, size_t len, const struct multipath * mpp)
+snprint_nb_paths (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_int(buff, len, count_active_paths(mpp));
+	return snprint_int(buff, count_active_paths(mpp));
 }
 
 static int
-snprint_dm_map_state (char * buff, size_t len, const struct multipath * mpp)
+snprint_dm_map_state (struct strbuf *buff, const struct multipath * mpp)
 {
 	if (mpp->dmi && mpp->dmi->suspended)
-		return snprintf(buff, len, "suspend");
+		return append_strbuf_str(buff, "suspend");
 	else
-		return snprintf(buff, len, "active");
+		return append_strbuf_str(buff, "active");
 }
 
 static int
-snprint_multipath_size (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_size (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_size(buff, len, mpp->size);
+	return snprint_size(buff, mpp->size);
 }
 
 static int
-snprint_features (char * buff, size_t len, const struct multipath * mpp)
+snprint_features (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_str(buff, len, mpp->features);
+	return snprint_str(buff, mpp->features);
 }
 
 static int
-snprint_hwhandler (char * buff, size_t len, const struct multipath * mpp)
+snprint_hwhandler (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_str(buff, len, mpp->hwhandler);
+	return snprint_str(buff, mpp->hwhandler);
 }
 
 static int
-snprint_path_faults (char * buff, size_t len, const struct multipath * mpp)
+snprint_path_faults (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_uint(buff, len, mpp->stat_path_failures);
+	return snprint_uint(buff, mpp->stat_path_failures);
 }
 
 static int
-snprint_switch_grp (char * buff, size_t len, const struct multipath * mpp)
+snprint_switch_grp (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_uint(buff, len, mpp->stat_switchgroup);
+	return snprint_uint(buff, mpp->stat_switchgroup);
 }
 
 static int
-snprint_map_loads (char * buff, size_t len, const struct multipath * mpp)
+snprint_map_loads (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_uint(buff, len, mpp->stat_map_loads);
+	return snprint_uint(buff, mpp->stat_map_loads);
 }
 
 static int
-snprint_total_q_time (char * buff, size_t len, const struct multipath * mpp)
+snprint_total_q_time (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_uint(buff, len, mpp->stat_total_queueing_time);
+	return snprint_uint(buff, mpp->stat_total_queueing_time);
 }
 
 static int
-snprint_q_timeouts (char * buff, size_t len, const struct multipath * mpp)
+snprint_q_timeouts (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_uint(buff, len, mpp->stat_queueing_timeouts);
+	return snprint_uint(buff, mpp->stat_queueing_timeouts);
 }
 
 static int
-snprint_map_failures (char * buff, size_t len, const struct multipath * mpp)
+snprint_map_failures (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_uint(buff, len, mpp->stat_map_failures);
+	return snprint_uint(buff, mpp->stat_map_failures);
 }
 
 static int
-snprint_multipath_uuid (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_uuid (struct strbuf *buff, const struct multipath * mpp)
 {
-	return snprint_str(buff, len, mpp->wwid);
+	return snprint_str(buff, mpp->wwid);
 }
 
 static int
-snprint_multipath_vpr (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_vpr (struct strbuf *buff, const struct multipath * mpp)
 {
 	struct pathgroup * pgp;
 	struct path * pp;
@@ -280,16 +247,16 @@ snprint_multipath_vpr (char * buff, size_t len, const struct multipath * mpp)
 	vector_foreach_slot(mpp->pg, pgp, i) {
 		vector_foreach_slot(pgp->paths, pp, j) {
 			if (strlen(pp->vendor_id) && strlen(pp->product_id))
-				return snprintf(buff, len, "%s,%s",
-						pp->vendor_id, pp->product_id);
+				return print_strbuf(buff, "%s,%s",
+						    pp->vendor_id, pp->product_id);
 		}
 	}
-	return snprintf(buff, len, "##,##");
+	return append_strbuf_str(buff, "##,##");
 }
 
 
 static int
-snprint_multipath_vend (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_vend (struct strbuf *buff, const struct multipath * mpp)
 {
 	struct pathgroup * pgp;
 	struct path * pp;
@@ -298,14 +265,14 @@ snprint_multipath_vend (char * buff, size_t len, const struct multipath * mpp)
 	vector_foreach_slot(mpp->pg, pgp, i) {
 		vector_foreach_slot(pgp->paths, pp, j) {
 			if (strlen(pp->vendor_id))
-				return snprintf(buff, len, "%s", pp->vendor_id);
+				return append_strbuf_str(buff, pp->vendor_id);
 		}
 	}
-	return snprintf(buff, len, "##");
+	return append_strbuf_str(buff, "##");
 }
 
 static int
-snprint_multipath_prod (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_prod (struct strbuf *buff, const struct multipath * mpp)
 {
 	struct pathgroup * pgp;
 	struct path * pp;
@@ -314,14 +281,14 @@ snprint_multipath_prod (char * buff, size_t len, const struct multipath * mpp)
 	vector_foreach_slot(mpp->pg, pgp, i) {
 		vector_foreach_slot(pgp->paths, pp, j) {
 			if (strlen(pp->product_id))
-				return snprintf(buff, len, "%s", pp->product_id);
+				return append_strbuf_str(buff, pp->product_id);
 		}
 	}
-	return snprintf(buff, len, "##");
+	return append_strbuf_str(buff, "##");
 }
 
 static int
-snprint_multipath_rev (char * buff, size_t len, const struct multipath * mpp)
+snprint_multipath_rev (struct strbuf *buff, const struct multipath * mpp)
 {
 	struct pathgroup * pgp;
 	struct path * pp;
@@ -330,40 +297,40 @@ snprint_multipath_rev (char * buff, size_t len, const struct multipath * mpp)
 	vector_foreach_slot(mpp->pg, pgp, i) {
 		vector_foreach_slot(pgp->paths, pp, j) {
 			if (strlen(pp->rev))
-				return snprintf(buff, len, "%s", pp->rev);
+				return append_strbuf_str(buff, pp->rev);
 		}
 	}
-	return snprintf(buff, len, "##");
+	return append_strbuf_str(buff, "##");
 }
 
 static int
-snprint_multipath_foreign (char * buff, size_t len,
+snprint_multipath_foreign (struct strbuf *buff,
 			   __attribute__((unused)) const struct multipath * pp)
 {
-	return snprintf(buff, len, "%s", "--");
+	return append_strbuf_str(buff, "--");
 }
 
 static int
-snprint_action (char * buff, size_t len, const struct multipath * mpp)
+snprint_action (struct strbuf *buff, const struct multipath * mpp)
 {
 	switch (mpp->action) {
 	case ACT_REJECT:
-		return snprint_str(buff, len, ACT_REJECT_STR);
+		return snprint_str(buff, ACT_REJECT_STR);
 	case ACT_RENAME:
-		return snprint_str(buff, len, ACT_RENAME_STR);
+		return snprint_str(buff, ACT_RENAME_STR);
 	case ACT_RELOAD:
-		return snprint_str(buff, len, ACT_RELOAD_STR);
+		return snprint_str(buff, ACT_RELOAD_STR);
 	case ACT_CREATE:
-		return snprint_str(buff, len, ACT_CREATE_STR);
+		return snprint_str(buff, ACT_CREATE_STR);
 	case ACT_SWITCHPG:
-		return snprint_str(buff, len, ACT_SWITCHPG_STR);
+		return snprint_str(buff, ACT_SWITCHPG_STR);
 	default:
 		return 0;
 	}
 }
 
 static int
-snprint_multipath_vpd_data(char * buff, size_t len,
+snprint_multipath_vpd_data(struct strbuf *buff,
 			   const struct multipath * mpp)
 {
 	struct pathgroup * pgp;
@@ -373,26 +340,26 @@ snprint_multipath_vpd_data(char * buff, size_t len,
 	vector_foreach_slot(mpp->pg, pgp, i)
 		vector_foreach_slot(pgp->paths, pp, j)
 			if (pp->vpd_data)
-				return snprintf(buff, len, "%s", pp->vpd_data);
-	return snprintf(buff, len, "[undef]");
+				return append_strbuf_str(buff, pp->vpd_data);
+	return append_strbuf_str(buff, "[undef]");
 }
 
 /*
  * path info printing functions
  */
 static int
-snprint_path_uuid (char * buff, size_t len, const struct path * pp)
+snprint_path_uuid (struct strbuf *buff, const struct path * pp)
 {
-	return snprint_str(buff, len, pp->wwid);
+	return snprint_str(buff, pp->wwid);
 }
 
 static int
-snprint_hcil (char * buff, size_t len, const struct path * pp)
+snprint_hcil (struct strbuf *buff, const struct path * pp)
 {
 	if (!pp || pp->sg_id.host_no < 0)
-		return snprintf(buff, len, "#:#:#:#");
+		return append_strbuf_str(buff, "#:#:#:#");
 
-	return snprintf(buff, len, "%i:%i:%i:%" PRIu64,
+	return print_strbuf(buff, "%i:%i:%i:%" PRIu64,
 			pp->sg_id.host_no,
 			pp->sg_id.channel,
 			pp->sg_id.scsi_id,
@@ -400,159 +367,158 @@ snprint_hcil (char * buff, size_t len, const struct path * pp)
 }
 
 static int
-snprint_dev (char * buff, size_t len, const struct path * pp)
+snprint_dev (struct strbuf *buff, const struct path * pp)
 {
 	if (!pp || !strlen(pp->dev))
-		return snprintf(buff, len, "-");
+		return append_strbuf_str(buff, "-");
 	else
-		return snprint_str(buff, len, pp->dev);
+		return snprint_str(buff, pp->dev);
 }
 
 static int
-snprint_dev_t (char * buff, size_t len, const struct path * pp)
+snprint_dev_t (struct strbuf *buff, const struct path * pp)
 {
 	if (!pp || !strlen(pp->dev))
-		return snprintf(buff, len, "#:#");
+		return append_strbuf_str(buff, "#:#");
 	else
-		return snprint_str(buff, len, pp->dev_t);
+		return snprint_str(buff, pp->dev_t);
 }
 
 static int
-snprint_offline (char * buff, size_t len, const struct path * pp)
+snprint_offline (struct strbuf *buff, const struct path * pp)
 {
 	if (!pp || !pp->mpp)
-		return snprintf(buff, len, "unknown");
+		return append_strbuf_str(buff, "unknown");
 	else if (pp->offline)
-		return snprintf(buff, len, "offline");
+		return append_strbuf_str(buff, "offline");
 	else
-		return snprintf(buff, len, "running");
+		return append_strbuf_str(buff, "running");
 }
 
 static int
-snprint_chk_state (char * buff, size_t len, const struct path * pp)
+snprint_chk_state (struct strbuf *buff, const struct path * pp)
 {
 	if (!pp || !pp->mpp)
-		return snprintf(buff, len, "undef");
+		return append_strbuf_str(buff, "undef");
 
 	switch (pp->state) {
 	case PATH_UP:
-		return snprintf(buff, len, "ready");
+		return append_strbuf_str(buff, "ready");
 	case PATH_DOWN:
-		return snprintf(buff, len, "faulty");
+		return append_strbuf_str(buff, "faulty");
 	case PATH_SHAKY:
-		return snprintf(buff, len, "shaky");
+		return append_strbuf_str(buff, "shaky");
 	case PATH_GHOST:
-		return snprintf(buff, len, "ghost");
+		return append_strbuf_str(buff, "ghost");
 	case PATH_PENDING:
-		return snprintf(buff, len, "i/o pending");
+		return append_strbuf_str(buff, "i/o pending");
 	case PATH_TIMEOUT:
-		return snprintf(buff, len, "i/o timeout");
+		return append_strbuf_str(buff, "i/o timeout");
 	case PATH_DELAYED:
-		return snprintf(buff, len, "delayed");
+		return append_strbuf_str(buff, "delayed");
 	default:
-		return snprintf(buff, len, "undef");
+		return append_strbuf_str(buff, "undef");
 	}
 }
 
 static int
-snprint_dm_path_state (char * buff, size_t len, const struct path * pp)
+snprint_dm_path_state (struct strbuf *buff, const struct path * pp)
 {
 	if (!pp)
-		return snprintf(buff, len, "undef");
+		return append_strbuf_str(buff, "undef");
 
 	switch (pp->dmstate) {
 	case PSTATE_ACTIVE:
-		return snprintf(buff, len, "active");
+		return append_strbuf_str(buff, "active");
 	case PSTATE_FAILED:
-		return snprintf(buff, len, "failed");
+		return append_strbuf_str(buff, "failed");
 	default:
-		return snprintf(buff, len, "undef");
+		return append_strbuf_str(buff, "undef");
 	}
 }
 
 static int
-snprint_vpr (char * buff, size_t len, const struct path * pp)
+snprint_vpr (struct strbuf *buff, const struct path * pp)
 {
-	return snprintf(buff, len, "%s,%s",
-			pp->vendor_id, pp->product_id);
+	return print_strbuf(buff, "%s,%s", pp->vendor_id, pp->product_id);
 }
 
 static int
-snprint_next_check (char * buff, size_t len, const struct path * pp)
+snprint_next_check (struct strbuf *buff, const struct path * pp)
 {
 	if (!pp || !pp->mpp)
-		return snprintf(buff, len, "orphan");
+		return append_strbuf_str(buff, "orphan");
 
-	return snprint_progress(buff, len, pp->tick, pp->checkint);
+	return snprint_progress(buff, pp->tick, pp->checkint);
 }
 
 static int
-snprint_pri (char * buff, size_t len, const struct path * pp)
+snprint_pri (struct strbuf *buff, const struct path * pp)
 {
-	return snprint_int(buff, len, pp ? pp->priority : -1);
+	return snprint_int(buff, pp ? pp->priority : -1);
 }
 
 static int
-snprint_pg_selector (char * buff, size_t len, const struct pathgroup * pgp)
+snprint_pg_selector (struct strbuf *buff, const struct pathgroup * pgp)
 {
 	const char *s = pgp->mpp->selector;
 
-	return snprint_str(buff, len, s ? s : "");
+	return snprint_str(buff, s ? s : "");
 }
 
 static int
-snprint_pg_pri (char * buff, size_t len, const struct pathgroup * pgp)
+snprint_pg_pri (struct strbuf *buff, const struct pathgroup * pgp)
 {
-	return snprint_int(buff, len, pgp->priority);
+	return snprint_int(buff, pgp->priority);
 }
 
 static int
-snprint_pg_state (char * buff, size_t len, const struct pathgroup * pgp)
+snprint_pg_state (struct strbuf *buff, const struct pathgroup * pgp)
 {
 	switch (pgp->status) {
 	case PGSTATE_ENABLED:
-		return snprintf(buff, len, "enabled");
+		return append_strbuf_str(buff, "enabled");
 	case PGSTATE_DISABLED:
-		return snprintf(buff, len, "disabled");
+		return append_strbuf_str(buff, "disabled");
 	case PGSTATE_ACTIVE:
-		return snprintf(buff, len, "active");
+		return append_strbuf_str(buff, "active");
 	default:
-		return snprintf(buff, len, "undef");
+		return append_strbuf_str(buff, "undef");
 	}
 }
 
 static int
-snprint_pg_marginal (char * buff, size_t len, const struct pathgroup * pgp)
+snprint_pg_marginal (struct strbuf *buff, const struct pathgroup * pgp)
 {
 	if (pgp->marginal)
-		return snprintf(buff, len, "marginal");
-	return snprintf(buff, len, "normal");
+		return append_strbuf_str(buff, "marginal");
+	return append_strbuf_str(buff, "normal");
 }
 
 static int
-snprint_path_size (char * buff, size_t len, const struct path * pp)
+snprint_path_size (struct strbuf *buff, const struct path * pp)
 {
-	return snprint_size(buff, len, pp->size);
+	return snprint_size(buff, pp->size);
 }
 
 int
-snprint_path_serial (char * buff, size_t len, const struct path * pp)
+snprint_path_serial (struct strbuf *buff, const struct path * pp)
 {
-	return snprint_str(buff, len, pp->serial);
+	return snprint_str(buff, pp->serial);
 }
 
 static int
-snprint_path_mpp (char * buff, size_t len, const struct path * pp)
+snprint_path_mpp (struct strbuf *buff, const struct path * pp)
 {
 	if (!pp->mpp)
-		return snprintf(buff, len, "[orphan]");
+		return append_strbuf_str(buff, "[orphan]");
 	if (!pp->mpp->alias)
-		return snprintf(buff, len, "[unknown]");
-	return snprint_str(buff, len, pp->mpp->alias);
+		return append_strbuf_str(buff, "[unknown]");
+	return snprint_str(buff, pp->mpp->alias);
 }
 
 static int
-snprint_host_attr (char * buff, size_t len, const struct path * pp, char *attr)
+snprint_host_attr (struct strbuf *buff, const struct path * pp, char *attr)
 {
 	struct udev_device *host_dev = NULL;
 	char host_id[32];
@@ -560,7 +526,7 @@ snprint_host_attr (char * buff, size_t len, const struct path * pp, char *attr)
 	int ret;
 
 	if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
-		return snprintf(buff, len, "[undef]");
+		return append_strbuf_str(buff, "[undef]");
 	sprintf(host_id, "host%d", pp->sg_id.host_no);
 	host_dev = udev_device_new_from_subsystem_sysname(udev, "fc_host",
 							  host_id);
@@ -570,28 +536,28 @@ snprint_host_attr (char * buff, size_t len, const struct path * pp, char *attr)
 	}
 	value = udev_device_get_sysattr_value(host_dev, attr);
 	if (value)
-		ret = snprint_str(buff, len, value);
+		ret = snprint_str(buff, value);
 	udev_device_unref(host_dev);
 out:
 	if (!value)
-		ret = snprintf(buff, len, "[unknown]");
+		ret = append_strbuf_str(buff, "[unknown]");
 	return ret;
 }
 
 int
-snprint_host_wwnn (char * buff, size_t len, const struct path * pp)
+snprint_host_wwnn (struct strbuf *buff, const struct path * pp)
 {
-	return snprint_host_attr(buff, len, pp, "node_name");
+	return snprint_host_attr(buff, pp, "node_name");
 }
 
 int
-snprint_host_wwpn (char * buff, size_t len, const struct path * pp)
+snprint_host_wwpn (struct strbuf *buff, const struct path * pp)
 {
-	return snprint_host_attr(buff, len, pp, "port_name");
+	return snprint_host_attr(buff, pp, "port_name");
 }
 
 int
-snprint_tgt_wwpn (char * buff, size_t len, const struct path * pp)
+snprint_tgt_wwpn (struct strbuf *buff, const struct path * pp)
 {
 	struct udev_device *rport_dev = NULL;
 	char rport_id[42];
@@ -599,7 +565,7 @@ snprint_tgt_wwpn (char * buff, size_t len, const struct path * pp)
 	int ret;
 
 	if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP)
-		return snprintf(buff, len, "[undef]");
+		return append_strbuf_str(buff, "[undef]");
 	sprintf(rport_id, "rport-%d:%d-%d",
 		pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
 	rport_dev = udev_device_new_from_subsystem_sysname(udev,
@@ -611,111 +577,111 @@ snprint_tgt_wwpn (char * buff, size_t len, const struct path * pp)
 	}
 	value = udev_device_get_sysattr_value(rport_dev, "port_name");
 	if (value)
-		ret = snprint_str(buff, len, value);
+		ret = snprint_str(buff, value);
 	udev_device_unref(rport_dev);
 out:
 	if (!value)
-		ret = snprintf(buff, len, "[unknown]");
+		ret = append_strbuf_str(buff, "[unknown]");
 	return ret;
 }
 
 
 int
-snprint_tgt_wwnn (char * buff, size_t len, const struct path * pp)
+snprint_tgt_wwnn (struct strbuf *buff, const struct path * pp)
 {
 	if (pp->tgt_node_name[0] == '\0')
-		return snprintf(buff, len, "[undef]");
-	return snprint_str(buff, len, pp->tgt_node_name);
+		return append_strbuf_str(buff, "[undef]");
+	return snprint_str(buff, pp->tgt_node_name);
 }
 
 static int
-snprint_host_adapter (char * buff, size_t len, const struct path * pp)
+snprint_host_adapter (struct strbuf *buff, const struct path * pp)
 {
 	char adapter[SLOT_NAME_SIZE];
 
 	if (sysfs_get_host_adapter_name(pp, adapter))
-		return snprintf(buff, len, "[undef]");
-	return snprint_str(buff, len, adapter);
+		return append_strbuf_str(buff, "[undef]");
+	return snprint_str(buff, adapter);
 }
 
 static int
-snprint_path_checker (char * buff, size_t len, const struct path * pp)
+snprint_path_checker (struct strbuf *buff, const struct path * pp)
 {
 	const struct checker * c = &pp->checker;
-	return snprint_str(buff, len, checker_name(c));
+	return snprint_str(buff, checker_name(c));
 }
 
 static int
-snprint_path_foreign (char * buff, size_t len,
+snprint_path_foreign (struct strbuf *buff,
 		      __attribute__((unused)) const struct path * pp)
 {
-	return snprintf(buff, len, "%s", "--");
+	return append_strbuf_str(buff, "--");
 }
 
 static int
-snprint_path_failures(char * buff, size_t len, const struct path * pp)
+snprint_path_failures(struct strbuf *buff, const struct path * pp)
 {
-	return snprint_int(buff, len, pp->failcount);
+	return snprint_int(buff, pp->failcount);
 }
 
 /* if you add a protocol string bigger than "scsi:unspec" you must
  * also change PROTOCOL_BUF_SIZE */
 int
-snprint_path_protocol(char * buff, size_t len, const struct path * pp)
+snprint_path_protocol(struct strbuf *buff, const struct path * pp)
 {
 	switch (pp->bus) {
 	case SYSFS_BUS_SCSI:
 		switch (pp->sg_id.proto_id) {
 		case SCSI_PROTOCOL_FCP:
-			return snprintf(buff, len, "scsi:fcp");
+			return append_strbuf_str(buff, "scsi:fcp");
 		case SCSI_PROTOCOL_SPI:
-			return snprintf(buff, len, "scsi:spi");
+			return append_strbuf_str(buff, "scsi:spi");
 		case SCSI_PROTOCOL_SSA:
-			return snprintf(buff, len, "scsi:ssa");
+			return append_strbuf_str(buff, "scsi:ssa");
 		case SCSI_PROTOCOL_SBP:
-			return snprintf(buff, len, "scsi:sbp");
+			return append_strbuf_str(buff, "scsi:sbp");
 		case SCSI_PROTOCOL_SRP:
-			return snprintf(buff, len, "scsi:srp");
+			return append_strbuf_str(buff, "scsi:srp");
 		case SCSI_PROTOCOL_ISCSI:
-			return snprintf(buff, len, "scsi:iscsi");
+			return append_strbuf_str(buff, "scsi:iscsi");
 		case SCSI_PROTOCOL_SAS:
-			return snprintf(buff, len, "scsi:sas");
+			return append_strbuf_str(buff, "scsi:sas");
 		case SCSI_PROTOCOL_ADT:
-			return snprintf(buff, len, "scsi:adt");
+			return append_strbuf_str(buff, "scsi:adt");
 		case SCSI_PROTOCOL_ATA:
-			return snprintf(buff, len, "scsi:ata");
+			return append_strbuf_str(buff, "scsi:ata");
 		case SCSI_PROTOCOL_USB:
-			return snprintf(buff, len, "scsi:usb");
+			return append_strbuf_str(buff, "scsi:usb");
 		case SCSI_PROTOCOL_UNSPEC:
 		default:
-			return snprintf(buff, len, "scsi:unspec");
+			return append_strbuf_str(buff, "scsi:unspec");
 		}
 	case SYSFS_BUS_CCW:
-		return snprintf(buff, len, "ccw");
+		return append_strbuf_str(buff, "ccw");
 	case SYSFS_BUS_CCISS:
-		return snprintf(buff, len, "cciss");
+		return append_strbuf_str(buff, "cciss");
 	case SYSFS_BUS_NVME:
-		return snprintf(buff, len, "nvme");
+		return append_strbuf_str(buff, "nvme");
 	case SYSFS_BUS_UNDEF:
 	default:
-		return snprintf(buff, len, "undef");
+		return append_strbuf_str(buff, "undef");
 	}
 }
 
 int
-snprint_path_marginal(char * buff, size_t len, const struct path * pp)
+snprint_path_marginal(struct strbuf *buff, const struct path * pp)
 {
 	if (pp->marginal)
-		return snprintf(buff, len, "marginal");
-	return snprintf(buff, len, "normal");
+		return append_strbuf_str(buff, "marginal");
+	return append_strbuf_str(buff, "normal");
 }
 
 static int
-snprint_path_vpd_data(char * buff, size_t len, const struct path * pp)
+snprint_path_vpd_data(struct strbuf *buff, const struct path * pp)
 {
 	if (pp->vpd_data)
-		return snprintf(buff, len, "%s", pp->vpd_data);
-	return snprintf(buff, len, "[undef]");
+		return append_strbuf_str(buff, pp->vpd_data);
+	return append_strbuf_str(buff, "[undef]");
 }
 
 struct multipath_data mpd[] = {
@@ -782,24 +748,33 @@ struct pathgroup_data pgd[] = {
 	{0, NULL, 0 , NULL}
 };
 
-int
-snprint_wildcards (char * buff, int len)
+int snprint_wildcards(struct strbuf *buff)
 {
-	int i, fwd = 0;
+	int initial_len = get_strbuf_len(buff);
+	int i, rc;
 
-	fwd += snprintf(buff + fwd, len - fwd, "multipath format wildcards:\n");
+	if ((rc = append_strbuf_str(buff, "multipath format wildcards:\n")) < 0)
+		return rc;
 	for (i = 0; mpd[i].header; i++)
-		fwd += snprintf(buff + fwd, len - fwd, "%%%c  %s\n",
-				mpd[i].wildcard, mpd[i].header);
-	fwd += snprintf(buff + fwd, len - fwd, "\npath format wildcards:\n");
+		if ((rc = print_strbuf(buff, "%%%c  %s\n",
+				       mpd[i].wildcard, mpd[i].header)) < 0)
+			return rc;
+
+	if ((rc = append_strbuf_str(buff, "\npath format wildcards:\n")) < 0)
+		return rc;
 	for (i = 0; pd[i].header; i++)
-		fwd += snprintf(buff + fwd, len - fwd, "%%%c  %s\n",
-				pd[i].wildcard, pd[i].header);
-	fwd += snprintf(buff + fwd, len - fwd, "\npathgroup format wildcards:\n");
+		if ((rc = print_strbuf(buff, "%%%c  %s\n",
+				       pd[i].wildcard, pd[i].header)) < 0)
+			return rc;
+
+	if ((rc = append_strbuf_str(buff, "\npathgroup format wildcards:\n")) < 0)
+		return rc;
 	for (i = 0; pgd[i].header; i++)
-		fwd += snprintf(buff + fwd, len - fwd, "%%%c  %s\n",
-				pgd[i].wildcard, pgd[i].header);
-	return fwd;
+		if ((rc = print_strbuf(buff, "%%%c  %s\n",
+				       pgd[i].wildcard, pgd[i].header)) < 0)
+			return rc;
+
+	return get_strbuf_len(buff) - initial_len;
 }
 
 void
@@ -832,10 +807,10 @@ void
 _get_path_layout (const struct _vector *gpvec, enum layout_reset reset)
 {
 	int i, j;
-	char buff[MAX_FIELD_LEN];
 	const struct gen_path *gp;
 
 	for (j = 0; pd[j].header; j++) {
+		STRBUF_ON_STACK(buff);
 
 		reset_width(&pd[j].width, reset, pd[j].header);
 
@@ -843,9 +818,9 @@ _get_path_layout (const struct _vector *gpvec, enum layout_reset reset)
 			continue;
 
 		vector_foreach_slot (gpvec, gp, i) {
-			gp->ops->snprint(gp, buff, MAX_FIELD_LEN,
-					 pd[j].wildcard);
-			pd[j].width = MAX(pd[j].width, strlen(buff));
+			gp->ops->snprint(gp, &buff, pd[j].wildcard);
+			pd[j].width = MAX(pd[j].width, get_strbuf_len(&buff));
+			truncate_strbuf(&buff, 0);
 		}
 	}
 }
@@ -873,10 +848,10 @@ _get_multipath_layout (const struct _vector *gmvec,
 			    enum layout_reset reset)
 {
 	int i, j;
-	char buff[MAX_FIELD_LEN];
 	const struct gen_multipath * gm;
 
 	for (j = 0; mpd[j].header; j++) {
+		STRBUF_ON_STACK(buff);
 
 		reset_width(&mpd[j].width, reset, mpd[j].header);
 
@@ -884,9 +859,9 @@ _get_multipath_layout (const struct _vector *gmvec,
 			continue;
 
 		vector_foreach_slot (gmvec, gm, i) {
-			gm->ops->snprint(gm, buff, MAX_FIELD_LEN,
-					 mpd[j].wildcard);
-			mpd[j].width = MAX(mpd[j].width, strlen(buff));
+			gm->ops->snprint(gm, &buff, mpd[j].wildcard);
+			mpd[j].width = MAX(mpd[j].width, get_strbuf_len(&buff));
+			truncate_strbuf(&buff, 0);
 		}
 		condlog(4, "%s: width %d", mpd[j].header, mpd[j].width);
 	}
@@ -905,14 +880,14 @@ mpd_lookup(char wildcard)
 }
 
 int snprint_multipath_attr(const struct gen_multipath* gm,
-			   char *buf, int len, char wildcard)
+			   struct strbuf *buf, char wildcard)
 {
 	const struct multipath *mpp = gen_multipath_to_dm(gm);
 	struct multipath_data *mpd = mpd_lookup(wildcard);
 
 	if (mpd == NULL)
 		return 0;
-	return mpd->snprint(buf, len, mpp);
+	return mpd->snprint(buf, mpp);
 }
 
 static struct path_data *
@@ -928,14 +903,14 @@ pd_lookup(char wildcard)
 }
 
 int snprint_path_attr(const struct gen_path* gp,
-		      char *buf, int len, char wildcard)
+		      struct strbuf *buf, char wildcard)
 {
 	const struct path *pp = gen_path_to_dm(gp);
 	struct path_data *pd = pd_lookup(wildcard);
 
 	if (pd == NULL)
 		return 0;
-	return pd->snprint(buf, len, pp);
+	return pd->snprint(buf, pp);
 }
 
 static struct pathgroup_data *
@@ -951,220 +926,168 @@ pgd_lookup(char wildcard)
 }
 
 int snprint_pathgroup_attr(const struct gen_pathgroup* gpg,
-			   char *buf, int len, char wildcard)
+			   struct strbuf *buf, char wildcard)
 {
 	const struct pathgroup *pg = gen_pathgroup_to_dm(gpg);
 	struct pathgroup_data *pdg = pgd_lookup(wildcard);
 
 	if (pdg == NULL)
 		return 0;
-	return pdg->snprint(buf, len, pg);
+	return pdg->snprint(buf, pg);
 }
 
-int
-snprint_multipath_header (char * line, int len, const char * format)
+int snprint_multipath_header(struct strbuf *line, const char *format)
 {
-	char * c = line;   /* line cursor */
-	char * s = line;   /* for padding */
-	const char * f = format; /* format string cursor */
-	int fwd;
+	int initial_len = get_strbuf_len(line);
+	const char *f;
 	struct multipath_data * data;
+	int rc;
 
-	do {
-		if (TAIL <= 0)
-			break;
+	for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+		if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+			return rc;
 
-		if (*f != '%') {
-			*c++ = *f;
-			NOPAD;
-			continue;
-		}
-		f++;
-
-		if (!(data = mpd_lookup(*f)))
+		format = f + 1;
+		if (!(data = mpd_lookup(*format)))
 			continue; /* unknown wildcard */
 
-		PRINT(c, TAIL, "%s", data->header);
-		PAD(data->width);
-	} while (*f++);
+		if ((rc = append_strbuf_str(line, data->header)) < 0)
+			return rc;
+		else if ((unsigned int)rc < data->width)
+			if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0)
+				return rc;
+	}
 
-	__endline(line, len, c);
-	return (c - line);
+	if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+		return rc;
+	return get_strbuf_len(line) - initial_len;
 }
 
-int
-_snprint_multipath (const struct gen_multipath * gmp,
-		    char * line, int len, const char * format, int pad)
+int _snprint_multipath(const struct gen_multipath *gmp,
+		       struct strbuf *line, const char *format, int pad)
 {
-	char * c = line;   /* line cursor */
-	char * s = line;   /* for padding */
-	const char * f = format; /* format string cursor */
-	int fwd;
+	int initial_len = get_strbuf_len(line);
+	const char *f;
 	struct multipath_data * data;
-	char buff[MAX_FIELD_LEN] = {};
+	int rc;
 
-	do {
-		if (TAIL <= 0)
-			break;
+	for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+		if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+			return rc;
 
-		if (*f != '%') {
-			*c++ = *f;
-			NOPAD;
-			continue;
-		}
-		f++;
-
-		if (!(data = mpd_lookup(*f)))
-			continue;
+		format = f + 1;
+		if (!(data = mpd_lookup(*format)))
+			continue; /* unknown wildcard */
 
-		gmp->ops->snprint(gmp, buff, MAX_FIELD_LEN, *f);
-		PRINT(c, TAIL, "%s", buff);
-		if (pad)
-			PAD(data->width);
-		buff[0] = '\0';
-	} while (*f++);
+		if ((rc = gmp->ops->snprint(gmp, line, *format)) < 0)
+			return rc;
+		else if (pad && (unsigned int)rc < data->width)
+			if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0)
+				return rc;
+	}
 
-	__endline(line, len, c);
-	return (c - line);
+	if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+		return rc;
+	return get_strbuf_len(line) - initial_len;
 }
 
-int
-snprint_path_header (char * line, int len, const char * format)
+int snprint_path_header(struct strbuf *line, const char *format)
 {
-	char * c = line;   /* line cursor */
-	char * s = line;   /* for padding */
-	const char * f = format; /* format string cursor */
-	int fwd;
-	struct path_data * data;
-
-	do {
-		if (TAIL <= 0)
-			break;
+	int initial_len = get_strbuf_len(line);
+	const char *f;
+	struct path_data *data;
+	int rc;
 
-		if (*f != '%') {
-			*c++ = *f;
-			NOPAD;
-			continue;
-		}
-		f++;
+	for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+		if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+			return rc;
 
-		if (!(data = pd_lookup(*f)))
+		format = f + 1;
+		if (!(data = pd_lookup(*format)))
 			continue; /* unknown wildcard */
 
-		PRINT(c, TAIL, "%s", data->header);
-		PAD(data->width);
-	} while (*f++);
+		if ((rc = append_strbuf_str(line, data->header)) < 0)
+			return rc;
+		else if ((unsigned int)rc < data->width)
+			if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0)
+				return rc;
+	}
 
-	__endline(line, len, c);
-	return (c - line);
+	if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+		return rc;
+	return get_strbuf_len(line) - initial_len;
 }
 
-int
-_snprint_path (const struct gen_path * gp, char * line, int len,
-	       const char * format, int pad)
+int _snprint_path(const struct gen_path *gp, struct strbuf *line,
+		  const char *format, int pad)
 {
-	char * c = line;   /* line cursor */
-	char * s = line;   /* for padding */
-	const char * f = format; /* format string cursor */
-	int fwd;
+	int initial_len = get_strbuf_len(line);
+	const char *f;
 	struct path_data * data;
-	char buff[MAX_FIELD_LEN];
+	int rc;
 
-	do {
-		if (TAIL <= 0)
-			break;
-
-		if (*f != '%') {
-			*c++ = *f;
-			NOPAD;
-			continue;
-		}
-		f++;
+	for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+		if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+			return rc;
 
-		if (!(data = pd_lookup(*f)))
-			continue;
+		format = f + 1;
+		if (!(data = pd_lookup(*format)))
+			continue; /* unknown wildcard */
 
-		gp->ops->snprint(gp, buff, MAX_FIELD_LEN, *f);
-		PRINT(c, TAIL, "%s", buff);
-		if (pad)
-			PAD(data->width);
-	} while (*f++);
+		if ((rc = gp->ops->snprint(gp, line, *format)) < 0)
+			return rc;
+		else if (pad && (unsigned int)rc < data->width)
+			if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0)
+				return rc;
+	}
 
-	__endline(line, len, c);
-	return (c - line);
+	if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+		return rc;
+	return get_strbuf_len(line) - initial_len;
 }
 
-int
-_snprint_pathgroup (const struct gen_pathgroup * ggp, char * line, int len,
-		    char * format)
-{
-	char * c = line;   /* line cursor */
-	char * s = line;   /* for padding */
-	char * f = format; /* format string cursor */
-	int fwd;
-	struct pathgroup_data * data;
-	char buff[MAX_FIELD_LEN];
-
-	do {
-		if (TAIL <= 0)
-			break;
+int _snprint_pathgroup(const struct gen_pathgroup *ggp, struct strbuf *line,
+		       const char *format)
+{
+	int initial_len = get_strbuf_len(line);
+	const char *f;
+	struct pathgroup_data *data;
+	int rc;
 
-		if (*f != '%') {
-			*c++ = *f;
-			NOPAD;
-			continue;
-		}
-		f++;
+	for (f = strchr(format, '%'); f; f = strchr(++format, '%')) {
+		if ((rc = __append_strbuf_str(line, format, f - format)) < 0)
+			return rc;
 
-		if (!(data = pgd_lookup(*f)))
-			continue;
+		format = f + 1;
+		if (!(data = pgd_lookup(*format)))
+			continue; /* unknown wildcard */
 
-		ggp->ops->snprint(ggp, buff, MAX_FIELD_LEN, *f);
-		PRINT(c, TAIL, "%s", buff);
-		PAD(data->width);
-	} while (*f++);
+		if ((rc = ggp->ops->snprint(ggp, line, *format)) < 0)
+			return rc;
+		else if ((unsigned int)rc < data->width)
+			if ((rc = fill_strbuf(line, ' ', data->width - rc)) < 0)
+				return rc;
+	}
 
-	__endline(line, len, c);
-	return (c - line);
+	if ((rc = print_strbuf(line, "%s\n", format)) < 0)
+		return rc;
+	return get_strbuf_len(line) - initial_len;
 }
-#define snprint_pathgroup(line, len, fmt, pgp) \
-	_snprint_pathgroup(dm_pathgroup_to_gen(pgp), line, len, fmt)
+
+#define snprint_pathgroup(line, fmt, pgp)				\
+	_snprint_pathgroup(dm_pathgroup_to_gen(pgp), line, fmt)
 
 void _print_multipath_topology(const struct gen_multipath *gmp, int verbosity)
 {
-	int resize;
-	char *buff = NULL;
-	char *old = NULL;
-	int len, maxlen = MAX_LINE_LEN * MAX_LINES;
-
-	buff = MALLOC(maxlen);
-	do {
-		if (!buff) {
-			if (old)
-				FREE(old);
-			condlog(0, "couldn't allocate memory for list: %s\n",
-				strerror(errno));
-			return;
-		}
+	STRBUF_ON_STACK(buff);
 
-		len = _snprint_multipath_topology(gmp, buff, maxlen, verbosity);
-		resize = (len == maxlen - 1);
-
-		if (resize) {
-			maxlen *= 2;
-			old = buff;
-			buff = REALLOC(buff, maxlen);
-		}
-	} while (resize);
-	printf("%s", buff);
-	FREE(buff);
+	_snprint_multipath_topology(gmp, &buff, verbosity);
+	printf("%s", get_strbuf_str(&buff));
 }
 
-int
-snprint_multipath_style(const struct gen_multipath *gmp, char *style, int len,
-			int verbosity)
+int snprint_multipath_style(const struct gen_multipath *gmp,
+			    struct strbuf *style, int verbosity)
 {
-	int n;
 	const struct multipath *mpp = gen_multipath_to_dm(gmp);
 	bool need_action = (verbosity > 1 &&
 			    mpp->action != ACT_NOTHING &&
@@ -1172,268 +1095,203 @@ snprint_multipath_style(const struct gen_multipath *gmp, char *style, int len,
 			    mpp->action != ACT_IMPOSSIBLE);
 	bool need_wwid = (strncmp(mpp->alias, mpp->wwid, WWID_SIZE));
 
-	n = snprintf(style, len, "%s%s%s%s",
-		     need_action ? "%A: " : "", "%n",
-		     need_wwid ? " (%w)" : "", " %d %s");
-	return MIN(n, len - 1);
+	return print_strbuf(style, "%s%s%s%s",
+			    need_action ? "%A: " : "", "%n",
+			    need_wwid ? " (%w)" : "", " %d %s");
 }
 
 int _snprint_multipath_topology(const struct gen_multipath *gmp,
-				char *buff, int len, int verbosity)
+				struct strbuf *buff, int verbosity)
 {
-	int j, i, fwd = 0;
+	int j, i, rc;
 	const struct _vector *pgvec;
 	const struct gen_pathgroup *gpg;
-	char style[64];
-	char * c = style;
-	char fmt[64];
-	char * f;
+	STRBUF_ON_STACK(style);
+	size_t initial_len = get_strbuf_len(buff);
 
 	if (verbosity <= 0)
-		return fwd;
+		return 0;
 
 	reset_multipath_layout();
 
 	if (verbosity == 1)
-		return _snprint_multipath(gmp, buff, len, "%n", 1);
-
-	if(isatty(1))
-		c += sprintf(c, "%c[%dm", 0x1B, 1); /* bold on */
+		return _snprint_multipath(gmp, buff, "%n", 1);
 
-	c += gmp->ops->style(gmp, c, sizeof(style) - (c - style),
-			     verbosity);
-	if(isatty(1))
-		c += sprintf(c, "%c[%dm", 0x1B, 0); /* bold off */
+	if(isatty(1) &&
+	   (rc = print_strbuf(&style, "%c[%dm", 0x1B, 1)) < 0) /* bold on */
+		return rc;
+	if ((rc = gmp->ops->style(gmp, &style, verbosity)) < 0)
+		return rc;
+	if(isatty(1) &&
+	   (rc = print_strbuf(&style, "%c[%dm", 0x1B, 0)) < 0) /* bold off */
+		return rc;
 
-	fwd += _snprint_multipath(gmp, buff + fwd, len - fwd, style, 1);
-	if (fwd >= len)
-		return len;
-	fwd += _snprint_multipath(gmp, buff + fwd, len - fwd,
-				  PRINT_MAP_PROPS, 1);
-	if (fwd >= len)
-		return len;
+	if ((rc = _snprint_multipath(gmp, buff, get_strbuf_str(&style), 1)) < 0
+	    || (rc = _snprint_multipath(gmp, buff, PRINT_MAP_PROPS, 1)) < 0)
+		return rc;
 
 	pgvec = gmp->ops->get_pathgroups(gmp);
 	if (pgvec == NULL)
-		return fwd;
+		goto out;
 
 	vector_foreach_slot (pgvec, gpg, j) {
 		const struct _vector *pathvec;
 		struct gen_path *gp;
+		bool last_group = j + 1 == VECTOR_SIZE(pgvec);
 
-		f=fmt;
-
-		if (j + 1 < VECTOR_SIZE(pgvec)) {
-			strcpy(f, "|-+- " PRINT_PG_INDENT);
-		} else
-			strcpy(f, "`-+- " PRINT_PG_INDENT);
-		fwd += _snprint_pathgroup(gpg, buff + fwd, len - fwd, fmt);
-
-		if (fwd >= len) {
-			fwd = len;
-			break;
-		}
+		if ((rc = print_strbuf(buff, "%c-+- ",
+				       last_group ? '`' : '|')) < 0 ||
+		    (rc = _snprint_pathgroup(gpg, buff, PRINT_PG_INDENT)) < 0)
+			return rc;
 
 		pathvec = gpg->ops->get_paths(gpg);
 		if (pathvec == NULL)
 			continue;
 
 		vector_foreach_slot (pathvec, gp, i) {
-			f=fmt;
-			if (*f != '|')
-				*f=' ';
-			f++;
-			if (i + 1 < VECTOR_SIZE(pathvec))
-				strcpy(f, " |- " PRINT_PATH_INDENT);
-			else
-				strcpy(f, " `- " PRINT_PATH_INDENT);
-			fwd += _snprint_path(gp, buff + fwd, len - fwd, fmt, 1);
-			if (fwd >= len) {
-				fwd = len;
-				break;
-			}
+			if ((rc = print_strbuf(buff, "%c %c- ",
+					       last_group ? ' ' : '|',
+					       i + 1 == VECTOR_SIZE(pathvec) ?
+					       '`': '|')) < 0 ||
+			    (rc = _snprint_path(gp, buff,
+						PRINT_PATH_INDENT, 1)) < 0)
+				return rc;
 		}
 		gpg->ops->rel_paths(gpg, pathvec);
-
-		if (fwd == len)
-			break;
 	}
+
 	gmp->ops->rel_pathgroups(gmp, pgvec);
-	return fwd;
+out:
+	return get_strbuf_len(buff) - initial_len;
 }
 
 
 static int
-snprint_json (char * buff, int len, int indent, char *json_str)
+snprint_json(struct strbuf *buff, int indent, const char *json_str)
 {
-	int fwd = 0, i;
+	int rc;
 
-	for (i = 0; i < indent; i++) {
-		fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_INDENT);
-		if (fwd >= len)
-			return fwd;
-	}
+	if ((rc = fill_strbuf(buff, ' ', indent * PRINT_JSON_INDENT_N)) < 0)
+		return rc;
 
-	fwd += snprintf(buff + fwd, len - fwd, "%s", json_str);
-	return fwd;
+	return append_strbuf_str(buff, json_str);
 }
 
-static int
-snprint_json_header (char * buff, int len)
+static int snprint_json_header(struct strbuf *buff)
 {
-	int fwd = 0;
-
-	fwd +=  snprint_json(buff, len, 0, PRINT_JSON_START_ELEM);
-	if (fwd >= len)
-		return fwd;
+	int rc;
 
-	fwd +=  snprintf(buff + fwd, len  - fwd, PRINT_JSON_START_VERSION,
-			PRINT_JSON_MAJOR_VERSION, PRINT_JSON_MINOR_VERSION);
-	return fwd;
+	if ((rc = snprint_json(buff, 0, PRINT_JSON_START_ELEM)) < 0)
+		return rc;
+	return print_strbuf(buff, PRINT_JSON_START_VERSION,
+			    PRINT_JSON_MAJOR_VERSION, PRINT_JSON_MINOR_VERSION);
 }
 
-static int
-snprint_json_elem_footer (char * buff, int len, int indent, int last)
+static int snprint_json_elem_footer(struct strbuf *buff, int indent, bool last)
 {
-	int fwd = 0, i;
+	int rc;
 
-	for (i = 0; i < indent; i++) {
-		fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_INDENT);
-		if (fwd >= len)
-			return fwd;
-	}
+	if ((rc = fill_strbuf(buff, ' ', indent * PRINT_JSON_INDENT_N)) < 0)
+		return rc;
 
-	if (last == 1)
-		fwd += snprintf(buff + fwd, len - fwd, "%s", PRINT_JSON_END_LAST_ELEM);
+	if (last)
+		return append_strbuf_str(buff, PRINT_JSON_END_LAST_ELEM);
 	else
-		fwd += snprintf(buff + fwd, len - fwd, "%s", PRINT_JSON_END_ELEM);
-	return fwd;
+		return append_strbuf_str(buff, PRINT_JSON_END_ELEM);
 }
 
-static int
-snprint_multipath_fields_json (char * buff, int len,
-		const struct multipath * mpp, int last)
+static int snprint_multipath_fields_json(struct strbuf *buff,
+					 const struct multipath *mpp, int last)
 {
-	int i, j, fwd = 0;
+	int i, j, rc;
 	struct path *pp;
 	struct pathgroup *pgp;
+	size_t initial_len = get_strbuf_len(buff);
 
-	fwd += snprint_multipath(buff, len, PRINT_JSON_MAP, mpp, 0);
-	if (fwd >= len)
-		return fwd;
-
-	fwd += snprint_json(buff + fwd, len - fwd, 2, PRINT_JSON_START_GROUPS);
-	if (fwd >= len)
-		return fwd;
+	if ((rc = snprint_multipath(buff, PRINT_JSON_MAP, mpp, 0)) < 0 ||
+	    (rc = snprint_json(buff, 2, PRINT_JSON_START_GROUPS)) < 0)
+		return rc;
 
 	vector_foreach_slot (mpp->pg, pgp, i) {
 
-		fwd += snprint_pathgroup(buff + fwd, len - fwd, PRINT_JSON_GROUP, pgp);
-		if (fwd >= len)
-			return fwd;
-
-		fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_GROUP_NUM, i + 1);
-		if (fwd >= len)
-			return fwd;
-
-		fwd += snprint_json(buff + fwd, len - fwd, 3, PRINT_JSON_START_PATHS);
-		if (fwd >= len)
-			return fwd;
+		if ((rc = snprint_pathgroup(buff, PRINT_JSON_GROUP, pgp)) < 0 ||
+		    (rc = print_strbuf(buff, PRINT_JSON_GROUP_NUM, i + 1)) < 0 ||
+		    (rc = snprint_json(buff, 3, PRINT_JSON_START_PATHS)) < 0)
+			return rc;
 
 		vector_foreach_slot (pgp->paths, pp, j) {
-			fwd += snprint_path(buff + fwd, len - fwd, PRINT_JSON_PATH, pp, 0);
-			if (fwd >= len)
-				return fwd;
-
-			fwd += snprint_json_elem_footer(buff + fwd,
-					len - fwd, 3, j + 1 == VECTOR_SIZE(pgp->paths));
-			if (fwd >= len)
-				return fwd;
+			if ((rc = snprint_path(buff, PRINT_JSON_PATH,
+					       pp, 0)) < 0 ||
+			    (rc = snprint_json_elem_footer(
+				    buff, 3,
+				    j + 1 == VECTOR_SIZE(pgp->paths))) < 0)
+				return rc;
 		}
-		fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
-		if (fwd >= len)
-			return fwd;
-
-		fwd +=  snprint_json_elem_footer(buff + fwd,
-				len - fwd, 2, i + 1 == VECTOR_SIZE(mpp->pg));
-		if (fwd >= len)
-			return fwd;
+		if ((rc = snprint_json(buff, 0, PRINT_JSON_END_ARRAY)) < 0 ||
+		    (rc = snprint_json_elem_footer(
+			    buff, 2, i + 1 == VECTOR_SIZE(mpp->pg))) < 0)
+			return rc;
 	}
 
-	fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
-	if (fwd >= len)
-		return fwd;
+	if ((rc = snprint_json(buff, 0, PRINT_JSON_END_ARRAY)) < 0 ||
+	    (rc = snprint_json_elem_footer(buff, 1, last)) < 0)
+		return rc;
 
-	fwd += snprint_json_elem_footer(buff + fwd, len - fwd, 1, last);
-	return fwd;
+	return get_strbuf_len(buff) - initial_len;
 }
 
-int
-snprint_multipath_map_json (char * buff, int len, const struct multipath * mpp)
+int snprint_multipath_map_json(struct strbuf *buff, const struct multipath * mpp)
 {
-	int fwd = 0;
-
-	fwd +=  snprint_json_header(buff, len);
-	if (fwd >= len)
-		return len;
+	size_t initial_len = get_strbuf_len(buff);
+	int rc;
 
-	fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_START_MAP);
-	if (fwd >= len)
-		return len;
+	if ((rc = snprint_json_header(buff)) < 0 ||
+	    (rc = snprint_json(buff, 0, PRINT_JSON_START_MAP)) < 0)
+		return rc;
 
-	fwd += snprint_multipath_fields_json(buff + fwd, len - fwd, mpp, 1);
-	if (fwd >= len)
-		return len;
+	if ((rc = snprint_multipath_fields_json(buff, mpp, 1)) < 0)
+		return rc;
 
-	fwd +=  snprint_json(buff + fwd, len - fwd, 0, "\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = snprint_json(buff, 0, "\n")) < 0 ||
+	    (rc = snprint_json(buff, 0, PRINT_JSON_END_LAST)) < 0)
+		return rc;
 
-	fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_LAST);
-	if (fwd >= len)
-		return len;
-	return fwd;
+	return get_strbuf_len(buff) - initial_len;
 }
 
-int
-snprint_multipath_topology_json (char * buff, int len, const struct vectors * vecs)
+int snprint_multipath_topology_json (struct strbuf *buff,
+				     const struct vectors * vecs)
 {
-	int i, fwd = 0;
+	int i;
 	struct multipath * mpp;
+	size_t initial_len = get_strbuf_len(buff);
+	int rc;
 
-	fwd +=  snprint_json_header(buff, len);
-	if (fwd >= len)
-		return len;
-
-	fwd +=  snprint_json(buff + fwd, len  - fwd, 1, PRINT_JSON_START_MAPS);
-	if (fwd >= len)
-		return len;
+	if ((rc = snprint_json_header(buff)) < 0 ||
+	    (rc = snprint_json(buff, 1, PRINT_JSON_START_MAPS)) < 0)
+		return rc;
 
 	vector_foreach_slot(vecs->mpvec, mpp, i) {
-		fwd += snprint_multipath_fields_json(buff + fwd, len - fwd,
-				mpp, i + 1 == VECTOR_SIZE(vecs->mpvec));
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_multipath_fields_json(
+			     buff, mpp, i + 1 == VECTOR_SIZE(vecs->mpvec))) < 0)
+			return rc;
 	}
 
-	fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY);
-	if (fwd >= len)
-		return len;
+	if ((rc = snprint_json(buff, 0, PRINT_JSON_END_ARRAY)) < 0 ||
+	    (rc = snprint_json(buff, 0, PRINT_JSON_END_LAST)) < 0)
+		return rc;
 
-	fwd +=  snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_LAST);
-	if (fwd >= len)
-		return len;
-	return fwd;
+	return get_strbuf_len(buff) - initial_len;
 }
 
 static int
 snprint_hwentry (const struct config *conf,
-		 char * buff, int len, const struct hwentry * hwe)
+		 struct strbuf *buff, const struct hwentry * hwe)
 {
-	int i;
-	int fwd = 0;
+	int i, rc;
 	struct keyword * kw;
 	struct keyword * rootkw;
+	size_t initial_len = get_strbuf_len(buff);
 
 	rootkw = find_keyword(conf->keywords, NULL, "devices");
 
@@ -1445,57 +1303,53 @@ snprint_hwentry (const struct config *conf,
 	if (!rootkw)
 		return 0;
 
-	fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = append_strbuf_str(buff, "\tdevice {\n")) < 0)
+		return rc;
+
 	iterate_sub_keywords(rootkw, kw, i) {
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-				kw, hwe);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, hwe)) < 0)
+			return rc;
 	}
-	fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-	if (fwd >= len)
-		return len;
-	return fwd;
+	if ((rc = append_strbuf_str(buff, "\t}\n")) < 0)
+		return rc;
+	return get_strbuf_len(buff) - initial_len;
 }
 
-static int snprint_hwtable(const struct config *conf,
-			   char *buff, int len,
+static int snprint_hwtable(const struct config *conf, struct strbuf *buff,
 			   const struct _vector *hwtable)
 {
-	int fwd = 0;
-	int i;
+	int i, rc;
 	struct hwentry * hwe;
 	struct keyword * rootkw;
+	size_t initial_len = get_strbuf_len(buff);
 
 	rootkw = find_keyword(conf->keywords, NULL, "devices");
 	if (!rootkw)
 		return 0;
 
-	fwd += snprintf(buff + fwd, len - fwd, "devices {\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = append_strbuf_str(buff, "devices {\n")) < 0)
+		return rc;
+
 	vector_foreach_slot (hwtable, hwe, i) {
-		fwd += snprint_hwentry(conf, buff + fwd, len - fwd, hwe);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_hwentry(conf, buff, hwe)) < 0)
+			return rc;
 	}
-	fwd += snprintf(buff + fwd, len - fwd, "}\n");
-	if (fwd >= len)
-		return len;
-	return fwd;
+
+	if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+		return rc;
+
+	return get_strbuf_len(buff) - initial_len;
 }
 
 static int
-snprint_mpentry (const struct config *conf, char * buff, int len,
+snprint_mpentry (const struct config *conf, struct strbuf *buff,
 		 const struct mpentry * mpe, const struct _vector *mpvec)
 {
-	int i;
-	int fwd = 0;
+	int i, rc;
 	struct keyword * kw;
 	struct keyword * rootkw;
 	struct multipath *mpp = NULL;
+	size_t initial_len = get_strbuf_len(buff);
 
 	if (mpvec != NULL && (mpp = find_mp_by_wwid(mpvec, mpe->wwid)) == NULL)
 		return 0;
@@ -1504,49 +1358,44 @@ snprint_mpentry (const struct config *conf, char * buff, int len,
 	if (!rootkw)
 		return 0;
 
-	fwd += snprintf(buff + fwd, len - fwd, "\tmultipath {\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = append_strbuf_str(buff, "\tmultipath {\n")) < 0)
+		return rc;
+
 	iterate_sub_keywords(rootkw, kw, i) {
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-				kw, mpe);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, mpe)) < 0)
+			return rc;
 	}
 	/*
 	 * This mpp doesn't have alias defined. Add the alias in a comment.
 	 */
-	if (mpp != NULL && strcmp(mpp->alias, mpp->wwid)) {
-		fwd += snprintf(buff + fwd, len - fwd, "\t\t# alias \"%s\"\n",
-				mpp->alias);
-		if (fwd >= len)
-			return len;
-	}
-	fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-	if (fwd >= len)
-		return len;
-	return fwd;
+	if (mpp != NULL && strcmp(mpp->alias, mpp->wwid) &&
+	    (rc = print_strbuf(buff, "\t\t# alias \"%s\"\n", mpp->alias)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "\t}\n")) < 0)
+		return rc;
+
+	return get_strbuf_len(buff) - initial_len;
 }
 
-static int snprint_mptable(const struct config *conf,
-			   char *buff, int len, const struct _vector *mpvec)
+static int snprint_mptable(const struct config *conf, struct strbuf *buff,
+			   const struct _vector *mpvec)
 {
-	int fwd = 0;
-	int i;
+	int i, rc;
 	struct mpentry * mpe;
 	struct keyword * rootkw;
+	size_t initial_len = get_strbuf_len(buff);
 
 	rootkw = find_keyword(conf->keywords, NULL, "multipaths");
 	if (!rootkw)
 		return 0;
 
-	fwd += snprintf(buff + fwd, len - fwd, "multipaths {\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = append_strbuf_str(buff, "multipaths {\n")) < 0)
+		return rc;
+
 	vector_foreach_slot (conf->mptable, mpe, i) {
-		fwd += snprint_mpentry(conf, buff + fwd, len - fwd, mpe, mpvec);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_mpentry(conf, buff, mpe, mpvec)) < 0)
+			return rc;
 	}
 	if (mpvec != NULL) {
 		struct multipath *mpp;
@@ -1555,496 +1404,397 @@ static int snprint_mptable(const struct config *conf,
 			if (find_mpe(conf->mptable, mpp->wwid) != NULL)
 				continue;
 
-			fwd += snprintf(buff + fwd, len - fwd,
-					"\tmultipath {\n");
-			if (fwd >= len)
-				return len;
-			fwd += snprintf(buff + fwd, len - fwd,
-					"\t\twwid \"%s\"\n", mpp->wwid);
-			if (fwd >= len)
-				return len;
+			if ((rc = print_strbuf(buff,
+					       "\tmultipath {\n\t\twwid \"%s\"\n",
+					       mpp->wwid)) < 0)
+				return rc;
 			/*
 			 * This mpp doesn't have alias defined in
 			 * multipath.conf - otherwise find_mpe would have
 			 * found it. Add the alias in a comment.
 			 */
-			if (strcmp(mpp->alias, mpp->wwid)) {
-				fwd += snprintf(buff + fwd, len - fwd,
-						"\t\t# alias \"%s\"\n",
-						mpp->alias);
-				if (fwd >= len)
-					return len;
-			}
-			fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-			if (fwd >= len)
-				return len;
+			if (strcmp(mpp->alias, mpp->wwid) &&
+			    (rc = print_strbuf(buff, "\t\t# alias \"%s\"\n",
+					       mpp->alias)) < 0)
+				return rc;
+			if ((rc = append_strbuf_str(buff, "\t}\n")) < 0)
+				return rc;
 		}
 	}
-	fwd += snprintf(buff + fwd, len - fwd, "}\n");
-	if (fwd >= len)
-		return len;
-	return fwd;
+	if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+		return rc;
+	return get_strbuf_len(buff) - initial_len;
 }
 
-static int snprint_overrides(const struct config *conf, char * buff, int len,
+static int snprint_overrides(const struct config *conf, struct strbuf *buff,
 			     const struct hwentry *overrides)
 {
-	int fwd = 0;
-	int i;
+	int i, rc;
 	struct keyword *rootkw;
 	struct keyword *kw;
+	size_t initial_len = get_strbuf_len(buff);
 
 	rootkw = find_keyword(conf->keywords, NULL, "overrides");
 	if (!rootkw)
 		return 0;
 
-	fwd += snprintf(buff + fwd, len - fwd, "overrides {\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = append_strbuf_str(buff, "overrides {\n")) < 0)
+		return rc;
 	if (!overrides)
 		goto out;
+
 	iterate_sub_keywords(rootkw, kw, i) {
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, NULL);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, NULL)) < 0)
+			return rc;
 	}
 out:
-	fwd += snprintf(buff + fwd, len - fwd, "}\n");
-	if (fwd >= len)
-		return len;
-	return fwd;
+	if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+		return rc;
+	return get_strbuf_len(buff) - initial_len;
 }
 
-static int snprint_defaults(const struct config *conf, char *buff, int len)
+static int snprint_defaults(const struct config *conf, struct strbuf *buff)
 {
-	int fwd = 0;
-	int i;
+	int i, rc;
 	struct keyword *rootkw;
 	struct keyword *kw;
+	size_t initial_len = get_strbuf_len(buff);
 
 	rootkw = find_keyword(conf->keywords, NULL, "defaults");
 	if (!rootkw)
 		return 0;
 
-	fwd += snprintf(buff + fwd, len - fwd, "defaults {\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = append_strbuf_str(buff, "defaults {\n")) < 0)
+		return rc;
 
 	iterate_sub_keywords(rootkw, kw, i) {
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				kw, NULL);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, NULL)) < 0)
+			return rc;
 	}
-	fwd += snprintf(buff + fwd, len - fwd, "}\n");
-	if (fwd >= len)
-		return len;
-	return fwd;
+	if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+		return rc;
+	return get_strbuf_len(buff) - initial_len;
 }
 
-static int
-snprint_blacklist_group (char *buff, int len, int *fwd, vector *vec)
+static int snprint_blacklist_group(struct strbuf *buff, vector *vec)
 {
-	int threshold = MAX_LINE_LEN;
 	struct blentry * ble;
-	int pos;
-	int i;
+	size_t initial_len = get_strbuf_len(buff);
+	int rc, i;
 
-	pos = *fwd;
 	if (!VECTOR_SIZE(*vec)) {
-		if ((len - pos - threshold) <= 0)
-			return 0;
-		pos += snprintf(buff + pos, len - pos, "        <empty>\n");
+		if ((rc = append_strbuf_str(buff, "        <empty>\n")) < 0)
+			return rc;
 	} else vector_foreach_slot (*vec, ble, i) {
-		if ((len - pos - threshold) <= 0)
-			return 0;
-		if (ble->origin == ORIGIN_CONFIG)
-			pos += snprintf(buff + pos, len - pos, "        (config file rule) ");
-		else if (ble->origin == ORIGIN_DEFAULT)
-			pos += snprintf(buff + pos, len - pos, "        (default rule)     ");
-		pos += snprintf(buff + pos, len - pos, "%s\n", ble->str);
+		rc = print_strbuf(buff, "        %s %s\n",
+				   ble->origin == ORIGIN_CONFIG ?
+				   "(config file rule)" :
+				   "(default rule)    ", ble->str);
+		if (rc < 0)
+			return rc;
 	}
 
-	*fwd = pos;
-	return pos;
+	return get_strbuf_len(buff) - initial_len;
 }
 
 static int
-snprint_blacklist_devgroup (char *buff, int len, int *fwd, vector *vec)
+snprint_blacklist_devgroup (struct strbuf *buff, vector *vec)
 {
-	int threshold = MAX_LINE_LEN;
 	struct blentry_device * bled;
-	int pos;
-	int i;
+	size_t initial_len = get_strbuf_len(buff);
+	int rc, i;
 
-	pos = *fwd;
 	if (!VECTOR_SIZE(*vec)) {
-		if ((len - pos - threshold) <= 0)
-			return 0;
-		pos += snprintf(buff + pos, len - pos, "        <empty>\n");
+		if ((rc = append_strbuf_str(buff, "        <empty>\n")) < 0)
+			return rc;
 	} else vector_foreach_slot (*vec, bled, i) {
-		if ((len - pos - threshold) <= 0)
-			return 0;
-		if (bled->origin == ORIGIN_CONFIG)
-			pos += snprintf(buff + pos, len - pos, "        (config file rule) ");
-		else if (bled->origin == ORIGIN_DEFAULT)
-			pos += snprintf(buff + pos, len - pos, "        (default rule)     ");
-		pos += snprintf(buff + pos, len - pos, "%s:%s\n", bled->vendor, bled->product);
+		rc = print_strbuf(buff, "        %s %s:%s\n",
+				  bled->origin == ORIGIN_CONFIG ?
+				  "(config file rule)" :
+				  "(default rule)    ",
+				  bled->vendor, bled->product);
+		if (rc < 0)
+			return rc;
 	}
 
-	*fwd = pos;
-	return pos;
-}
-
-int snprint_blacklist_report(struct config *conf, char *buff, int len)
-{
-	int threshold = MAX_LINE_LEN;
-	int fwd = 0;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "device node rules:\n"
-					       "- blacklist:\n");
-	if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_devnode))
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-	if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_devnode) == 0)
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "udev property rules:\n"
-					       "- blacklist:\n");
-	if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_property))
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-	if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_property) == 0)
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "protocol rules:\n"
-					       "- blacklist:\n");
-	if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_protocol))
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-	if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_protocol) == 0)
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n"
-					       "- blacklist:\n");
-	if (snprint_blacklist_group(buff, len, &fwd, &conf->blist_wwid) == 0)
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-	if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_wwid) == 0)
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "device rules:\n"
-					       "- blacklist:\n");
-	if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->blist_device) == 0)
-		return len;
-
-	if ((len - fwd - threshold) <= 0)
-		return len;
-	fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n");
-	if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->elist_device) == 0)
-		return len;
-
-	if (fwd > len)
-		return len;
-	return fwd;
-}
-
-static int snprint_blacklist(const struct config *conf, char *buff, int len)
+	return get_strbuf_len(buff) - initial_len;
+}
+
+int snprint_blacklist_report(struct config *conf, struct strbuf *buff)
 {
-	int i;
+	size_t initial_len = get_strbuf_len(buff);
+	int rc;
+
+	if ((rc = append_strbuf_str(buff, "device node rules:\n- blacklist:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_group(buff, &conf->blist_devnode)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_group(buff, &conf->elist_devnode)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "udev property rules:\n- blacklist:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_group(buff, &conf->blist_property)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_group(buff, &conf->elist_property)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "protocol rules:\n- blacklist:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_group(buff, &conf->blist_protocol)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_group(buff, &conf->elist_protocol)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "wwid rules:\n- blacklist:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_group(buff, &conf->blist_wwid)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_group(buff, &conf->elist_wwid)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "device rules:\n- blacklist:\n")) < 0)
+		return rc;
+	if ((rc = snprint_blacklist_devgroup(buff, &conf->blist_device)) < 0)
+		return rc;
+
+	if ((rc = append_strbuf_str(buff, "- exceptions:\n")) < 0)
+	     return rc;
+	if ((rc = snprint_blacklist_devgroup(buff, &conf->elist_device)) < 0)
+		return rc;
+
+	return get_strbuf_len(buff) - initial_len;
+}
+
+static int snprint_blacklist(const struct config *conf, struct strbuf *buff)
+{
+	int i, rc;
 	struct blentry * ble;
 	struct blentry_device * bled;
-	int fwd = 0;
 	struct keyword *rootkw;
 	struct keyword *kw;
+	size_t initial_len = get_strbuf_len(buff);
 
 	rootkw = find_keyword(conf->keywords, NULL, "blacklist");
 	if (!rootkw)
 		return 0;
 
-	fwd += snprintf(buff + fwd, len - fwd, "blacklist {\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = append_strbuf_str(buff, "blacklist {\n")) < 0)
+		return rc;
 
 	vector_foreach_slot (conf->blist_devnode, ble, i) {
 		kw = find_keyword(conf->keywords, rootkw->sub, "devnode");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, ble);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ble)) < 0)
+			return rc;
 	}
 	vector_foreach_slot (conf->blist_wwid, ble, i) {
 		kw = find_keyword(conf->keywords, rootkw->sub, "wwid");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, ble);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ble)) < 0)
+			return rc;
 	}
 	vector_foreach_slot (conf->blist_property, ble, i) {
 		kw = find_keyword(conf->keywords, rootkw->sub, "property");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, ble);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ble)) < 0)
+			return rc;
 	}
 	vector_foreach_slot (conf->blist_protocol, ble, i) {
 		kw = find_keyword(conf->keywords, rootkw->sub, "protocol");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, ble);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ble)) < 0)
+			return rc;
 	}
+
 	rootkw = find_keyword(conf->keywords, rootkw->sub, "device");
 	if (!rootkw)
 		return 0;
 
 	vector_foreach_slot (conf->blist_device, bled, i) {
-		fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
-		if (fwd >= len)
-			return len;
+		if ((rc = append_strbuf_str(buff, "\tdevice {\n")) < 0)
+			return rc;
+
 		kw = find_keyword(conf->keywords, rootkw->sub, "vendor");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-				       kw, bled);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, bled)) < 0)
+			return rc;
 		kw = find_keyword(conf->keywords, rootkw->sub, "product");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-				       kw, bled);
-		if (fwd >= len)
-			return len;
-		fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff,
+					  "\t\t%k %v\n\t}\n", kw, bled)) < 0)
+			return rc;
 	}
-	fwd += snprintf(buff + fwd, len - fwd, "}\n");
-	if (fwd >= len)
-		return len;
-	return fwd;
+
+	if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+		return rc;
+	return get_strbuf_len(buff) - initial_len;
 }
 
 static int snprint_blacklist_except(const struct config *conf,
-				    char *buff, int len)
+				    struct strbuf *buff)
 {
-	int i;
+	int i, rc;
 	struct blentry * ele;
 	struct blentry_device * eled;
-	int fwd = 0;
 	struct keyword *rootkw;
 	struct keyword *kw;
+	size_t initial_len = get_strbuf_len(buff);
 
 	rootkw = find_keyword(conf->keywords, NULL, "blacklist_exceptions");
 	if (!rootkw)
 		return 0;
 
-	fwd += snprintf(buff + fwd, len - fwd, "blacklist_exceptions {\n");
-	if (fwd >= len)
-		return len;
+	if ((rc = append_strbuf_str(buff, "blacklist_exceptions {\n")) < 0)
+		return rc;
 
 	vector_foreach_slot (conf->elist_devnode, ele, i) {
 		kw = find_keyword(conf->keywords, rootkw->sub, "devnode");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, ele);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ele)) < 0)
+			return rc;
 	}
 	vector_foreach_slot (conf->elist_wwid, ele, i) {
 		kw = find_keyword(conf->keywords, rootkw->sub, "wwid");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, ele);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ele)) < 0)
+			return rc;
 	}
 	vector_foreach_slot (conf->elist_property, ele, i) {
 		kw = find_keyword(conf->keywords, rootkw->sub, "property");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, ele);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ele)) < 0)
+			return rc;
 	}
 	vector_foreach_slot (conf->elist_protocol, ele, i) {
 		kw = find_keyword(conf->keywords, rootkw->sub, "protocol");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n",
-				       kw, ele);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, ele)) < 0)
+			return rc;
 	}
+
 	rootkw = find_keyword(conf->keywords, rootkw->sub, "device");
 	if (!rootkw)
 		return 0;
 
 	vector_foreach_slot (conf->elist_device, eled, i) {
-		fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n");
-		if (fwd >= len)
-			return len;
+		if ((rc = append_strbuf_str(buff, "\tdevice {\n")) < 0)
+			return rc;
+
 		kw = find_keyword(conf->keywords, rootkw->sub, "vendor");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-				       kw, eled);
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, eled)) < 0)
+			return rc;
 		kw = find_keyword(conf->keywords, rootkw->sub, "product");
 		if (!kw)
 			return 0;
-		fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n",
-				       kw, eled);
-		if (fwd >= len)
-			return len;
-		fwd += snprintf(buff + fwd, len - fwd, "\t}\n");
-		if (fwd >= len)
-			return len;
+		if ((rc = snprint_keyword(buff, "\t\t%k %v\n\t}\n",
+					  kw, eled)) < 0)
+			return rc;
 	}
-	fwd += snprintf(buff + fwd, len - fwd, "}\n");
-	if (fwd >= len)
-		return len;
-	return fwd;
+
+	if ((rc = append_strbuf_str(buff, "}\n")) < 0)
+		return rc;
+	return get_strbuf_len(buff) - initial_len;
 }
 
 char *snprint_config(const struct config *conf, int *len,
 		     const struct _vector *hwtable, const struct _vector *mpvec)
 {
+	STRBUF_ON_STACK(buff);
 	char *reply;
-	/* built-in config is >20kB already */
-	unsigned int maxlen = 32768;
-
-	for (reply = NULL; maxlen <= UINT_MAX/2; maxlen *= 2) {
-		char *c, *tmp = reply;
-
-		reply = REALLOC(reply, maxlen);
-		if (!reply) {
-			if (tmp)
-				free(tmp);
+	int rc;
+
+	if ((rc = snprint_defaults(conf, &buff)) < 0 ||
+	    (rc = snprint_blacklist(conf, &buff)) < 0 ||
+	    (rc = snprint_blacklist_except(conf, &buff)) < 0 ||
+	    (rc = snprint_hwtable(conf, &buff,
+				  hwtable ? hwtable : conf->hwtable)) < 0 ||
+	    (rc = snprint_overrides(conf, &buff, conf->overrides)) < 0)
+		return NULL;
+	if (VECTOR_SIZE(conf->mptable) > 0 ||
+	    (mpvec != NULL && VECTOR_SIZE(mpvec) > 0))
+		if ((rc = snprint_mptable(conf, &buff, mpvec)) < 0)
 			return NULL;
-		}
-
-		c = reply + snprint_defaults(conf, reply, maxlen);
-		if (c == reply + maxlen)
-			continue;
 
-		c += snprint_blacklist(conf, c, reply + maxlen - c);
-		if (c == reply + maxlen)
-			continue;
-
-		c += snprint_blacklist_except(conf, c, reply + maxlen - c);
-		if (c == reply + maxlen)
-			continue;
-
-		c += snprint_hwtable(conf, c, reply + maxlen - c,
-				     hwtable ? hwtable : conf->hwtable);
-		if (c == reply + maxlen)
-			continue;
+	if (len)
+		*len = get_strbuf_len(&buff);
+	reply = steal_strbuf_str(&buff);
 
-		c += snprint_overrides(conf, c, reply + maxlen - c,
-				       conf->overrides);
-		if (c == reply + maxlen)
-			continue;
-
-		if (VECTOR_SIZE(conf->mptable) > 0 ||
-		    (mpvec != NULL && VECTOR_SIZE(mpvec) > 0))
-			c += snprint_mptable(conf, c, reply + maxlen - c,
-					     mpvec);
-
-		if (c < reply + maxlen) {
-			if (len)
-				*len = c - reply;
-			return reply;
-		}
-	}
-
-	free(reply);
-	return NULL;
+	return reply;
 }
 
-int snprint_status(char *buff, int len, const struct vectors *vecs)
+int snprint_status(struct strbuf *buff, const struct vectors *vecs)
 {
-	int fwd = 0;
-	int i;
+	int i, rc;
 	unsigned int count[PATH_MAX_STATE] = {0};
+	int monitored_count = 0;
 	struct path * pp;
+	size_t initial_len = get_strbuf_len(buff);
 
 	vector_foreach_slot (vecs->pathvec, pp, i) {
 		count[pp->state]++;
 	}
-	fwd += snprintf(buff + fwd, len - fwd, "path checker states:\n");
-	for (i=0; i<PATH_MAX_STATE; i++) {
+	if ((rc = append_strbuf_str(buff, "path checker states:\n")) < 0)
+		return rc;
+	for (i = 0; i < PATH_MAX_STATE; i++) {
 		if (!count[i])
 			continue;
-		fwd += snprintf(buff + fwd, len - fwd, "%-20s%u\n",
-				checker_state_name(i), count[i]);
+		if ((rc = print_strbuf(buff, "%-20s%u\n",
+				       checker_state_name(i), count[i])) < 0)
+			return rc;
 	}
 
-	int monitored_count = 0;
-
 	vector_foreach_slot(vecs->pathvec, pp, i)
 		if (pp->fd >= 0)
 			monitored_count++;
-	fwd += snprintf(buff + fwd, len - fwd, "\npaths: %d\nbusy: %s\n",
-			monitored_count, is_uevent_busy()? "True" : "False");
+	if ((rc = print_strbuf(buff, "\npaths: %d\nbusy: %s\n",
+			       monitored_count,
+			       is_uevent_busy()? "True" : "False")) < 0)
+		return rc;
 
-	if (fwd >= len)
-		return len;
-	return fwd;
+	return get_strbuf_len(buff) - initial_len;
 }
 
-int snprint_devices(struct config *conf, char *buff, size_t len,
+int snprint_devices(struct config *conf, struct strbuf *buff,
 		    const struct vectors *vecs)
 {
-	size_t fwd = 0;
 	int r;
 	struct udev_enumerate *enm;
 	struct udev_list_entry *item, *first;
-
 	struct path * pp;
+	size_t initial_len = get_strbuf_len(buff);
 
 	enm = udev_enumerate_new(udev);
 	if (!enm)
 		return 1;
 	udev_enumerate_add_match_subsystem(enm, "block");
 
-	fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n");
+	if ((r = append_strbuf_str(buff, "available block devices:\n")) < 0)
+		goto out;
 	r = udev_enumerate_scan_devices(enm);
 	if (r < 0)
 		goto out;
@@ -2066,10 +1816,6 @@ int snprint_devices(struct config *conf, char *buff, size_t len,
 			continue;
 		}
 
-		fwd += snprintf(buff + fwd, len - fwd, "    %s", devname);
-		if (fwd >= len)
-			break;
-
 		pp = find_path_by_dev(vecs->pathvec, devname);
 		if (!pp) {
 			const char *hidden;
@@ -2092,41 +1838,27 @@ int snprint_devices(struct config *conf, char *buff, size_t len,
 		} else
 			status = " devnode whitelisted, monitored";
 
-		fwd += snprintf(buff + fwd, len - fwd, " %s\n", status);
+		r = print_strbuf(buff, "    %s %s\n", devname, status);
 		udev_device_unref(u_dev);
-		if (fwd >= len)
+		if (r < 0)
 			break;
 	}
 out:
 	udev_enumerate_unref(enm);
+	if (r < 0)
+		return r;
 
-	if (fwd >= len)
-		return len;
-	return fwd;
+	return get_strbuf_len(buff) - initial_len;
 }
 
 /*
  * stdout printing helpers
  */
-void print_path(struct path *pp, char *style)
-{
-	char line[MAX_LINE_LEN];
-
-	memset(&line[0], 0, MAX_LINE_LEN);
-	snprint_path(&line[0], MAX_LINE_LEN, style, pp, 1);
-	printf("%s", line);
-}
-
-void print_all_paths(vector pathvec, int banner)
-{
-	print_all_paths_custo(pathvec, banner, PRINT_PATH_LONG);
-}
-
-void print_all_paths_custo(vector pathvec, int banner, char *fmt)
+static void print_all_paths_custo(vector pathvec, int banner, const char *fmt)
 {
 	int i;
 	struct path * pp;
-	char line[MAX_LINE_LEN];
+	STRBUF_ON_STACK(line);
 
 	if (!VECTOR_SIZE(pathvec)) {
 		if (banner)
@@ -2135,12 +1867,18 @@ void print_all_paths_custo(vector pathvec, int banner, char *fmt)
 	}
 
 	if (banner)
-		fprintf(stdout, "===== paths list =====\n");
+		append_strbuf_str(&line, "===== paths list =====\n");
 
 	get_path_layout(pathvec, 1);
-	snprint_path_header(line, MAX_LINE_LEN, fmt);
-	fprintf(stdout, "%s", line);
+	snprint_path_header(&line, fmt);
 
 	vector_foreach_slot (pathvec, pp, i)
-		print_path(pp, fmt);
+		snprint_path(&line, fmt, pp, 1);
+
+	printf("%s", get_strbuf_str(&line));
+}
+
+void print_all_paths(vector pathvec, int banner)
+{
+	print_all_paths_custo(pathvec, banner, PRINT_PATH_LONG);
 }
diff --git a/libmultipath/print.h b/libmultipath/print.h
index 0042cef..b922812 100644
--- a/libmultipath/print.h
+++ b/libmultipath/print.h
@@ -2,6 +2,8 @@
 #define _PRINT_H
 #include "dm-generic.h"
 
+struct strbuf;
+
 #define PRINT_PATH_LONG      "%w %i %d %D %p %t %T %s %o"
 #define PRINT_PATH_INDENT    "%i %d %D %t %T %o"
 #define PRINT_PATH_CHECKER   "%i %d %D %p %t %T %o %C"
@@ -25,7 +27,7 @@
 #define PRINT_JSON_END_LAST_ELEM  "}"
 #define PRINT_JSON_END_LAST       "}\n"
 #define PRINT_JSON_END_ARRAY      "]\n"
-#define PRINT_JSON_INDENT    "   "
+#define PRINT_JSON_INDENT_N    3
 #define PRINT_JSON_MAP       "{\n" \
 			     "      \"name\" : \"%n\",\n" \
 			     "      \"uuid\" : \"%w\",\n" \
@@ -79,21 +81,21 @@ struct path_data {
 	char wildcard;
 	char * header;
 	unsigned int width;
-	int (*snprint)(char * buff, size_t len, const struct path * pp);
+	int (*snprint)(struct strbuf *, const struct path * pp);
 };
 
 struct multipath_data {
 	char wildcard;
 	char * header;
 	unsigned int width;
-	int (*snprint)(char * buff, size_t len, const struct multipath * mpp);
+	int (*snprint)(struct strbuf *, const struct multipath * mpp);
 };
 
 struct pathgroup_data {
 	char wildcard;
 	char * header;
 	unsigned int width;
-	int (*snprint)(char * buff, size_t len, const struct pathgroup * pgp);
+	int (*snprint)(struct strbuf *, const struct pathgroup * pgp);
 };
 
 enum layout_reset {
@@ -106,37 +108,35 @@ void _get_path_layout (const struct _vector *gpvec, enum layout_reset);
 void get_path_layout (vector pathvec, int header);
 void _get_multipath_layout (const struct _vector *gmvec, enum layout_reset);
 void get_multipath_layout (vector mpvec, int header);
-int snprint_path_header (char *, int, const char *);
-int snprint_multipath_header (char *, int, const char *);
-int _snprint_path (const struct gen_path *, char *, int, const char *, int);
-#define snprint_path(buf, len, fmt, pp, v) \
-	_snprint_path(dm_path_to_gen(pp), buf, len, fmt,  v)
-int _snprint_multipath (const struct gen_multipath *, char *, int,
+int snprint_path_header(struct strbuf *, const char *);
+int snprint_multipath_header(struct strbuf *, const char *);
+int _snprint_path (const struct gen_path *, struct strbuf *, const char *, int);
+#define snprint_path(buf, fmt, pp, v) \
+	_snprint_path(dm_path_to_gen(pp), buf, fmt,  v)
+int _snprint_multipath (const struct gen_multipath *, struct strbuf *,
 			const char *, int);
-#define snprint_multipath(buf, len, fmt, mp, v)				\
-	_snprint_multipath(dm_multipath_to_gen(mp), buf, len, fmt,  v)
-int _snprint_multipath_topology (const struct gen_multipath *, char *, int,
+#define snprint_multipath(buf, fmt, mp, v)				\
+	_snprint_multipath(dm_multipath_to_gen(mp), buf, fmt,  v)
+int _snprint_multipath_topology (const struct gen_multipath *, struct strbuf *,
 				 int verbosity);
-#define snprint_multipath_topology(buf, len, mpp, v) \
-	_snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, len, v)
-int snprint_multipath_topology_json (char * buff, int len,
-				const struct vectors * vecs);
+#define snprint_multipath_topology(buf, mpp, v) \
+	_snprint_multipath_topology (dm_multipath_to_gen(mpp), buf, v)
+int snprint_multipath_topology_json(struct strbuf *, const struct vectors *vecs);
 char *snprint_config(const struct config *conf, int *len,
 		     const struct _vector *hwtable,
 		     const struct _vector *mpvec);
-int snprint_multipath_map_json (char * buff, int len,
-				const struct multipath * mpp);
-int snprint_blacklist_report (struct config *, char *, int);
-int snprint_wildcards (char *, int);
-int snprint_status (char *, int, const struct vectors *);
-int snprint_devices (struct config *, char *, size_t, const struct vectors *);
-int snprint_path_serial (char *, size_t, const struct path *);
-int snprint_host_wwnn (char *, size_t, const struct path *);
-int snprint_host_wwpn (char *, size_t, const struct path *);
-int snprint_tgt_wwnn (char *, size_t, const struct path *);
-int snprint_tgt_wwpn (char *, size_t, const struct path *);
+int snprint_multipath_map_json(struct strbuf *, const struct multipath *mpp);
+int snprint_blacklist_report(struct config *, struct strbuf *);
+int snprint_wildcards(struct strbuf *);
+int snprint_status(struct strbuf *, const struct vectors *);
+int snprint_devices(struct config *, struct strbuf *, const struct vectors *);
+int snprint_path_serial(struct strbuf *, const struct path *);
+int snprint_host_wwnn(struct strbuf *, const struct path *);
+int snprint_host_wwpn(struct strbuf *, const struct path *);
+int snprint_tgt_wwnn(struct strbuf *, const struct path *);
+int snprint_tgt_wwpn(struct strbuf *, const struct path *);
 #define PROTOCOL_BUF_SIZE sizeof("scsi:unspec")
-int snprint_path_protocol(char *, size_t, const struct path *);
+int snprint_path_protocol(struct strbuf *, const struct path *);
 
 void _print_multipath_topology (const struct gen_multipath * gmp,
 				int verbosity);
@@ -144,14 +144,13 @@ void _print_multipath_topology (const struct gen_multipath * gmp,
 	_print_multipath_topology(dm_multipath_to_gen(mpp), v)
 
 void print_all_paths (vector pathvec, int banner);
-void print_all_paths_custo (vector pathvec, int banner, char *fmt);
 
 int snprint_path_attr(const struct gen_path* gp,
-		      char *buf, int len, char wildcard);
+		      struct strbuf *buf, char wildcard);
 int snprint_pathgroup_attr(const struct gen_pathgroup* gpg,
-			   char *buf, int len, char wildcard);
+			   struct strbuf *buf, char wildcard);
 int snprint_multipath_attr(const struct gen_multipath* gm,
-			   char *buf, int len, char wildcard);
+			   struct strbuf *buf, char wildcard);
 int snprint_multipath_style(const struct gen_multipath *gmp,
-			    char *style, int len, int verbosity);
+			    struct strbuf *style, int verbosity);
 #endif /* _PRINT_H */
diff --git a/libmultipath/prioritizers/weightedpath.c b/libmultipath/prioritizers/weightedpath.c
index 650088b..ea03fc3 100644
--- a/libmultipath/prioritizers/weightedpath.c
+++ b/libmultipath/prioritizers/weightedpath.c
@@ -35,52 +35,37 @@
 #include "structs_vec.h"
 #include "print.h"
 #include "util.h"
-
-#define CHECK_LEN \
-do { \
-	if ((p - str) >= (len - 1)) { \
-		condlog(0, "%s: %s - buffer size too small", pp->dev, pp->prio.name); \
-		return -1; \
-	} \
-} while(0)
+#include "strbuf.h"
 
 static int
-build_serial_path(struct path *pp, char *str, int len)
+build_serial_path(struct path *pp, struct strbuf *buf)
 {
-	char *p = str;
+	int rc = snprint_path_serial(buf, pp);
 
-	p += snprint_path_serial(p, str + len - p, pp);
-	CHECK_LEN;
-	return 0;
+	return rc < 0 ? rc : 0;
 }
 
 static int
-build_wwn_path(struct path *pp, char *str, int len)
+build_wwn_path(struct path *pp, struct strbuf *buf)
 {
-	char *p = str;
-
-	p += snprint_host_wwnn(p, str + len - p, pp);
-	CHECK_LEN;
-	p += snprintf(p, str + len - p, ":");
-	CHECK_LEN;
-	p += snprint_host_wwpn(p, str + len - p, pp);
-	CHECK_LEN;
-	p += snprintf(p, str + len - p, ":");
-	CHECK_LEN;
-	p += snprint_tgt_wwnn(p, str + len - p, pp);
-	CHECK_LEN;
-	p += snprintf(p, str + len - p, ":");
-	CHECK_LEN;
-	p += snprint_tgt_wwpn(p, str + len - p, pp);
-	CHECK_LEN;
+	int rc;
+
+	if ((rc = snprint_host_wwnn(buf, pp)) < 0 ||
+	    (rc = fill_strbuf(buf, ':', 1)) < 0 ||
+	    (rc = snprint_host_wwpn(buf, pp)) < 0 ||
+	    (rc = fill_strbuf(buf, ':', 1)) < 0 ||
+	    (rc = snprint_tgt_wwnn(buf, pp) < 0) ||
+	    (rc = fill_strbuf(buf, ':', 1) < 0) ||
+	    (rc = snprint_tgt_wwpn(buf, pp) < 0))
+		return rc;
 	return 0;
 }
 
 /* main priority routine */
 int prio_path_weight(struct path *pp, char *prio_args)
 {
-	char path[FILE_NAME_SIZE];
-	char *arg;
+	STRBUF_ON_STACK(path);
+	char *arg __attribute__((cleanup(cleanup_charp))) = NULL;
 	char *temp, *regex, *prio;
 	char split_char[] = " \t";
 	int priority = DEFAULT_PRIORITY, path_found = 0;
@@ -101,24 +86,22 @@ int prio_path_weight(struct path *pp, char *prio_args)
 	}
 
 	if (!strcmp(regex, HBTL)) {
-		sprintf(path, "%d:%d:%d:%" PRIu64, pp->sg_id.host_no,
-			pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun);
+		if (print_strbuf(&path, "%d:%d:%d:%" PRIu64, pp->sg_id.host_no,
+				 pp->sg_id.channel, pp->sg_id.scsi_id,
+				 pp->sg_id.lun) < 0)
+			return priority;
 	} else if (!strcmp(regex, DEV_NAME)) {
-		strcpy(path, pp->dev);
+		if (append_strbuf_str(&path, pp->dev) < 0)
+			return priority;
 	} else if (!strcmp(regex, SERIAL)) {
-		if (build_serial_path(pp, path, FILE_NAME_SIZE) != 0) {
-			FREE(arg);
+		if (build_serial_path(pp, &path) != 0)
 			return priority;
-		}
 	} else if (!strcmp(regex, WWN)) {
-		if (build_wwn_path(pp, path, FILE_NAME_SIZE) != 0) {
-			FREE(arg);
+		if (build_wwn_path(pp, &path) != 0)
 			return priority;
-		}
 	} else {
 		condlog(0, "%s: %s - Invalid arguments", pp->dev,
 			pp->prio.name);
-		FREE(arg);
 		return priority;
 	}
 
@@ -131,7 +114,8 @@ int prio_path_weight(struct path *pp, char *prio_args)
 			break;
 
 		if (!regcomp(&pathe, regex, REG_EXTENDED|REG_NOSUB)) {
-			if (!regexec(&pathe, path, 0, NULL, 0)) {
+			if (!regexec(&pathe, get_strbuf_str(&path), 0,
+				     NULL, 0)) {
 				path_found = 1;
 				priority = atoi(prio);
 			}
@@ -139,7 +123,6 @@ int prio_path_weight(struct path *pp, char *prio_args)
 		}
 	}
 
-	FREE(arg);
 	return priority;
 }
 
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index bce40b1..6d3a0ae 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -30,6 +30,7 @@
 #include "cli.h"
 #include "uevent.h"
 #include "foreign.h"
+#include "strbuf.h"
 #include "cli_handlers.h"
 
 #define SET_REPLY_AND_LEN(__rep, __len, string_literal)			\
@@ -42,49 +43,30 @@ int
 show_paths (char ** r, int * len, struct vectors * vecs, char * style,
 	    int pretty)
 {
+	STRBUF_ON_STACK(reply);
 	int i;
 	struct path * pp;
-	char * c;
-	char * reply, * header;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
+	int hdr_len = 0;
 
 	get_path_layout(vecs->pathvec, 1);
 	foreign_path_layout();
 
-	reply = MALLOC(maxlen);
+	if (pretty && (hdr_len = snprint_path_header(&reply, style)) < 0)
+		return 1;
 
-	while (again) {
-		if (!reply)
+	vector_foreach_slot(vecs->pathvec, pp, i) {
+		if (snprint_path(&reply, style, pp, pretty) < 0)
 			return 1;
-
-		c = reply;
-
-		if (pretty)
-			c += snprint_path_header(c, reply + maxlen - c,
-						 style);
-		header = c;
-
-		vector_foreach_slot(vecs->pathvec, pp, i)
-			c += snprint_path(c, reply + maxlen - c,
-					  style, pp, pretty);
-
-		c += snprint_foreign_paths(c, reply + maxlen - c,
-					   style, pretty);
-
-		again = (c == reply + maxlen - 1);
-
-		REALLOC_REPLY(reply, again, maxlen);
 	}
+	if (snprint_foreign_paths(&reply, style, pretty) < 0)
+		return 1;
 
-	if (pretty && c == header) {
+	if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len)
 		/* No output - clear header */
-		*reply = '\0';
-		c = reply;
-	}
+		truncate_strbuf(&reply, 0);
 
-	*r = reply;
-	*len = (int)(c - reply + 1);
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
@@ -92,28 +74,14 @@ int
 show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
 	   char * style)
 {
-	char * c;
-	char * reply;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
+	STRBUF_ON_STACK(reply);
 
 	get_path_layout(vecs->pathvec, 1);
-	reply = MALLOC(maxlen);
-
-	while (again) {
-		if (!reply)
-			return 1;
-
-		c = reply;
-
-		c += snprint_path(c, reply + maxlen - c, style, pp, 0);
-
-		again = (c == reply + maxlen - 1);
+	if (snprint_path(&reply, style, pp, 0) < 0)
+		return 1;
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 
-		REALLOC_REPLY(reply, again, maxlen);
-	}
-	*r = reply;
-	*len = (int)(c - reply + 1);
 	return 0;
 }
 
@@ -121,84 +89,51 @@ int
 show_map_topology (char ** r, int * len, struct multipath * mpp,
 		   struct vectors * vecs)
 {
-	char * c;
-	char * reply;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
+	STRBUF_ON_STACK(reply);
 
 	if (update_multipath(vecs, mpp->alias, 0))
 		return 1;
-	reply = MALLOC(maxlen);
 
-	while (again) {
-		if (!reply)
-			return 1;
-
-		c = reply;
-
-		c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2);
-		again = (c == reply + maxlen - 1);
+	if (snprint_multipath_topology(&reply, mpp, 2) < 0)
+		return 1;
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 
-		REALLOC_REPLY(reply, again, maxlen);
-	}
-	*r = reply;
-	*len = (int)(c - reply + 1);
 	return 0;
 }
 
 int
 show_maps_topology (char ** r, int * len, struct vectors * vecs)
 {
+	STRBUF_ON_STACK(reply);
 	int i;
 	struct multipath * mpp;
-	char * c;
-	char * reply;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
 
 	get_path_layout(vecs->pathvec, 0);
 	foreign_path_layout();
 
-	reply = MALLOC(maxlen);
-
-	while (again) {
-		if (!reply)
-			return 1;
-
-		c = reply;
-
-		vector_foreach_slot(vecs->mpvec, mpp, i) {
-			if (update_multipath(vecs, mpp->alias, 0)) {
-				i--;
-				continue;
-			}
-			c += snprint_multipath_topology(c, reply + maxlen - c,
-							mpp, 2);
+	vector_foreach_slot(vecs->mpvec, mpp, i) {
+		if (update_multipath(vecs, mpp->alias, 0)) {
+			i--;
+			continue;
 		}
-		c += snprint_foreign_topology(c, reply + maxlen - c, 2);
-
-		again = (c == reply + maxlen - 1);
-
-		REALLOC_REPLY(reply, again, maxlen);
+		if (snprint_multipath_topology(&reply, mpp, 2) < 0)
+			return 1;
 	}
+	if (snprint_foreign_topology(&reply, 2) < 0)
+		return 1;
 
-	*r = reply;
-	*len = (int)(c - reply + 1);
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 int
 show_maps_json (char ** r, int * len, struct vectors * vecs)
 {
+	STRBUF_ON_STACK(reply);
 	int i;
 	struct multipath * mpp;
-	char * c;
-	char * reply;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
-
-	if (VECTOR_SIZE(vecs->mpvec) > 0)
-		maxlen *= PRINT_JSON_MULTIPLIER * VECTOR_SIZE(vecs->mpvec);
 
 	vector_foreach_slot(vecs->mpvec, mpp, i) {
 		if (update_multipath(vecs, mpp->alias, 0)) {
@@ -206,21 +141,11 @@ show_maps_json (char ** r, int * len, struct vectors * vecs)
 		}
 	}
 
-	reply = MALLOC(maxlen);
-
-	while (again) {
-		if (!reply)
-			return 1;
-
-		c = reply;
-
-		c += snprint_multipath_topology_json(c, maxlen, vecs);
-		again = (c == reply + maxlen);
+	if (snprint_multipath_topology_json(&reply, vecs) < 0)
+		return 1;
 
-		REALLOC_REPLY(reply, again, maxlen);
-	}
-	*r = reply;
-	*len = (int)(c - reply);
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
@@ -228,28 +153,16 @@ int
 show_map_json (char ** r, int * len, struct multipath * mpp,
 		   struct vectors * vecs)
 {
-	char * c;
-	char * reply;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
+	STRBUF_ON_STACK(reply);
 
 	if (update_multipath(vecs, mpp->alias, 0))
 		return 1;
-	reply = MALLOC(maxlen);
-
-	while (again) {
-		if (!reply)
-			return 1;
 
-		c = reply;
-
-		c += snprint_multipath_map_json(c, maxlen, mpp);
-		again = (c == reply + maxlen);
+	if (snprint_multipath_map_json(&reply, mpp) < 0)
+		return 1;
 
-		REALLOC_REPLY(reply, again, maxlen);
-	}
-	*r = reply;
-	*len = (int)(c - reply);
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
@@ -420,58 +333,40 @@ cli_list_maps_json (void * v, char ** reply, int * len, void * data)
 int
 cli_list_wildcards (void * v, char ** reply, int * len, void * data)
 {
-	char * c;
-
-	*reply = MALLOC(INITIAL_REPLY_LEN);
+	STRBUF_ON_STACK(buf);
 
-	if (!*reply)
+	if (snprint_wildcards(&buf) < 0)
 		return 1;
 
-	c = *reply;
-	c += snprint_wildcards(c, INITIAL_REPLY_LEN);
-
-	*len = INITIAL_REPLY_LEN;
+	*len = get_strbuf_len(&buf) + 1;
+	*reply = steal_strbuf_str(&buf);
 	return 0;
 }
 
 int
 show_status (char ** r, int *len, struct vectors * vecs)
 {
-	char * c;
-	char * reply;
-
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	reply = MALLOC(maxlen);
+	STRBUF_ON_STACK(reply);
 
-	if (!reply)
+	if (snprint_status(&reply, vecs) < 0)
 		return 1;
 
-	c = reply;
-	c += snprint_status(c, reply + maxlen - c, vecs);
-
-	*r = reply;
-	*len = (int)(c - reply + 1);
+	*len = get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
 int
 show_daemon (char ** r, int *len)
 {
-	char * c;
-	char * reply;
-
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	reply = MALLOC(maxlen);
+	STRBUF_ON_STACK(reply);
 
-	if (!reply)
+	if (print_strbuf(&reply, "pid %d %s\n",
+			 daemon_pid, daemon_status()) < 0)
 		return 1;
 
-	c = reply;
-	c += snprintf(c, INITIAL_REPLY_LEN, "pid %d %s\n",
-		      daemon_pid, daemon_status());
-
-	*r = reply;
-	*len = (int)(c - reply + 1);
+	*len = get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
@@ -479,26 +374,13 @@ int
 show_map (char ** r, int *len, struct multipath * mpp, char * style,
 	  int pretty)
 {
-	char * c;
-	char * reply;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
-
-	reply = MALLOC(maxlen);
-	while (again) {
-		if (!reply)
-			return 1;
+	STRBUF_ON_STACK(reply);
 
-		c = reply;
-		c += snprint_multipath(c, reply + maxlen - c, style,
-				       mpp, pretty);
-
-		again = (c == reply + maxlen - 1);
+	if (snprint_multipath(&reply, style, mpp, pretty) < 0)
+		return 1;
 
-		REALLOC_REPLY(reply, again, maxlen);
-	}
-	*r = reply;
-	*len = (int)(c - reply + 1);
+	*len = get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
@@ -506,51 +388,34 @@ int
 show_maps (char ** r, int *len, struct vectors * vecs, char * style,
 	   int pretty)
 {
+	STRBUF_ON_STACK(reply);
 	int i;
 	struct multipath * mpp;
-	char * c, *header;
-	char * reply;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
+	int hdr_len = 0;
 
 	get_multipath_layout(vecs->mpvec, 1);
 	foreign_multipath_layout();
 
-	reply = MALLOC(maxlen);
-
-	while (again) {
-		if (!reply)
-			return 1;
-
-		c = reply;
-		if (pretty)
-			c += snprint_multipath_header(c, reply + maxlen - c,
-						      style);
-		header = c;
-
-		vector_foreach_slot(vecs->mpvec, mpp, i) {
-			if (update_multipath(vecs, mpp->alias, 0)) {
-				i--;
-				continue;
-			}
-			c += snprint_multipath(c, reply + maxlen - c,
-					       style, mpp, pretty);
+	if (pretty && (hdr_len = snprint_multipath_header(&reply, style)) < 0)
+		return 1;
 
+	vector_foreach_slot(vecs->mpvec, mpp, i) {
+		if (update_multipath(vecs, mpp->alias, 0)) {
+			i--;
+			continue;
 		}
-		c += snprint_foreign_multipaths(c, reply + maxlen - c,
-						style, pretty);
-		again = (c == reply + maxlen - 1);
-
-		REALLOC_REPLY(reply, again, maxlen);
+		if (snprint_multipath(&reply, style, mpp, pretty) < 0)
+			return 1;
 	}
+	if (snprint_foreign_multipaths(&reply, style, pretty) < 0)
+		return 1;
 
-	if (pretty && c == header) {
+	if (pretty && get_strbuf_len(&reply) == (size_t)hdr_len)
 		/* No output - clear header */
-		*reply = '\0';
-		c = reply;
-	}
-	*r = reply;
-	*len = (int)(c - reply + 1);
+		truncate_strbuf(&reply, 0);
+
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
@@ -1366,34 +1231,20 @@ cli_fail(void * v, char ** reply, int * len, void * data)
 int
 show_blacklist (char ** r, int * len)
 {
-	char *c = NULL;
-	char *reply = NULL;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
+	STRBUF_ON_STACK(reply);
 	struct config *conf;
-	int fail = 0;
-
-	reply = MALLOC(maxlen);
+	bool fail;
 
 	conf = get_multipath_config();
 	pthread_cleanup_push(put_multipath_config, conf);
-	while (again) {
-		if (!reply) {
-			fail = 1;
-			break;
-		}
-
-		c = reply;
-		c += snprint_blacklist_report(conf, c, maxlen);
-		again = (c == reply + maxlen);
-		REALLOC_REPLY(reply, again, maxlen);
-	}
+	fail = snprint_blacklist_report(conf, &reply) < 0;
 	pthread_cleanup_pop(1);
 
 	if (fail)
 		return 1;
-	*r = reply;
-	*len = (int)(c - reply + 1);
+
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 	return 0;
 }
 
@@ -1408,34 +1259,20 @@ cli_list_blacklist (void * v, char ** reply, int * len, void * data)
 int
 show_devices (char ** r, int * len, struct vectors *vecs)
 {
-	char *c = NULL;
-	char *reply = NULL;
-	unsigned int maxlen = INITIAL_REPLY_LEN;
-	int again = 1;
+	STRBUF_ON_STACK(reply);
 	struct config *conf;
-	int fail = 0;
-
-	reply = MALLOC(maxlen);
+	bool fail;
 
 	conf = get_multipath_config();
 	pthread_cleanup_push(put_multipath_config, conf);
-	while (again) {
-		if (!reply) {
-			fail = 1;
-			break;
-		}
-
-		c = reply;
-		c += snprint_devices(conf, c, maxlen, vecs);
-		again = (c == reply + maxlen);
-		REALLOC_REPLY(reply, again, maxlen);
-	}
+	fail = snprint_devices(conf, &reply, vecs) < 0;
 	pthread_cleanup_pop(1);
 
 	if (fail)
 		return 1;
-	*r = reply;
-	*len = (int)(c - reply + 1);
+
+	*len = (int)get_strbuf_len(&reply) + 1;
+	*r = steal_strbuf_str(&reply);
 
 	return 0;
 }
-- 
2.32.0


--
dm-devel mailing list
dm-devel@redhat.com
https://listman.redhat.com/mailman/listinfo/dm-devel


  parent reply	other threads:[~2021-08-11 15:43 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-11 15:41 [dm-devel] [PATCH v2 0/9] multipath-tools: use variable-size string buffers mwilck
2021-08-11 15:41 ` [dm-devel] [PATCH v2 1/9] libmultipath: variable-size parameters in dm_get_map() mwilck
2021-08-30 20:44   ` Benjamin Marzinski
2021-08-11 15:41 ` [dm-devel] [PATCH v2 2/9] libmultipath: strbuf: simple api for growing string buffers mwilck
2021-08-11 16:08   ` Bart Van Assche
2021-08-12  7:19     ` Martin Wilck
2021-08-30 20:45   ` Benjamin Marzinski
2021-08-11 15:41 ` [dm-devel] [PATCH v2 3/9] libmultipath: variable-size parameters in assemble_map() mwilck
2021-08-30 20:45   ` Benjamin Marzinski
2021-08-11 15:41 ` [dm-devel] [PATCH v2 4/9] libmultipath: use strbuf in dict.c mwilck
2021-08-30 20:46   ` Benjamin Marzinski
2021-08-11 15:41 ` mwilck [this message]
2021-08-30 20:47   ` [dm-devel] [PATCH v2 5/9] libmultipath: use strbuf in print.c Benjamin Marzinski
2021-08-11 15:41 ` [dm-devel] [PATCH v2 6/9] libmultipath: print.c: fail hard if keywords are not found mwilck
2021-08-11 15:41 ` [dm-devel] [PATCH v2 7/9] libmultipath: print.h: move macros to print.c mwilck
2021-08-11 15:41 ` [dm-devel] [PATCH v2 8/9] libmultipath: use strbuf in alias.c mwilck
2021-08-11 15:41 ` [dm-devel] [PATCH v2 9/9] multipathd: use strbuf in cli.c mwilck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210811154150.24714-6-mwilck@suse.com \
    --to=mwilck@suse.com \
    --cc=bmarzins@redhat.com \
    --cc=christophe.varoqui@opensvc.com \
    --cc=dm-devel@redhat.com \
    --subject='Re: [dm-devel] [PATCH v2 5/9] libmultipath: use strbuf in print.c' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).