+/* Semantically identical to ADVANCE_INPUT above, only no >255
+ checking is needed for decoding -- checking is covered by IS_BASE64
+ below. */
+#define ADVANCE_INPUT(c, stream) \
+ (ec = Lstream_get_emchar (stream), \
+ ec == -1 ? 0 : (c = (Bufbyte)ec, 1))
+
+/* Get next character from the stream, but ignore it if it's
+ whitespace. ENDP is set to 1 if EOF is hit. */
+#define ADVANCE_INPUT_IGNORE_WHITESPACE(c, endp, stream) do { \
+ endp = 0; \
+ do { \
+ if (!ADVANCE_INPUT (c, stream)) \
+ endp = 1; \
+ } while (!endp && (c == ' ' || c == '\t' || c == '\r' || c == '\n' \
+ || c == '\f' || c == '\v')); \
+} while (0)
+
+#define STORE_BYTE(pos, val) do { \
+ pos += set_charptr_emchar (pos, (Emchar)((unsigned char)(val))); \
+ ++*ccptr; \
+} while (0)
+
+static Bytind
+base64_decode_1 (Lstream *istream, Bufbyte *to, Charcount *ccptr)
+{
+ Bufbyte *e = to;
+ unsigned long value;
+
+ *ccptr = 0;
+ while (1)
+ {
+ Bufbyte c;
+ Emchar ec;
+ int endp;
+
+ ADVANCE_INPUT_IGNORE_WHITESPACE (c, endp, istream);
+ if (endp)
+ break;
+
+ /* Process first byte of a quadruplet. */
+ if (!IS_BASE64 (c))
+ return -1;
+ value = base64_char_to_value[c] << 18;
+
+ /* Process second byte of a quadruplet. */
+ ADVANCE_INPUT_IGNORE_WHITESPACE (c, endp, istream);
+ if (endp)
+ return -1;
+
+ if (!IS_BASE64 (c))
+ return -1;
+ value |= base64_char_to_value[c] << 12;
+
+ STORE_BYTE (e, value >> 16);
+
+ /* Process third byte of a quadruplet. */
+ ADVANCE_INPUT_IGNORE_WHITESPACE (c, endp, istream);
+ if (endp)
+ return -1;
+
+ if (c == '=')
+ {
+ ADVANCE_INPUT_IGNORE_WHITESPACE (c, endp, istream);
+ if (endp)
+ return -1;
+ if (c != '=')
+ return -1;
+ continue;
+ }
+
+ if (!IS_BASE64 (c))
+ return -1;
+ value |= base64_char_to_value[c] << 6;
+
+ STORE_BYTE (e, 0xff & value >> 8);
+
+ /* Process fourth byte of a quadruplet. */
+ ADVANCE_INPUT_IGNORE_WHITESPACE (c, endp, istream);
+ if (endp)
+ return -1;
+
+ if (c == '=')
+ continue;
+
+ if (!IS_BASE64 (c))
+ return -1;
+ value |= base64_char_to_value[c];
+
+ STORE_BYTE (e, 0xff & value);
+ }
+
+ return e - to;
+}
+#undef ADVANCE_INPUT
+#undef ADVANCE_INPUT_IGNORE_WHITESPACE
+#undef STORE_BYTE
+
+static Lisp_Object
+free_malloced_ptr (Lisp_Object unwind_obj)
+{
+ void *ptr = (void *)get_opaque_ptr (unwind_obj);
+ xfree (ptr);
+ free_opaque_ptr (unwind_obj);
+ return Qnil;
+}
+
+/* Don't use alloca for regions larger than this, lest we overflow
+ the stack. */
+#define MAX_ALLOCA 65536
+
+/* We need to setup proper unwinding, because there is a number of
+ ways these functions can blow up, and we don't want to have memory
+ leaks in those cases. */
+#define XMALLOC_OR_ALLOCA(ptr, len, type) do { \
+ size_t XOA_len = (len); \
+ if (XOA_len > MAX_ALLOCA) \
+ { \
+ ptr = xnew_array (type, XOA_len); \
+ record_unwind_protect (free_malloced_ptr, \
+ make_opaque_ptr ((void *)ptr)); \
+ } \
+ else \
+ ptr = alloca_array (type, XOA_len); \
+} while (0)
+
+#define XMALLOC_UNBIND(ptr, len, speccount) do { \
+ if ((len) > MAX_ALLOCA) \
+ unbind_to (speccount, Qnil); \
+} while (0)
+
+DEFUN ("base64-encode-region", Fbase64_encode_region, 2, 3, "r", /*
+Base64-encode the region between BEG and END.
+Return the length of the encoded text.
+Optional third argument NO-LINE-BREAK means do not break long lines
+into shorter lines.
+*/
+ (beg, end, no_line_break))
+{
+ Bufbyte *encoded;
+ Bytind encoded_length;
+ Charcount allength, length;
+ struct buffer *buf = current_buffer;
+ Bufpos begv, zv, old_pt = BUF_PT (buf);
+ Lisp_Object input;
+ int speccount = specpdl_depth();
+
+ get_buffer_range_char (buf, beg, end, &begv, &zv, 0);
+ barf_if_buffer_read_only (buf, begv, zv);
+
+ /* We need to allocate enough room for encoding the text.
+ We need 33 1/3% more space, plus a newline every 76
+ characters, and then we round up. */
+ length = zv - begv;
+ allength = length + length/3 + 1;
+ allength += allength / MIME_LINE_LENGTH + 1 + 6;
+
+ input = make_lisp_buffer_input_stream (buf, begv, zv, 0);
+ /* We needn't multiply allength with MAX_EMCHAR_LEN because all the
+ base64 characters will be single-byte. */
+ XMALLOC_OR_ALLOCA (encoded, allength, Bufbyte);
+ encoded_length = base64_encode_1 (XLSTREAM (input), encoded,
+ NILP (no_line_break));
+ if (encoded_length > allength)
+ abort ();
+ Lstream_delete (XLSTREAM (input));
+
+ /* Now we have encoded the region, so we insert the new contents
+ and delete the old. (Insert first in order to preserve markers.) */
+ buffer_insert_raw_string_1 (buf, begv, encoded, encoded_length, 0);
+ XMALLOC_UNBIND (encoded, allength, speccount);
+ buffer_delete_range (buf, begv + encoded_length, zv + encoded_length, 0);
+
+ /* Simulate FSF Emacs: if point was in the region, place it at the
+ beginning. */
+ if (old_pt >= begv && old_pt < zv)
+ BUF_SET_PT (buf, begv);
+
+ /* We return the length of the encoded text. */
+ return make_int (encoded_length);
+}
+
+DEFUN ("base64-encode-string", Fbase64_encode_string, 1, 2, 0, /*
+Base64 encode STRING and return the result.
+*/
+ (string, no_line_break))
+{
+ Charcount allength, length;
+ Bytind encoded_length;
+ Bufbyte *encoded;
+ Lisp_Object input, result;
+ int speccount = specpdl_depth();
+
+ CHECK_STRING (string);
+
+ length = XSTRING_CHAR_LENGTH (string);
+ allength = length + length/3 + 1;
+ allength += allength / MIME_LINE_LENGTH + 1 + 6;
+
+ input = make_lisp_string_input_stream (string, 0, -1);
+ XMALLOC_OR_ALLOCA (encoded, allength, Bufbyte);
+ encoded_length = base64_encode_1 (XLSTREAM (input), encoded,
+ NILP (no_line_break));
+ if (encoded_length > allength)
+ abort ();
+ Lstream_delete (XLSTREAM (input));
+ result = make_string (encoded, encoded_length);
+ XMALLOC_UNBIND (encoded, allength, speccount);
+ return result;
+}
+
+DEFUN ("base64-decode-region", Fbase64_decode_region, 2, 2, "r", /*
+Base64-decode the region between BEG and END.
+Return the length of the decoded text.
+If the region can't be decoded, return nil and don't modify the buffer.
+*/
+ (beg, end))
+{
+ struct buffer *buf = current_buffer;
+ Bufpos begv, zv, old_pt = BUF_PT (buf);
+ Bufbyte *decoded;
+ Bytind decoded_length;
+ Charcount length, cc_decoded_length;
+ Lisp_Object input;
+ int speccount = specpdl_depth();
+
+ get_buffer_range_char (buf, beg, end, &begv, &zv, 0);
+ barf_if_buffer_read_only (buf, begv, zv);
+
+ length = zv - begv;
+
+ input = make_lisp_buffer_input_stream (buf, begv, zv, 0);
+ /* We need to allocate enough room for decoding the text. */
+ XMALLOC_OR_ALLOCA (decoded, length * MAX_EMCHAR_LEN, Bufbyte);
+ decoded_length = base64_decode_1 (XLSTREAM (input), decoded, &cc_decoded_length);
+ if (decoded_length > length * MAX_EMCHAR_LEN)
+ abort ();
+ Lstream_delete (XLSTREAM (input));
+
+ if (decoded_length < 0)
+ {
+ /* The decoding wasn't possible. */
+ XMALLOC_UNBIND (decoded, length * MAX_EMCHAR_LEN, speccount);
+ return Qnil;
+ }
+
+ /* Now we have decoded the region, so we insert the new contents
+ and delete the old. (Insert first in order to preserve markers.) */
+ BUF_SET_PT (buf, begv);
+ buffer_insert_raw_string_1 (buf, begv, decoded, decoded_length, 0);
+ XMALLOC_UNBIND (decoded, length * MAX_EMCHAR_LEN, speccount);
+ buffer_delete_range (buf, begv + cc_decoded_length,
+ zv + cc_decoded_length, 0);
+
+ /* Simulate FSF Emacs: if point was in the region, place it at the
+ beginning. */
+ if (old_pt >= begv && old_pt < zv)
+ BUF_SET_PT (buf, begv);
+
+ return make_int (cc_decoded_length);
+}
+
+DEFUN ("base64-decode-string", Fbase64_decode_string, 1, 1, 0, /*
+Base64-decode STRING and return the result.
+*/
+ (string))
+{
+ Bufbyte *decoded;
+ Bytind decoded_length;
+ Charcount length, cc_decoded_length;
+ Lisp_Object input, result;
+ int speccount = specpdl_depth();
+
+ CHECK_STRING (string);
+
+ length = XSTRING_CHAR_LENGTH (string);
+ /* We need to allocate enough room for decoding the text. */
+ XMALLOC_OR_ALLOCA (decoded, length * MAX_EMCHAR_LEN, Bufbyte);
+
+ input = make_lisp_string_input_stream (string, 0, -1);
+ decoded_length = base64_decode_1 (XLSTREAM (input), decoded,
+ &cc_decoded_length);
+ if (decoded_length > length * MAX_EMCHAR_LEN)
+ abort ();
+ Lstream_delete (XLSTREAM (input));
+
+ if (decoded_length < 0)
+ {
+ /* The decoding wasn't possible. */
+ XMALLOC_UNBIND (decoded, length * MAX_EMCHAR_LEN, speccount);
+ return Qnil;
+ }
+
+ result = make_string (decoded, decoded_length);
+ XMALLOC_UNBIND (decoded, length * MAX_EMCHAR_LEN, speccount);
+ return result;
+}