changeset 23208:1abc842b1ca7

(base64_decode_1, base64_encode_1): New functions. (Fbase64_decode_string, Fbase64_encode_string): New functions. (Fbase64_decode_region, Fbase64_encode_region): New functions.
author Karl Heuer <kwzh@gnu.org>
date Mon, 07 Sep 1998 19:58:05 +0000
parents 302eccdcb73c
children 601bf7681009
files src/fns.c
diffstat 1 files changed, 376 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/fns.c	Mon Sep 07 17:34:59 1998 +0000
+++ b/src/fns.c	Mon Sep 07 19:58:05 1998 +0000
@@ -2716,6 +2716,378 @@
   return result;
 }
 
+/* base64 encode/decode functions.
+   Based on code from GNU recode. */
+
+#define MIME_LINE_LENGTH 76
+
+#define IS_ASCII(Character) \
+  ((Character) < 128)
+#define IS_BASE64(Character) \
+  (IS_ASCII (Character) && base64_char_to_value[Character] >= 0)
+
+/* Table of characters coding the 64 values.  */
+static char base64_value_to_char[64] =
+{
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',	/*  0- 9 */
+  'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',	/* 10-19 */
+  'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',	/* 20-29 */
+  'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',	/* 30-39 */
+  'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',	/* 40-49 */
+  'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',	/* 50-59 */
+  '8', '9', '+', '/'					/* 60-63 */
+};
+
+/* Table of base64 values for first 128 characters.  */
+static short base64_char_to_value[128] =
+{
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,	/*   0-  9 */
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,	/*  10- 19 */
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,	/*  20- 29 */
+  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,	/*  30- 39 */
+  -1,  -1,  -1,  62,  -1,  -1,  -1,  63,  52,  53,	/*  40- 49 */
+  54,  55,  56,  57,  58,  59,  60,  61,  -1,  -1,	/*  50- 59 */
+  -1,  -1,  -1,  -1,  -1,  0,   1,   2,   3,   4,	/*  60- 69 */
+  5,   6,   7,   8,   9,   10,  11,  12,  13,  14,	/*  70- 79 */
+  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,	/*  80- 89 */
+  25,  -1,  -1,  -1,  -1,  -1,  -1,  26,  27,  28,	/*  90- 99 */
+  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,	/* 100-109 */
+  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,	/* 110-119 */
+  49,  50,  51,  -1,  -1,  -1,  -1,  -1			/* 120-127 */
+};
+
+/* The following diagram shows the logical steps by which three octets
+   get transformed into four base64 characters.
+
+		 .--------.  .--------.  .--------.
+		 |aaaaaabb|  |bbbbcccc|  |ccdddddd|
+		 `--------'  `--------'  `--------'
+                    6   2      4   4       2   6
+	       .--------+--------+--------+--------.
+	       |00aaaaaa|00bbbbbb|00cccccc|00dddddd|
+	       `--------+--------+--------+--------'
+
+	       .--------+--------+--------+--------.
+	       |AAAAAAAA|BBBBBBBB|CCCCCCCC|DDDDDDDD|
+	       `--------+--------+--------+--------'
+
+   The octets are divided into 6 bit chunks, which are then encoded into
+   base64 characters.  */
+
+
+static int base64_encode_1 P_ ((const char *, char *, int, int));
+static int base64_decode_1 P_ ((const char *, char *, int));
+
+DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region,
+       2, 3, "r",
+       "base64 encode the region between BEG and END.\n\
+Return the length of the encoded text.
+Optional third argument NO-LINE-BREAK means do not break long lines\n\
+into shorter lines.")
+     (beg, end, no_line_break)
+     Lisp_Object beg, end, no_line_break;
+{
+  char *encoded;
+  int allength, length;
+  int ibeg, iend, encoded_length;
+  int old_pos = PT;
+
+  validate_region (&beg, &end);
+
+  ibeg = CHAR_TO_BYTE (XFASTINT (beg));
+  iend = CHAR_TO_BYTE (XFASTINT (end));
+  move_gap_both (XFASTINT (beg), ibeg);
+
+  /* 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 = iend - ibeg;
+  allength = length + length/3 + 1;
+  allength += allength / MIME_LINE_LENGTH + 1 + 6;
+
+  encoded = (char *) alloca (allength);
+  encoded_length = base64_encode_1 (BYTE_POS_ADDR (ibeg), encoded, length,
+				    NILP (no_line_break));
+  if (encoded_length > allength)
+    abort ();
+
+  /* Now we have encoded the region, so we insert the new contents
+     and delete the old.  (Insert first in order to preserve markers.)  */
+  SET_PT (beg);
+  insert (encoded, encoded_length);
+  del_range_byte (ibeg + encoded_length, iend + encoded_length, 1);
+
+  /* If point was outside of the region, restore it exactly; else just
+     move to the beginning of the region.  */
+  if (old_pos >= XFASTINT (end))
+    old_pos += encoded_length - (XFASTINT (end) - XFASTINT (beg));
+  else if (old_pos > beg)
+    old_pos = beg;
+  SET_PT (old_pos);
+
+  /* We return the length of the encoded text. */
+  return make_number (encoded_length);
+}
+
+DEFUN ("base64-encode-string", Fbase64_encode_string, Sbase64_encode_string,
+       1, 1, 0,
+       "base64 encode STRING and return the result.")
+     (string)
+     Lisp_Object string;
+{
+  int allength, length, encoded_length;
+  char *encoded;
+
+  CHECK_STRING (string, 1);
+
+  length = STRING_BYTES (XSTRING (string));
+  allength = length + length/3 + 1 + 6;
+
+  /* We need to allocate enough room for decoding the text. */
+  encoded = (char *) alloca (allength);
+
+  encoded_length = base64_encode_1 (XSTRING (string)->data,
+				    encoded, length, 0);
+  if (encoded_length > allength)
+    abort ();
+
+  return make_unibyte_string (encoded, encoded_length);
+}
+
+static int
+base64_encode_1 (from, to, length, line_break)
+     const char *from;
+     char *to;
+     int length;
+     int line_break;
+{
+  int counter = 0, i = 0;
+  char *e = to;
+  unsigned char c;
+  unsigned int value;
+
+  while (i < length)
+    {
+      c = from[i++];
+
+      /* Wrap line every 76 characters.  */
+
+      if (line_break)
+	{
+	  if (counter < MIME_LINE_LENGTH / 4)
+	    counter++;
+	  else
+	    {
+	      *e++ = '\n';
+	      counter = 1;
+	    }
+	}
+
+      /* Process first byte of a triplet.  */
+
+      *e++ = base64_value_to_char[0x3f & c >> 2];
+      value = (0x03 & c) << 4;
+
+      /* Process second byte of a triplet.  */
+
+      if (i == length)
+	{
+	  *e++ = base64_value_to_char[value];
+	  *e++ = '=';
+	  *e++ = '=';
+	  break;
+	}
+
+      c = from[i++];
+
+      *e++ = base64_value_to_char[value | (0x0f & c >> 4)];
+      value = (0x0f & c) << 2;
+
+      /* Process third byte of a triplet.  */
+
+      if (i == length)
+	{
+	  *e++ = base64_value_to_char[value];
+	  *e++ = '=';
+	  break;
+	}
+
+      c = from[i++];
+
+      *e++ = base64_value_to_char[value | (0x03 & c >> 6)];
+      *e++ = base64_value_to_char[0x3f & c];
+    }
+
+  /* Complete last partial line.  */
+
+  if (line_break)
+    if (counter > 0)
+      *e++ = '\n';
+
+  return e - to;
+}
+
+
+DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region,
+  2, 2, "r",
+  "base64 decode the region between BEG and END.\n\
+Return the length of the decoded text.
+If the region can't be decoded, return nil and don't modify the buffer.")
+     (beg, end)
+     Lisp_Object beg, end;
+{
+  int ibeg, iend, length;
+  char *decoded;
+  int old_pos = PT;
+  int decoded_length;
+
+  validate_region (&beg, &end);
+
+  ibeg = CHAR_TO_BYTE (XFASTINT (beg));
+  iend = CHAR_TO_BYTE (XFASTINT (end));
+
+  length = iend - ibeg;
+  /* We need to allocate enough room for decoding the text. */
+  decoded = (char *) alloca (length);
+
+  move_gap_both (XFASTINT (beg), ibeg);
+  decoded_length = base64_decode_1 (BYTE_POS_ADDR (ibeg), decoded, length);
+  if (decoded_length > length)
+    abort ();
+
+  if (decoded_length < 0)
+    /* The decoding wasn't possible. */
+    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.)  */
+  SET_PT (beg);
+  insert (decoded, decoded_length);
+  del_range_byte (ibeg + decoded_length, iend + decoded_length, 1);
+
+  /* If point was outside of the region, restore it exactly; else just
+     move to the beginning of the region.  */
+  if (old_pos >= XFASTINT (end))
+    old_pos += decoded_length - length;
+  else if (old_pos > beg)
+    old_pos = beg;
+  SET_PT (old_pos);
+
+  return make_number (decoded_length);
+}
+
+DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
+       1, 1, 0,
+       "base64 decode STRING and return the result.")
+     (string)
+     Lisp_Object string;
+{
+  char *decoded;
+  int length, decoded_length;
+
+  CHECK_STRING (string, 1);
+
+  length = STRING_BYTES (XSTRING (string));
+  /* We need to allocate enough room for decoding the text. */
+  decoded = (char *) alloca (length);
+
+  decoded_length = base64_decode_1 (XSTRING (string)->data, decoded, length);
+  if (decoded_length > length)
+    abort ();
+
+  if (decoded_length < 0)
+      return Qnil;
+
+  return make_string (decoded, decoded_length);
+}
+
+static int
+base64_decode_1 (from, to, length)
+     const char *from;
+     char *to;
+     int length;
+{
+  int counter = 0, i = 0;
+  char *e = to;
+  unsigned char c;
+  unsigned long value;
+
+  while (i < length)
+    {
+      /* Accept wrapping lines, reversibly if at each 76 characters.  */
+
+      c = from[i++];
+      if (c == '\n')
+	{
+	  if (i == length)
+	    break;
+	  c = from[i++];
+	  if (i == length)
+	    break;
+	  if (counter != MIME_LINE_LENGTH / 4)
+	    return -1;
+	  counter = 1;
+	}
+      else
+	counter++;
+
+      /* 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.  */
+
+      if (i == length)
+	return -1;
+      c = from[i++];
+
+      if (!IS_BASE64 (c))
+	return -1;
+      value |= base64_char_to_value[c] << 12;
+
+      *e++ = (unsigned char) (value >> 16);
+
+      /* Process third byte of a quadruplet.  */
+
+      if (i == length)
+	return -1;
+      c = from[i++];
+
+      if (c == '=')
+	{
+	  c = from[i++];
+	  if (c != '=')
+	    return -1;
+	  continue;
+	}
+
+      if (!IS_BASE64 (c))
+	return -1;
+      value |= base64_char_to_value[c] << 6;
+
+      *e++ = (unsigned char) (0xff & value >> 8);
+
+      /* Process fourth byte of a quadruplet.  */
+
+      if (i == length)
+	return -1;
+      c = from[i++];
+
+      if (c == '=')
+	continue;
+
+      if (!IS_BASE64 (c))
+	return -1;
+      value |= base64_char_to_value[c];
+
+      *e++ = (unsigned char) (0xff & value);
+    }
+
+  return e - to;
+}
+
 void
 syms_of_fns ()
 {
@@ -2808,4 +3180,8 @@
   defsubr (&Swidget_put);
   defsubr (&Swidget_get);
   defsubr (&Swidget_apply);
+  defsubr (&Sbase64_encode_region);
+  defsubr (&Sbase64_decode_region);
+  defsubr (&Sbase64_encode_string);
+  defsubr (&Sbase64_decode_string);
 }