/* Copyright (c) 2021 Ben Fuller
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUF_SIZE 2097152
const char tbl_base64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
'=' /* termination character */
};
static void usage(const char *name) {
fprintf(stderr, "usage: %s [-d] [-w COLS] [FILE]\n", name);
}
char *base64(unsigned char *i, ssize_t length, unsigned dflg){
if (dflg) return NULL;
char *out = malloc(length/3 * 4 + 100);
int o = 0; /* index of position in out */
while (length > 0) {
unsigned char i1, i2;
i1 = i2 = 0;
length -= 3;
if (length >= -2) {
i2 = i[2];
if (length >= -1) i1 = i[1];
}
out[o] = tbl_base64[i[0] >> 2];
++o;
out[o] = tbl_base64[((i[0] & 3) << 4) | (i1 >> 4)];
++o;
out[o] = tbl_base64[((i1 & 0xf) << 2) | (i2 >> 6)];
++o;
out[o] = tbl_base64[i2 & 0x3f];
++o;
i += 3;
}
out[o] = '\0';
while (length < 0){
length++;
o--;
out[o] = tbl_base64[64];
}
return out;
}
void print_wrapped(char *s, int line_length){
/* printf a string wrapped to line_length chars */
int c = 0;
while (1){
if (*s == '\0') break;
printf("%c", *s++);
c++;
if (c == line_length){
c = 0;
printf("\n");
}
}
if (c != 0) printf("\n");
}
int main(int argc, char **argv) {
int c;
int wrap_cols = 76;
unsigned dflg = 0;
const char *name = argv[0];
while ((c = getopt(argc, argv, "dhw:")) != -1) {
switch(c) {
case 'd':
dflg++;
break;
case 'w':
wrap_cols = atoi(optarg);
break;
case 'h':
usage(name);
return 0;
case '?':
usage(name);
return 2;
}
}
/* only work on one file */
if (optind + 1 < argc) {
fprintf(stderr, "too many arguments\n");
usage(name);
return 2;
}
int fd;
if (optind == argc || *argv[optind] == '-') {
/* read from stdin */
fd = STDIN_FILENO;
} else {
/* open the named file */
if ((fd = open(argv[optind], O_RDONLY)) == -1 ) {
perror(argv[optind]);
return 1;
}
}
/* read the whole file into *buf */
ssize_t bytes;
size_t used = 0, size = 0;
unsigned char *buf = NULL, *temp;
while (1){
if (used + BUF_SIZE + 1 > size){
size = used + BUF_SIZE + 1;
temp = realloc(buf,size);
if (temp == NULL) {
/* out of memory */
free(buf);
return 1;
}
buf = temp;
}
if ((bytes = read(fd, buf+used, BUF_SIZE)) <= 0 ) {
if (bytes == 0) break; /* read returns 0 when EOF has been reached */
perror("read");
return 1;
}
used += bytes;
}
char *output = base64(buf, used, dflg);
print_wrapped(output, wrap_cols);
if (close(fd) != 0) {
perror("close");
return 1;
}
return 0;
}