Signed-off-by: Mattias Andrée <[email protected]>
---
 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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


Reply via email to