From 274369e625cd972aa95aa900eb861a6dc5a24712 Mon Sep 17 00:00:00 2001
From: katherine <shmibs@shmibbles.me>
Date: Sun, 25 Mar 2018 14:02:56 -0700
Subject: use word-wrapping for error printing

---
 README.md        |   4 +-
 doc/example.c    |   2 +-
 doc/interface.md |   5 ++-
 simple-opt.h     | 129 ++++++++++++++++++++++++++++++++++++++-----------------
 4 files changed, 97 insertions(+), 43 deletions(-)

diff --git a/README.md b/README.md
index 8fc29f9..4dc9861 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ int main(int argc, char **argv)
 	/* catch any errors and print a default error message. you can do this bit
 	 * yourself, if you'd like more control of the output */
 	if (result.result_type != SIMPLE_OPT_RESULT_SUCCESS) {
-		simple_opt_print_error(stderr, argv[0], result);
+		simple_opt_print_error(stderr, 80, argv[0], result);
 		return 1;
 	}
 
@@ -160,7 +160,7 @@ $ ./a.out --int
 ```
 $ ./a.out --bool fake
 ./a.out: bad argument `fake` passed to option `--bool`
-expected boolean, (yes|true|on) or (no|false|off)
+         expected a boolean: (yes|true|on) or (no|false|off)
 ```
 
 if one of the options passed is the help flag (`-h` or `--help`), this example
diff --git a/doc/example.c b/doc/example.c
index 2c21341..324473e 100644
--- a/doc/example.c
+++ b/doc/example.c
@@ -38,7 +38,7 @@ int main(int argc, char **argv)
 	/* catch any errors and print a default error message. you can do this bit
 	 * yourself, if you'd like more control of the output */
 	if (result.result_type != SIMPLE_OPT_RESULT_SUCCESS) {
-		simple_opt_print_error(stderr, argv[0], result);
+		simple_opt_print_error(stderr, 80, argv[0], result);
 		return 1;
 	}
 
diff --git a/doc/interface.md b/doc/interface.md
index e757e80..f8d5886 100644
--- a/doc/interface.md
+++ b/doc/interface.md
@@ -281,12 +281,15 @@ an alternative method for usage printing.
 message, if there is one to be printed:
 
 ```
-static void simple_opt_print_error(FILE *f, char *command_name,
+static void simple_opt_print_error(FILE *f, unsigned width, char *command_name,
 		struct simple_opt_result result);
 ```
 
 `f` is a file pointer to which the message should be printed.
 
+`width` is the width to which the output should be word-wrapped. if 0, no
+wrapping will be performed. see `width` in `simple_opt_print_usage` above.
+
 `command_name` is the name of the command as it will be printed in the usage
 statement. easiest is just to pass `argv[0]` here. if NULL is passed, "err:"
 will be printed instead.
diff --git a/simple-opt.h b/simple-opt.h
index 0260467..56ff1f3 100644
--- a/simple-opt.h
+++ b/simple-opt.h
@@ -26,8 +26,8 @@
 
 /* an internal print buffer width for usage printing. you shouldn't have to
  * worry about this if you're sane */
-#ifndef SIMPLE_OPT_USAGE_PRINT_BUFFER_WIDTH
-#define SIMPLE_OPT_USAGE_PRINT_BUFFER_WIDTH 256
+#ifndef SIMPLE_OPT_PRINT_BUFFER_WIDTH
+#define SIMPLE_OPT_PRINT_BUFFER_WIDTH 4096
 #endif
 
 enum simple_opt_type {
@@ -100,7 +100,7 @@ static void simple_opt_print_usage(FILE *f, unsigned width,
 		char *command_name, char *command_options, char *command_summary,
 		struct simple_opt *options);
 
-static void simple_opt_print_error(FILE *f, char *command_name,
+static void simple_opt_print_error(FILE *f, unsigned width, char *command_name,
 		struct simple_opt_result result);
 
 
@@ -529,15 +529,15 @@ static void simple_opt_print_usage(FILE *f, unsigned width,
 		char *command_name, char *command_options, char *command_summary,
 		struct simple_opt *options)
 {
-	char print_buffer[SIMPLE_OPT_USAGE_PRINT_BUFFER_WIDTH];
+	char print_buffer[SIMPLE_OPT_PRINT_BUFFER_WIDTH];
 	unsigned i, j, col, print_buffer_offset, desc_line_start;
 
 	/* calculate the required line_start for printing descriptions (leaving
 	 * space for the widest existing long-option) */
 
 	/* check for space for column 1 (short_name) */
-	if (5 >= SIMPLE_OPT_USAGE_PRINT_BUFFER_WIDTH) {
-		fprintf(f, "simple-opt internal err: usage print buffer too small\n");
+	if (5 >= SIMPLE_OPT_PRINT_BUFFER_WIDTH) {
+		fprintf(f, "simple-opt internal err: print buffer too small\n");
 		return;
 	}
 
@@ -596,7 +596,7 @@ static void simple_opt_print_usage(FILE *f, unsigned width,
 	}
 
 	/* check for space for long_name printing */
-	if (desc_line_start - 5 - 2 >= SIMPLE_OPT_USAGE_PRINT_BUFFER_WIDTH) {
+	if (desc_line_start - 5 - 2 >= SIMPLE_OPT_PRINT_BUFFER_WIDTH) {
 		fprintf(f, "simple-opt internal err: usage print buffer too small\n");
 		return;
 	}
@@ -741,92 +741,143 @@ static void simple_opt_print_usage(FILE *f, unsigned width,
 	}
 }
 
-static void simple_opt_print_error(FILE *f, char *command_name,
+static void simple_opt_print_error(FILE *f, unsigned width, char *command_name,
 		struct simple_opt_result result)
 {
-	char *s;
-	unsigned i;
+	char print_buffer[SIMPLE_OPT_PRINT_BUFFER_WIDTH];
+	unsigned i, line_start, col;
+	int rval;
+
+	/* just easier to write */
+	size_t size = SIMPLE_OPT_PRINT_BUFFER_WIDTH;
 
 	if (result.result_type == SIMPLE_OPT_RESULT_SUCCESS)
 		return;
 
 	if (command_name != NULL)
-		fprintf(f, "%s: ", command_name);
+		rval = snprintf(print_buffer, size, "%s:", command_name);
 	else
-		fprintf(f, "err: ");
+		rval = snprintf(print_buffer, size, "err:");
+
+	if (rval < 0 || (unsigned)rval >= size) {
+		fprintf(f, "simple-opt internal err: print buffer too small\n");
+		return;
+	}
+
+	col = sub_simple_opt_wrap_print(f, width, 0, 0, print_buffer);
+
+	line_start = strlen(print_buffer) + 1;
 
 	switch (result.result_type) {
 	case SIMPLE_OPT_RESULT_UNRECOGNISED_OPTION:
-		fprintf(f, "unrecognised option `%s`\n",
+		rval = snprintf(print_buffer, size, "unrecognised option `%s`",
 				result.option_string);
 		break;
 
 	case SIMPLE_OPT_RESULT_BAD_ARG:
-		fprintf(f, "bad argument `%s` passed to option `%s`\n",
+		rval = snprintf(print_buffer, size,
+				"bad argument `%s` passed to option `%s`",
 				result.argument_string, result.option_string);
+
+		if (rval < 0 || (unsigned)rval >= size) {
+			fprintf(f, "simple-opt internal err: print buffer too small\n");
+			return;
+		}
+
+		sub_simple_opt_wrap_print(f, width, col, line_start,
+				print_buffer);
+		fprintf(f, "\n");
+		col = 0;
+
 		switch (result.option_type) {
 		case SIMPLE_OPT_BOOL:
-			fprintf(f,
-					"expected boolean, (yes|true|on) or (no|false|off)\n");
+			rval = snprintf(print_buffer, size,
+					"expected a boolean: (yes|true|on) or (no|false|off)");
 			break;
 		case SIMPLE_OPT_INT:
-			fprintf(f, "expected integer value\n");
+			rval = snprintf(print_buffer, size,"expected integer value");
 			break;
 		case SIMPLE_OPT_UNSIGNED:
-			fprintf(f, "expected unsigned integer value\n");
+			rval = snprintf(print_buffer, size,
+					"expected unsigned integer value");
 			break;
 		case SIMPLE_OPT_DOUBLE:
-			fprintf(f, "expected floating-point value\n");
+			rval = snprintf(print_buffer, size,
+					"expected floating-point value");
 			break;
 		case SIMPLE_OPT_CHAR:
-			fprintf(f, "expected single character\n");
+			rval = snprintf(print_buffer, size,
+					"expected single character");
 			break;
 		case SIMPLE_OPT_STRING:
-			fprintf(f, "expected a string\n");
+			rval = snprintf(print_buffer, size,
+					"expected a string");
 			break;
 		case SIMPLE_OPT_STRING_SET:
 			for (i = 0; result.option->string_set[i] != NULL; i++);
-			if (i == 1)
-				fprintf(f, "expected \"%s\"\n", result.option->string_set[0]);
-			else if (i == 2)
-				fprintf(f, "expected \"%s\" or \"%s\"\n",
-						result.option->string_set[0], result.option->string_set[1]);
-			else if (i == 3)
-				fprintf(f, "expected \"%s\", \"%s\" or \"%s\"\n",
-						result.option->string_set[0], result.option->string_set[1],
+			if (i == 1) {
+				rval = snprintf(print_buffer, size, "expected \"%s\"",
+						result.option->string_set[0]);
+			} else if (i == 2) {
+				rval = snprintf(print_buffer, size,
+						"expected \"%s\" or \"%s\"",
+						result.option->string_set[0],
+						result.option->string_set[1]);
+			} else if (i == 3) {
+				rval = snprintf(print_buffer, size,
+						"expected \"%s\", \"%s\" or \"%s\"",
+						result.option->string_set[0],
+						result.option->string_set[1],
 						result.option->string_set[2]);
-			else if (i == 4)
-				fprintf(f, "expected \"%s\", \"%s\", \"%s\", or \"%s\"\n",
-						result.option->string_set[0], result.option->string_set[1],
-						result.option->string_set[2], result.option->string_set[3]);
-			else
-				fprintf(f, "expected a string\n");
+			} else if (i == 4) {
+				rval = snprintf(print_buffer, size,
+						"expected \"%s\", \"%s\", \"%s\", or \"%s\"",
+						result.option->string_set[0],
+						result.option->string_set[1],
+						result.option->string_set[2],
+						result.option->string_set[3]);
+			} else {
+				rval = snprintf(print_buffer, size,
+						"expected one of %u possibile strings", i);
+			}
 		default:
 			break;
 		}
 		break;
 
 	case SIMPLE_OPT_RESULT_MISSING_ARG:
-		fprintf(f, "argument expected for option `%s`\n",
+		rval = snprintf(print_buffer, size,
+				"argument expected for option `%s`",
 				result.option_string);
 		break;
 
 	case SIMPLE_OPT_RESULT_OPT_ARG_TOO_LONG:
-		fprintf(f, "argument passed to option `%s` is too long\n",
+		rval = snprintf(print_buffer, size,
+				"argument passed to option `%s` is too long",
 				result.option_string);
 		break;
 
 	case SIMPLE_OPT_RESULT_TOO_MANY_ARGS:
-		fprintf(f, "too many cli arguments received\n");
+		rval = snprintf(print_buffer, size,
+				"too many cli arguments received");
 		break;
 
 	case SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT:
-		fprintf(f, "malformed option struct (internal err)\n");
+		rval = snprintf(print_buffer, size,
+				"malformed option struct (internal err)");
 		break;
 
 	default:
 		break;
 	}
+
+	if (rval < 0 || (unsigned)rval >= size) {
+		fprintf(f, "simple-opt internal err: print buffer too small");
+		return;
+	}
+
+	sub_simple_opt_wrap_print(f, width, col, line_start, print_buffer);
+	fprintf(f, "\n");
 }
 
 #endif
-- 
cgit v1.2.3