From: bhoughto@sedona.intel.com (Blair P. Houghton)
Newsgroups: comp.mail.mime
Subject: Re: X.400 and multimedia mail
Date: 13 Dec 1992 19:27:15 GMT
Organization: Intel Corp., Chandler, Arizona
NNTP-Posting-Host: alfalfa.intel.com

In article <1gfg5uINN5ej@calvin.NYU.EDU> roy@mchip00.med.nyu.edu (Roy Smith) writes:
>henry@zoo.toronto.edu (Henry Spencer) writes:
>> And uuencode is essentially identical to base64 except that it's not as
>> good, due to some fundamental design mistakes; in particular, it isn't any
>> more compact.
>
>	What ever happened to btoa?  It came out a bunch of years ago, but
>never seemed to catch on.  As I remember, it was touted as a replacement for
>uuencode with the advantage that it only incurred a 5/4ths expansion instead
>of 4/3rds.  Was there some reason why MIME didn't use btoa?

Portability of the character representation.

RFC 1341 explicitly cites a desire to use only characters
which appear in all[*] extant literal standards.  The set
[A-Za-z0-9+/] (the brackets aren't included) appears just
about everywhere.  You could probably add [-,.=] to
that[**], but you don't need more than 64 for a 6-bit
code.

By the way, in case anyone wants it, I've enclosed some
simple base64 stuff below, as an example of the ascii
implementation on a machine with 8-bit bytes.  It doesn't
ignore newlines in the ascii, yet, but it doesn't create
them, either.

				--Blair
				  "Boy, are we gonna get mail..."
				  -D. Letterman


[*] almost all? I don't remember any exceptions.
[**] '=', of course, _is_ included, as the padding character.


--
/* base64.c -- routines to convert to and from base64 encoding */
/* (C) 1992 Blair P. Houghton; All Rights Reserved */

#include <stdio.h>
#include <stddef.h>
#include <errno.h>

char base64_to_ascii[] = {
    'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',      /* 00-07 */
    'I',  'J',  'K',  'L',  'M',  'N',  'O',  'P',      /* 08-0f */
    'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',      /* 10-17 */
    'Y',  'Z',  'a',  'b',  'c',  'd',  'e',  'f',      /* 18-1f */
    'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',      /* 20-27 */
    'o',  'p',  'q',  'r',  's',  't',  'u',  'v',      /* 28-2f */
    'w',  'x',  'y',  'z',  '0',  '1',  '2',  '3',      /* 30-37 */
    '4',  '5',  '6',  '7',  '8',  '9',  '+',  '/'       /* 38-3f */
};

#define PAD '='


char ascii_to_base64[] = {
      0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,    0,    0,    0,    0,    0,
      0,    0,    0,   62,    0,    0,    0,   63,
     52,   53,   54,   55,   56,   57,   58,   59,
     60,   61,    0,    0,    0,/**/0,    0,    0,      /* comment at '=' */
      0,    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,    0,    0,    0,    0,    0,
      0,   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,    0,    0,    0,    0,    0
};


static char s1, s2, s3, s4;

void binary_to_ascii_base64( char *s, const char *t )
{
   /*
    *  using `*t++' here instead of the array notation
    *  is an invitation to undefined-order-of-evaluation bugs
    */
    s1 = (t[0] & 0xfc)>>2;
    s2 = (t[0] & 0x03)<<4 | (t[1]&0xf0)>>4;
    s3 = (t[1] & 0x0f)<<2 | (t[2]&0xc0)>>6;
    s4 =  t[2] & 0x3f;

    *s++ = (char)base64_to_ascii[ s1 ];
    *s++ = (char)base64_to_ascii[ s2 ];
    *s++ = (char)base64_to_ascii[ s3 ];
    *s = (char)base64_to_ascii[ s4 ];
}

void ascii_base64_to_binary( const char *s, char *t )
{
    s1 = ascii_to_base64[ *s++ ];
    s2 = ascii_to_base64[ *s++ ];
    s3 = ascii_to_base64[ *s++ ];
    s4 = ascii_to_base64[ *s ];

    *t++ = s1<<2 | (s2&0x30)>>4;
    *t++ = (s2&0x0f)<<4 | (s3&0x3c)>>2;
    *t = (s3&0x03)<<6 | s4;
}


char *convert_to_binary( const char *s, size_t n_s, size_t *n_t )
{
    static char sx[4], tx[4], *t, *t_start;
    static unsigned spares, n_loop, n;

    spares = (s[n_s - 1] == PAD) + (s[n_s - 2] == PAD); /* adding logicals */

    n_loop = (spares ? (n_s - 4) : n_s) / 4;            /* # of whole groups */

    *n_t = 3 * n_loop + (spares ? (3 - spares) : 0);

    if ( *n_t == 0 )
        return NULL;

    errno = 0;
    t = t_start = (char *) malloc( *n_t );
    if ( !t ) {
        perror( "convert_to_binary(): allocating binary array" );
        exit( 1 );
    }

    while ( n_loop-- ) {
        ascii_base64_to_binary(s,t);
        s += 4;
        t += 3;
    }

    /* last quartet */
    if ( spares ) {
        /* one or two PAD characters were found */
        sx[0] = sx[1] = sx[2] = sx[3] = 0;
        tx[0] = tx[1] = tx[2] = 0;
        for( n = 0; n < spares; n++ )
            sx[n] = s[n];
        ascii_base64_to_binary(sx,tx);
        *t = tx[0];                     /* ...X; two PAD chars --> one bin */
        if ( spares == 1 )
            *++t = tx[1];               /* ...XX; one PAD --> two extra bins */
    }

    return t_start;
}


char *convert_to_ascii_base_64( const char *t, size_t n_t, size_t *n_s )
{
    static char tx[4], *s, *s_start;
    static unsigned spares, n_loop, n;

    switch( n_t % 3 ) {
        case 1:
            spares = 2;
            break;
        case 2:
            spares = 1;
            break;
        case 0:
            spares = 0;
            break;
    }

    n_loop = (spares ? (n_t - (3-spares)) : n_t) / 3;   /* # of whole groups */

    *n_s = 4 * (n_loop + (spares != 0));                /* adding logicals */

    if ( *n_s < 4 )
        return NULL;

    errno = 0;
    s = s_start = (char *) malloc( *n_s );
    if ( !s ) {
        perror( "convert_to_ascii_base_64(): allocating ascii array" );
        exit( 1 );
    }

    while ( n_loop-- ) {
        binary_to_ascii_base64(s,t);
        s += 4;
        t += 3;
    }

    if ( spares ) {
        /* one or two PAD characters are needed */
        tx[0] = tx[1] = tx[2] = 0;
        for( n = 0; n < spares; n++ )
            tx[n] = t[n];
        binary_to_ascii_base64(s,tx);
        s[3] = PAD;                     /* "....XXX=" */
        if ( spares == 2 )
            s[2] = PAD;                 /* "....XX==" */
    }

    return s_start;
}

#ifdef BASE64MAIN

#include "/eng/eng21/bhoughto/include/slurp.h"

int main( void )
{
    char u[4] = { 'a', 'r', '8', 'N' };
    char t[] = { 0xfa, 0xeb, 0xdc, 0xcd };
    char *s, *r;
    size_t n_u, n_t, n_s, n_r, i;

    n_u = 4;
    for ( i = 0; i < n_u; i++ )
        printf( "%c ", base64_to_ascii[ ascii_to_base64[ u[i] ] ] );
    printf( "(%d bytes)\n", n_u );

    n_t = sizeof(t);
    printf( "%x %x %x %x (%d bytes)\n", t[0], t[1], t[2], t[3], n_t );

    s = convert_to_ascii_base_64( t, n_t, &n_s );

    for ( i = 0; i < n_s; i++ )
        printf( "%x ", ascii_to_base64[ s[i] ] );
    printf( "\n\"%s\" (%d chars)\n", s, n_s );

    r = convert_to_binary( s, n_s, &n_r );

    for ( i = 0; i < n_r; i++ )
        printf( "%x ", r[i] );
    printf( "(%d bytes)\n", n_r );

    s = slurp( "/tmp/foo" );

    n_s = strlen( s ) + 1;

    r = convert_to_ascii_base_64( s, n_s, &n_r );

    fputs( r, stdout );

    fputs( convert_to_binary( r, n_r, &n_s ) , stdout);
}

#endif

Newsgroups: comp.mail.mime