git: 2ad786c63ae2 - main - b64encode: implement -w to wrap lines

From: Piotr Pawel Stefaniak <pstef_at_FreeBSD.org>
Date: Mon, 18 Apr 2022 11:56:27 UTC
The branch main has been updated by pstef:

URL: https://cgit.FreeBSD.org/src/commit/?id=2ad786c63ae2f8018f67414076c9aa023e062028

commit 2ad786c63ae2f8018f67414076c9aa023e062028
Author:     Piotr Pawel Stefaniak <pstef@FreeBSD.org>
AuthorDate: 2022-04-06 18:03:02 +0000
Commit:     Piotr Pawel Stefaniak <pstef@FreeBSD.org>
CommitDate: 2022-04-18 11:53:58 +0000

    b64encode: implement -w to wrap lines
    
    This functionality is present in GNU base64 and I find it useful when
    I want to generate random, ASCII-clean data of specific width.
    
    Reviewed by:    delphij
    Differential Revision:  https://reviews.freebsd.org/D32944
---
 usr.bin/bintrans/bintrans.1 | 10 ++++++++
 usr.bin/bintrans/uuencode.c | 60 ++++++++++++++++++++++++++++++++++++---------
 2 files changed, 59 insertions(+), 11 deletions(-)

diff --git a/usr.bin/bintrans/bintrans.1 b/usr.bin/bintrans/bintrans.1
index e3ad5813f4cd..cdc819aa7a18 100644
--- a/usr.bin/bintrans/bintrans.1
+++ b/usr.bin/bintrans/bintrans.1
@@ -56,6 +56,7 @@
 .Fl o Ar output_file
 .Nm b64encode
 .Op Fl r
+.Op Fl w Ar column
 .Op Fl o Ar output_file
 .Op Ar file
 .Ar name
@@ -181,6 +182,15 @@ deletes any prefix ending with the last slash '/' for security
 reasons.
 .El
 .Pp
+Additionally,
+.Nm b64encode
+accepts the following option:
+.Bl -tag -width ident
+.It Fl w Ar column
+Wrap encoded output after
+.Ar column .
+.El
+.Pp
 .Nm
 is a generic utility that can run
 any of the aforementioned encoders and decoders.
diff --git a/usr.bin/bintrans/uuencode.c b/usr.bin/bintrans/uuencode.c
index f2d4b5b2b498..4837d3310578 100644
--- a/usr.bin/bintrans/uuencode.c
+++ b/usr.bin/bintrans/uuencode.c
@@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/in.h>
 
 #include <err.h>
+#include <errno.h>
 #include <libgen.h>
 #include <resolv.h>
 #include <stdio.h>
@@ -67,12 +68,14 @@ extern int main_encode(int, char *[]);
 
 static void encode(void);
 static void base64_encode(void);
+static int arg_to_col(const char *);
 static void usage(void);
 
 static FILE *output;
 static int mode;
 static bool raw;
 static char **av;
+static int columns = 76;
 
 int
 main_encode(int argc, char *argv[])
@@ -88,7 +91,7 @@ main_encode(int argc, char *argv[])
 	if (strcmp(basename(argv[0]), "b64encode") == 0)
 		base64 = 1;
 
-	while ((ch = getopt(argc, argv, "mo:r")) != -1) {
+	while ((ch = getopt(argc, argv, "mo:rw:")) != -1) {
 		switch (ch) {
 		case 'm':
 			base64 = true;
@@ -99,6 +102,9 @@ main_encode(int argc, char *argv[])
 		case 'r':
 			raw = true;
 			break;
+		case 'w':
+			columns = arg_to_col(optarg);
+			break;
 		case '?':
 		default:
 			usage();
@@ -151,27 +157,37 @@ static void
 base64_encode(void)
 {
 	/*
-	 * Output must fit into 80 columns, chunks come in 4, leave 1.
+	 * This buffer's length should be a multiple of 24 bits to avoid "="
+	 * padding. Once it reached ~1 KB, further expansion didn't improve
+	 * performance for me.
 	 */
-#define	GROUPS	((80 / 4) - 1)
-	unsigned char buf[3];
+	unsigned char buf[1023];
 	char buf2[sizeof(buf) * 2 + 1];
 	size_t n;
-	int rv, sequence;
-
-	sequence = 0;
+	unsigned carry = 0;
+	int rv, written;
 
 	if (!raw)
 		fprintf(output, "begin-base64 %o %s\n", mode, *av);
 	while ((n = fread(buf, 1, sizeof(buf), stdin))) {
-		++sequence;
 		rv = b64_ntop(buf, n, buf2, nitems(buf2));
 		if (rv == -1)
 			errx(1, "b64_ntop: error encoding base64");
-		fprintf(output, "%s%s", buf2, (sequence % GROUPS) ? "" : "\n");
+		if (columns == 0) {
+			fputs(buf2, output);
+			continue;
+		}
+		for (int i = 0; i < rv; i += written) {
+			written = fprintf(output, "%.*s", columns - carry,
+			    &buf2[i]);
+
+			carry = (carry + written) % columns;
+			if (carry == 0)
+				fputc('\n', output);
+		}
 	}
-	if (sequence % GROUPS)
-		fprintf(output, "\n");
+	if (columns == 0 || carry != 0)
+		fputc('\n', output);
 	if (!raw)
 		fprintf(output, "====\n");
 }
@@ -225,6 +241,28 @@ encode(void)
 		(void)fprintf(output, "%c\nend\n", ENC('\0'));
 }
 
+static int
+arg_to_col(const char *w)
+{
+	char *ep;
+	long option;
+
+	errno = 0;
+	option = strtol(w, &ep, 10);
+	if (option > INT_MAX)
+		errno = ERANGE;
+	else if (ep[0] != '\0')
+		errno = EINVAL;
+	if (errno != 0)
+		err(2, NULL);
+
+	if (option < 0) {
+		errno = EINVAL;
+		err(2, "columns argument must be non-negative");
+	}
+	return (option);
+}
+
 static void
 usage(void)
 {