| 
1 |  | -#define _XOPEN_SOURCE		// IOV_MAX  | 
 | 1 | +// Test for MinGW.  | 
 | 2 | +#if defined(__MINGW32__) || defined(__MINGW64__)  | 
 | 3 | +#  define MINGW  | 
 | 4 | +#endif  | 
 | 5 | + | 
 | 6 | +// Decide if the writev(2) system call needs to be emulated as a series of  | 
 | 7 | +// write(2) calls. At least MinGW does not support writev(2).  | 
 | 8 | +#ifdef MINGW  | 
 | 9 | +#  define EMULATE_WRITEV  | 
 | 10 | +#endif  | 
 | 11 | + | 
 | 12 | +// Include the necessary system header when using the system's writev(2).  | 
 | 13 | +#ifndef EMULATE_WRITEV  | 
 | 14 | +#  define _XOPEN_SOURCE		// Unlock IOV_MAX  | 
 | 15 | +#  include <sys/uio.h>  | 
 | 16 | +#endif  | 
2 | 17 | 
 
  | 
3 | 18 | #include <stdbool.h>  | 
4 | 19 | #include <stdlib.h>  | 
 | 
8 | 23 | #include <getopt.h>  | 
9 | 24 | #include <errno.h>  | 
10 | 25 | #include <limits.h>  | 
11 |  | -#include <sys/uio.h>  | 
 | 26 | + | 
12 | 27 | #include "../include/libbase64.h"  | 
13 | 28 | 
 
  | 
14 | 29 | // Size of the buffer for the "raw" (not base64-encoded) data in bytes.  | 
@@ -50,6 +65,59 @@ struct buffer {  | 
50 | 65 | 	char *enc;  | 
51 | 66 | };  | 
52 | 67 | 
 
  | 
 | 68 | +// Optionally emulate writev(2) as a series of write calls.  | 
 | 69 | +#ifdef EMULATE_WRITEV  | 
 | 70 | + | 
 | 71 | +// Quick and dirty definition of IOV_MAX as it is probably not defined.  | 
 | 72 | +#ifndef IOV_MAX  | 
 | 73 | +#  define IOV_MAX 1024  | 
 | 74 | +#endif  | 
 | 75 | + | 
 | 76 | +// Quick and dirty definition of this system struct, for local use only.  | 
 | 77 | +struct iovec {  | 
 | 78 | + | 
 | 79 | +	// Opaque data pointer.  | 
 | 80 | +	void *iov_base;  | 
 | 81 | + | 
 | 82 | +	// Length of the data in bytes.  | 
 | 83 | +	size_t iov_len;  | 
 | 84 | +};  | 
 | 85 | + | 
 | 86 | +static ssize_t  | 
 | 87 | +writev (const int fd, const struct iovec *iov, int iovcnt)  | 
 | 88 | +{  | 
 | 89 | +	ssize_t r, nwrite = 0;  | 
 | 90 | + | 
 | 91 | +	// Reset the error marker.  | 
 | 92 | +	errno = 0;  | 
 | 93 | + | 
 | 94 | +	while (iovcnt-- > 0) {  | 
 | 95 | + | 
 | 96 | +		// Write the vector; propagate errors back to the caller. Note  | 
 | 97 | +		// that this loses information about how much vectors have been  | 
 | 98 | +		// successfully written, but that also seems to be the case  | 
 | 99 | +		// with the real function. The API is somewhat flawed.  | 
 | 100 | +		if ((r = write(fd, iov->iov_base, iov->iov_len)) < 0) {  | 
 | 101 | +			return r;  | 
 | 102 | +		}  | 
 | 103 | + | 
 | 104 | +		// Update the total write count.  | 
 | 105 | +		nwrite += r;  | 
 | 106 | + | 
 | 107 | +		// Return early after a partial write; the caller should retry.  | 
 | 108 | +		if ((size_t) r != iov->iov_len) {  | 
 | 109 | +			break;  | 
 | 110 | +		}  | 
 | 111 | + | 
 | 112 | +		// Move to the next vector.  | 
 | 113 | +		iov++;  | 
 | 114 | +	}  | 
 | 115 | + | 
 | 116 | +	return nwrite;  | 
 | 117 | +}  | 
 | 118 | + | 
 | 119 | +#endif	// EMULATE_WRITEV  | 
 | 120 | + | 
53 | 121 | static bool  | 
54 | 122 | buffer_alloc (const struct config *config, struct buffer *buf)  | 
55 | 123 | {  | 
@@ -272,29 +340,75 @@ encode (const struct config *config, struct buffer *buf)  | 
272 | 340 | 	return true;  | 
273 | 341 | }  | 
274 | 342 | 
 
  | 
275 |  | -static int  | 
 | 343 | +static inline size_t  | 
 | 344 | +find_newline (const char *p, const size_t avail)  | 
 | 345 | +{  | 
 | 346 | +	// This is very naive and can probably be improved by vectorization.  | 
 | 347 | +	for (size_t len = 0; len < avail; len++) {  | 
 | 348 | +		if (p[len] == '\n') {  | 
 | 349 | +			return len;  | 
 | 350 | +		}  | 
 | 351 | +	}  | 
 | 352 | + | 
 | 353 | +	return avail;  | 
 | 354 | +}  | 
 | 355 | + | 
 | 356 | +static bool  | 
276 | 357 | decode (const struct config *config, struct buffer *buf)  | 
277 | 358 | {  | 
278 |  | -	size_t nread, nout;  | 
 | 359 | +	size_t avail;  | 
279 | 360 | 	struct base64_state state;  | 
280 | 361 | 
 
  | 
281 | 362 | 	// Initialize the decoder's state structure.  | 
282 | 363 | 	base64_stream_decode_init(&state, 0);  | 
283 | 364 | 
 
  | 
284 | 365 | 	// Read encoded data into the buffer. Use the smallest buffer size to  | 
285 | 366 | 	// be on the safe side: the decoded output will fit the raw buffer.  | 
286 |  | -	while ((nread = fread(buf->enc, 1, BUFFER_RAW_SIZE, config->fp)) > 0) {  | 
 | 367 | +	while ((avail = fread(buf->enc, 1, BUFFER_RAW_SIZE, config->fp)) > 0) {  | 
 | 368 | +		char  *start  = buf->enc;  | 
 | 369 | +		char  *outbuf = buf->raw;  | 
 | 370 | +		size_t ototal = 0;  | 
 | 371 | + | 
 | 372 | +		// By popular demand, this utility tries to be bug-compatible  | 
 | 373 | +		// with GNU `base64'. That includes silently ignoring newlines  | 
 | 374 | +		// in the input. Tokenize the input on newline characters.  | 
 | 375 | +		while (avail > 0) {  | 
 | 376 | + | 
 | 377 | +			// Find the offset of the next newline character, which  | 
 | 378 | +			// is also the length of the next chunk.  | 
 | 379 | +			size_t outlen, len = find_newline(start, avail);  | 
 | 380 | + | 
 | 381 | +			// Ignore empty chunks.  | 
 | 382 | +			if (len == 0) {  | 
 | 383 | +				start++;  | 
 | 384 | +				avail--;  | 
 | 385 | +				continue;  | 
 | 386 | +			}  | 
287 | 387 | 
 
  | 
288 |  | -		// Decode the input into the raw buffer.  | 
289 |  | -		if (base64_stream_decode(&state, buf->enc, nread,  | 
290 |  | -		                         buf->raw, &nout) == 0) {  | 
291 |  | -			fprintf(stderr, "%s: %s: decoding error\n",  | 
292 |  | -			        config->name, config->file);  | 
293 |  | -			return false;  | 
 | 388 | +			// Decode the chunk into the raw buffer.  | 
 | 389 | +			if (base64_stream_decode(&state, start, len,  | 
 | 390 | +			                         outbuf, &outlen) == 0) {  | 
 | 391 | +				fprintf(stderr, "%s: %s: decoding error\n",  | 
 | 392 | +				        config->name, config->file);  | 
 | 393 | +				return false;  | 
 | 394 | +			}  | 
 | 395 | + | 
 | 396 | +			// Update the output buffer pointer and total size.  | 
 | 397 | +			outbuf += outlen;  | 
 | 398 | +			ototal += outlen;  | 
 | 399 | + | 
 | 400 | +			// Bail out if the whole string has been consumed.  | 
 | 401 | +			if (len == avail) {  | 
 | 402 | +				break;  | 
 | 403 | +			}  | 
 | 404 | + | 
 | 405 | +			// Move the start pointer past the newline.  | 
 | 406 | +			start += len + 1;  | 
 | 407 | +			avail -= len + 1;  | 
294 | 408 | 		}  | 
295 | 409 | 
 
  | 
296 | 410 | 		// Append the raw data to the output stream.  | 
297 |  | -		if (write_stdout(config, buf->raw, nout) == false) {  | 
 | 411 | +		if (write_stdout(config, buf->raw, ototal) == false) {  | 
298 | 412 | 			return false;  | 
299 | 413 | 		}  | 
300 | 414 | 	}  | 
 | 
0 commit comments