diff modules/base64/base64.c @ 265:8efd647ea9ca r20-5b31

Import from CVS: tag r20-5b31
author cvs
date Mon, 13 Aug 2007 10:25:37 +0200
parents
children aabb7f5b1c81
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/base64/base64.c	Mon Aug 13 10:25:37 2007 +0200
@@ -0,0 +1,429 @@
+/* base64 interface for XEmacs.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+
+This file is part of XEmacs.
+
+XEmacs is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* Author: William Perry <wmperry@aventail.com> */
+
+#include <config.h>
+
+#include "lisp.h"
+#include "buffer.h"
+#include "insdel.h"
+#include "lstream.h"
+#ifdef FILE_CODING
+#include "file-coding.h"
+#endif
+
+unsigned char alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+DEFUN ("base64-encode", Fbase64_encode, 1, 5, 0, /*
+Return the base64 encoding of an object.
+OBJECT is either a string or a buffer.
+Optional arguments START and END denote buffer positions for computing the
+hash of a portion of OBJECT.  The optional CODING argument specifies the coding
+system the text is to be represented in while computing the digest.  This only
+has meaning with MULE, and defaults to the current format of the data.
+If ERROR-ME-NOT is nil, report an error if the coding system can't be
+determined.  Else assume binary coding if all else fails.
+*/
+       (object, start, end, coding, error_me_not))
+{
+	int cols,bits,char_count;
+	Lisp_Object instream, outstream,deststream;
+	Lstream *istr, *ostr, *dstr;
+	static Extbyte_dynarr *conversion_out_dynarr;
+	static Extbyte_dynarr *out_dynarr;
+	char tempbuf[1024]; /* some random amount */
+	struct gcpro gcpro1, gcpro2;
+#ifdef FILE_CODING
+	Lisp_Object conv_out_stream, coding_system;
+	Lstream *costr;
+	struct gcpro gcpro3;
+#endif
+
+	if (!conversion_out_dynarr)
+		conversion_out_dynarr = Dynarr_new (Extbyte);
+	else
+		Dynarr_reset (conversion_out_dynarr);
+
+	if (!out_dynarr)
+		out_dynarr = Dynarr_new(Extbyte);
+	else
+		Dynarr_reset (out_dynarr);
+
+	char_count = bits = cols = 0;
+
+	/* set up the in stream */
+	if (BUFFERP (object))
+	{
+		struct buffer *b = decode_buffer (object, 1);
+		Bufpos begv, endv;
+		/* Figure out where we need to get info from */
+		get_buffer_range_char (b, start, end, &begv, &endv, GB_ALLOW_NIL);
+
+		instream = make_lisp_buffer_input_stream (b, begv, endv, 0);
+	}
+	else
+	{
+		Bytecount bstart, bend;
+		CHECK_STRING (object);
+		get_string_range_byte (object, start, end, &bstart, &bend,
+							   GB_HISTORICAL_STRING_BEHAVIOR);
+		instream = make_lisp_string_input_stream (object, bstart, bend);
+	}
+	istr = XLSTREAM (instream);
+
+#ifdef FILE_CODING
+	/* Find out what format the buffer will be saved in, so we can make
+	   the digest based on what it will look like on disk */
+	if (NILP(coding))
+	{
+		if (BUFFERP(object)) 
+	    {
+			/* Use the file coding for this buffer by default */
+			coding_system = XBUFFER(object)->buffer_file_coding_system;
+	    }
+		else
+	    {
+			/* attempt to autodetect the coding of the string.  Note: this VERY hit-and-miss */
+			enum eol_type eol = EOL_AUTODETECT;
+			coding_system = Fget_coding_system(Qundecided);
+			determine_real_coding_system(istr, &coding_system, &eol);
+	    }
+		if (NILP(coding_system)) 
+			coding_system = Fget_coding_system(Qbinary);
+		else
+	    {
+			coding_system = Ffind_coding_system (coding_system);
+			if (NILP(coding_system))
+				coding_system = Fget_coding_system(Qbinary);
+	    }
+	}
+	else
+	{
+		coding_system = Ffind_coding_system (coding);
+		if (NILP(coding_system))
+	    {
+			if (NILP(error_me_not))
+				signal_simple_error("No such coding system", coding);
+			else
+				coding_system = Fget_coding_system(Qbinary); /* default to binary */
+	    }
+	}
+#endif
+
+	/* setup the out stream */
+	outstream = make_dynarr_output_stream((unsigned_char_dynarr *)conversion_out_dynarr);
+	ostr = XLSTREAM (outstream);
+	deststream = make_dynarr_output_stream((unsigned_char_dynarr *)out_dynarr);
+	dstr = XLSTREAM (deststream);
+#ifdef FILE_CODING
+	/* setup the conversion stream */
+	conv_out_stream = make_encoding_output_stream (ostr, coding_system);
+	costr = XLSTREAM (conv_out_stream);
+	GCPRO3 (instream, outstream, conv_out_stream);
+#else
+	GCPRO2 (instream, outstream);
+#endif
+
+	/* Get the data while doing the conversion */
+	while (1) {
+		int size_in_bytes = Lstream_read (istr, tempbuf, sizeof (tempbuf));
+		int l;
+		if (!size_in_bytes)
+			break;
+		/* It does seem the flushes are necessary... */
+#ifdef FILE_CODING
+		Lstream_write (costr, tempbuf, size_in_bytes);
+		Lstream_flush (costr);
+#else
+		Lstream_write (ostr, tempbuf, size_in_bytes);
+#endif
+		Lstream_flush (ostr);
+
+		/* Update the base64 output buffer */
+		for (l = 0; l < size_in_bytes; l++) {
+			bits += Dynarr_at(conversion_out_dynarr,l);
+			char_count++;
+			if (char_count == 3) {
+				static char obuf[4];
+				obuf[0] = alphabet[(bits >> 18)];
+				obuf[1] = alphabet[(bits >> 12) & 0x3f];
+				obuf[2] = alphabet[(bits >>  6) & 0x3f];
+				obuf[3] = alphabet[bits & 0x3f];
+
+				Lstream_write(dstr,obuf,sizeof(obuf));
+				cols += 4;
+				if (cols == 72) {
+					Lstream_write(dstr,"\n",sizeof(unsigned char));
+					cols = 0;
+				}
+				bits = char_count = 0;
+			} else {
+				bits <<= 8;
+			}
+		}
+		/* reset the dynarr */
+		Lstream_rewind(ostr);
+	}
+	Lstream_close (istr);
+#ifdef FILE_CODING
+	Lstream_close (costr);
+#endif
+	Lstream_close (ostr);
+
+	if (char_count != 0) {
+		bits <<= 16 - (8 * char_count);
+		Lstream_write(dstr,&alphabet[bits >> 18],sizeof(unsigned char));
+		Lstream_write(dstr,&alphabet[(bits >> 12) & 0x3f],sizeof(unsigned char));
+		if (char_count == 1) {
+			Lstream_write(dstr,"==",2 * sizeof(unsigned char));
+		} else {
+			Lstream_write(dstr,&alphabet[(bits >> 6) & 0x3f],sizeof(unsigned char));
+			Lstream_write(dstr,"=",sizeof(unsigned char));
+		}
+	}
+#if 0
+	if (cols > 0) {
+		Lstream_write(dstr,"\n",sizeof(unsigned char));
+	}
+#endif
+	UNGCPRO;
+	Lstream_delete (istr);
+	Lstream_delete (ostr);
+#ifdef FILE_CODING
+	Lstream_delete (costr);
+#endif
+	Lstream_flush(dstr);
+	Lstream_delete(dstr);
+
+	return(make_string(Dynarr_atp(out_dynarr,0),Dynarr_length(out_dynarr)));
+}
+
+DEFUN ("base64-decode", Fbase64_decode, 1, 5, 0, /*
+Undo the base64 encoding of an object.
+OBJECT is either a string or a buffer.
+Optional arguments START and END denote buffer positions for computing the
+hash of a portion of OBJECT.  The optional CODING argument specifies the coding
+system the text is to be represented in while computing the digest.  This only
+has meaning with MULE, and defaults to the current format of the data.
+If ERROR-ME-NOT is nil, report an error if the coding system can't be
+determined.  Else assume binary coding if all else fails.
+*/
+       (object, start, end, coding, error_me_not))
+{
+    static char inalphabet[256], decoder[256];
+	int i,cols,bits,char_count,hit_eof;
+	Lisp_Object instream, outstream,deststream;
+	Lstream *istr, *ostr, *dstr;
+	static Extbyte_dynarr *conversion_out_dynarr;
+	static Extbyte_dynarr *out_dynarr;
+	char tempbuf[1024]; /* some random amount */
+	struct gcpro gcpro1, gcpro2;
+#ifdef FILE_CODING
+	Lisp_Object conv_out_stream, coding_system;
+	Lstream *costr;
+	struct gcpro gcpro3;
+#endif
+
+    for (i = (sizeof alphabet) - 1; i >= 0 ; i--) {
+		inalphabet[alphabet[i]] = 1;
+		decoder[alphabet[i]] = i;
+    }
+
+	if (!conversion_out_dynarr)
+		conversion_out_dynarr = Dynarr_new (Extbyte);
+	else
+		Dynarr_reset (conversion_out_dynarr);
+
+	if (!out_dynarr)
+		out_dynarr = Dynarr_new(Extbyte);
+	else
+		Dynarr_reset (out_dynarr);
+
+	char_count = bits = cols = hit_eof = 0;
+
+	/* set up the in stream */
+	if (BUFFERP (object))
+	{
+		struct buffer *b = decode_buffer (object, 1);
+		Bufpos begv, endv;
+		/* Figure out where we need to get info from */
+		get_buffer_range_char (b, start, end, &begv, &endv, GB_ALLOW_NIL);
+
+		instream = make_lisp_buffer_input_stream (b, begv, endv, 0);
+	}
+	else
+	{
+		Bytecount bstart, bend;
+		CHECK_STRING (object);
+		get_string_range_byte (object, start, end, &bstart, &bend,
+							   GB_HISTORICAL_STRING_BEHAVIOR);
+		instream = make_lisp_string_input_stream (object, bstart, bend);
+	}
+	istr = XLSTREAM (instream);
+
+#ifdef FILE_CODING
+	/* Find out what format the buffer will be saved in, so we can make
+	   the digest based on what it will look like on disk */
+	if (NILP(coding))
+	{
+		if (BUFFERP(object)) 
+	    {
+			/* Use the file coding for this buffer by default */
+			coding_system = XBUFFER(object)->buffer_file_coding_system;
+	    }
+		else
+	    {
+			/* attempt to autodetect the coding of the string.  Note: this VERY hit-and-miss */
+			enum eol_type eol = EOL_AUTODETECT;
+			coding_system = Fget_coding_system(Qundecided);
+			determine_real_coding_system(istr, &coding_system, &eol);
+	    }
+		if (NILP(coding_system)) 
+			coding_system = Fget_coding_system(Qbinary);
+		else
+	    {
+			coding_system = Ffind_coding_system (coding_system);
+			if (NILP(coding_system))
+				coding_system = Fget_coding_system(Qbinary);
+	    }
+	}
+	else
+	{
+		coding_system = Ffind_coding_system (coding);
+		if (NILP(coding_system))
+	    {
+			if (NILP(error_me_not))
+				signal_simple_error("No such coding system", coding);
+			else
+				coding_system = Fget_coding_system(Qbinary); /* default to binary */
+	    }
+	}
+#endif
+
+	/* setup the out stream */
+	outstream = make_dynarr_output_stream((unsigned_char_dynarr *)conversion_out_dynarr);
+	ostr = XLSTREAM (outstream);
+	deststream = make_dynarr_output_stream((unsigned_char_dynarr *)out_dynarr);
+	dstr = XLSTREAM (deststream);
+#ifdef FILE_CODING
+	/* setup the conversion stream */
+	conv_out_stream = make_encoding_output_stream (ostr, coding_system);
+	costr = XLSTREAM (conv_out_stream);
+	GCPRO3 (instream, outstream, conv_out_stream);
+#else
+	GCPRO2 (instream, outstream);
+#endif
+
+	/* Get the data while doing the conversion */
+	while (1) {
+		int size_in_bytes = Lstream_read (istr, tempbuf, sizeof (tempbuf));
+		int l;
+		if (!size_in_bytes) {
+			hit_eof = 1;
+			break;
+		}
+		/* It does seem the flushes are necessary... */
+#ifdef FILE_CODING
+		Lstream_write (costr, tempbuf, size_in_bytes);
+		Lstream_flush (costr);
+#else
+		Lstream_write (ostr, tempbuf, size_in_bytes);
+#endif
+		Lstream_flush (ostr);
+
+		/* Update the base64 output buffer */
+		for (l = 0; l < size_in_bytes; l++) {
+			if (Dynarr_at(conversion_out_dynarr,l) == '=')
+				goto decoder_out;
+			bits += decoder[Dynarr_at(conversion_out_dynarr,l)];
+			fprintf(stderr,"%d\n",bits);
+			char_count++;
+			if (char_count == 4) {
+				static unsigned char obuf[3];
+				obuf[0] = (bits >> 16);
+				obuf[1] = (bits >> 8) & 0xff;
+				obuf[2] = (bits & 0xff);
+
+				Lstream_write(dstr,obuf,sizeof(obuf));
+				bits = char_count = 0;
+			} else {
+				bits <<= 6;
+			}
+		}
+		/* reset the dynarr */
+		Lstream_rewind(ostr);
+	}
+ decoder_out:
+	Lstream_close (istr);
+#ifdef FILE_CODING
+	Lstream_close (costr);
+#endif
+	Lstream_close (ostr);
+
+	if (hit_eof) {
+		if (char_count) {
+			error_with_frob(object,"base64-decode failed: at least %d bits truncated",((4 - char_count) * 6));
+		}
+	}
+	switch(char_count) {
+	case 1:
+		error_with_frob(object, "base64 encoding incomplete: at least 2 bits missing");
+		break;
+	case 2:
+		char_count = bits >> 10;
+		Lstream_write(dstr,&char_count,sizeof(char_count));
+		break;
+	case 3:
+	{
+		unsigned char buf[2];
+		buf[0] = (bits >> 16);
+		buf[1] = (bits >> 8) & 0xff;
+		Lstream_write(dstr,buf,sizeof(buf));
+		break;
+	}
+	}
+
+	UNGCPRO;
+	Lstream_delete (istr);
+	Lstream_delete (ostr);
+#ifdef FILE_CODING
+	Lstream_delete (costr);
+#endif
+	Lstream_flush(dstr);
+	Lstream_delete(dstr);
+
+	return(make_string(Dynarr_atp(out_dynarr,0),Dynarr_length(out_dynarr)));
+}
+
+void
+syms_of (void)
+{
+  DEFSUBR(Fbase64_encode);
+  DEFSUBR(Fbase64_decode);
+}
+
+void
+vars_of (void)
+{
+  Fprovide (intern ("base64"));
+}