sbase

suckless unix tools
git clone git://git.2f30.org/sbase
Log | Files | Refs | README | LICENSE

commit ec02816d3edfbc4de55afca285eba3377cda2ab5
parent 93d017885224d0d4c6e3d97ac9241d52beba060c
Author: Tai Chi Minh Ralph Eastwood <tcmreastwood@gmail.com>
Date:   Tue, 10 Feb 2015 19:14:27 +0000

uuencode: add support for base64 and -o to stdout

Diffstat:
Muudecode.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Muuencode.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 175 insertions(+), 23 deletions(-)

diff --git a/uudecode.c b/uudecode.c @@ -11,26 +11,38 @@ #include "text.h" #include "util.h" +static void uudecodeb64(FILE *, FILE *); static void uudecode(FILE *, FILE *); -static void parseheader(FILE *, const char *, const char *, mode_t *, char **); +static void parseheader(FILE *, const char *, char **, mode_t *, char **); static FILE *parsefile(const char *); static void usage(void) { - eprintf("usage: %s [file]\n", argv0); + eprintf("usage: %s [-m] [-o output] [file]\n", argv0); } +static int mflag = 0; +static int oflag = 0; + int main(int argc, char *argv[]) { FILE *fp = NULL, *nfp = NULL; - char *fname; + char *fname, *header; + const char *ifname; mode_t mode = 0; + void (*d) (FILE *, FILE *) = NULL; + char *ofname = NULL; ARGBEGIN { case 'm': - eprintf("-m not implemented\n"); + mflag = 1; /* accepted but unused (autodetect file type) */ + break; + case 'o': + oflag = 1; + ofname = EARGF(usage()); + break; default: usage(); } ARGEND; @@ -39,20 +51,31 @@ main(int argc, char *argv[]) usage(); if (argc == 0) { - parseheader(stdin, "<stdin>", "begin ", &mode, &fname); - if (!(nfp = parsefile(fname))) - eprintf("fopen %s:", fname); - uudecode(stdin, nfp); + fp = stdin; + ifname = "<stdin>"; } else { if (!(fp = fopen(argv[0], "r"))) eprintf("fopen %s:", argv[0]); - parseheader(fp, argv[0], "begin ", &mode, &fname); - if (!(nfp = parsefile(fname))) - eprintf("fopen %s:", fname); - uudecode(fp, nfp); + ifname = argv[0]; } - if (chmod(fname, mode) < 0) + parseheader(fp, ifname, &header, &mode, &fname); + + if (!strncmp(header, "begin", sizeof("begin"))) + d = uudecode; + else if (!strncmp(header, "begin-base64", sizeof("begin-base64"))) + d = uudecodeb64; + else + eprintf("unknown header %s:", header); + + if (oflag) + fname = ofname; + if (!(nfp = parsefile(fname))) + eprintf("fopen %s:", fname); + + d(fp, nfp); + + if (nfp != stdout && chmod(fname, mode) < 0) eprintf("chmod %s:", fname); if (fp) fclose(fp); @@ -68,7 +91,7 @@ parsefile(const char *fname) struct stat st; int ret; - if (strcmp(fname, "/dev/stdout") == 0) + if (strcmp(fname, "/dev/stdout") == 0 || strcmp(fname, "-") == 0) return stdout; ret = lstat(fname, &st); /* if it is a new file, try to open it */ @@ -87,9 +110,10 @@ tropen: } static void -parseheader(FILE *fp, const char *s, const char *header, mode_t *mode, char **fname) +parseheader(FILE *fp, const char *s, char **header, mode_t *mode, + char **fname) { - char bufs[PATH_MAX + 11]; /* len header + mode + maxname */ + char bufs[PATH_MAX + 18]; /* len header + mode + maxname */ char *p, *q; size_t n; @@ -101,9 +125,12 @@ parseheader(FILE *fp, const char *s, const char *header, mode_t *mode, char **fn if (!(p = strchr(bufs, '\n'))) eprintf("header string too long or non-newline terminated file\n"); p = bufs; - if (strncmp(bufs, header, strlen(header)) != 0) - eprintf("malformed header prefix\n"); - p += strlen(header); + if (!(q = strchr(p, ' '))) + eprintf("malformed mode string in header\n"); + *header = bufs; + *q++ = '\0'; + p = q; + /* now header should be null terminated, q points to mode */ if (!(q = strchr(p, ' '))) eprintf("malformed mode string in header\n"); *q++ = '\0'; @@ -115,6 +142,73 @@ parseheader(FILE *fp, const char *s, const char *header, mode_t *mode, char **fn if (n > 0) *fname = q; } +static const char b64dt[] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,0,-1,-1,-1,0,1,2,3,4,5,6, + 7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, + 49,50,51,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +}; + +static void +uudecodeb64(FILE *fp, FILE *outfp) +{ + char bufb[60], *pb; + char out[45], *po; + size_t n; + int b = 0, e, t = 0; + unsigned char b24[3] = {0, 0, 0}; + + while ((n = fread(bufb, 1, sizeof(bufb), fp))) { + for (pb = bufb, po = out; pb < bufb + n; pb++) { + if (*pb == '=') { + if (b == 0 || t) { + /* footer size is ==== is 4 */ + if (++t < 4) + continue; + else + return; + } else if (b == 1) { + eprintf("unexpected \"=\" appeared."); + } else if (b == 2) { + *po++ = b24[0]; + fwrite(out, 1, (po - out), outfp); + } else if (b == 3) { + *po++ = b24[0]; + *po++ = b24[1]; + fwrite(out, 1, (po - out), outfp); + } + } + if ((e = b64dt[(int)*pb]) == -1) { + eprintf("invalid byte \"%c\"", *pb); + } else if (e == -2) /* whitespace */ + continue; + switch (b) { + case 0: b24[0] |= e << 2; break; + case 1: b24[0] |= (e >> 4) & 0x3; + b24[1] |= (e & 0xf) << 4; break; + case 2: b24[1] |= (e >> 2) & 0xf; + b24[2] |= (e & 0x3) << 6; break; + case 3: b24[2] |= e; break; + } + if (++b == 4) { + *po++ = b24[0]; + *po++ = b24[1]; + *po++ = b24[2]; + b24[0] = b24[1] = b24[2] = 0; + b = 0; + } + } + fwrite(out, 1, (po - out), outfp); + } + eprintf("invalid uudecode footer \"====\" not found\n"); +} static void uudecode(FILE *fp, FILE *outfp) diff --git a/uuencode.c b/uuencode.c @@ -4,15 +4,19 @@ #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <assert.h> #include "util.h" +static int mflag = 0; + +static void uuencodeb64(FILE *, const char *, const char *); static void uuencode(FILE *, const char *, const char *); static void usage(void) { - eprintf("usage: %s [file] name\n", argv0); + eprintf("usage: %s [-m] [file] name\n", argv0); } int @@ -22,7 +26,8 @@ main(int argc, char *argv[]) ARGBEGIN { case 'm': - eprintf("-m not implemented\n"); + mflag = 1; + break; default: usage(); } ARGEND; @@ -31,16 +36,69 @@ main(int argc, char *argv[]) usage(); if (argc == 1) { - uuencode(stdin, argv[0], "<stdin>"); + if (mflag) + uuencodeb64(stdin, argv[0], "<stdin>"); + else + uuencode(stdin, argv[0], "<stdin>"); } else { if (!(fp = fopen(argv[0], "r"))) eprintf("fopen %s:", argv[0]); - uuencode(fp, argv[1], argv[0]); + if (mflag) + uuencodeb64(fp, argv[1], argv[0]); + else + uuencode(fp, argv[1], argv[0]); fclose(fp); } return 0; } +static const char b64et[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +/* much faster */ +static unsigned int +b64e(unsigned char b[2]) +{ + unsigned int o = 0; + unsigned int p = b[2] | (b[1] << 8) | (b[0] << 16); + o = b64et[p & 0x3f]; p >>= 6; + o = (o << 8) | b64et[p & 0x3f]; p >>= 6; + o = (o << 8) | b64et[p & 0x3f]; p >>= 6; + o = (o << 8) | b64et[p & 0x3f]; + return o; +} + +static void +uuencodeb64(FILE *fp, const char *name, const char *s) +{ + struct stat st; + ssize_t n, m = 0; + unsigned char buf[45], *pb; + unsigned int out[sizeof(buf)/3 + 1], *po; + if (fstat(fileno(fp), &st) < 0) + eprintf("fstat %s:", s); + printf("begin-base64 %o %s\n", st.st_mode & 0777, name); + /* write line by line */ + while ((n = fread(buf, 1, sizeof(buf), fp))) { + /* clear old buffer if converting with non-multiple of 3 */ + if (n != sizeof(buf) && (m = n % 3) != 0) { + buf[n] = '\0'; /* m == 2 */ + if (m == 1) buf[n+1] = '\0'; /* m == 1 */ + } + for (pb = buf, po = out; pb < buf + n; pb += 3) + *po++ = b64e(pb); + if (m != 0) { + unsigned int mask = 0xffffffff, dest = 0x3d3d3d3d; + /* m==2 -> 0x00ffffff; m==1 -> 0x0000ffff */ + mask >>= ((3-m) << 3); + po[-1] = (po[-1] & mask) | (dest & ~mask); + } + *po++ = '\n'; + fwrite(out, 1, (po - out) * sizeof(unsigned int) - 3, stdout); + } + if (ferror(fp)) + eprintf("'%s' read error:", s); + puts("===="); +} + static void uuencode(FILE *fp, const char *name, const char *s) {