From 4afd46286ba0c282c67593d060dc9d35377a9f36 Mon Sep 17 00:00:00 2001 From: Alfred Klomp Date: Tue, 9 Jan 2024 21:57:38 +0100 Subject: [PATCH] bin/base64: decode: ignore newlines By popular demand, ignore newlines in the encoded input. This achieves bug compatibility with GNU base64. Using `memchr(3)' is probably going to slow down things, but we can vectorize the algorithm later. --- bin/base64.c | 62 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/bin/base64.c b/bin/base64.c index 6a94074..e7c0125 100644 --- a/bin/base64.c +++ b/bin/base64.c @@ -340,7 +340,7 @@ encode (const struct config *config, struct buffer *buf) return true; } -static int +static bool decode (const struct config *config, struct buffer *buf) { size_t nread, nout; @@ -352,18 +352,58 @@ decode (const struct config *config, struct buffer *buf) // Read encoded data into the buffer. Use the smallest buffer size to // be on the safe side: the decoded output will fit the raw buffer. while ((nread = fread(buf->enc, 1, BUFFER_RAW_SIZE, config->fp)) > 0) { + char *s = buf->enc; + size_t avail = nread; + + // By popular demand, this utility tries to be bug-compatible + // with GNU `base64'. That includes silently ignoring newlines + // in the input. Use `memchr(3)' to tokenize the input on + // newline characters. This is probably quite slow, and at some + // point we will want to vectorize this. + // Ignore \r for now, because there is no equivalent to + // `strpbrk(3)' in the mem* series of library functions. Also, + // this program is intended to be used in Unix-like + // environments where \r is not treated specially. + while (avail > 0) { + + // Find the next newline character. + char *p = memchr(s, '\n', avail); + + // Find the length of the next chunk. If no newline is + // found, use the rest of the buffer. + size_t len = p == NULL ? avail : (size_t) (p - s); + + // Ignore empty chunks. + if (len == 0) { + s++; + avail--; + continue; + } - // Decode the input into the raw buffer. - if (base64_stream_decode(&state, buf->enc, nread, - buf->raw, &nout) == 0) { - fprintf(stderr, "%s: %s: decoding error\n", - config->name, config->file); - return false; - } + // Decode the input into the raw buffer. + if (base64_stream_decode(&state, s, len, + buf->raw, &nout) == 0) { + fprintf(stderr, "%s: %s: decoding error\n", + config->name, config->file); + return false; + } - // Append the raw data to the output stream. - if (write_stdout(config, buf->raw, nout) == false) { - return false; + // Append the raw data to the output stream. + if (write_stdout(config, buf->raw, nout) == false) { + return false; + } + + // Bail out if the whole string has been consumed. + if (p == NULL) { + break; + } + + // Add the newline to the chunk length. + len++; + + // Move the start pointer and length past the chunk. + s += len; + avail -= len; } }