aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore8
-rw-r--r--LICENSE15
-rw-r--r--Makefile51
-rw-r--r--arg.h38
-rw-r--r--b224sum.168
-rw-r--r--b256sum.168
-rw-r--r--b384sum.168
-rw-r--r--b512sum.168
-rw-r--r--bsum.172
-rw-r--r--bsum.c533
-rw-r--r--common.h14
-rw-r--r--config.mk8
12 files changed, 1011 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d17d682
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*\#*
+*~
+*.o
+*.su
+*.gcov
+*.gcno
+*.gcda
+/bsum
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d0aa103
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2022 Mattias Andrée <maandree@kth.se>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3f743a8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
+.POSIX:
+
+CONFIGFILE = config.mk
+include $(CONFIGFILE)
+
+OBJ =\
+ bsum.o
+
+HDR =\
+ arg.h\
+ common.h
+
+all: bsum
+$(OBJ): $(HDR)
+
+.c.o:
+ $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+
+bsum: $(OBJ)
+ $(CC) -o $@ $(OBJ) $(LDFLAGS)
+
+install: bsum
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/bin"
+ mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1/"
+ cp -- bsum "$(DESTDIR)$(PREFIX)/bin/"
+ ln -sf -- bsum "$(DESTDIR)$(PREFIX)/bin/b224sum"
+ ln -sf -- bsum "$(DESTDIR)$(PREFIX)/bin/b256sum"
+ ln -sf -- bsum "$(DESTDIR)$(PREFIX)/bin/b384sum"
+ ln -sf -- bsum "$(DESTDIR)$(PREFIX)/bin/b512sum"
+ cp -- bsum.1 "$(DESTDIR)$(MANPREFIX)/man1/"
+
+uninstall:
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/bsum"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/b224sum"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/b256sum"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/b384sum"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/b512sum"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/bsum.1"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/b224sum.1"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/b256sum.1"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/b384sum.1"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/b512sum.1"
+
+clean:
+ -rm -f -- *.o *.su *.gcov *.gcno *.gcda
+ -rm -f -- bsum
+
+.SUFFIXES:
+.SUFFIXES: .o .c
+
+.PHONY: all install uninstall clean
diff --git a/arg.h b/arg.h
new file mode 100644
index 0000000..1231320
--- /dev/null
+++ b/arg.h
@@ -0,0 +1,38 @@
+/* Trivial code, not subject to copyright, use as you see fit.
+ * Reimplementation of 20h's arg.h */
+
+#ifndef ARG_H
+#define ARG_H
+
+#include <stddef.h>
+
+
+extern const char *argv0;
+
+
+#define ARGBEGIN do {\
+ char arg_h_flag_, arg_h_break_;\
+ if (!argc)\
+ break;\
+ argv0 = argv[0];\
+ while (--argc, *++argv && argv[0][0] == '-' && argv[0][1]) {\
+ if (argv[0][1] == '-' && !argv[0][2]) {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (arg_h_break_ = 0; !arg_h_break_ && *++*argv;) {\
+ switch ((arg_h_flag_ = **argv))
+
+#define ARGEND }\
+ }\
+ } while (0)
+
+
+#define FLAG() (arg_h_flag_)
+
+#define ARG() (arg_h_break_ = 1, argv[0][1] ? &argv[0][1] :\
+ argv[1] ? (argc--, *++argv) :\
+ (usage(), NULL))
+
+#endif
diff --git a/b224sum.1 b/b224sum.1
new file mode 100644
index 0000000..82c6a48
--- /dev/null
+++ b/b224sum.1
@@ -0,0 +1,68 @@
+.TH B224SUM 1 blakesum
+.SH NAME
+b224sum - Compute and check BLAKE-224 message digests
+.SH SYNOPSIS
+.B bsum
+[-c | -B | -L | -U] [-xz]
+.RI [ file "] ..."
+.SH DESCRIPTION
+Print or check BLAKE-224 checksums.
+.SH OPTIONS
+The
+.B b224sum
+utility conforms to the Base Definitions volume of POSIX.1-2017,
+.IR "Section 12.2" ,
+.IR "Utility Syntax Guidelines" .
+.PP
+The following options are supported:
+.TP
+.B -B
+Output checksums in binary representation. This suppresses
+the filenames and checksum delimiters. Only the checksums
+are printed.
+.TP
+.B -c
+Read BLAKE-224 sums from the file and check them against
+the files on your systems. The input files files should be
+formatted as the output of this program, or similarly.
+This is not going to work if any of the filenames in the
+input files starts with <space> or <tab>, or if they
+contain a <newline, unless the
+.B -z
+option is also used.
+.TP
+.B -L
+Output checksums in lower-case hexadecimal representation. (Default)
+.TP
+.B -U
+Output checksums in upper-case hexadecimal representation.
+.TP
+.B -x
+Convert input files from hexadecimal form to binary form
+before calculating the checksums.
+.TP
+.B -z
+Lines end with NUL instead of LF. If used with
+.BR -c ,
+this applies to read files (not the output), but it will
+also apply more strict parsing and allow any whitespace
+in file names.
+.SH OPERANDS
+The following operands are supported:
+.TP
+.I file
+File to read. The standard input will be used
+.B -
+or no
+.I file
+is specified.
+.SH EXIT STATUS
+.TP
+0
+Successful completion.
+.TP
+1
+Checksums did not match or a file did not exist.
+.TP
+2
+An error occurred.
diff --git a/b256sum.1 b/b256sum.1
new file mode 100644
index 0000000..4998b57
--- /dev/null
+++ b/b256sum.1
@@ -0,0 +1,68 @@
+.TH B256SUM 1 blakesum
+.SH NAME
+b256sum - Compute and check BLAKE-256 message digests
+.SH SYNOPSIS
+.B bsum
+[-c | -B | -L | -U] [-xz]
+.RI [ file "] ..."
+.SH DESCRIPTION
+Print or check BLAKE-256 checksums.
+.SH OPTIONS
+The
+.B b256sum
+utility conforms to the Base Definitions volume of POSIX.1-2017,
+.IR "Section 12.2" ,
+.IR "Utility Syntax Guidelines" .
+.PP
+The following options are supported:
+.TP
+.B -B
+Output checksums in binary representation. This suppresses
+the filenames and checksum delimiters. Only the checksums
+are printed.
+.TP
+.B -c
+Read BLAKE-256 sums from the file and check them against
+the files on your systems. The input files files should be
+formatted as the output of this program, or similarly.
+This is not going to work if any of the filenames in the
+input files starts with <space> or <tab>, or if they
+contain a <newline, unless the
+.B -z
+option is also used.
+.TP
+.B -L
+Output checksums in lower-case hexadecimal representation. (Default)
+.TP
+.B -U
+Output checksums in upper-case hexadecimal representation.
+.TP
+.B -x
+Convert input files from hexadecimal form to binary form
+before calculating the checksums.
+.TP
+.B -z
+Lines end with NUL instead of LF. If used with
+.BR -c ,
+this applies to read files (not the output), but it will
+also apply more strict parsing and allow any whitespace
+in file names.
+.SH OPERANDS
+The following operands are supported:
+.TP
+.I file
+File to read. The standard input will be used
+.B -
+or no
+.I file
+is specified.
+.SH EXIT STATUS
+.TP
+0
+Successful completion.
+.TP
+1
+Checksums did not match or a file did not exist.
+.TP
+2
+An error occurred.
diff --git a/b384sum.1 b/b384sum.1
new file mode 100644
index 0000000..925b698
--- /dev/null
+++ b/b384sum.1
@@ -0,0 +1,68 @@
+.TH B384SUM 1 blakesum
+.SH NAME
+b384sum - Compute and check BLAKE-384 message digests
+.SH SYNOPSIS
+.B bsum
+[-c | -B | -L | -U] [-xz]
+.RI [ file "] ..."
+.SH DESCRIPTION
+Print or check BLAKE-384 checksums.
+.SH OPTIONS
+The
+.B b384sum
+utility conforms to the Base Definitions volume of POSIX.1-2017,
+.IR "Section 12.2" ,
+.IR "Utility Syntax Guidelines" .
+.PP
+The following options are supported:
+.TP
+.B -B
+Output checksums in binary representation. This suppresses
+the filenames and checksum delimiters. Only the checksums
+are printed.
+.TP
+.B -c
+Read BLAKE-384 sums from the file and check them against
+the files on your systems. The input files files should be
+formatted as the output of this program, or similarly.
+This is not going to work if any of the filenames in the
+input files starts with <space> or <tab>, or if they
+contain a <newline, unless the
+.B -z
+option is also used.
+.TP
+.B -L
+Output checksums in lower-case hexadecimal representation. (Default)
+.TP
+.B -U
+Output checksums in upper-case hexadecimal representation.
+.TP
+.B -x
+Convert input files from hexadecimal form to binary form
+before calculating the checksums.
+.TP
+.B -z
+Lines end with NUL instead of LF. If used with
+.BR -c ,
+this applies to read files (not the output), but it will
+also apply more strict parsing and allow any whitespace
+in file names.
+.SH OPERANDS
+The following operands are supported:
+.TP
+.I file
+File to read. The standard input will be used
+.B -
+or no
+.I file
+is specified.
+.SH EXIT STATUS
+.TP
+0
+Successful completion.
+.TP
+1
+Checksums did not match or a file did not exist.
+.TP
+2
+An error occurred.
diff --git a/b512sum.1 b/b512sum.1
new file mode 100644
index 0000000..ebaf844
--- /dev/null
+++ b/b512sum.1
@@ -0,0 +1,68 @@
+.TH B512SUM 1 blakesum
+.SH NAME
+b512sum - Compute and check BLAKE-512 message digests
+.SH SYNOPSIS
+.B bsum
+[-c | -B | -L | -U] [-xz]
+.RI [ file "] ..."
+.SH DESCRIPTION
+Print or check BLAKE-512 checksums.
+.SH OPTIONS
+The
+.B b512sum
+utility conforms to the Base Definitions volume of POSIX.1-2017,
+.IR "Section 12.2" ,
+.IR "Utility Syntax Guidelines" .
+.PP
+The following options are supported:
+.TP
+.B -B
+Output checksums in binary representation. This suppresses
+the filenames and checksum delimiters. Only the checksums
+are printed.
+.TP
+.B -c
+Read BLAKE-512 sums from the file and check them against
+the files on your systems. The input files files should be
+formatted as the output of this program, or similarly.
+This is not going to work if any of the filenames in the
+input files starts with <space> or <tab>, or if they
+contain a <newline, unless the
+.B -z
+option is also used.
+.TP
+.B -L
+Output checksums in lower-case hexadecimal representation. (Default)
+.TP
+.B -U
+Output checksums in upper-case hexadecimal representation.
+.TP
+.B -x
+Convert input files from hexadecimal form to binary form
+before calculating the checksums.
+.TP
+.B -z
+Lines end with NUL instead of LF. If used with
+.BR -c ,
+this applies to read files (not the output), but it will
+also apply more strict parsing and allow any whitespace
+in file names.
+.SH OPERANDS
+The following operands are supported:
+.TP
+.I file
+File to read. The standard input will be used
+.B -
+or no
+.I file
+is specified.
+.SH EXIT STATUS
+.TP
+0
+Successful completion.
+.TP
+1
+Checksums did not match or a file did not exist.
+.TP
+2
+An error occurred.
diff --git a/bsum.1 b/bsum.1
new file mode 100644
index 0000000..d8ca915
--- /dev/null
+++ b/bsum.1
@@ -0,0 +1,72 @@
+.TH BSUM 1 blakesum
+.SH NAME
+bsum - Compute and check BLAKE message digests
+.SH SYNOPSIS
+.B bsum
+[-l bits] [-c | -B | -L | -U] [-xz]
+.RI [ file "] ..."
+.SH DESCRIPTION
+Print or check BLAKE checksums.
+.SH OPTIONS
+The
+.B bsum
+utility conforms to the Base Definitions volume of POSIX.1-2017,
+.IR "Section 12.2" ,
+.IR "Utility Syntax Guidelines" .
+.PP
+The following options are supported:
+.TP
+.B -B
+Output checksums in binary representation. This suppresses
+the filenames and checksum delimiters. Only the checksums
+are printed.
+.TP
+.B -c
+Read BLAKE sums from the file and check them against the
+files on your systems. The input files files should be
+formatted as the output of this program, or similarly.
+This is not going to work if any of the filenames in the
+input files starts with <space> or <tab>, or if they
+contain a <newline, unless the
+.B -z
+option is also used.
+.TP
+.B -L
+Output checksums in lower-case hexadecimal representation. (Default)
+.TP
+.B -l
+Select version of the BLAKE algorithm. Valid values
+are 224 (default), 256, 384, and 512.
+.TP
+.B -U
+Output checksums in upper-case hexadecimal representation.
+.TP
+.B -x
+Convert input files from hexadecimal form to binary form
+before calculating the checksums.
+.TP
+.B -z
+Lines end with NUL instead of LF. If used with
+.BR -c ,
+this applies to read files (not the output), but it will
+also apply more strict parsing and allow any whitespace
+in file names.
+.SH OPERANDS
+The following operands are supported:
+.TP
+.I file
+File to read. The standard input will be used
+.B -
+or no
+.I file
+is specified.
+.SH EXIT STATUS
+.TP
+0
+Successful completion.
+.TP
+1
+Checksums did not match or a file did not exist.
+.TP
+2
+An error occurred.
diff --git a/bsum.c b/bsum.c
new file mode 100644
index 0000000..cbc8b51
--- /dev/null
+++ b/bsum.c
@@ -0,0 +1,533 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+const char *argv0 = "bsum";
+
+static int lenght_by_command_name = 0;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s%s [-c | -B | -L | -U] [-xz] [file] ...",
+ argv0, lenght_by_command_name ? "" : " [-l bits]");
+ exit(2);
+}
+
+static void *
+erealloc(void *ptr, size_t n)
+{
+ ptr = realloc(ptr, n);
+ if (!ptr) {
+ fprintf(stderr, "%s: %s\n", argv0, strerror(errno));
+ exit(2);
+ }
+ return ptr;
+}
+
+static void
+get_lenght_by_command_name(const char *command)
+{
+ const char *p;
+ p = strrchr(command, '/');
+ p = p ? &p[1] : command;
+ if (strstr(p, "b224sum")) {
+ lenght_by_command_name = 224;
+ } else if (strstr(p, "b256sum")) {
+ lenght_by_command_name = 256;
+ } else if (strstr(p, "b384sum")) {
+ lenght_by_command_name = 384;
+ } else if (strstr(p, "b512sum")) {
+ lenght_by_command_name = 512;
+ }
+}
+
+static int
+hash_file_blake224(int fd, const char *fname, int decode_hex, unsigned char hash[], size_t *hash_lenp)
+{
+ struct libblake_blake224_state state;
+ char *buf = NULL;
+ size_t size = 0;
+ size_t len = 0;
+ size_t off = 0;
+ size_t req;
+ ssize_t r;
+ int ok;
+ libblake_blake224_init(&state);
+ for (;;) {
+ if (len == size)
+ buf = erealloc(buf, size += 8 << 10);
+ r = read(fd, &buf[len], size - len);
+ if (r <= 0) {
+ if (!r)
+ break;
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s: %s: %s\n", argv0, fname, strerror(errno));
+ return -1;
+ }
+ len += (size_t)r;
+ if (!decode_hex) {
+ off += libblake_blake224_update(&state, &buf[off], len - off);
+ if (off == len)
+ off = 0;
+ }
+ }
+ if (off)
+ memmove(&buf[0], &buf[off], len -= off);
+ if (decode_hex) {
+ len = libblake_decode_hex(buf, len, buf, &ok);
+ if (!ok) {
+ fprintf(stderr, "%s: %s: %s\n", argv0, fname, "invalid hexadecimal input");
+ return -1;
+ }
+ }
+ req = libblake_blake224_digest_get_required_input_size(len, 0, NULL);
+ if (req > size)
+ buf = erealloc(buf, size);
+ libblake_blake224_digest(&state, buf, len, 0, NULL, hash);
+ *hash_lenp = LIBBLAKE_BLAKE224_OUTPUT_SIZE;
+ free(buf);
+ return 0;
+}
+
+static int
+hash_file_blake256(int fd, const char *fname, int decode_hex, unsigned char hash[], size_t *hash_lenp)
+{
+ struct libblake_blake256_state state;
+ char *buf = NULL;
+ size_t size = 0;
+ size_t len = 0;
+ size_t off = 0;
+ size_t req;
+ ssize_t r;
+ int ok;
+ libblake_blake256_init(&state);
+ for (;;) {
+ if (len == size)
+ buf = erealloc(buf, size += 8 << 10);
+ r = read(fd, &buf[len], size - len);
+ if (r <= 0) {
+ if (!r)
+ break;
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s: %s: %s\n", argv0, fname, strerror(errno));
+ return -1;
+ }
+ len += (size_t)r;
+ if (!decode_hex) {
+ off += libblake_blake256_update(&state, &buf[off], len - off);
+ if (off == len)
+ off = 0;
+ }
+ }
+ if (off)
+ memmove(&buf[0], &buf[off], len -= off);
+ if (decode_hex) {
+ len = libblake_decode_hex(buf, len, buf, &ok);
+ if (!ok) {
+ fprintf(stderr, "%s: %s: %s\n", argv0, fname, "invalid hexadecimal input");
+ return -1;
+ }
+ }
+ req = libblake_blake256_digest_get_required_input_size(len, 0, NULL);
+ if (req > size)
+ buf = erealloc(buf, size);
+ libblake_blake256_digest(&state, buf, len, 0, NULL, hash);
+ *hash_lenp = LIBBLAKE_BLAKE256_OUTPUT_SIZE;
+ free(buf);
+ return 0;
+}
+
+static int
+hash_file_blake384(int fd, const char *fname, int decode_hex, unsigned char hash[], size_t *hash_lenp)
+{
+ struct libblake_blake384_state state;
+ char *buf = NULL;
+ size_t size = 0;
+ size_t len = 0;
+ size_t off = 0;
+ size_t req;
+ ssize_t r;
+ int ok;
+ libblake_blake384_init(&state);
+ for (;;) {
+ if (len == size)
+ buf = erealloc(buf, size += 8 << 10);
+ r = read(fd, &buf[len], size - len);
+ if (r <= 0) {
+ if (!r)
+ break;
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s: %s: %s\n", argv0, fname, strerror(errno));
+ return -1;
+ }
+ len += (size_t)r;
+ if (!decode_hex) {
+ off += libblake_blake384_update(&state, &buf[off], len - off);
+ if (off == len)
+ off = 0;
+ }
+ }
+ if (off)
+ memmove(&buf[0], &buf[off], len -= off);
+ if (decode_hex) {
+ len = libblake_decode_hex(buf, len, buf, &ok);
+ if (!ok) {
+ fprintf(stderr, "%s: %s: %s\n", argv0, fname, "invalid hexadecimal input");
+ return -1;
+ }
+ }
+ req = libblake_blake384_digest_get_required_input_size(len, 0, NULL);
+ if (req > size)
+ buf = erealloc(buf, size);
+ libblake_blake384_digest(&state, buf, len, 0, NULL, hash);
+ *hash_lenp = LIBBLAKE_BLAKE384_OUTPUT_SIZE;
+ free(buf);
+ return 0;
+}
+
+static int
+hash_file_blake512(int fd, const char *fname, int decode_hex, unsigned char hash[], size_t *hash_lenp)
+{
+ struct libblake_blake512_state state;
+ char *buf = NULL;
+ size_t size = 0;
+ size_t len = 0;
+ size_t off = 0;
+ size_t req;
+ ssize_t r;
+ int ok;
+ libblake_blake512_init(&state);
+ for (;;) {
+ if (len == size)
+ buf = erealloc(buf, size += 8 << 10);
+ r = read(fd, &buf[len], size - len);
+ if (r <= 0) {
+ if (!r)
+ break;
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s: %s: %s\n", argv0, fname, strerror(errno));
+ return -1;
+ }
+ len += (size_t)r;
+ if (!decode_hex) {
+ off += libblake_blake512_update(&state, &buf[off], len - off);
+ if (off == len)
+ off = 0;
+ }
+ }
+ if (off)
+ memmove(&buf[0], &buf[off], len -= off);
+ if (decode_hex) {
+ len = libblake_decode_hex(buf, len, buf, &ok);
+ if (!ok) {
+ fprintf(stderr, "%s: %s: %s\n", argv0, fname, "invalid hexadecimal input");
+ return -1;
+ }
+ }
+ req = libblake_blake512_digest_get_required_input_size(len, 0, NULL);
+ if (req > size)
+ buf = erealloc(buf, size);
+ libblake_blake512_digest(&state, buf, len, 0, NULL, hash);
+ *hash_lenp = LIBBLAKE_BLAKE512_OUTPUT_SIZE;
+ free(buf);
+ return 0;
+}
+
+static int
+parse_fd(const char *name)
+{
+ long int num;
+ char *end;
+ if (!isdigit(*name))
+ return -1;
+ errno = 0;
+ num = strtol(name, &end, 10);
+ if (num > INT_MAX || *end || errno)
+ return -1;
+ return (int)num;
+}
+
+static int
+open_file(const char *path, int *closep)
+{
+ int fd = -1;
+
+ *closep = 0;
+
+ if (!strcmp(path, "-"))
+ fd = STDIN_FILENO;
+ else if (!strcmp(path, "/dev/stdin"))
+ fd = STDIN_FILENO;
+ else if (!strcmp(path, "/dev/stdout"))
+ fd = STDOUT_FILENO;
+ else if (!strcmp(path, "/dev/stderr"))
+ fd = STDERR_FILENO;
+ else if (!strncmp(path, "/dev/fd/", sizeof("/dev/fd/") - 1))
+ fd = parse_fd(&path[sizeof("/dev/fd/") - 1]);
+ else if (!strncmp(path, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1))
+ fd = parse_fd(&path[sizeof("/proc/self/fd/") - 1]);
+
+ if (fd < 0) {
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+ *closep = 1;
+ }
+
+ return fd;
+}
+
+static int
+hash_file(const char *path, int length, int decode_hex, unsigned char hash[], size_t *hash_lenp)
+{
+ int ret, fd, close_fd;
+
+ fd = open_file(path, &close_fd);
+ if (fd < 0) {
+ fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno));
+ return -1;
+ }
+
+ if (length == 224)
+ ret = hash_file_blake224(fd, path, decode_hex, hash, hash_lenp);
+ else if (length == 256)
+ ret = hash_file_blake256(fd, path, decode_hex, hash, hash_lenp);
+ else if (length == 384)
+ ret = hash_file_blake384(fd, path, decode_hex, hash, hash_lenp);
+ else if (length == 512)
+ ret = hash_file_blake512(fd, path, decode_hex, hash, hash_lenp);
+ else
+ abort();
+
+ if (close_fd)
+ close(fd);
+ return ret;
+}
+
+static int
+hash_and_print(const char *path, int length, int decode_hex, char newline, int output_case)
+{
+ unsigned char hash[LIBBLAKE_BLAKE512_OUTPUT_SIZE];
+ char hex[LIBBLAKE_BLAKE512_OUTPUT_SIZE * 2 + 1];
+ size_t hash_len;
+
+ if (hash_file(path, length, decode_hex, hash, &hash_len))
+ return -1;
+
+ if (output_case < 0) {
+ fwrite(hash, 1, hash_len, stdout);
+ } else {
+ libblake_encode_hex(hash, hash_len, hex, output_case);
+ printf("%s %s%c", hex, path, newline);
+ }
+
+ return 0;
+}
+
+static int
+check_and_print_file(const char *path, int length, int decode_hex, char *expected)
+{
+ unsigned char hash[LIBBLAKE_BLAKE512_OUTPUT_SIZE];
+ int r, fd, close_fd;
+
+ fd = open_file(path, &close_fd);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno));
+ missing:
+ printf("%s: Missing\n", path);
+ return -1;
+ }
+
+ if (length == 224)
+ r = hash_file_blake224(fd, path, decode_hex, hash, &(size_t){0});
+ else if (length == 256)
+ r = hash_file_blake256(fd, path, decode_hex, hash, &(size_t){0});
+ else if (length == 384)
+ r = hash_file_blake384(fd, path, decode_hex, hash, &(size_t){0});
+ else if (length == 512)
+ r = hash_file_blake512(fd, path, decode_hex, hash, &(size_t){0});
+ else
+ abort();
+
+ if (close_fd)
+ close(fd);
+
+ if (r < 0)
+ goto missing;
+
+ libblake_decode_hex(expected, (size_t)length / 4, expected, &(int){0});
+ if (!memcmp(hash, expected, (size_t)length / 8)) {
+ printf("%s: OK\n", path);
+ return 0;
+ } else {
+ printf("%s: Fail\n", path);
+ return -1;
+ }
+}
+
+static int
+check_and_print(const char *path, int length, int decode_hex, char newline)
+{
+ int fd, close_fd, status = 0;
+ char *buf = NULL;
+ size_t size = 0;
+ size_t len = 0;
+ ssize_t r;
+ size_t i, j, k;
+
+ fd = open_file(path, &close_fd);
+ if (fd < 0) {
+ fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno));
+ exit(2);
+ }
+
+ for (;;) {
+ if (len == size)
+ buf = erealloc(buf, size += 8 << 10);
+ r = read(fd, &buf[len], size - len);
+ if (r > 0) {
+ len += (size_t)r;
+ } else if (!r) {
+ break;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno));
+ exit(2);
+ }
+ }
+ buf = erealloc(buf, len + 1);
+ buf[len] = '\0';
+
+ if (newline) {
+ for (i = 0; i < len; i = k + 1) {
+ while (isspace(buf[i]))
+ i++;
+ for (j = i; j - i < (size_t)length / 4; j++)
+ if (!isxdigit(buf[j]))
+ goto corrupt;
+ if (j == len || !isblank(buf[j]))
+ goto corrupt;
+ buf[j] = '\0';
+ j++;
+ while (isblank(buf[j]))
+ j++;
+ if (!buf[j])
+ goto corrupt;
+ for (k = j; buf[k] && buf[k] != newline;)
+ k++;
+ buf[k] = '\0';
+ status |= check_and_print_file(&buf[j], length, decode_hex, &buf[i]);
+ }
+ } else {
+ for (i = 0; i < len; i = k + 1) {
+ for (j = i; j - i < (size_t)length / 4; j++)
+ if (!isxdigit(buf[j]))
+ goto corrupt;
+ if (buf[j + 0] != ' ' || buf[j + 1] != ' ')
+ goto corrupt;
+ buf[j] = '\0';
+ j += 2;
+ k = j + strlen(&buf[j]);
+ status |= check_and_print_file(&buf[j], length, decode_hex, &buf[i]);
+ }
+ }
+
+ if (close_fd)
+ close(fd);
+ return status;
+
+corrupt:
+ fprintf(stderr, "%s: %s: invalid file content\n", argv0, path);
+ exit(2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int flag_check = 0;
+ int flag_binary = 0;
+ int flag_lower = 0;
+ int flag_upper = 0;
+ int flag_hex = 0;
+ int flag_zero = 0;
+ int length;
+
+ int status = 0;
+ int output_case;
+ char newline;
+
+ if (argv[0])
+ get_lenght_by_command_name(argv[0]);
+
+ length = lenght_by_command_name;
+
+ ARGBEGIN {
+ case 'c':
+ flag_check = 1;
+ break;
+ case 'B':
+ flag_binary = 1;
+ break;
+ case 'L':
+ flag_lower = 1;
+ flag_upper = 0;
+ break;
+ case 'U':
+ flag_upper = 1;
+ flag_lower = 0;
+ break;
+ case 'x':
+ flag_hex = 1;
+ break;
+ case 'z':
+ flag_zero = 1;
+ break;
+ case 'l':
+ if (length)
+ usage();
+ length = atoi(ARG());
+ if (length != 224 && length != 256 && length != 384 && length != 512) {
+ fprintf(stderr, "%s: valid arguments for -l are 224 (default), 256, 384, and 512\n", argv0);
+ return 2;
+ }
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (flag_check + flag_binary + flag_lower + flag_upper > 1)
+ usage();
+
+ if (!length)
+ length = 224;
+
+ newline = flag_zero ? '\0' : '\n';
+ if (flag_check) {
+ if (!argc) {
+ status |= -check_and_print("-", length, flag_hex, newline);
+ } else {
+ for (; *argv; argv++)
+ status |= -check_and_print(*argv, length, flag_hex, newline);
+ }
+ } else {
+ output_case = flag_binary ? -1 : flag_upper;
+ if (!argc) {
+ status |= -hash_and_print("-", length, flag_hex, newline, output_case);
+ } else {
+ for (; *argv; argv++)
+ status |= -hash_and_print(*argv, length, flag_hex, newline, output_case);
+ }
+ }
+
+ if (fflush(stdout) || ferror(stdout) || fclose(stdout)) {
+ fprintf(stderr, "%s: %s\n", argv0, strerror(errno));
+ return 2;
+ }
+ return status;
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..b0cbd63
--- /dev/null
+++ b/common.h
@@ -0,0 +1,14 @@
+/* See LICENSE file for copyright and license details. */
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <libblake.h>
+
+#include "arg.h"
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..5656df1
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,8 @@
+PREFIX = /usr
+MANPREFIX = $(PREFIX)/share/man
+
+CC = c99
+
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
+CFLAGS = -Wall -O3
+LDFLAGS = -s -lblake