/* public domain */

/*
 * arbitrary data on stdin -> BASE64 data on stdout
 *
 * UNIX's newline convention is used, i.e. one ASCII control-j (10 decimal).
 */

#include <glib.h>
#include "base64.h"

unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

gchar *
base64_encode (const gchar *input)
{
    int cols, bits, c, char_count, i;
    GString *work;

    char_count = 0;
    bits = 0;
    cols = 0;
    i = 0;
    work = g_string_new(NULL);
    while (input[i] != '\0')
    {
	    c = input[i];
	    if (c > 255)
	    {
		    g_string_free(work, TRUE);
		    return NULL;
	    }
	    bits += c;
	    char_count++;
	    if (char_count == 3)
	    {
		    g_string_append_c(work, alphabet[bits >> 18]);
		    g_string_append_c(work, alphabet[(bits >> 12) & 0x3f]);
		    g_string_append_c(work, alphabet[(bits >> 6) & 0x3f]);
		    g_string_append_c(work, alphabet[bits & 0x3f]);
		    cols += 4;
		    if (cols == 72)
		    {
			    g_string_append_c(work, '\n');
			    cols = 0;
		    }
		    bits = 0;
		    char_count = 0;
	    }
	    else 
	    {
		    bits <<= 8;
	    }
	    i++;
    }
    if (char_count != 0)
    {
	    bits <<= 16 - (8 * char_count);
	    g_string_append_c(work, alphabet[bits >> 18]);
	    g_string_append_c(work, alphabet[(bits >> 12) & 0x3f]);
	    if (char_count == 1)
	    {
		    g_string_append_c(work, '=');
		    g_string_append_c(work, '=');
	    }
	    else 
	    {
		    g_string_append_c(work, alphabet[(bits >> 6) & 0x3f]);
		    g_string_append_c(work, '=');
	    }
	    if (cols > 0)
		    g_string_append_c(work, '\n');
    }

    return g_string_free(work, FALSE);
}
