/*
 * $Header: /san01/cvs/ashpool/csrc/continuity/mecha/Attic/b64.c,v 1.9 2004/04/30 13:41:56 aleigh Exp $
 */

/*
 * Copyright (c) 1994, 2004 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include "mecha.h"

/*
 * MODULE                                                       HTUU.c
 * 
 * UENCODE AND UUDECODE
 * 
 * ACKNOWLEDGEMENT: This code is taken from rpem distribution, and was
 * originally written by Mark Riordan.
 * 
 * AUTHORS: MR      Mark Riordan    riordanmr@clvax1.cl.msu.edu AL      Ari
 * Luotonen luotonen@dxcern.cern.ch
 * 
 * HISTORY: Added as part of the WWW library and edited to conform with the WWW
 * project coding standards by: AL  5 Aug 1993 Originally written by:
 * MR 12 Aug 1990 Original header text:
 * ------------------------------------------------------------- File
 * containing routines to convert a buffer of bytes to/from RFC 1113
 * printable encoding format.
 * 
 * This technique is similar to the familiar Unix uuencode format in that it
 * maps 6 binary bits to one ASCII character (or more aptly, 3 binary bytes
 * to 4 ASCII characters).  However, RFC 1113 does not use the same mapping
 * to printable characters as uuencode.
 * 
 * Mark Riordan   12 August 1990 and 17 Feb 1991. This code is hereby placed in
 * the public domain.
 * -------------------------------------------------------------
 * 
 * BUGS:
 * 
 */


/*
 * htuu.c --
 * 
 * Uuencoding and decoding routines.
 * 
 * 
 */


static char six2pr[64] = {
    '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', '+', '/'
};

static unsigned char pr2six[256];


/*--- function HTUU_encode -----------------------------------------------
 *
 *   Encode a single line of binary data to a standard format that
 *   uses only printing ASCII characters (but takes up 33% more bytes).
 *
 *    Entry    bufin    points to a buffer of bytes.  If nbytes is not
 *                      a multiple of three, then the byte just beyond
 *                      the last byte in the buffer must be 0.
 *             nbytes   is the number of bytes in that buffer.
 *                      This cannot be more than 48.
 *             bufcoded points to an output buffer.  Be sure that this
 *                      can hold at least 1 + (4*nbytes)/3 characters.
 *
 *    Exit     bufcoded contains the coded line.  The first 4*nbytes/3 bytes
 *                      contain printing ASCII characters representing
 *                      those binary bytes. This may include one or
 *                      two '=' characters used as padding at the end.
 *                      The last byte is a zero byte.
 *             Returns the number of ASCII characters in "bufcoded".
 */
int b64Fencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded)
{
    /* ENC is the basic 1 character encoding function to make a char printing */
#define ENC(c) six2pr[c]

    register char *outptr = bufcoded;
    unsigned int i;

    assert(bufin != NULL);
    assert(bufcoded != NULL);

    for (i = 0; i < nbytes; i += 3) {
        /* c1 */
        *(outptr++) = ENC(*bufin >> 2);
        /* c2 */
        *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017));
        /* c3 */
        *(outptr++) =
            ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));
        /* c4 */
        *(outptr++) = ENC(bufin[2] & 077);

        bufin += 3;
    }

    /*
     * If nbytes was not a multiple of 3, then we have encoded too many
     * characters.  Adjust appropriately.
     */
    if (i == nbytes + 1) {
        /* There were only 2 bytes in that last group */
        outptr[-1] = '=';
    } else if (i == nbytes + 2) {
        /* There was only 1 byte in that last group */
        outptr[-1] = '=';
        outptr[-2] = '=';
    }
    *outptr = '\0';
    return (outptr - bufcoded);
}


/*--- function HTUU_decode ------------------------------------------------
 *
 *  Decode an ASCII-encoded buffer back to its original binary form.
 *
 *    Entry    bufcoded    points to a uuencoded string.  It is
 *                         terminated by any character not in
 *                         the printable character table six2pr, but
 *                         leading whitespace is stripped.
 *             bufplain    points to the output buffer; must be big
 *                         enough to hold the decoded string (generally
 *                         shorter than the encoded string) plus
 *                         as many as two extra bytes used during
 *                         the decoding process.
 *             outbufsize  is the maximum number of bytes that
 *                         can fit in bufplain.
 *
 *    Exit     Returns the number of binary bytes decoded.
 *             bufplain    contains these bytes.
 */
int b64Fdecode(const char *bufcoded, unsigned char *bufplain,
               int outbufsize)
{
    /* single character decode */
#define DEC(c) pr2six[(int)c]
#define MAXVAL 63

    static int first = 1;

    int nbytesdecoded, j;
    register const char *bufin = bufcoded;
    register unsigned char *bufout = bufplain;
    register int nprbytes;

    assert(bufcoded != NULL);
    assert(bufplain != NULL);
    /*
     * If this is the first call, initialize the mapping table. This code
     * should work even on non-ASCII machines.
     */
    if (first) {
        first = 0;
        for (j = 0; j < 256; j++)
            pr2six[j] = MAXVAL + 1;

        for (j = 0; j < 64; j++)
            pr2six[(int) six2pr[j]] = (unsigned char) j;
    }
    /* Strip leading whitespace. */

    while (*bufcoded == ' ' || *bufcoded == '\t')
        bufcoded++;

    /*
     * Figure out how many characters are in the input buffer. If this would
     * decode into more bytes than would fit into the output buffer, adjust
     * the number of input bytes downwards.
     */
    bufin = bufcoded;
    while (pr2six[(int) *(bufin++)] <= MAXVAL);
    nprbytes = bufin - bufcoded - 1;
    nbytesdecoded = ((nprbytes + 3) / 4) * 3;
    if (nbytesdecoded > outbufsize) {
        nprbytes = (outbufsize * 4) / 3;
    }
    bufin = bufcoded;

    while (nprbytes > 0) {
        *(bufout++) =
            (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4);
        *(bufout++) =
            (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2);
        *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3]));
        bufin += 4;
        nprbytes -= 4;
    }

    if (nprbytes & 03) {
        if (pr2six[(int) bufin[-2]] > MAXVAL) {
            nbytesdecoded -= 2;
        } else {
            nbytesdecoded -= 1;
        }
    }
    return (nbytesdecoded);
}
