%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/base64/base64/bin/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/base64/base64/bin/base64.c |
// Test for MinGW. #if defined(__MINGW32__) || defined(__MINGW64__) # define MINGW #endif // Decide if the writev(2) system call needs to be emulated as a series of // write(2) calls. At least MinGW does not support writev(2). #ifdef MINGW # define EMULATE_WRITEV #endif // Include the necessary system header when using the system's writev(2). #ifndef EMULATE_WRITEV # define _XOPEN_SOURCE // Unlock IOV_MAX # include <sys/uio.h> #endif #include <stdbool.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <getopt.h> #include <errno.h> #include <limits.h> #include "../include/libbase64.h" // Size of the buffer for the "raw" (not base64-encoded) data in bytes. #define BUFFER_RAW_SIZE (1024 * 1024) // Size of the buffer for the base64-encoded data in bytes. The base64-encoded // data is 4/3 the size of the input, with some margin to be sure. #define BUFFER_ENC_SIZE (BUFFER_RAW_SIZE * 4 / 3 + 16) // Global config structure. struct config { // Name by which the program was called on the command line. const char *name; // Name of the input file for logging purposes. const char *file; // Input file handle. FILE *fp; // Wrap width in characters, for encoding only. size_t wrap; // Whether to run in decode mode. bool decode; // Whether to just print the help text and exit. bool print_help; }; // Input/output buffer structure. struct buffer { // Runtime-allocated buffer for raw (unencoded) data. char *raw; // Runtime-allocated buffer for base64-encoded data. char *enc; }; // Optionally emulate writev(2) as a series of write calls. #ifdef EMULATE_WRITEV // Quick and dirty definition of IOV_MAX as it is probably not defined. #ifndef IOV_MAX # define IOV_MAX 1024 #endif // Quick and dirty definition of this system struct, for local use only. struct iovec { // Opaque data pointer. void *iov_base; // Length of the data in bytes. size_t iov_len; }; static ssize_t writev (const int fd, const struct iovec *iov, int iovcnt) { ssize_t r, nwrite = 0; // Reset the error marker. errno = 0; while (iovcnt-- > 0) { // Write the vector; propagate errors back to the caller. Note // that this loses information about how much vectors have been // successfully written, but that also seems to be the case // with the real function. The API is somewhat flawed. if ((r = write(fd, iov->iov_base, iov->iov_len)) < 0) { return r; } // Update the total write count. nwrite += r; // Return early after a partial write; the caller should retry. if ((size_t) r != iov->iov_len) { break; } // Move to the next vector. iov++; } return nwrite; } #endif // EMULATE_WRITEV static bool buffer_alloc (const struct config *config, struct buffer *buf) { if ((buf->raw = malloc(BUFFER_RAW_SIZE)) == NULL || (buf->enc = malloc(BUFFER_ENC_SIZE)) == NULL) { free(buf->raw); fprintf(stderr, "%s: malloc: %s\n", config->name, strerror(errno)); return false; } return true; } static void buffer_free (struct buffer *buf) { free(buf->raw); free(buf->enc); } static bool writev_retry (const struct config *config, struct iovec *iov, size_t nvec) { // Writing nothing always succeeds. if (nvec == 0) { return true; } while (true) { ssize_t nwrite; // Try to write the vectors to stdout. if ((nwrite = writev(1, iov, nvec)) < 0) { // Retry on EINTR. if (errno == EINTR) { continue; } // Quit on other errors. fprintf(stderr, "%s: writev: %s\n", config->name, strerror(errno)); return false; } // The return value of `writev' is the number of bytes written. // To check for success, we traverse the list and remove all // written vectors. The call succeeded if the list is empty. while (true) { // Retry if this vector is not or partially written. if (iov->iov_len > (size_t) nwrite) { char *base = iov->iov_base; iov->iov_base = (size_t) nwrite + base; iov->iov_len -= (size_t) nwrite; break; } // Move to the next vector. nwrite -= iov->iov_len; iov++; // Return successfully if all vectors were written. if (--nvec == 0) { return true; } } } } static inline bool iov_append (const struct config *config, struct iovec *iov, size_t *nvec, char *base, const size_t len) { // Add the buffer to the IO vector array. iov[*nvec].iov_base = base; iov[*nvec].iov_len = len; // Increment the array index. Flush the array if it is full. if (++(*nvec) == IOV_MAX) { if (writev_retry(config, iov, IOV_MAX) == false) { return false; } *nvec = 0; } return true; } static bool write_stdout (const struct config *config, const char *buf, size_t len) { while (len > 0) { ssize_t nwrite; // Try to write the buffer to stdout. if ((nwrite = write(1, buf, len)) < 0) { // Retry on EINTR. if (errno == EINTR) { continue; } // Quit on other errors. fprintf(stderr, "%s: write: %s\n", config->name, strerror(errno)); return false; } // Update the buffer position. buf += (size_t) nwrite; len -= (size_t) nwrite; } return true; } static bool write_wrapped (const struct config *config, char *buf, size_t len) { static size_t col = 0; // Special case: if buf is NULL, print final trailing newline. if (buf == NULL) { if (config->wrap > 0 && col > 0) { return write_stdout(config, "\n", 1); } return true; } // If no wrap width is given, write the entire buffer. if (config->wrap == 0) { return write_stdout(config, buf, len); } // Statically allocated IO vector buffer. static struct iovec iov[IOV_MAX]; size_t nvec = 0; while (len > 0) { // Number of characters to fill the current line. size_t nwrite = config->wrap - col; // Do not write more data than is available. if (nwrite > len) { nwrite = len; } // Append the data to the IO vector array. if (iov_append(config, iov, &nvec, buf, nwrite) == false) { return false; } // Advance the buffer. len -= nwrite; buf += nwrite; col += nwrite; // If the line is full, append a newline. if (col == config->wrap) { if (iov_append(config, iov, &nvec, "\n", 1) == false) { return false; } col = 0; } } // Write the remaining vectors. if (writev_retry(config, iov, nvec) == false) { return false; } return true; } static bool encode (const struct config *config, struct buffer *buf) { size_t nread, nout; struct base64_state state; // Initialize the encoder's state structure. base64_stream_encode_init(&state, 0); // Read raw data into the buffer. while ((nread = fread(buf->raw, 1, BUFFER_RAW_SIZE, config->fp)) > 0) { // Encode the raw input into the encoded buffer. base64_stream_encode(&state, buf->raw, nread, buf->enc, &nout); // Append the encoded data to the output stream. if (write_wrapped(config, buf->enc, nout) == false) { return false; } } // Check for stream errors. if (ferror(config->fp)) { fprintf(stderr, "%s: %s: read error\n", config->name, config->file); return false; } // Finalize the encoding by adding proper stream terminators. base64_stream_encode_final(&state, buf->enc, &nout); // Append this tail to the output stream. if (write_wrapped(config, buf->enc, nout) == false) { return false; } // Print optional trailing newline. if (write_wrapped(config, NULL, 0) == false) { return false; } return true; } static inline size_t find_newline (const char *p, const size_t avail) { // This is very naive and can probably be improved by vectorization. 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 avail; struct base64_state state; // Initialize the decoder's state structure. base64_stream_decode_init(&state, 0); // 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 ((avail = fread(buf->enc, 1, BUFFER_RAW_SIZE, config->fp)) > 0) { char *start = buf->enc; char *outbuf = buf->raw; size_t ototal = 0; // 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. while (avail > 0) { // Find the offset of the next newline character, which // is also the length of the next chunk. size_t outlen, len = find_newline(start, avail); // Ignore empty chunks. if (len == 0) { start++; avail--; continue; } // Decode the chunk into the raw buffer. if (base64_stream_decode(&state, start, len, outbuf, &outlen) == 0) { fprintf(stderr, "%s: %s: decoding error\n", config->name, config->file); return false; } // Update the output buffer pointer and total size. outbuf += outlen; ototal += outlen; // Bail out if the whole string has been consumed. if (len == avail) { break; } // Move the start pointer past the newline. start += len + 1; avail -= len + 1; } // Append the raw data to the output stream. if (write_stdout(config, buf->raw, ototal) == false) { return false; } } // Check for stream errors. if (ferror(config->fp)) { fprintf(stderr, "%s: %s: read error\n", config->name, config->file); return false; } return true; } static void usage (FILE *fp, const struct config *config) { const char *usage = "Usage: %s [OPTION]... [FILE]\n" "If no FILE is given or is specified as '-', " "read from standard input.\n" "Options:\n" " -d, --decode Decode a base64 stream.\n" " -h, --help Print this help text.\n" " -w, --wrap=COLS Wrap encoded lines at this column. " "Default 76, 0 to disable.\n"; fprintf(fp, usage, config->name); } static bool get_wrap (struct config *config, const char *str) { char *eptr; // Reject empty strings. if (*str == '\0') { return false; } // Convert the input string to a signed long. const long wrap = strtol(str, &eptr, 10); // Reject negative numbers. if (wrap < 0) { return false; } // Reject strings containing non-digits. if (*eptr != '\0') { return false; } config->wrap = (size_t) wrap; return true; } static bool parse_opts (int argc, char **argv, struct config *config) { int c; static const struct option opts[] = { { "decode", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "wrap", required_argument, NULL, 'w' }, { NULL } }; // Remember the program's name. config->name = *argv; // Parse command line options. while ((c = getopt_long(argc, argv, ":dhw:", opts, NULL)) != -1) { switch (c) { case 'd': config->decode = true; break; case 'h': config->print_help = true; return true; case 'w': if (get_wrap(config, optarg) == false) { fprintf(stderr, "%s: invalid wrap value '%s'\n", config->name, optarg); return false; } break; case ':': fprintf(stderr, "%s: missing argument for '%c'\n", config->name, optopt); return false; default: fprintf(stderr, "%s: unknown option '%c'\n", config->name, optopt); return false; } } // Return successfully if no filename was given. if (optind >= argc) { return true; } // Return unsuccessfully if more than one filename was given. if (optind + 1 < argc) { fprintf(stderr, "%s: too many files\n", config->name); return false; } // For compatibility with GNU Coreutils base64, treat a filename of '-' // as standard input. if (strcmp(argv[optind], "-") == 0) { return true; } // Save the name of the file. config->file = argv[optind]; // Open the file. if ((config->fp = fopen(config->file, "rb")) == NULL) { fprintf(stderr, "%s: %s: %s\n", config->name, config->file, strerror(errno)); return false; } return true; } int main (int argc, char **argv) { // Default program config. struct config config = { .file = "stdin", .fp = stdin, .wrap = 76, .decode = false, .print_help = false, }; struct buffer buf; // Parse options from the command line. if (parse_opts(argc, argv, &config) == false) { usage(stderr, &config); return 1; } // Return early if the user just wanted the help text. if (config.print_help) { usage(stdout, &config); return 0; } // Allocate buffers. if (buffer_alloc(&config, &buf) == false) { return 1; } // Encode or decode the input based on the user's choice. const bool ret = config.decode ? decode(&config, &buf) : encode(&config, &buf); // Free the buffers. buffer_free(&buf); // Close the input file. fclose(config.fp); // Close the output stream. fclose(stdout); // That's all, folks. return ret ? 0 : 1; }