diff options
Diffstat (limited to 'bin')
-rw-r--r-- | bin/beef.c | 2 | ||||
-rw-r--r-- | bin/bit.y | 2 | ||||
-rw-r--r-- | bin/c11.l | 2 | ||||
-rw-r--r-- | bin/dehtml.l | 2 | ||||
-rw-r--r-- | bin/downgrade.c | 2 | ||||
-rw-r--r-- | bin/dtch.c | 2 | ||||
-rw-r--r-- | bin/ever.c | 2 | ||||
-rw-r--r-- | bin/freecell.c | 2 | ||||
-rw-r--r-- | bin/git-comment.pl | 2 | ||||
-rw-r--r-- | bin/glitch.c | 687 | ||||
-rw-r--r-- | bin/hilex.c | 4 | ||||
-rw-r--r-- | bin/hilex.h | 2 | ||||
-rw-r--r-- | bin/make.l | 2 | ||||
-rw-r--r-- | bin/man1/up.1 | 18 | ||||
-rw-r--r-- | bin/man1/when.1 | 34 | ||||
-rw-r--r-- | bin/mdoc.l | 2 | ||||
-rw-r--r-- | bin/modem.c | 2 | ||||
-rw-r--r-- | bin/mtags.c | 2 | ||||
-rw-r--r-- | bin/nudge.c | 2 | ||||
-rw-r--r-- | bin/order.y | 2 | ||||
-rw-r--r-- | bin/pbd.c | 2 | ||||
-rw-r--r-- | bin/png.h | 2 | ||||
-rw-r--r-- | bin/pngo.c | 2 | ||||
-rw-r--r-- | bin/psf2png.c | 2 | ||||
-rw-r--r-- | bin/ptee.c | 2 | ||||
-rw-r--r-- | bin/quick.c | 2 | ||||
-rw-r--r-- | bin/relay.c | 2 | ||||
-rw-r--r-- | bin/scheme.c | 2 | ||||
-rw-r--r-- | bin/sh.l | 14 | ||||
-rw-r--r-- | bin/shotty.l | 6 | ||||
-rw-r--r-- | bin/title.c | 2 | ||||
-rw-r--r-- | bin/up.sh | 30 | ||||
-rw-r--r-- | bin/when.y | 130 | ||||
-rw-r--r-- | bin/xx.c | 2 |
34 files changed, 585 insertions, 390 deletions
diff --git a/bin/beef.c b/bin/beef.c index b2579b73..556f3088 100644 --- a/bin/beef.c +++ b/bin/beef.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/bit.y b/bin/bit.y index ab310492..1119bce6 100644 --- a/bin/bit.y +++ b/bin/bit.y @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/c11.l b/bin/c11.l index a4b8c25a..b1f0b960 100644 --- a/bin/c11.l +++ b/bin/c11.l @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 C. McEnroe <june@causal.agency> +/* Copyright (C) 2020 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/dehtml.l b/bin/dehtml.l index 3f2de592..799f0926 100644 --- a/bin/dehtml.l +++ b/bin/dehtml.l @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 C. McEnroe <june@causal.agency> +/* Copyright (C) 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/downgrade.c b/bin/downgrade.c index d4c5b598..31019714 100644 --- a/bin/downgrade.c +++ b/bin/downgrade.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 C. McEnroe <june@causal.agency> +/* Copyright (C) 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/dtch.c b/bin/dtch.c index 2aea53ae..026493dd 100644 --- a/bin/dtch.c +++ b/bin/dtch.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2017-2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2017-2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/ever.c b/bin/ever.c index f983912b..f8ff943b 100644 --- a/bin/ever.c +++ b/bin/ever.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 C. McEnroe <june@causal.agency> +/* Copyright (C) 2017 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/freecell.c b/bin/freecell.c index 11bed1c0..fbc0fe22 100644 --- a/bin/freecell.c +++ b/bin/freecell.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2021 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019, 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/git-comment.pl b/bin/git-comment.pl index 5100941f..5352702d 100644 --- a/bin/git-comment.pl +++ b/bin/git-comment.pl @@ -1,5 +1,5 @@ #!/usr/bin/env perl -# Copyright (C) 2021 C. McEnroe <june@causal.agency> +# Copyright (C) 2021 June McEnroe <june@causal.agency> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by diff --git a/bin/glitch.c b/bin/glitch.c index 9747f35a..d0c926f9 100644 --- a/bin/glitch.c +++ b/bin/glitch.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 C. McEnroe <june@causal.agency> +/* Copyright (C) 2018, 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -14,8 +14,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <arpa/inet.h> #include <err.h> +#include <inttypes.h> +#include <limits.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> @@ -25,286 +26,328 @@ #include <unistd.h> #include <zlib.h> -#define PACKED __attribute__((packed)) - -#define CRC_INIT (crc32(0, Z_NULL, 0)) +#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) static const char *path; static FILE *file; static uint32_t crc; -static void readExpect(void *ptr, size_t size, const char *expect) { - fread(ptr, size, 1, file); - if (ferror(file)) err(EX_IOERR, "%s", path); - if (feof(file)) errx(EX_DATAERR, "%s: missing %s", path, expect); - crc = crc32(crc, ptr, size); +static void pngRead(void *ptr, size_t len, const char *desc) { + size_t n = fread(ptr, len, 1, file); + if (!n && ferror(file)) err(EX_IOERR, "%s", path); + if (!n) errx(EX_DATAERR, "%s: missing %s", path, desc); + crc = crc32(crc, ptr, len); } -static void writeExpect(const void *ptr, size_t size) { - fwrite(ptr, size, 1, file); - if (ferror(file)) err(EX_IOERR, "%s", path); - crc = crc32(crc, ptr, size); +static void pngWrite(const void *ptr, size_t len) { + size_t n = fwrite(ptr, len, 1, file); + if (!n) err(EX_IOERR, "%s", path); + crc = crc32(crc, ptr, len); } -static const uint8_t Signature[8] = "\x89PNG\r\n\x1A\n"; +static const uint8_t Sig[8] = "\x89PNG\r\n\x1A\n"; -static void readSignature(void) { - uint8_t signature[8]; - readExpect(signature, 8, "signature"); - if (0 != memcmp(signature, Signature, 8)) { +static void sigRead(void) { + uint8_t sig[sizeof(Sig)]; + pngRead(sig, sizeof(sig), "signature"); + if (memcmp(sig, Sig, sizeof(sig))) { errx(EX_DATAERR, "%s: invalid signature", path); } } -static void writeSignature(void) { - writeExpect(Signature, sizeof(Signature)); +static void sigWrite(void) { + pngWrite(Sig, sizeof(Sig)); } -struct PACKED Chunk { - uint32_t size; - char type[4]; -}; +static uint32_t u32Read(const char *desc) { + uint8_t b[4]; + pngRead(b, sizeof(b), desc); + return (uint32_t)b[0] << 24 | (uint32_t)b[1] << 16 + | (uint32_t)b[2] << 8 | (uint32_t)b[3]; +} -static const char *typeStr(struct Chunk chunk) { - static char buf[5]; - memcpy(buf, chunk.type, 4); - return buf; +static void u32Write(uint32_t x) { + uint8_t b[4] = { x >> 24 & 0xFF, x >> 16 & 0xFF, x >> 8 & 0xFF, x & 0xFF }; + pngWrite(b, sizeof(b)); } -static struct Chunk readChunk(void) { +struct Chunk { + uint32_t len; + char type[5]; +}; + +static struct Chunk chunkRead(void) { struct Chunk chunk; - readExpect(&chunk, sizeof(chunk), "chunk"); - chunk.size = ntohl(chunk.size); - crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type)); + chunk.len = u32Read("chunk length"); + crc = crc32(0, Z_NULL, 0); + pngRead(chunk.type, 4, "chunk type"); + chunk.type[4] = 0; return chunk; } -static void writeChunk(struct Chunk chunk) { - chunk.size = htonl(chunk.size); - writeExpect(&chunk, sizeof(chunk)); - crc = crc32(CRC_INIT, (Byte *)chunk.type, sizeof(chunk.type)); +static void chunkWrite(struct Chunk chunk) { + u32Write(chunk.len); + crc = crc32(0, Z_NULL, 0); + pngWrite(chunk.type, 4); } -static void readCrc(void) { - uint32_t expected = crc; - uint32_t found; - readExpect(&found, sizeof(found), "CRC32"); - found = ntohl(found); - if (found != expected) { - errx( - EX_DATAERR, "%s: expected CRC32 %08X, found %08X", - path, expected, found - ); - } +static void crcRead(void) { + uint32_t expect = crc; + uint32_t actual = u32Read("CRC32"); + if (actual == expect) return; + errx( + EX_DATAERR, "%s: expected CRC32 %08X, found %08X", + path, expect, actual + ); } -static void writeCrc(void) { - uint32_t net = htonl(crc); - writeExpect(&net, sizeof(net)); +static void crcWrite(void) { + u32Write(crc); } -static void skipChunk(struct Chunk chunk) { - uint8_t discard[chunk.size]; - readExpect(discard, sizeof(discard), "chunk data"); - readCrc(); +static void chunkSkip(struct Chunk chunk) { + if (!(chunk.type[0] & 0x20)) { + errx(EX_CONFIG, "%s: unsupported critical chunk %s", path, chunk.type); + } + uint8_t buf[4096]; + while (chunk.len > sizeof(buf)) { + pngRead(buf, sizeof(buf), "chunk data"); + chunk.len -= sizeof(buf); + } + if (chunk.len) pngRead(buf, chunk.len, "chunk data"); + crcRead(); } -static struct PACKED { +enum Color { + Grayscale = 0, + Truecolor = 2, + Indexed = 3, + GrayscaleAlpha = 4, + TruecolorAlpha = 6, +}; +enum Compression { + Deflate, +}; +enum FilterMethod { + Adaptive, +}; +enum Interlace { + Progressive, + Adam7, +}; + +enum { HeaderLen = 13 }; +static struct { uint32_t width; uint32_t height; uint8_t depth; - enum PACKED { - Grayscale = 0, - Truecolor = 2, - Indexed = 3, - GrayscaleAlpha = 4, - TruecolorAlpha = 6, - } color; + uint8_t color; uint8_t compression; uint8_t filter; uint8_t interlace; } header; -_Static_assert(13 == sizeof(header), "header size"); -static size_t pixelBits(void) { +static size_t pixelLen; +static size_t lineLen; +static size_t dataLen; + +static void recalc(void) { + size_t pixelBits = header.depth; switch (header.color) { - case Grayscale: return 1 * header.depth; - case Truecolor: return 3 * header.depth; - case Indexed: return 1 * header.depth; - case GrayscaleAlpha: return 2 * header.depth; - case TruecolorAlpha: return 4 * header.depth; - default: abort(); + break; case GrayscaleAlpha: pixelBits *= 2; + break; case Truecolor: pixelBits *= 3; + break; case TruecolorAlpha: pixelBits *= 4; } + pixelLen = (pixelBits + 7) / 8; + lineLen = (header.width * pixelBits + 7) / 8; + dataLen = (1 + lineLen) * header.height; } -static size_t pixelSize(void) { - return (pixelBits() + 7) / 8; +static void headerRead(struct Chunk chunk) { + if (chunk.len != HeaderLen) { + errx( + EX_DATAERR, "%s: expected %s length %" PRIu32 ", found %" PRIu32, + path, chunk.type, (uint32_t)HeaderLen, chunk.len + ); + } + header.width = u32Read("header width"); + header.height = u32Read("header height"); + pngRead(&header.depth, 1, "header depth"); + pngRead(&header.color, 1, "header color"); + pngRead(&header.compression, 1, "header compression"); + pngRead(&header.filter, 1, "header filter"); + pngRead(&header.interlace, 1, "header interlace"); + crcRead(); + recalc(); +} + +static void headerWrite(void) { + struct Chunk ihdr = { HeaderLen, "IHDR" }; + chunkWrite(ihdr); + u32Write(header.width); + u32Write(header.height); + pngWrite(&header.depth, 1); + pngWrite(&header.color, 1); + pngWrite(&header.compression, 1); + pngWrite(&header.filter, 1); + pngWrite(&header.interlace, 1); + crcWrite(); } -static size_t lineSize(void) { - return (header.width * pixelBits() + 7) / 8; -} +static struct { + uint32_t len; + uint8_t rgb[256][3]; +} pal; -static size_t dataSize(void) { - return (1 + lineSize()) * header.height; +static struct { + uint32_t len; + uint8_t a[256]; +} trans; + +static void palClear(void) { + pal.len = 0; + trans.len = 0; } -static void readHeader(void) { - struct Chunk ihdr = readChunk(); - if (0 != memcmp(ihdr.type, "IHDR", 4)) { - errx(EX_DATAERR, "%s: expected IHDR, found %s", path, typeStr(ihdr)); +static void palRead(struct Chunk chunk) { + if (chunk.len % 3) { + errx( + EX_DATAERR, "%s: %s length %" PRIu32 " not divisible by 3", + path, chunk.type, chunk.len + ); } - if (ihdr.size != sizeof(header)) { + pal.len = chunk.len / 3; + if (pal.len > 256) { errx( - EX_DATAERR, "%s: expected IHDR size %zu, found %u", - path, sizeof(header), ihdr.size + EX_DATAERR, "%s: %s length %" PRIu32 " > 256", + path, chunk.type, pal.len ); } - readExpect(&header, sizeof(header), "header"); - readCrc(); - header.width = ntohl(header.width); - header.height = ntohl(header.height); - if (!header.width) errx(EX_DATAERR, "%s: invalid width 0", path); - if (!header.height) errx(EX_DATAERR, "%s: invalid height 0", path); + pngRead(pal.rgb, chunk.len, "palette data"); + crcRead(); } -static void writeHeader(void) { - struct Chunk ihdr = { .size = sizeof(header), .type = "IHDR" }; - writeChunk(ihdr); - header.width = htonl(header.width); - header.height = htonl(header.height); - writeExpect(&header, sizeof(header)); - writeCrc(); - header.width = ntohl(header.width); - header.height = ntohl(header.height); +static void palWrite(void) { + struct Chunk plte = { 3 * pal.len, "PLTE" }; + chunkWrite(plte); + pngWrite(pal.rgb, plte.len); + crcWrite(); } -static struct { - uint32_t len; - uint8_t entries[256][3]; -} palette; - -static void readPalette(void) { - struct Chunk chunk; - for (;;) { - chunk = readChunk(); - if (0 == memcmp(chunk.type, "PLTE", 4)) break; - skipChunk(chunk); +static void transRead(struct Chunk chunk) { + trans.len = chunk.len; + if (trans.len > 256) { + errx( + EX_DATAERR, "%s: %s length %" PRIu32 " > 256", + path, chunk.type, trans.len + ); } - palette.len = chunk.size / 3; - readExpect(palette.entries, chunk.size, "palette data"); - readCrc(); + pngRead(trans.a, chunk.len, "transparency data"); + crcRead(); } -static void writePalette(void) { - struct Chunk plte = { .size = 3 * palette.len, .type = "PLTE" }; - writeChunk(plte); - writeExpect(palette.entries, plte.size); - writeCrc(); +static void transWrite(void) { + struct Chunk trns = { trans.len, "tRNS" }; + chunkWrite(trns); + pngWrite(trans.a, trns.len); + crcWrite(); } static uint8_t *data; -static void readData(void) { - data = malloc(dataSize()); - if (!data) err(EX_OSERR, "malloc(%zu)", dataSize()); +static void dataAlloc(void) { + data = malloc(dataLen); + if (!data) err(EX_OSERR, "malloc"); +} - struct z_stream_s stream = { .next_out = data, .avail_out = dataSize() }; +static void dataRead(struct Chunk chunk) { + z_stream stream = { .next_out = data, .avail_out = dataLen }; int error = inflateInit(&stream); - if (error != Z_OK) errx(EX_SOFTWARE, "%s: inflateInit: %s", path, stream.msg); + if (error != Z_OK) errx(EX_SOFTWARE, "inflateInit: %s", stream.msg); for (;;) { - struct Chunk chunk = readChunk(); - if (0 == memcmp(chunk.type, "IDAT", 4)) { - uint8_t *idat = malloc(chunk.size); - if (!idat) err(EX_OSERR, "malloc"); - - readExpect(idat, chunk.size, "image data"); - readCrc(); + if (strcmp(chunk.type, "IDAT")) { + errx(EX_DATAERR, "%s: missing IDAT chunk", path); + } - stream.next_in = idat; - stream.avail_in = chunk.size; - int error = inflate(&stream, Z_SYNC_FLUSH); - free(idat); + uint8_t *idat = malloc(chunk.len); + if (!idat) err(EX_OSERR, "malloc"); - if (error == Z_STREAM_END) break; - if (error != Z_OK) errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg); + pngRead(idat, chunk.len, "image data"); + crcRead(); + + stream.next_in = idat; + stream.avail_in = chunk.len; + error = inflate(&stream, Z_SYNC_FLUSH); + free(idat); - } else if (0 == memcmp(chunk.type, "IEND", 4)) { - errx(EX_DATAERR, "%s: missing IDAT chunk", path); - } else { - skipChunk(chunk); + if (error == Z_STREAM_END) break; + if (error != Z_OK) { + errx(EX_DATAERR, "%s: inflate: %s", path, stream.msg); } - } + chunk = chunkRead(); + } inflateEnd(&stream); - if ((size_t)stream.total_out != dataSize()) { + if ((size_t)stream.total_out != dataLen) { errx( - EX_DATAERR, "%s: expected data size %zu, found %zu", - path, dataSize(), (size_t)stream.total_out + EX_DATAERR, "%s: expected data length %zu, found %zu", + path, dataLen, (size_t)stream.total_out ); } } -static void writeData(void) { - uLong size = compressBound(dataSize()); - uint8_t *deflate = malloc(size); - if (!deflate) err(EX_OSERR, "malloc"); +static void dataWrite(void) { + z_stream stream = { + .next_in = data, + .avail_in = dataLen, + }; + int error = deflateInit2( + &stream, Z_BEST_COMPRESSION, Z_DEFLATED, 15, 8, Z_FILTERED + ); + if (error != Z_OK) errx(EX_SOFTWARE, "deflateInit2: %s", stream.msg); - int error = compress2(deflate, &size, data, dataSize(), Z_BEST_SPEED); - if (error != Z_OK) errx(EX_SOFTWARE, "%s: compress2: %d", path, error); + uLong bound = deflateBound(&stream, dataLen); + uint8_t *buf = malloc(bound); + if (!buf) err(EX_OSERR, "malloc"); - struct Chunk idat = { .size = size, .type = "IDAT" }; - writeChunk(idat); - writeExpect(deflate, size); - writeCrc(); + stream.next_out = buf; + stream.avail_out = bound; + deflate(&stream, Z_FINISH); + deflateEnd(&stream); - free(deflate); -} + struct Chunk idat = { stream.total_out, "IDAT" }; + chunkWrite(idat); + pngWrite(buf, stream.total_out); + crcWrite(); + free(buf); -static void writeEnd(void) { - struct Chunk iend = { .size = 0, .type = "IEND" }; - writeChunk(iend); - writeCrc(); + struct Chunk iend = { 0, "IEND" }; + chunkWrite(iend); + crcWrite(); } -enum PACKED Filter { +enum Filter { None, Sub, Up, Average, Paeth, - FilterCount, + FilterCap, }; -static struct { - bool brokenPaeth; - bool filt; - bool recon; - uint8_t declareFilter; - uint8_t applyFilter; - enum Filter declareFilters[255]; - enum Filter applyFilters[255]; - bool invert; - bool mirror; - bool zeroX; - bool zeroY; -} options; - struct Bytes { - uint8_t x; - uint8_t a; - uint8_t b; - uint8_t c; + uint8_t x, a, b, c; }; +static bool brokenPaeth; static uint8_t paethPredictor(struct Bytes f) { int32_t p = (int32_t)f.a + (int32_t)f.b - (int32_t)f.c; - int32_t pa = abs(p - (int32_t)f.a); - int32_t pb = abs(p - (int32_t)f.b); - int32_t pc = abs(p - (int32_t)f.c); + int32_t pa = labs(p - (int32_t)f.a); + int32_t pb = labs(p - (int32_t)f.b); + int32_t pc = labs(p - (int32_t)f.c); if (pa <= pb && pa <= pc) return f.a; - if (options.brokenPaeth) { + if (brokenPaeth) { if (pb < pc) return f.b; } else { if (pb <= pc) return f.b; @@ -319,7 +362,7 @@ static uint8_t recon(enum Filter type, struct Bytes f) { case Up: return f.x + f.b; case Average: return f.x + ((uint32_t)f.a + (uint32_t)f.b) / 2; case Paeth: return f.x + paethPredictor(f); - default: abort(); + default: abort(); } } @@ -330,59 +373,59 @@ static uint8_t filt(enum Filter type, struct Bytes f) { case Up: return f.x - f.b; case Average: return f.x - ((uint32_t)f.a + (uint32_t)f.b) / 2; case Paeth: return f.x - paethPredictor(f); - default: abort(); + default: abort(); } } -static struct Line { - enum Filter type; - uint8_t data[]; -} **lines; - -static void scanlines(void) { - lines = calloc(header.height, sizeof(*lines)); - if (!lines) err(EX_OSERR, "calloc(%u, %zu)", header.height, sizeof(*lines)); - - size_t stride = 1 + lineSize(); - for (uint32_t y = 0; y < header.height; ++y) { - lines[y] = (struct Line *)&data[y * stride]; - if (lines[y]->type >= FilterCount) { - errx(EX_DATAERR, "%s: invalid filter type %hhu", path, lines[y]->type); - } - } +static uint8_t *lineType(uint32_t y) { + return &data[y * (1 + lineLen)]; +} +static uint8_t *lineData(uint32_t y) { + return 1 + lineType(y); } static struct Bytes origBytes(uint32_t y, size_t i) { - bool a = (i >= pixelSize()), b = (y > 0), c = (a && b); + bool a = (i >= pixelLen), b = (y > 0), c = (a && b); return (struct Bytes) { - .x = lines[y]->data[i], - .a = a ? lines[y]->data[i - pixelSize()] : 0, - .b = b ? lines[y - 1]->data[i] : 0, - .c = c ? lines[y - 1]->data[i - pixelSize()] : 0, + .x = lineData(y)[i], + .a = (a ? lineData(y)[i-pixelLen] : 0), + .b = (b ? lineData(y-1)[i] : 0), + .c = (c ? lineData(y-1)[i-pixelLen] : 0), }; } -static void reconData(void) { +static bool reconFilter; +static void dataRecon(void) { for (uint32_t y = 0; y < header.height; ++y) { - for (size_t i = 0; i < lineSize(); ++i) { - if (options.filt) { - lines[y]->data[i] = filt(lines[y]->type, origBytes(y, i)); + for (size_t i = 0; i < lineLen; ++i) { + if (reconFilter) { + lineData(y)[i] = filt(*lineType(y), origBytes(y, i)); } else { - lines[y]->data[i] = recon(lines[y]->type, origBytes(y, i)); + lineData(y)[i] = recon(*lineType(y), origBytes(y, i)); } } - lines[y]->type = None; + *lineType(y) = None; } } -static void filterData(void) { - for (uint32_t y = header.height - 1; y < header.height; --y) { - uint8_t filter[FilterCount][lineSize()]; - uint32_t heuristic[FilterCount] = {0}; +static bool filterRecon; +static size_t applyFilter; +static enum Filter applyFilters[256]; +static size_t declFilter; +static enum Filter declFilters[256]; + +static void dataFilter(void) { + uint8_t *filter[FilterCap]; + for (enum Filter i = None; i < FilterCap; ++i) { + filter[i] = malloc(lineLen); + if (!filter[i]) err(EX_OSERR, "malloc"); + } + for (uint32_t y = header.height-1; y < header.height; --y) { + uint32_t heuristic[FilterCap] = {0}; enum Filter minType = None; - for (enum Filter type = None; type < FilterCount; ++type) { - for (size_t i = 0; i < lineSize(); ++i) { - if (options.recon) { + for (enum Filter type = None; type < FilterCap; ++type) { + for (size_t i = 0; i < lineLen; ++i) { + if (filterRecon) { filter[type][i] = recon(type, origBytes(y, i)); } else { filter[type][i] = filt(type, origBytes(y, i)); @@ -391,49 +434,26 @@ static void filterData(void) { } if (heuristic[type] < heuristic[minType]) minType = type; } - - if (options.declareFilter) { - lines[y]->type = options.declareFilters[y % options.declareFilter]; + if (declFilter) { + *lineType(y) = declFilters[y % declFilter]; } else { - lines[y]->type = minType; + *lineType(y) = minType; } - - if (options.applyFilter) { - enum Filter type = options.applyFilters[y % options.applyFilter]; - memcpy(lines[y]->data, filter[type], lineSize()); + if (applyFilter) { + memcpy(lineData(y), filter[applyFilters[y % applyFilter]], lineLen); } else { - memcpy(lines[y]->data, filter[minType], lineSize()); + memcpy(lineData(y), filter[minType], lineLen); } } -} - -static void invert(void) { - for (uint32_t y = 0; y < header.height; ++y) { - for (size_t i = 0; i < lineSize(); ++i) { - lines[y]->data[i] ^= 0xFF; - } - } -} - -static void mirror(void) { - for (uint32_t y = 0; y < header.height; ++y) { - for (size_t i = 0, j = lineSize() - 1; i < j; ++i, --j) { - uint8_t t = lines[y]->data[i]; - lines[y]->data[i] = lines[y]->data[j]; - lines[y]->data[j] = t; - } - } -} - -static void zeroX(void) { - for (uint32_t y = 0; y < header.height; ++y) { - memset(lines[y]->data, 0, pixelSize()); + for (enum Filter i = None; i < FilterCap; ++i) { + free(filter[i]); } } -static void zeroY(void) { - memset(lines[0]->data, 0, lineSize()); -} +static bool invertData; +static bool mirrorData; +static bool zeroX; +static bool zeroY; static void glitch(const char *inPath, const char *outPath) { if (inPath) { @@ -441,98 +461,145 @@ static void glitch(const char *inPath, const char *outPath) { file = fopen(path, "r"); if (!file) err(EX_NOINPUT, "%s", path); } else { - path = "(stdin)"; + path = "stdin"; file = stdin; } - readSignature(); - readHeader(); - if (header.color == Indexed) readPalette(); - readData(); + sigRead(); + struct Chunk ihdr = chunkRead(); + if (strcmp(ihdr.type, "IHDR")) { + errx(EX_DATAERR, "%s: expected IHDR, found %s", path, ihdr.type); + } + headerRead(ihdr); + if (header.interlace != Progressive) { + errx(EX_CONFIG, "%s: unsupported interlacing", path); + } + + palClear(); + dataAlloc(); + for (;;) { + struct Chunk chunk = chunkRead(); + if (!strcmp(chunk.type, "PLTE")) { + palRead(chunk); + } else if (!strcmp(chunk.type, "tRNS")) { + transRead(chunk); + } else if (!strcmp(chunk.type, "IDAT")) { + dataRead(chunk); + } else if (!strcmp(chunk.type, "IEND")) { + break; + } else { + chunkSkip(chunk); + } + } fclose(file); - scanlines(); - reconData(); - filterData(); - if (options.invert) invert(); - if (options.mirror) mirror(); - if (options.zeroX) zeroX(); - if (options.zeroY) zeroY(); - free(lines); + dataRecon(); + dataFilter(); + if (invertData) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0; i < lineLen; ++i) { + lineData(y)[i] ^= 0xFF; + } + } + } + if (mirrorData) { + for (uint32_t y = 0; y < header.height; ++y) { + for (size_t i = 0, j = lineLen-1; i < j; ++i, --j) { + uint8_t x = lineData(y)[i]; + lineData(y)[i] = lineData(y)[j]; + lineData(y)[j] = x; + } + } + } + if (zeroX) { + for (uint32_t y = 0; y < header.height; ++y) { + memset(lineData(y), 0, pixelLen); + } + } + if (zeroY) { + memset(lineData(0), 0, lineLen); + } + + char buf[PATH_MAX]; if (outPath) { path = outPath; - file = fopen(path, "w"); - if (!file) err(EX_CANTCREAT, "%s", path); + if (outPath == inPath) { + snprintf(buf, sizeof(buf), "%sg", outPath); + file = fopen(buf, "wx"); + if (!file) err(EX_CANTCREAT, "%s", buf); + } else { + file = fopen(path, "w"); + if (!file) err(EX_CANTCREAT, "%s", outPath); + } } else { - path = "(stdout)"; + path = "stdout"; file = stdout; } - writeSignature(); - writeHeader(); - if (header.color == Indexed) writePalette(); - writeData(); - writeEnd(); + sigWrite(); + headerWrite(); + if (header.color == Indexed) { + palWrite(); + if (trans.len) transWrite(); + } + dataWrite(); free(data); - int error = fclose(file); if (error) err(EX_IOERR, "%s", path); + + if (outPath && outPath == inPath) { + error = rename(buf, outPath); + if (error) err(EX_CANTCREAT, "%s", outPath); + } } -static enum Filter parseFilter(const char *s) { - switch (s[0]) { +static enum Filter parseFilter(const char *str) { + switch (str[0]) { case 'N': case 'n': return None; case 'S': case 's': return Sub; case 'U': case 'u': return Up; case 'A': case 'a': return Average; case 'P': case 'p': return Paeth; - default: errx(EX_USAGE, "invalid filter type %s", s); + default: errx(EX_USAGE, "invalid filter type %s", str); } } -static uint8_t parseFilters(enum Filter *filters, const char *s) { - uint8_t len = 0; - do { - filters[len++] = parseFilter(s); - s = strchr(s, ','); - } while (s++); +static size_t parseFilters(enum Filter *filters, char *str) { + size_t len = 0; + while (str) { + char *filt = strsep(&str, ","); + filters[len++] = parseFilter(filt); + } return len; } int main(int argc, char *argv[]) { bool stdio = false; - char *output = NULL; + char *outPath = NULL; - int opt; - while (0 < (opt = getopt(argc, argv, "a:cd:fimo:prxy"))) { + for (int opt; 0 < (opt = getopt(argc, argv, "a:cd:fimo:prxy"));) { switch (opt) { - break; case 'a': - options.applyFilter = parseFilters(options.applyFilters, optarg); + break; case 'a': applyFilter = parseFilters(applyFilters, optarg); break; case 'c': stdio = true; - break; case 'd': - options.declareFilter = parseFilters(options.declareFilters, optarg); - break; case 'f': options.filt = true; - break; case 'i': options.invert = true; - break; case 'm': options.mirror = true; - break; case 'o': output = optarg; - break; case 'p': options.brokenPaeth = true; - break; case 'r': options.recon = true; - break; case 'x': options.zeroX = true; - break; case 'y': options.zeroY = true; - break; default: return EX_USAGE; + break; case 'd': declFilter = parseFilters(declFilters, optarg); + break; case 'f': reconFilter = true; + break; case 'i': invertData = true; + break; case 'm': mirrorData = true; + break; case 'o': outPath = optarg; + break; case 'p': brokenPaeth = true; + break; case 'r': filterRecon = true; + break; case 'x': zeroX = true; + break; case 'y': zeroY = true; + break; default: return EX_USAGE; } } - if (argc - optind == 1 && (output || stdio)) { - glitch(argv[optind], output); - } else if (optind < argc) { + if (optind < argc) { for (int i = optind; i < argc; ++i) { - glitch(argv[i], argv[i]); + glitch(argv[i], (stdio ? NULL : outPath ? outPath : argv[i])); } } else { - glitch(NULL, output); + glitch(NULL, outPath); } - - return EX_OK; } diff --git a/bin/hilex.c b/bin/hilex.c index 0bcc5c7f..7d7b3f2d 100644 --- a/bin/hilex.c +++ b/bin/hilex.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 C. McEnroe <june@causal.agency> +/* Copyright (C) 2020 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -53,7 +53,7 @@ static const struct { { &LexC, "c", "[.][chlmy]$", NULL }, { &LexMake, "make", "[.](mk|am)$|^Makefile$", NULL }, { &LexMdoc, "mdoc", "[.][1-9]$", "^[.]Dd" }, - { &LexSh, "sh", "[.]sh$|^[.](profile|shrc)$", "^#![ ]?/bin/sh" }, + { &LexSh, "sh", "[.]sh$|^[.](profile|shrc)$", "^#![ ]?/bin/k?sh" }, { &LexText, "text", "[.]txt$", NULL }, }; diff --git a/bin/hilex.h b/bin/hilex.h index 882b5f95..b57fc8cc 100644 --- a/bin/hilex.h +++ b/bin/hilex.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 C. McEnroe <june@causal.agency> +/* Copyright (C) 2020 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/make.l b/bin/make.l index ae1be2f5..6296716d 100644 --- a/bin/make.l +++ b/bin/make.l @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 C. McEnroe <june@causal.agency> +/* Copyright (C) 2020 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/man1/up.1 b/bin/man1/up.1 index 2240b99a..aece79bd 100644 --- a/bin/man1/up.1 +++ b/bin/man1/up.1 @@ -1,4 +1,4 @@ -.Dd June 21, 2021 +.Dd July 26, 2022 .Dt UP 1 .Os . @@ -8,15 +8,9 @@ . .Sh SYNOPSIS .Nm -.Op Fl h -.Op Ar file -. -.Nm -.Fl c | t -.Ar command -. -.Nm -.Fl s +.Op Fl c | h | s | t +.Op Fl w Ar warn +.Op Ar file | command . .Sh DESCRIPTION .Nm @@ -65,6 +59,10 @@ Run a command with and .Xr shotty 1 to produce an HTML file for upload. +.It Fl w Ar warn +Create an HTML redirect with +.Ar warn +in its title. .El . .Pp diff --git a/bin/man1/when.1 b/bin/man1/when.1 index 0b473573..3f2735f7 100644 --- a/bin/man1/when.1 +++ b/bin/man1/when.1 @@ -1,4 +1,4 @@ -.Dd July 24, 2019 +.Dd September 19, 2022 .Dt WHEN 1 .Os . @@ -9,6 +9,8 @@ .Sh SYNOPSIS .Nm .Op Ar expr +.Nm +.Cm - . .Sh DESCRIPTION .Nm @@ -18,24 +20,32 @@ If no is given, expressions are read from standard input. +If +.Cm - +is given, +the intervals between each named date +and today are printed. . .Pp The grammar is as follows: .Bl -tag -width Ds .It Sy \&. Today's date. +The empty expression is equivalent. +. +.It Ar name Op Sy = Ar date +A named date. +Names are alphanumeric including underscores. . .It Ar month Ar date Op Ar year A full date, or a date in the current year. -.Ar month -must be at least three letters. +Months can be abbreviated to three letters. . .It Ar day A day of the week in the current week. -.Ar day -must be at least three letters. +Days can be abbreviated to three letters. . .It Sy < Ar date The date one week before. @@ -65,6 +75,14 @@ A number of months. A number of years. .El . +.Sh FILES +The file +.Pa $XDG_CONFIG_HOME/when/dates +or +.Pa ~/.config/when/dates +is read before any other expressions, +if it exists. +. .Sh EXAMPLES .Bl -tag -width "Dec 25 - ." .It Ic Dec 25 - \&. @@ -74,3 +92,9 @@ The date next Friday. .It Ic \&. + 2w Your last day at work. .El +.Pp +Checking a milestone: +.Bd -literal -offset indent +$ echo 'hrt = oct 15 2021' >> ~/.config/when/dates +$ when -hrt +.Ed diff --git a/bin/mdoc.l b/bin/mdoc.l index f29b6ceb..b6deacbe 100644 --- a/bin/mdoc.l +++ b/bin/mdoc.l @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 C. McEnroe <june@causal.agency> +/* Copyright (C) 2020 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/modem.c b/bin/modem.c index 2133ae08..4392e071 100644 --- a/bin/modem.c +++ b/bin/modem.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 C. McEnroe <june@causal.agency> +/* Copyright (C) 2018 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/mtags.c b/bin/mtags.c index 11cd9c8a..5c1a057e 100644 --- a/bin/mtags.c +++ b/bin/mtags.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 C. McEnroe <june@causal.agency> +/* Copyright (C) 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/nudge.c b/bin/nudge.c index 108d2459..8ae916eb 100644 --- a/bin/nudge.c +++ b/bin/nudge.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 C. McEnroe <june@causal.agency> +/* Copyright (C) 2020 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/order.y b/bin/order.y index c2e87971..b3cbf2df 100644 --- a/bin/order.y +++ b/bin/order.y @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/pbd.c b/bin/pbd.c index 2fd401ae..9f47b63e 100644 --- a/bin/pbd.c +++ b/bin/pbd.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 C. McEnroe <june@causal.agency> +/* Copyright (C) 2017 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/png.h b/bin/png.h index 15b6d13d..0df4699b 100644 --- a/bin/png.h +++ b/bin/png.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 C. McEnroe <june@causal.agency> +/* Copyright (C) 2018 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/pngo.c b/bin/pngo.c index ba90889a..eb51ccc2 100644 --- a/bin/pngo.c +++ b/bin/pngo.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018, 2021 C. McEnroe <june@causal.agency> +/* Copyright (C) 2018, 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/psf2png.c b/bin/psf2png.c index 1aaa8635..c36238a0 100644 --- a/bin/psf2png.c +++ b/bin/psf2png.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 C. McEnroe <june@causal.agency> +/* Copyright (C) 2018 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/ptee.c b/bin/ptee.c index 8374bd8f..52350a21 100644 --- a/bin/ptee.c +++ b/bin/ptee.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/quick.c b/bin/quick.c index edf35a3b..d814873d 100644 --- a/bin/quick.c +++ b/bin/quick.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 C. McEnroe <june@causal.agency> +/* Copyright (C) 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/relay.c b/bin/relay.c index aa7913c9..fd799462 100644 --- a/bin/relay.c +++ b/bin/relay.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/scheme.c b/bin/scheme.c index 33fd8b60..2bae8f82 100644 --- a/bin/scheme.c +++ b/bin/scheme.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018, 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2018, 2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/sh.l b/bin/sh.l index 9664b009..8f0f7723 100644 --- a/bin/sh.l +++ b/bin/sh.l @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 C. McEnroe <june@causal.agency> +/* Copyright (C) 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -36,7 +36,7 @@ static int pop(void) { } %} -%s Param Command Arith Backtick +%s Param Command Arith Backtick Subshell %x DQuote HereDocDel HereDoc HereDocLit word [[:alnum:]_.-]+ @@ -51,7 +51,7 @@ reserved [!{}]|else|do|elif|for|done|fi|then|until|while|if|case|esac "\\". { return Escape; } -<INITIAL,DQuote,HereDoc,Param,Command,Arith>{ +<INITIAL,DQuote,HereDoc,Param,Command,Arith,Subshell>{ "$"[*@#?$!0-9-] | "$"[_[:alpha:][_[:alnum:]]* | "${"[#]?{param}"}" { @@ -73,6 +73,10 @@ reserved [!{}]|else|do|elif|for|done|fi|then|until|while|if|case|esac BEGIN(push(Backtick)); return Subst; } + "(" { + BEGIN(push(Subshell)); + return Normal; + } } <Param>"}" | <Command>")" | @@ -81,6 +85,10 @@ reserved [!{}]|else|do|elif|for|done|fi|then|until|while|if|case|esac BEGIN(pop()); return Subst; } +<Subshell>")" { + BEGIN(pop()); + return Normal; +} "\n" { first = true; diff --git a/bin/shotty.l b/bin/shotty.l index ff78d241..dcac43ec 100644 --- a/bin/shotty.l +++ b/bin/shotty.l @@ -1,4 +1,4 @@ -/* Copyright (C) 2019, 2021 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019, 2021 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -181,13 +181,13 @@ ESC \x1B | (wchar_t)(yytext[1] & 0x3F); return Data; } -[\xE0-\xEF][\x80-\xBF]{2} { +[\xE0-\xEF]([\x80-\xBF]{2}) { ch = (wchar_t)(yytext[0] & 0x0F) << 12 | (wchar_t)(yytext[1] & 0x3F) << 6 | (wchar_t)(yytext[2] & 0x3F); return Data; } -[\xF0-\xF7][\x80-\xBF]{3} { +[\xF0-\xF7]([\x80-\xBF]{3}) { ch = (wchar_t)(yytext[0] & 0x07) << 18 | (wchar_t)(yytext[1] & 0x3F) << 12 | (wchar_t)(yytext[2] & 0x3F) << 6 diff --git a/bin/title.c b/bin/title.c index 82f89d95..47ff720a 100644 --- a/bin/title.c +++ b/bin/title.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by diff --git a/bin/up.sh b/bin/up.sh index cebc29c4..6305b1ee 100644 --- a/bin/up.sh +++ b/bin/up.sh @@ -4,21 +4,32 @@ set -eu readonly Host='temp.causal.agency' readonly Root='/var/www' +temp= +temp() { + temp=$(mktemp -d) + trap 'rm -r "$temp"' EXIT +} + +warn= upload() { src=$1 ext=${src##*.} - ts=$(date +'%s') - rand=$(openssl rand -hex 4) - url=$(printf '%s/%x%s.%s' "$Host" "$ts" "$rand" "$ext") + name=$(printf '%x%s' "$(date +%s)" "$(openssl rand -hex 4)") + url="${Host}/${name}.${ext}" scp -q "$src" "${Host}:${Root}/${url}" + if test -n "$warn"; then + test -n "$temp" || temp + cat >"${temp}/warn.html" <<-EOF + <!DOCTYPE html> + <title>${warn}</title> + <meta http-equiv="refresh" content="0;url=${name}.${ext}"> + EOF + url="${Host}/${name}.html" + scp -q "${temp}/warn.html" "${Host}:${Root}/${url}" + fi echo "https://${url}" } -temp() { - temp=$(mktemp -d) - trap 'rm -r "$temp"' EXIT -} - uploadText() { temp cat >"${temp}/input.txt" @@ -64,12 +75,13 @@ uploadTerminal() { upload "${temp}/term.html" } -while getopts 'chst' opt; do +while getopts 'chstw:' opt; do case $opt in (c) fn=uploadCommand;; (h) fn=uploadHilex;; (s) fn=uploadScreen;; (t) fn=uploadTerminal;; + (w) warn=$OPTARG;; (?) exit 1;; esac done diff --git a/bin/when.y b/bin/when.y index 64859da7..46651ebb 100644 --- a/bin/when.y +++ b/bin/when.y @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 June McEnroe <june@causal.agency> +/* Copyright (C) 2019, 2022 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -18,6 +18,9 @@ #include <ctype.h> #include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <strings.h> @@ -30,12 +33,13 @@ static int yylex(void); #define YYSTYPE struct tm static const char *Days[7] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", }; static const char *Months[12] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", }; static const struct tm Week = { .tm_mday = 7 }; @@ -117,7 +121,10 @@ static struct tm dateDiff(struct tm a, struct tm b) { .tm_mon = a.tm_mon - b.tm_mon, .tm_mday = a.tm_mday - b.tm_mday, }; - if (a.tm_mon < b.tm_mon) { + if ( + a.tm_mon < b.tm_mon || + (a.tm_mon == b.tm_mon && a.tm_mday < b.tm_mday) + ) { diff.tm_year--; diff.tm_mon += 12; } @@ -130,15 +137,50 @@ static struct tm dateDiff(struct tm a, struct tm b) { return diff; } +static struct { + size_t cap, len; + struct tm *ptr; +} dates; + +static struct tm getDate(const char *name) { + for (size_t i = 0; i < dates.len; ++i) { + if (!strcmp(dates.ptr[i].tm_zone, name)) return dates.ptr[i]; + } + return (struct tm) {0}; +} + +static void setDate(const char *name, struct tm date) { + for (size_t i = 0; i < dates.len; ++i) { + if (strcmp(dates.ptr[i].tm_zone, name)) continue; + char *tm_zone = dates.ptr[i].tm_zone; + dates.ptr[i] = date; + dates.ptr[i].tm_zone = tm_zone; + return; + } + if (dates.len == dates.cap) { + dates.cap = (dates.cap ? dates.cap * 2 : 8); + dates.ptr = realloc(dates.ptr, sizeof(*dates.ptr) * dates.cap); + if (!dates.ptr) err(EX_OSERR, "realloc"); + } + dates.ptr[dates.len] = date; + dates.ptr[dates.len].tm_zone = strdup(name); + if (!dates.ptr[dates.len].tm_zone) err(EX_OSERR, "strdup"); + dates.len++; +} + +static bool silent; + static void printDate(struct tm date) { + if (silent) return; printf( - "%s %s %d %d\n", + "%.3s %.3s %d %d\n", Days[date.tm_wday], Months[date.tm_mon], date.tm_mday, 1900 + date.tm_year ); } static void printScalar(struct tm scalar) { + if (silent) return; if (scalar.tm_year) printf("%dy ", scalar.tm_year); if (scalar.tm_mon) printf("%dm ", scalar.tm_mon); if (scalar.tm_mday % 7) { @@ -161,9 +203,9 @@ static void printScalar(struct tm scalar) { %} -%token Number Month Day +%token Name Number Month Day %left '+' '-' -%right '<' '>' +%right '=' '<' '>' %% @@ -174,6 +216,8 @@ expr: date: dateLit + | Name { $$ = getDate($1.tm_zone); free($1.tm_zone); } + | Name '=' date { setDate($1.tm_zone, $3); free($1.tm_zone); $$ = $3; } | '(' date ')' { $$ = $2; } | '<' date { $$ = dateSub($2, Week); } | '>' date { $$ = dateAdd($2, Week); } @@ -223,35 +267,77 @@ static int yylex(void) { return Number; } - for (int i = 0; i < 7; ++i) { - if (strncasecmp(input, Days[i], 3)) continue; - while (isalpha(*input)) input++; - yylval.tm_wday = i; - return Day; + size_t len; + for (len = 0; isalnum(input[len]) || input[len] == '_'; ++len); + + if (len >= 3) { + for (int i = 0; i < 7; ++i) { + if (strncasecmp(input, Days[i], len)) continue; + yylval.tm_wday = i; + input += len; + return Day; + } + + for (int i = 0; i < 12; ++i) { + if (strncasecmp(input, Months[i], len)) continue; + yylval.tm_mon = i; + input += len; + return Month; + } } - for (int i = 0; i < 12; ++i) { - if (strncasecmp(input, Months[i], 3)) continue; - while (isalpha(*input)) input++; - yylval.tm_mon = i; - return Month; + if (len && (len != 1 || !strchr("dwmy", *input))) { + yylval.tm_zone = strndup(input, len); + if (!yylval.tm_zone) err(EX_OSERR, "strndup"); + input += len; + return Name; } return *input++; } int main(int argc, char *argv[]) { + size_t cap = 0; + char *line = NULL; + + char path[PATH_MAX]; + const char *configHome = getenv("XDG_CONFIG_HOME"); + if (configHome) { + snprintf(path, sizeof(path), "%s/when/dates", configHome); + } else { + snprintf(path, sizeof(path), "%s/.config/when/dates", getenv("HOME")); + } + + FILE *file = fopen(path, "r"); + if (file) { + silent = true; + while (0 < getline(&line, &cap, file)) { + input = line; + yyparse(); + } + fclose(file); + silent = false; + } else if (errno != ENOENT) { + err(EX_CONFIG, "%s", path); + } + if (argc > 1) { - input = argv[1]; - return yyparse(); + if (strcmp(argv[1], "-")) { + input = argv[1]; + return yyparse(); + } else { + for (size_t i = 0; i < dates.len; ++i) { + printf("%s: ", dates.ptr[i].tm_zone); + printScalar(dateDiff(today(), dates.ptr[i])); + } + return EX_OK; + } } struct tm date = today(); printDate(date); printf("\n"); - char *line = NULL; - size_t cap = 0; while (0 < getline(&line, &cap, stdin)) { if (line[0] == '\n') continue; diff --git a/bin/xx.c b/bin/xx.c index 6d04f2f5..39d7ec07 100644 --- a/bin/xx.c +++ b/bin/xx.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 C. McEnroe <june@causal.agency> +/* Copyright (C) 2017 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by |