123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/types.h>
- // How long is a tuple like "...[exax-x]..."
- #define TUP_LEN 6
- // DEC_BUF_LEN must always be a multiple of TUP_LEN.
- //
- // Furthermore, until some way is found to deal with pipe buffering better,
- // this should be kept small. Modest data doesn't pose any issue, but decoding
- // isn't robust against getting hammered.
- //
- // The number 24 works okay for tests like this:
- //
- // $ time ( dd if=/dev/urandom bs=1M count=32 status=none | bb | buffer | bb -d > /dev/null )
- //
- // Without the 'buffer' command, even DEC_BUF_LEN = 6 is a problem.
- // Whereas, even with the 'buffer' command, 48 is too big.
- //
- #define DEC_BUF_LEN 24
- // This must always be a multiple of two.
- #define ENC_BUF_LEN 4096
- const char *V = "aeiouy";
- const char *C = "bcdfghklmnprstvzx";
- void encode() {
- unsigned char D[ENC_BUF_LEN] = { 0 };
- int navail = 0;
- int bytes_read = 0;
- unsigned int chksum = 1;
- unsigned int a, b, c, d, e, n;
- unsigned int byte[2];
- printf("x");
- while((bytes_read = read(0, D, ENC_BUF_LEN)) != 0) {
- int err = errno;
- if(bytes_read < 0) {
- fprintf(stderr, "Encoding error %d: %s\n", err, strerror(err));
- }
- navail = bytes_read; // need to save number available for after the loop exits
- for(n = 0; n < navail/2; ++n) {
- byte[0] = (unsigned int)D[n*2];
- byte[1] = (unsigned int)D[n*2+1];
- a = (((byte[0] >> 6) & 3) + chksum)%6;
- b = (byte[0] >> 2) & 15;
- c = ((byte[0] & 3) + chksum/6)%6;
- d = (byte[1] >> 4) & 15;
- e = byte[1] & 15;
- printf("%c%c%c%c-%c", V[a], C[b], V[c], C[d], C[e]);
- chksum = (chksum*5 + byte[0]*7 + byte[1]) % 36;
- }
- }
- int err = errno;
- if(bytes_read < 0) {
- fprintf(stderr, "Encoding error %d: %s\n", err, strerror(err));
- }
- if(navail%2) {
- byte[0] = (unsigned int)D[navail-1];
- a = (((byte[0] >> 6) & 3) + chksum) % 6;
- b = (byte[0] >> 2) & 15;
- c = ((byte[0] & 3) + chksum/6) % 6;
- } else {
- a = chksum % 6;
- b = 16;
- c = chksum/6;
- }
- printf("%c%c%c", V[a], C[b], V[c]);
- printf("x\n");
- }
- unsigned int decode_2way_byte(const int a1, const int a2) {
- if(a1 > 16 || a2 > 16) {
- fprintf(stderr, "Invalid input: 'x' detected mid-stream while decoding 2-way byte\n");
- exit(1);
- }
- return (a1 << 4) | a2;
- }
- unsigned int decode_3way_byte(const int a1, const int a2, const int a3, const int c) {
- if(a2 > 16) {
- fprintf(stderr, "Invalid input: 'x' detected mid-stream while decoding 3-way byte\n");
- exit(1);
- }
- unsigned int high2 = (a1 - (c%6) + 6) % 6;
- unsigned int mid4 = a2;
- unsigned int low2 = (a3 - (c/6)%6 + 6) % 6;
- if(high2 >= 4 || low2 >= 4) {
- fprintf(stderr, "Invalid input: corruption detected while decoding 3-way byte\n");
- exit(1);
- }
- return (high2 << 6) | (mid4 << 2) | low2;
- }
- // Based on https://github.com/eur0pa/bubblepy
- void decode() {
- int inv[256] = {0};
- int n = 0;
- for(n = 0; n < strnlen(V, 32); ++n) {
- inv[(int)V[n]] = n;
- }
- for(n = 0; n < strnlen(C, 32); ++n) {
- inv[(int)C[n]] = n;
- }
- unsigned char E[DEC_BUF_LEN] = {0};
- read(0, E, 1);
- if(E[0] != 'x') {
- fprintf(stderr, "Invalid input: first character was not 'x'\n");
- exit(1);
- }
- int navail = 0;
- int bytes_read = 0;
- unsigned int chksum = 1;
- int tup[TUP_LEN];
- unsigned int byte[2];
- while((bytes_read = read(0, E, DEC_BUF_LEN)) > 0) {
- int err = errno;
- if(bytes_read < 0) {
- fprintf(stderr, "Decoding error %d: %s\n", err, strerror(err));
- }
- navail = bytes_read;
- for(n = 0; n < navail/TUP_LEN; ++n) {
- tup[0] = inv[(int)E[n*TUP_LEN]];
- tup[1] = inv[(int)E[n*TUP_LEN+1]];
- tup[2] = inv[(int)E[n*TUP_LEN+2]];
- tup[3] = inv[(int)E[n*TUP_LEN+3]];
- if(E[n*TUP_LEN+4] != '-') {
- fprintf(stderr, "Invalid input: missing dash delimiter. (Got a '%c' with value %d instead.)\n", E[n*TUP_LEN]+4, E[n*TUP_LEN]+4);
- exit(1);
- }
- tup[5] = inv[(int)E[n*TUP_LEN+5]];
- byte[0] = decode_3way_byte(tup[0], tup[1], tup[2], chksum);
- byte[1] = decode_2way_byte(tup[3], tup[5]);
- printf("%c%c", byte[0], byte[1]);
- chksum = (chksum*5 + byte[0]*7 + byte[1]) % 36;
- }
- }
- int err = errno;
- if(bytes_read < 0) {
- fprintf(stderr, "Decoding error %d: %s\n", err, strerror(err));
- }
- int remaining = navail-n*TUP_LEN;
- if(E[n*TUP_LEN+remaining-1] == '\n') {
- --remaining;
- }
- if(E[n*TUP_LEN+remaining-1] == 'x') {
- --remaining;
- } else {
- unsigned char L = E[n*TUP_LEN+remaining-1];
- fprintf(stderr, "Invalid input: final character was '%c' with value '%d' instead of 'x'\n", L, L);
- exit(1);
- }
- tup[0] = inv[(int)E[n*TUP_LEN]];
- tup[1] = inv[(int)E[n*TUP_LEN+1]];
- tup[2] = inv[(int)E[n*TUP_LEN+2]];
- char last = '\0';
- if(tup[1] == 16) {
- if(tup[0] != chksum%6 || tup[2] != chksum/6) {
- fprintf(stderr, "Invalid input: corruption detected\n");
- exit(1);
- }
- } else {
- last = decode_3way_byte(tup[0], tup[1], tup[2], chksum);
- printf("%c", last);
- }
- // Add a newline at the end, but not if piping to other commands, and only if one isn't already there.
- if(last != '\n' && isatty(1)) {
- printf("\n");
- }
- }
- void usage(char *name) {
- printf("%s [-d]\n\nEncode data from stdin into 'bubble babble'.\n\n\t-d\tDecode instead of encode.\n", name);
- }
- int main(int argc, char **argv) {
- if(argc == 2) {
- if(strncmp(argv[1], "-d", 4) == 0) {
- decode();
- } else {
- usage(argv[0]);
- }
- } else {
- encode();
- }
- return 0;
- }
|