diff --git a/bin/base64.c b/bin/base64.c index 6a94074..b2dcebf 100644 --- a/bin/base64.c +++ b/bin/base64.c @@ -340,7 +340,20 @@ encode (const struct config *config, struct buffer *buf) return true; } -static int +static inline size_t +find_newline (const char *p, const size_t avail) +{ + // This is very naive and can definitely be improved. + for (size_t len = 0; len < avail; len++) { + if (p[len] == '\n') { + return len; + } + } + + return avail; +} + +static bool decode (const struct config *config, struct buffer *buf) { size_t nread, nout; @@ -352,18 +365,50 @@ 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. Tokenize the input on newline characters. + // This is quite slow, and at some point we will want to + // vectorize this. + while (avail > 0) { + + // Find the offset of the next newline character. + size_t len = find_newline(s, avail); + + // 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 (len == avail) { + break; + } + + // Add the newline to the chunk length. + len++; + + // Move the start pointer and length past the chunk. + s += len; + avail -= len; } }