[hackers] [sbase][PATCH] Add base64(1), base32(1), and base16(1)

From: Mattias Andrée <maandree_AT_kth.se>
Date: Tue, 29 Mar 2016 18:59:24 +0200

Signed-off-by: Mattias Andrée <maandree_AT_kth.se>
---
 Makefile |   3 +
 README   |   3 +
 base16.1 |  40 ++++++++++++
 base16.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++
 base32.1 |  40 ++++++++++++
 base32.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 base64.1 |  40 ++++++++++++
 base64.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 706 insertions(+)
 create mode 100644 base16.1
 create mode 100644 base16.c
 create mode 100644 base32.1
 create mode 100644 base32.c
 create mode 100644 base64.1
 create mode 100644 base64.c
diff --git a/Makefile b/Makefile
index 6b2bfdf..9600639 100644
--- a/Makefile
+++ b/Makefile
_AT_@ -84,6 +84,9 @@ LIBUTILSRC =\
 LIB = $(LIBUTF) $(LIBUTIL)
 
 BIN =\
+	base16\
+	base32\
+	base64\
 	basename\
 	cal\
 	cat\
diff --git a/README b/README
index d60d8fc..ebc20a4 100644
--- a/README
+++ b/README
_AT_@ -12,6 +12,9 @@ The following tools are implemented:
 
       UTILITY         MISSING
       -------         -------
+0=* x base16          .
+0=* x base32          .
+0=* x base64          .
 0=*|o basename        .
 0=*|o cal             .
 0=*|o cat             .
diff --git a/base16.1 b/base16.1
new file mode 100644
index 0000000..2debf6a
--- /dev/null
+++ b/base16.1
_AT_@ -0,0 +1,40 @@
+.Dd 2016-03-29
+.Dt BASE16 1
+.Os sbase
+.Sh NAME
+.Nm base16
+.Nd encode data in or decode data from base16
+.Sh SYNOPSIS
+.Nm
+.Op Fl di
+.Op Fl w Ar cols
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+read the
+.Ar file
+in either encode the file in base16 (default)
+or decode it from base16
+.Op Fl d .
+If no
+.Ar file
+is given
+.Nm
+reads from stdin.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d
+Decode instead of encode.
+.It Fl i
+Ignore garbage if decode. <newline> is always ignored.
+.It Fl w Ar cols
+Wrap the output at
+.Ar cols
+columns (default 76). If
+.Ar cols
+is zero, do not wrap.
+.El
+.Sh STANDARDS
+The output of the
+.Nm
+utility is compliant with the RFC 4648 specification.
diff --git a/base16.c b/base16.c
new file mode 100644
index 0000000..9940a02
--- /dev/null
+++ b/base16.c
_AT_@ -0,0 +1,168 @@
+/* See LICENSE file for copyright and license details. */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define ALPHABET "0123456789ABCDEF"
+#define ALTALPHABET "0123456789ABCDEF"
+
+static unsigned char lut[256] =
+	ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET
+	ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET;
+static int iflag = 0;
+static size_t cols;
+static size_t pos = 0;
+static size_t (*output)(const void *restrict, size_t, size_t, FILE *);
+
+static size_t
+wrapwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *stream)
+{
+	const char *restrict buf = ptr;
+	size_t n = cols - pos;
+
+	if (nitems >= n) {
+		fwrite(buf, 1, n, stream);
+		nitems -= n;
+		buf += n;
+		pos = 0;
+		fputc('\n', stdout);
+	}
+
+	while (nitems >= cols) {
+		fwrite(buf, 1, cols, stream);
+		nitems -= cols;
+		buf += cols;
+		fputc('\n', stdout);
+	}
+
+	if (nitems) {
+		fwrite(buf, 1, nitems, stream);
+		pos = nitems;
+	}
+
+	(void) size;
+	return 0;
+}
+
+static void
+encode(FILE *fp)
+{
+	unsigned char ibuf[BUFSIZ / 2];
+	unsigned char obuf[sizeof(ibuf) * 2];
+	register unsigned char *p, *out;
+	unsigned char *pend;
+	size_t n;
+
+	while ((n = fread(ibuf, 1, sizeof(ibuf), fp))) {
+		out = obuf;
+		for (p = ibuf, pend = p + n; p != pend; p += 1, out += 2) {
+			out[0] = lut[*p >> 4];
+			out[1] = lut[*p];
+		}
+		output(obuf, 1, n << 1, stdout);
+	}
+
+	if (pos)
+		fputc('\n', stdout);
+}
+
+static void
+decode(FILE *fp)
+{
+	unsigned char ibuf[BUFSIZ / 2];
+	unsigned char obuf[BUFSIZ / 2 - ((BUFSIZ / 2) % 2)];
+	unsigned char jbuf[2];
+	unsigned char value;
+	size_t i, j = 0, n, o = 0;
+	register unsigned char *p;
+
+	memset(lut, ~0, sizeof(lut));
+	for (i = 0; i < 16; i++) {
+		lut[(int)(ALPHABET[i])] = i;
+		lut[(int)(ALTALPHABET[i])] = i;
+	}
+
+	while ((n = fread(ibuf, 1, sizeof(ibuf), fp))) {
+		for (p = ibuf; n--; p++) {
+			value = lut[*p];
+			if (value == 0xFF) {
+				if (*p == '\n')
+					continue;
+				else if (iflag)
+					continue;
+				else
+					eprintf("invalid input\n");
+			}
+			jbuf[j++] = value;
+			if (j == 2) {
+				j = 0;
+				obuf[o++] = (jbuf[0] << 4) | jbuf[1];
+				if (o == sizeof(obuf)) {
+					fwrite(obuf, 1, o, stdout);
+					o = 0;
+				}
+			}
+		}
+	}
+	if (j && feof(fp))
+		eprintf("invalid input\n");
+	if (o)
+		fwrite(obuf, 1, o, stdout);
+}
+
+static void
+usage(void)
+{
+	/* Logically, it should be [-d [-i] | -w col], but
+	 * for consistency we let it be [-di] [-w col]. */
+
+	eprintf("usage: %s [-di] [-w col] [file]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int dflag = 0;
+	long wflag = -1;
+	FILE *fp = stdin;
+	char *path = "<stdin>";
+
+	ARGBEGIN {
+	case 'd':
+		dflag = 1;
+		break;
+	case 'i':
+		iflag = 1;
+		break;
+	case 'w':
+		wflag = estrtonum(EARGF(usage()), 0, LONG_MAX);
+		break;
+	default:
+		usage();
+	} ARGEND
+
+	if (argc > 1)
+		usage();
+
+	if (argc && strcmp(*argv, "-")) {
+		path = *argv;
+		if (!(fp = fopen(*argv, "r")))
+			eprintf("fopen %s:", *argv);
+	}
+
+	if (wflag == 0) {
+		output = &fwrite;
+		pos = 1;
+	} else {
+		output = &wrapwrite;
+		cols = wflag > 0 ? wflag : 76;
+	}
+
+	(dflag ? decode : encode)(fp);
+	return fshut(fp, path) || fshut(stdout, "<stdout>");
+}
diff --git a/base32.1 b/base32.1
new file mode 100644
index 0000000..51a279a
--- /dev/null
+++ b/base32.1
_AT_@ -0,0 +1,40 @@
+.Dd 2016-03-29
+.Dt BASE32 1
+.Os sbase
+.Sh NAME
+.Nm base32
+.Nd encode data in or decode data from base32
+.Sh SYNOPSIS
+.Nm
+.Op Fl di
+.Op Fl w Ar cols
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+read the
+.Ar file
+in either encode the file in base32 (default)
+or decode it from base32
+.Op Fl d .
+If no
+.Ar file
+is given
+.Nm
+reads from stdin.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d
+Decode instead of encode.
+.It Fl i
+Ignore garbage if decode. <newline> is always ignored.
+.It Fl w Ar cols
+Wrap the output at
+.Ar cols
+columns (default 76). If
+.Ar cols
+is zero, do not wrap.
+.El
+.Sh STANDARDS
+The output of the
+.Nm
+utility is compliant with the RFC 4648 specification.
diff --git a/base32.c b/base32.c
new file mode 100644
index 0000000..49457e0
--- /dev/null
+++ b/base32.c
_AT_@ -0,0 +1,214 @@
+/* See LICENSE file for copyright and license details. */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
+
+static unsigned char lut[256] = ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET ALPHABET;
+static int iflag = 0;
+static size_t cols;
+static size_t pos = 0;
+static size_t (*output)(const void *restrict, size_t, size_t, FILE *);
+
+static size_t
+wrapwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *stream)
+{
+	const char *restrict buf = ptr;
+	size_t n = cols - pos;
+
+	if (nitems >= n) {
+		fwrite(buf, 1, n, stream);
+		nitems -= n;
+		buf += n;
+		pos = 0;
+		fputc('\n', stdout);
+	}
+
+	while (nitems >= cols) {
+		fwrite(buf, 1, cols, stream);
+		nitems -= cols;
+		buf += cols;
+		fputc('\n', stdout);
+	}
+
+	if (nitems) {
+		fwrite(buf, 1, nitems, stream);
+		pos = nitems;
+	}
+
+	(void) size;
+	return 0;
+}
+
+static void
+encode(FILE *fp)
+{
+	unsigned char ibuf[(BUFSIZ / 2) - ((BUFSIZ / 2) % 5)];
+	unsigned char obuf[sizeof(ibuf) / 5 * 8];
+	register unsigned char *p, *out;
+	unsigned char *pend;
+	size_t n, o;
+
+	do {
+		for (o = 0; o < sizeof(ibuf); o += n)
+			if (!(n = fread(ibuf + o, 1, sizeof(ibuf) - o, fp)))
+				break;
+		if (!o)
+			goto end;
+		n = o;
+		o %= 5;
+		out = obuf;
+		for (p = ibuf, pend = p + n - o; p != pend; p += 5, out += 8) {
+			out[0] = lut[p[0] >> 3];
+			out[1] = lut[(unsigned char)(p[0] << 2) | (p[1] >> 6)];
+			out[2] = lut[p[1] >> 1];
+			out[3] = lut[(unsigned char)(p[1] << 4) | (p[2] >> 4)];
+			out[4] = lut[(unsigned char)(p[2] << 1) | (p[3] >> 7)];
+			out[5] = lut[p[3] >> 2];
+			out[6] = lut[(unsigned char)(p[3] << 3) | (p[4] >> 5)];
+			out[7] = lut[p[4]];
+		}
+		n = (size_t)(out - obuf);
+		output(obuf, 1, n, stdout);
+	} while (!o);
+
+	/* This part is only reached if padding is required. { */
+
+	memset(obuf, '=', 8);
+	memset(p + o, 0, 8 - o);
+	obuf[0] = lut[p[0] >> 3];
+	obuf[1] = lut[(unsigned char)(p[0] << 2) | (p[1] >> 6)];
+	if (o == 1) goto done;
+	obuf[2] = lut[p[1] >> 1];
+	obuf[3] = lut[(unsigned char)(p[1] << 4) | (p[2] >> 4)];
+	if (o == 2) goto done;
+	obuf[4] = lut[(unsigned char)(p[2] << 1) | (p[3] >> 7)];
+	if (o == 3) goto done;
+	obuf[5] = lut[p[3] >> 2];
+	obuf[6] = lut[(unsigned char)(p[3] << 3)];
+done:
+	output(obuf, 1, 8, stdout);
+
+	/* } */
+
+end:
+	if (pos)
+		fputc('\n', stdout);
+}
+
+static void
+decode(FILE *fp)
+{
+	unsigned char ibuf[BUFSIZ / 2];
+	unsigned char obuf[BUFSIZ / 2 - ((BUFSIZ / 2) % 5)];
+	unsigned char jbuf[8];
+	unsigned char value;
+	size_t i, j = 0, n, o = 0, pads = 0, erasure;
+	register unsigned char *p;
+	static const int pads_to_erasure[] = {0, 1, -1, 2, 3, -1, 4, -1};
+
+	memset(lut, ~0, sizeof(lut));
+	for (i = 0; i < 32; i++)
+		lut[(int)(ALPHABET[i])] = i;
+
+	while ((n = fread(ibuf, 1, sizeof(ibuf), fp))) {
+		for (p = ibuf; n--; p++) {
+			value = lut[*p];
+			if (value == 0xFF) {
+				if (*p == '=') {
+					if (++pads > 6)
+						eprintf("1 invalid input\n");
+					value = 0;
+				} else if (*p == '\n') {
+					continue;
+				} else if (pads) {
+					eprintf("invalid input\n");
+				} else if (iflag) {
+					continue;
+				} else {
+					eprintf("invalid input\n");
+				}
+			}
+			jbuf[j++] = value;
+			if (j == 8) {
+				j = 0;
+				obuf[o++] = (jbuf[0] << 3) | (jbuf[1] >> 2);
+				obuf[o++] = (jbuf[1] << 6) | (jbuf[2] << 1) | (jbuf[3] >> 4);
+				obuf[o++] = (jbuf[3] << 4) | (jbuf[4] >> 1);
+				obuf[o++] = (jbuf[4] << 7) | (jbuf[5] << 2) | (jbuf[6] >> 3);
+				obuf[o++] = (jbuf[6] << 5) | (jbuf[7] >> 0);
+
+				if (o == sizeof(obuf) && !pads) {
+					fwrite(obuf, 1, o, stdout);
+					o = 0;
+				}
+			}
+		}
+	}
+	if (j && feof(fp))
+		eprintf("invalid input\n");
+	if (o) {
+		erasure = pads_to_erasure[pads];
+		if (erasure < 0)
+			eprintf("invalid input\n");
+		fwrite(obuf, 1, o - erasure, stdout);
+	}
+}
+
+static void
+usage(void)
+{
+	/* Logically, it should be [-d [-i] | -w col], but
+	 * for compatibility we let it be [-di] [-w col]. */
+
+	eprintf("usage: %s [-di] [-w col] [file]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int dflag = 0;
+	long wflag = -1;
+	FILE *fp = stdin;
+	char *path = "<stdin>";
+
+	ARGBEGIN {
+	case 'd':
+		dflag = 1;
+		break;
+	case 'i':
+		iflag = 1;
+		break;
+	case 'w':
+		wflag = estrtonum(EARGF(usage()), 0, LONG_MAX);
+		break;
+	default:
+		usage();
+	} ARGEND
+
+	if (argc > 1)
+		usage();
+
+	if (argc && strcmp(*argv, "-")) {
+		path = *argv;
+		if (!(fp = fopen(*argv, "r")))
+			eprintf("fopen %s:", *argv);
+	}
+
+	if (wflag == 0) {
+		output = &fwrite;
+		pos = 1;
+	} else {
+		output = &wrapwrite;
+		cols = wflag > 0 ? wflag : 76;
+	}
+
+	(dflag ? decode : encode)(fp);
+	return fshut(fp, path) || fshut(stdout, "<stdout>");
+}
diff --git a/base64.1 b/base64.1
new file mode 100644
index 0000000..d857784
--- /dev/null
+++ b/base64.1
_AT_@ -0,0 +1,40 @@
+.Dd 2016-03-29
+.Dt BASE64 1
+.Os sbase
+.Sh NAME
+.Nm base64
+.Nd encode data in or decode data from base64
+.Sh SYNOPSIS
+.Nm
+.Op Fl di
+.Op Fl w Ar cols
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+read the
+.Ar file
+in either encode the file in base64 (default)
+or decode it from base64
+.Op Fl d .
+If no
+.Ar file
+is given
+.Nm
+reads from stdin.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl d
+Decode instead of encode.
+.It Fl i
+Ignore garbage if decode. <newline> is always ignored.
+.It Fl w Ar cols
+Wrap the output at
+.Ar cols
+columns (default 76). If
+.Ar cols
+is zero, do not wrap.
+.El
+.Sh STANDARDS
+The output of the
+.Nm
+utility is compliant with the RFC 4648 specification.
diff --git a/base64.c b/base64.c
new file mode 100644
index 0000000..4dfc25b
--- /dev/null
+++ b/base64.c
_AT_@ -0,0 +1,198 @@
+/* See LICENSE file for copyright and license details. */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+static unsigned char lut[256] = ALPHABET ALPHABET ALPHABET ALPHABET;
+static int iflag = 0;
+static size_t cols;
+static size_t pos = 0;
+static size_t (*output)(const void *restrict, size_t, size_t, FILE *);
+
+static size_t
+wrapwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *stream)
+{
+	const char *restrict buf = ptr;
+	size_t n = cols - pos;
+
+	if (nitems >= n) {
+		fwrite(buf, 1, n, stream);
+		nitems -= n;
+		buf += n;
+		pos = 0;
+		fputc('\n', stdout);
+	}
+
+	while (nitems >= cols) {
+		fwrite(buf, 1, cols, stream);
+		nitems -= cols;
+		buf += cols;
+		fputc('\n', stdout);
+	}
+
+	if (nitems) {
+		fwrite(buf, 1, nitems, stream);
+		pos = nitems;
+	}
+
+	(void) size;
+	return 0;
+}
+
+static void
+encode(FILE *fp)
+{
+	unsigned char ibuf[(BUFSIZ / 2) - ((BUFSIZ / 2) % 3)];
+	unsigned char obuf[sizeof(ibuf) / 3 * 4];
+	register unsigned char *p, *out;
+	unsigned char *pend;
+	size_t n, o;
+
+	do {
+		for (o = 0; o < sizeof(ibuf); o += n)
+			if (!(n = fread(ibuf + o, 1, sizeof(ibuf) - o, fp)))
+				break;
+		if (!o)
+			goto end;
+		n = o;
+		o %= 3;
+		out = obuf;
+		for (p = ibuf, pend = p + n - o; p != pend; p += 3, out += 4) {
+			out[0] = lut[p[0] >> 2];
+			out[1] = lut[(unsigned char)(p[0] << 4) | (p[1] >> 4)];
+			out[2] = lut[(unsigned char)(p[1] << 2) | (p[2] >> 6)];
+			out[3] = lut[p[2]];
+		}
+		n = (size_t)(out - obuf);
+		output(obuf, 1, n, stdout);
+	} while (!o);
+
+	/* This part is only reached if padding is required. { */
+
+	obuf[0] = lut[p[0] >> 2];
+	if (o == 1) {
+		obuf[1] = lut[(unsigned char)(p[0] << 4)];
+		obuf[2] = '=';
+	} else {
+		obuf[1] = lut[(unsigned char)(p[0] << 4) | (p[1] >> 4)];
+		obuf[2] = lut[(unsigned char)(p[1] << 2)];
+	}
+	obuf[3] = '=';
+	output(obuf, 1, 4, stdout);
+
+	/* } */
+
+end:
+	if (pos)
+		fputc('\n', stdout);
+}
+
+static void
+decode(FILE *fp)
+{
+	unsigned char ibuf[BUFSIZ / 2];
+	unsigned char obuf[BUFSIZ / 2 - ((BUFSIZ / 2) % 3)];
+	unsigned char jbuf[4];
+	unsigned char value;
+	size_t i, j = 0, n, o = 0, pads = 0;
+	register unsigned char *p;
+
+	memset(lut, ~0, sizeof(lut));
+	for (i = 0; i < 64; i++)
+		lut[(int)(ALPHABET[i])] = i;
+
+	while ((n = fread(ibuf, 1, sizeof(ibuf), fp))) {
+		for (p = ibuf; n--; p++) {
+			value = lut[*p];
+			if (value == 0xFF) {
+				if (*p == '=') {
+					if (++pads > 2)
+						eprintf("invalid input\n");
+					value = 0;
+				} else if (*p == '\n') {
+					continue;
+				} else if (pads) {
+					eprintf("invalid input\n");
+				} else if (iflag) {
+					continue;
+				} else {
+					eprintf("invalid input\n");
+				}
+			}
+			jbuf[j++] = value;
+			if (j == 4) {
+				j = 0;
+				obuf[o++] = (jbuf[0] << 2) | (jbuf[1] >> 4);
+				obuf[o++] = (jbuf[1] << 4) | (jbuf[2] >> 2);
+				obuf[o++] = (jbuf[2] << 6) | (jbuf[3] >> 0);
+				if (o == sizeof(obuf)) {
+					fwrite(obuf, 1, o - pads, stdout);
+					o = 0;
+				}
+			}
+		}
+	}
+	if (j && feof(fp))
+		eprintf("invalid input\n");
+	if (o)
+		fwrite(obuf, 1, o - pads, stdout);
+}
+
+static void
+usage(void)
+{
+	/* Logically, it should be [-d [-i] | -w col], but
+	 * for compatibility we let it be [-di] [-w col]. */
+
+	eprintf("usage: %s [-di] [-w col] [file]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int dflag = 0;
+	long wflag = -1;
+	FILE *fp = stdin;
+	char *path = "<stdin>";
+
+	ARGBEGIN {
+	case 'd':
+		dflag = 1;
+		break;
+	case 'i':
+		iflag = 1;
+		break;
+	case 'w':
+		wflag = estrtonum(EARGF(usage()), 0, LONG_MAX);
+		break;
+	default:
+		usage();
+	} ARGEND
+
+	if (argc > 1)
+		usage();
+
+	if (argc && strcmp(*argv, "-")) {
+		path = *argv;
+		if (!(fp = fopen(*argv, "r")))
+			eprintf("fopen %s:", *argv);
+	}
+
+	if (wflag == 0) {
+		output = &fwrite;
+		pos = 1;
+	} else {
+		output = &wrapwrite;
+		cols = wflag > 0 ? wflag : 76;
+	}
+
+	(dflag ? decode : encode)(fp);
+	return fshut(fp, path) || fshut(stdout, "<stdout>");
+}
-- 
2.7.4
Received on Tue Mar 29 2016 - 18:59:24 CEST

This archive was generated by hypermail 2.3.0 : Tue Mar 29 2016 - 19:00:19 CEST