diff options
Diffstat (limited to '')
| -rw-r--r-- | .gitignore | 17 | ||||
| -rw-r--r-- | LICENSE | 15 | ||||
| -rw-r--r-- | Makefile | 37 | ||||
| -rw-r--r-- | README | 41 | ||||
| -rw-r--r-- | config.mk | 8 | ||||
| -rw-r--r-- | sumart.1 | 79 | ||||
| -rw-r--r-- | sumart.c | 215 |
7 files changed, 412 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ead19db --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*\#* +*~ +*.o +*.a +*.t +*.lo +*.to +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda +/sumart @@ -0,0 +1,15 @@ +ISC License + +© 2026 Mattias Andrée <m@maandree.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..dc89666 --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OBJ =\ + sumart.o + +HDR = + +all: sumart +$(OBJ): $(HDR) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +sumart: $(OBJ) + $(CC) -o $@ $(OBJ) $(LDFLAGS) + +install: sumart + mkdir -p -- "$(DESTDIR)$(PREFIX)/bin" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1/" + cp -- sumart "$(DESTDIR)$(PREFIX)/bin/" + cp -- sumart.1 "$(DESTDIR)$(MANPREFIX)/man1/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/bin/sumart" + -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/sumart.1" + +clean: + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.gch *.gcov *.gcno *.gcda + -rm -f -- sumart + +.SUFFIXES: +.SUFFIXES: .o .c + +.PHONY: all install uninstall clean @@ -0,0 +1,41 @@ +NAME + sumart - Visualise a checksum + +SYNOPSIS + sumart [rows columns] [checksum] + +DESCRIPTION + The sumart utility creates a visualisation of a checksum + using symbols and colours in a grid. + + If no checksum is specified, it is read from the standard + input. + + Any symbol that is not a hexadecimal digit is silently + discarded from the checksum. + +OPTIONS + The sumart utility conforms to the Base Definitions volume of + POSIX.1-2024, Section 12.2, Utility Syntax Guidelines. + + No options are supported. + +OPERANDS + The following operands are supported: + + rows + The number of rows tall the visualisation should be. + + colums + The number of columns wide the visualisation should be. + + checksum + The checksum to visualise. + +EXIT STATUS + 0 Successful completion. + + 1 An error occurred. + +SEE ALSO + None. diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..aeb8cec --- /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 = +LDFLAGS = -lsimple diff --git a/sumart.1 b/sumart.1 new file mode 100644 index 0000000..bf50214 --- /dev/null +++ b/sumart.1 @@ -0,0 +1,79 @@ +.TH SUMART 1 SUMART +.SH NAME +sumart \- Visualise a checksum + +.SH SYNOPSIS +.B sumart +.RI [ rows +.IR columns ] +.RI [ checksum ] + +.SH DESCRIPTION +The +.B sumart +utility creates a visualisation of a checksum +using symbols and colours in a grid. +.PP +If no +.I checksum +is specified, it is read from the standard input. +.PP +Any symbol that is not a hexadecimal digit +is silently discarded from the checksum. + +.SH OPTIONS +The +.B sumart +utility conforms to the Base Definitions volume of +POSIX.1-2024, +.IR "Section 12.2" , +.IR "Utility Syntax Guidelines" . +.PP +No options are supported. + +.SH OPERANDS +The following operands are supported: +.TP +.I rows +The number of rows tall the visualisation should be. +.TP +.I colums +The number of columns wide the visualisation should be. +.TP +.I checksum +The checksum to visualise. + +.SH STDIN +The +.B sumart +utility reads the checksum from the standard input +if no checksum is specified. + +.SH INPUT FILES +None. + +.SH ENVIRONMENT VARIABLES +No environment variables affect the execution of sumart. + +.SH STDOUT +The +.B sumart +utility prints the visualisation to the +standard output. + +.SH STDERR +The standard error is only used for diagnostic messages. + +.SH OUTPUT FILES +None. + +.SH EXIT STATUS +.TP +0 +Successful completion. +.TP +1 +An error occurred. + +.SH SEE ALSO +None. diff --git a/sumart.c b/sumart.c new file mode 100644 index 0000000..e5ae509 --- /dev/null +++ b/sumart.c @@ -0,0 +1,215 @@ +/* See LICENSE file for copyright and license details. */ +#include <libsimple.h> +#include <libsimple-arg.h> + +USAGE("[rows columns] [checksum]"); + +#define BITS 2 + + +int +main(int argc, char *argv[]) +{ +#if BITS == 5 + static const ssize_t x_movements[1 << BITS] = { + -3, -2, -1, +1, +2, +3, + -3, -2, -1, +1, +2, +3, + -3, -2, +2, +3, + -3, -2, +2, +3, + -3, -2, -1, +1, +2, +3, + -3, -2, -1, +1, +2, +3, + }; + static const ssize_t y_movements[1 << BITS] = { + -3, -3, -3, -3, -3, -3, + -2, -2, -2, -2, -2, -2, + -1, -1, -1, -1, + +1, +1, +1, +1, + +2, +2, +2, +2, +2, +2, + +3, +3, +3, +3, +3, +3, + }; +#elif BITS == 4 + static const ssize_t x_movements[1 << BITS] = { + -2, -1, 0, 0, +1, +2, + -2, -1, +1, +2, + -2, -1, 0, 0, +1, +2 + }; + static const ssize_t y_movements[1 << BITS] = { + -2, -1, -2, -1, -2, -1, + 0, 0, 0, 0, + +1, +2, +1, +2, +1, +2 + }; +#elif BITS == 3 + static const ssize_t x_movements[1 << BITS] = { + -1, 0, +1, + -1, +1, + -1, 0, +1 + }; + static const ssize_t y_movements[1 << BITS] = { + -1, -1, -1, + 0, 0, + +1, +1, +1 + }; +#elif BITS == 2 +# if 0 + static const ssize_t x_movements[1 << BITS] = { + -1, +1, + -1, +1 + }; + static const ssize_t y_movements[1 << BITS] = { + -1, -1, + +1, +1 + }; +# else + static const ssize_t x_movements[1 << BITS] = { + 0, -1, + +1, 0 + }; + static const ssize_t y_movements[1 << BITS] = { + -1, 0, + 0, +1 + }; +# endif +#endif + + static const char *const symbols[] = { + ":", ".", "+", "=", + "¤", "o", "*", "£", + "X", "%", "&", "B", + "@", "#", "/", "^" + }; + static const char *const colours[] = { + "", "34;1", "33;1" + }; + + const char *checksum; + char *data = NULL, *end; + size_t size = 0u; + size_t len = 0u, cell; + size_t i, n, x[2], y[2], v, bits; + int d, colour; + ssize_t r; + size_t rows = 15u, cols = 32u; + unsigned char *art; + + ARGBEGIN { + default: + usage(); + } ARGEND; + + if (argc > 1) { + if ('1' > argv[0][0u] || argv[0][0u] > '9' || + '1' > argv[1][0u] || argv[1][0u] > '9') + usage(); + + errno = 0; + rows = strtouz(argv[0], &end, 10); + if (!rows || *end || errno) + usage(); + cols = strtouz(argv[1], &end, 10); + if (!cols || *end || errno) + usage(); + + argc -= 2; + argv += 2; + } + + if (argc > 1) + usage(); + + if (argc) { + checksum = *argv; + } else { + for (;;) { + if (len == size) + data = erealloc(data, size += 128u); + r = read(STDIN_FILENO, &data[len], size - len); + if (r <= 0) { + if (!r) + break; + if (errno == EINVAL) + continue; + eprintf("read <stdin>:"); + } + n = len + (size_t)r; + for (i = len; i < n; i++) { + if (('0' <= data[i] && data[i] <= '9') || + ('A' <= data[i] && data[i] <= 'F') || + ('a' <= data[i] && data[i] <= 'f')) + data[len++] = data[i]; + } + } + data = erealloc(data, len + 1u); + data[len] = '\0'; + checksum = data; + } + + art = ecalloc(rows, cols); + + x[0] = x[1] = cols / 2u; + y[0] = y[1] = rows / 2u; + + colour = 0; + v = 0u; + bits = 0u; + for (i = 0u; checksum[i]; i++) { + if (('0' > checksum[i] || checksum[i] > '9') && + ('A' > checksum[i] || checksum[i] > 'F') && + ('a' > checksum[i] || checksum[i] > 'f')) + continue; + d = ((int)checksum[i] & 15) + (checksum[i] > '9' ? 9 : 0); + v <<= 4; + v |= (size_t)d; + bits += 4u; + while (bits >= BITS) { + x[colour] += (size_t)x_movements[v % ELEMSOF(x_movements)]; + y[colour] += (size_t)y_movements[v % ELEMSOF(y_movements)]; + x[colour] %= cols; + y[colour] %= rows; + cell = y[colour] * cols + x[colour]; + art[cell] = (unsigned char)(art[cell] + (colour ? 0x20u : 1u)); + art[cell] &= 0xEFu; + v >>= BITS; + bits -= BITS; + if (ELEMSOF(colours) > 1u) + colour ^= 1; + } + } + if (bits) { + x[colour] += (size_t)x_movements[v % ELEMSOF(x_movements)]; + y[colour] += (size_t)y_movements[v % ELEMSOF(y_movements)]; + x[colour] %= cols; + y[colour] %= rows; + cell = y[colour] * cols + x[colour]; + art[cell] = (unsigned char)(art[cell] + (colour ? 0x20u : 1u)); + art[cell] &= 0xEFu; + } + + printf("+"); + for (i = 0u; i < cols; i++) + printf("-"); + printf("+\n"); + for (*y = 0u; *y < rows; ++*y) { + printf("|"); + for (*x = 0u; *x < cols; ++*x) { + cell = *y * cols + *x; + if (!art[cell]) { + printf(" "); + continue; + } + printf("\033[%sm%s\033[m", + colours[(art[cell] / 0x20u) % ELEMSOF(colours)], + symbols[(art[cell] & 0x0Fu) % ELEMSOF(symbols)]); + } + printf("|\n"); + } + printf("+"); + for (i = 0u; i < cols; i++) + printf("-"); + printf("+\n"); + + if (ferror(stdout) || fclose(stdout)) + eprintf("printf:"); + free(art); + free(data); + return 0; +} |
