Index: uspace/app/bdsh/util.c
===================================================================
--- uspace/app/bdsh/util.c	(revision 216d6fc606ae06cb619154c1ca9763af0f2c1978)
+++ uspace/app/bdsh/util.c	(revision e2ea8d7e7e3b905a345a693589f810965ea6e22d)
@@ -53,4 +53,6 @@
 #include "util.h"
 
+extern volatile int cli_errno;
+
 /* some platforms do not have strdup, implement it here.
  * Returns a pointer to an allocated string or NULL on failure */
@@ -60,7 +62,10 @@
 	void *ret = malloc(len);
 
-	if (ret == NULL)
+	if (ret == NULL) {
+		cli_errno = CL_ENOMEM;
 		return (char *) NULL;
-
+	}
+
+	cli_errno = CL_EOK;
 	return (char *) memcpy(ret, s1, len);
 }
@@ -80,52 +85,106 @@
 	*s1 = realloc(*s1, len);
 
-	if (*s1 == NULL)
-		return -1;
+	if (*s1 == NULL) {
+		cli_errno = CL_ENOMEM;
+		return -1;
+	}
 
 	memset(*s1, 0, sizeof(*s1));
 	memcpy(*s1, s2, len);
+	cli_errno = CL_EOK;
 	return (int) len;
 }
 
-/* An asprintf() for concantenating paths. Allocates the system PATH_MAX value,
- * expands the formatted string and re-sizes the block s1 points to accordingly.
- *
- * Returns the length of the new s1 on success, -1 on failure. On failure, an
- * attempt is made to return s1 unmodified for sanity, in this case 0 is returned.
- * to indicate that s1 was not modified.
- *
- * FIXME: ugly hack to get around asprintf(), if you use this, CHECK ITS VALUE! */
+/* An asprintf() for formatting paths, similar to asprintf() but ensures
+ * the returned allocated string is <= PATH_MAX. On failure, an attempt
+ * is made to return the original string (if not null) unmodified.
+ *
+ * Returns: Length of the new string on success, 0 if the string was handed
+ * back unmodified, -1 on failure. On failure, cli_errno is set.
+ *
+ * We do not use POSIX_PATH_MAX, as it is typically much smaller than the
+ * PATH_MAX defined by the kernel.
+ *
+ * Use this like:
+ * if (1 > cli_psprintf(&char, "%s/%s", foo, bar)) {
+ *   cli_error(cli_errno, "Failed to format path");
+ *   stop_what_your_doing_as_your_out_of_memory();
+ * }
+ */
+
 int cli_psprintf(char **s1, const char *fmt, ...)
 {
 	va_list ap;
 	size_t needed, base = PATH_MAX + 1;
+	int skipped = 0;
+	char *orig = NULL;
 	char *tmp = (char *) malloc(base);
 
-	if (NULL == tmp)
-		return -1;
-
-	char *orig = *s1;
-
+	/* Don't even touch s1, not enough memory */
+	if (NULL == tmp) {
+		cli_errno = CL_ENOMEM;
+		return -1;
+	}
+
+	/* If re-allocating s1, save a copy in case we fail */
+	if (NULL != *s1)
+		orig = cli_strdup(*s1);
+
+	/* Print the string to tmp so we can determine the size that
+	 * we actually need */
 	memset(tmp, 0, sizeof(tmp));
 	va_start(ap, fmt);
-	vsnprintf(tmp, base, fmt, ap);
+	/* vsnprintf will return the # of bytes not written */
+	skipped = vsnprintf(tmp, base, fmt, ap);
 	va_end(ap);
+
+	/* realloc/alloc s1 to be just the size that we need */
 	needed = strlen(tmp) + 1;
 	*s1 = realloc(*s1, needed);
+
 	if (NULL == *s1) {
+		/* No string lived here previously, or we failed to
+		 * make a copy of it, either way there's nothing we
+		 * can do. */
+		if (NULL == *orig) {
+			cli_errno = CL_ENOMEM;
+			return -1;
+		}
+		/* We can't even allocate enough size to restore the
+		 * saved copy, just give up */
 		*s1 = realloc(*s1, strlen(orig) + 1);
 		if (NULL == *s1) {
 			free(tmp);
+			free(orig);
+			cli_errno = CL_ENOMEM;
 			return -1;
 		}
+		/* Give the string back as we found it */
 		memset(*s1, 0, sizeof(*s1));
 		memcpy(*s1, orig, strlen(orig) + 1);
 		free(tmp);
+		free(orig);
+		cli_errno = CL_ENOMEM;
 		return 0;
 	}
+
+	/* Ok, great, we have enough room */
 	memset(*s1, 0, sizeof(*s1));
 	memcpy(*s1, tmp, needed);
 	free(tmp);
 
+	/* Free tmp only if s1 was reallocated instead of allocated */
+	if (NULL != orig)
+		free(orig);
+
+	if (skipped) {
+		/* s1 was bigger than PATH_MAX when expanded, however part
+		 * of the string was printed. Tell the caller not to use it */
+		cli_errno = CL_ETOOBIG;
+		return -1;
+	}
+
+	/* Success! */
+	cli_errno = CL_EOK;
 	return (int) needed;
 }
@@ -137,6 +196,8 @@
 	int c, sc;
 
-	if (s == NULL && (s = *last) == NULL)
+	if (s == NULL && (s = *last) == NULL) {
+		cli_errno = CL_EFAIL;
 		return (NULL);
+	}
 
 cont:
