(x_free_gcs): Add prototype.
[bpt/emacs.git] / src / fns.c
index 24107f2..a317f1b 100644 (file)
--- a/src/fns.c
+++ b/src/fns.c
@@ -1,5 +1,6 @@
 /* Random utility Lisp functions.
-   Copyright (C) 1985, 86, 87, 93, 94, 95, 97, 98, 99, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1985, 86, 87, 93, 94, 95, 97, 98, 99, 2000
+   Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -554,7 +555,7 @@ concat (nargs, args, target_type, last_special)
   register Lisp_Object tail;
   register Lisp_Object this;
   int toindex;
-  int toindex_byte;
+  int toindex_byte = 0;
   register int result_len;
   register int result_len_byte;
   register int argnum;
@@ -566,10 +567,12 @@ concat (nargs, args, target_type, last_special)
      string can't be decided until we finish the whole concatination.
      So, we record strings that have text properties to be copied
      here, and copy the text properties after the concatination.  */
-  struct textprop_rec  *textprops;
+  struct textprop_rec  *textprops = NULL;
   /* Number of elments in textprops.  */
   int num_textprops = 0;
 
+  tail = Qnil;
+
   /* In append, the last arg isn't treated like the others */
   if (last_special && nargs > 0)
     {
@@ -681,7 +684,7 @@ concat (nargs, args, target_type, last_special)
   for (argnum = 0; argnum < nargs; argnum++)
     {
       Lisp_Object thislen;
-      int thisleni;
+      int thisleni = 0;
       register unsigned int thisindex = 0;
       register unsigned int thisindex_byte = 0;
 
@@ -826,12 +829,22 @@ concat (nargs, args, target_type, last_special)
 
   if (num_textprops > 0)
     {
+      Lisp_Object props;
+
       for (argnum = 0; argnum < num_textprops; argnum++)
        {
          this = args[textprops[argnum].argnum];
-         copy_text_properties (make_number (textprops[argnum].from),
-                               make_number (XSTRING (this)->size), this,
-                               make_number (textprops[argnum].to), val, Qnil);
+         props = text_property_list (this,
+                                     make_number (0),
+                                     make_number (XSTRING (this)->size),
+                                     Qnil);
+         /* If successive arguments have properites, be sure that the
+            value of `composition' property be the copy.  */
+         if (argnum > 0
+             && textprops[argnum - 1].argnum + 1 == textprops[argnum].argnum)
+           make_composition_value_copy (props);
+         add_text_properties_from_list (val, props,
+                                        make_number (textprops[argnum].to));
        }
     }
   return val;
@@ -1071,8 +1084,9 @@ DEFUN ("string-as-unibyte", Fstring_as_unibyte, Sstring_as_unibyte,
   "Return a unibyte string with the same individual bytes as STRING.\n\
 If STRING is unibyte, the result is STRING itself.\n\
 Otherwise it is a newly created string, with no text properties.\n\
-If STRING is multibyte and contains a character of charset `binary',\n\
-it is converted to the corresponding single byte.")
+If STRING is multibyte and contains a character of charset\n\
+`eight-bit-control' or `eight-bit-graphic', it is converted to the\n\
+corresponding single byte.")
   (string)
      Lisp_Object string;
 {
@@ -1097,8 +1111,8 @@ DEFUN ("string-as-multibyte", Fstring_as_multibyte, Sstring_as_multibyte,
 If STRING is multibyte, the result is STRING itself.\n\
 Otherwise it is a newly created string, with no text properties.\n\
 If STRING is unibyte and contains an individual 8-bit byte (i.e. not\n\
-part of multibyte form), it is converted to the corresponding\n\
-multibyte character of charset `binary'.")
+part of multibyte form), it is converted to the corresponding\n\
+multibyte character of charset `eight-bit-control' or `eight-bit-graphic'.")
   (string)
      Lisp_Object string;
 {
@@ -1163,9 +1177,9 @@ This function allows vectors as well as strings.")
 {
   Lisp_Object res;
   int size;
-  int size_byte;
+  int size_byte = 0;
   int from_char, to_char;
-  int from_byte, to_byte;
+  int from_byte = 0, to_byte = 0;
 
   if (! (STRINGP (string) || VECTORP (string)))
     wrong_type_argument (Qarrayp, string);
@@ -1341,7 +1355,7 @@ whose car is ELT.")
     {
       if (!CONSP (list) || EQ (XCAR (list), elt))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list) || EQ (XCAR (list), elt))
        break;
@@ -1375,19 +1389,19 @@ Elements of LIST that are not conses are ignored.")
          || (CONSP (XCAR (list))
              && EQ (XCAR (XCAR (list)), key)))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list)
          || (CONSP (XCAR (list))
              && EQ (XCAR (XCAR (list)), key)))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list)
          || (CONSP (XCAR (list))
              && EQ (XCAR (XCAR (list)), key)))
        break;
-      
+
       list = XCDR (list);
       QUIT;
     }
@@ -1432,21 +1446,21 @@ The value is actually the element of LIST whose car equals KEY.")
              && (car = XCAR (XCAR (list)),
                  EQ (car, key) || !NILP (Fequal (car, key)))))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list)
          || (CONSP (XCAR (list))
              && (car = XCAR (XCAR (list)),
                  EQ (car, key) || !NILP (Fequal (car, key)))))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list)
          || (CONSP (XCAR (list))
              && (car = XCAR (XCAR (list)),
                  EQ (car, key) || !NILP (Fequal (car, key)))))
        break;
-      
+
       list = XCDR (list);
       QUIT;
     }
@@ -1476,19 +1490,19 @@ The value is actually the element of LIST whose cdr is KEY.")
          || (CONSP (XCAR (list))
              && EQ (XCDR (XCAR (list)), key)))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list)
          || (CONSP (XCAR (list))
              && EQ (XCDR (XCAR (list)), key)))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list)
          || (CONSP (XCAR (list))
              && EQ (XCDR (XCAR (list)), key)))
        break;
-      
+
       list = XCDR (list);
       QUIT;
     }
@@ -1518,21 +1532,21 @@ The value is actually the element of LIST whose cdr equals KEY.")
              && (cdr = XCDR (XCAR (list)),
                  EQ (cdr, key) || !NILP (Fequal (cdr, key)))))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list)
          || (CONSP (XCAR (list))
              && (cdr = XCDR (XCAR (list)),
                  EQ (cdr, key) || !NILP (Fequal (cdr, key)))))
        break;
-      
+
       list = XCDR (list);
       if (!CONSP (list)
          || (CONSP (XCAR (list))
              && (cdr = XCDR (XCAR (list)),
                  EQ (cdr, key) || !NILP (Fequal (cdr, key)))))
        break;
-      
+
       list = XCDR (list);
       QUIT;
     }
@@ -1583,39 +1597,128 @@ to be sure of changing the value of `foo'.")
 }
 
 DEFUN ("delete", Fdelete, Sdelete, 2, 2, 0,
-  "Delete by side effect any occurrences of ELT as a member of LIST.\n\
-The modified LIST is returned.  Comparison is done with `equal'.\n\
-If the first member of LIST is ELT, deleting it is not a side effect;\n\
-it is simply using a different list.\n\
+  "Delete by side effect any occurrences of ELT as a member of SEQ.\n\
+SEQ must be a list, a vector, or a string.\n\
+The modified SEQ is returned.  Comparison is done with `equal'.\n\
+If SEQ is not a list, or the first member of SEQ is ELT, deleting it\n\
+is not a side effect; it is simply using a different sequence.\n\
 Therefore, write `(setq foo (delete element foo))'\n\
 to be sure of changing the value of `foo'.")
-  (elt, list)
-     register Lisp_Object elt;
-     Lisp_Object list;
+  (elt, seq)
+     Lisp_Object elt, seq;
 {
-  register Lisp_Object tail, prev;
-  register Lisp_Object tem;
+  if (VECTORP (seq))
+    {
+      EMACS_INT i, n, size;
 
-  tail = list;
-  prev = Qnil;
-  while (!NILP (tail))
+      for (i = n = 0; i < ASIZE (seq); ++i)
+       if (NILP (Fequal (AREF (seq, i), elt)))
+         ++n;
+
+      if (n != ASIZE (seq))
+       {
+         struct Lisp_Vector *p = allocate_vectorlike (n);
+
+         for (i = n = 0; i < ASIZE (seq); ++i)
+           if (NILP (Fequal (AREF (seq, i), elt)))
+             p->contents[n++] = AREF (seq, i);
+
+         p->size = n;
+         XSETVECTOR (seq, p);
+       }
+    }
+  else if (STRINGP (seq))
     {
-      if (! CONSP (tail))
-       wrong_type_argument (Qlistp, list);
-      tem = XCAR (tail);
-      if (! NILP (Fequal (elt, tem)))
+      EMACS_INT i, ibyte, nchars, nbytes, cbytes;
+      int c;
+
+      for (i = nchars = nbytes = ibyte = 0;
+          i < XSTRING (seq)->size;
+          ++i, ibyte += cbytes)
        {
-         if (NILP (prev))
-           list = XCDR (tail);
+         if (STRING_MULTIBYTE (seq))
+           {
+             c = STRING_CHAR (&XSTRING (seq)->data[ibyte],
+                              STRING_BYTES (XSTRING (seq)) - ibyte);
+             cbytes = CHAR_BYTES (c);
+           }
          else
-           Fsetcdr (prev, XCDR (tail));
+           {
+             c = XSTRING (seq)->data[i];
+             cbytes = 1;
+           }
+
+         if (!INTEGERP (elt) || c != XINT (elt))
+           {
+             ++nchars;
+             nbytes += cbytes;
+           }
+       }
+
+      if (nchars != XSTRING (seq)->size)
+       {
+         Lisp_Object tem;
+
+         tem = make_uninit_multibyte_string (nchars, nbytes);
+         if (!STRING_MULTIBYTE (seq))
+           SET_STRING_BYTES (XSTRING (tem), -1);
+
+         for (i = nchars = nbytes = ibyte = 0;
+              i < XSTRING (seq)->size;
+              ++i, ibyte += cbytes)
+           {
+             if (STRING_MULTIBYTE (seq))
+               {
+                 c = STRING_CHAR (&XSTRING (seq)->data[ibyte],
+                                  STRING_BYTES (XSTRING (seq)) - ibyte);
+                 cbytes = CHAR_BYTES (c);
+               }
+             else
+               {
+                 c = XSTRING (seq)->data[i];
+                 cbytes = 1;
+               }
+
+             if (!INTEGERP (elt) || c != XINT (elt))
+               {
+                 unsigned char *from = &XSTRING (seq)->data[ibyte];
+                 unsigned char *to   = &XSTRING (tem)->data[nbytes];
+                 EMACS_INT n;
+
+                 ++nchars;
+                 nbytes += cbytes;
+
+                 for (n = cbytes; n--; )
+                   *to++ = *from++;
+               }
+           }
+
+         seq = tem;
        }
-      else
-       prev = tail;
-      tail = XCDR (tail);
-      QUIT;
     }
-  return list;
+  else
+    {
+      Lisp_Object tail, prev;
+
+      for (tail = seq, prev = Qnil; !NILP (tail); tail = XCDR (tail))
+       {
+         if (!CONSP (tail))
+           wrong_type_argument (Qlistp, seq);
+
+         if (!NILP (Fequal (elt, XCAR (tail))))
+           {
+             if (NILP (prev))
+               seq = XCDR (tail);
+             else
+               Fsetcdr (prev, XCDR (tail));
+           }
+         else
+           prev = tail;
+         QUIT;
+       }
+    }
+
+  return seq;
 }
 
 DEFUN ("nreverse", Fnreverse, Snreverse, 1, 1, 0,
@@ -1945,7 +2048,13 @@ internal_equal (o1, o2, depth)
                STRING_BYTES (XSTRING (o1))))
        return 0;
       return 1;
+
+    case Lisp_Int:
+    case Lisp_Symbol:
+    case Lisp_Type_Limit:
+      break;
     }
+  
   return 0;
 }
 \f
@@ -2310,7 +2419,7 @@ DEFUN ("optimize-char-table", Foptimize_char_table, Soptimize_char_table,
       elt = XCHAR_TABLE (table)->contents[i];
       if (!SUB_CHAR_TABLE_P (elt))
        continue;
-      dim = CHARSET_DIMENSION (i);
+      dim = CHARSET_DIMENSION (i - 128);
       if (dim == 2)
        for (j = 32; j < SUB_CHAR_TABLE_ORDINARY_SLOTS; j++)
          optimize_sub_char_table (XCHAR_TABLE (elt)->contents + j, dim);
@@ -2417,6 +2526,41 @@ The key is always a possible IDX argument to `aref'.")
   map_char_table (NULL, function, char_table, char_table, 0, indices);
   return Qnil;
 }
+
+/* Return a value for character C in char-table TABLE.  Store the
+   actual index for that value in *IDX.  Ignore the default value of
+   TABLE.  */
+
+Lisp_Object
+char_table_ref_and_index (table, c, idx)
+     Lisp_Object table;
+     int c, *idx;
+{
+  int charset, c1, c2;
+  Lisp_Object elt;
+
+  if (SINGLE_BYTE_CHAR_P (c))
+    {
+      *idx = c;
+      return XCHAR_TABLE (table)->contents[c];
+    }
+  SPLIT_CHAR (c, charset, c1, c2);
+  elt = XCHAR_TABLE (table)->contents[charset + 128];
+  *idx = MAKE_CHAR (charset, 0, 0);
+  if (!SUB_CHAR_TABLE_P (elt))
+    return elt;
+  if (c1 < 32 || NILP (XCHAR_TABLE (elt)->contents[c1]))
+    return XCHAR_TABLE (elt)->defalt;
+  elt = XCHAR_TABLE (elt)->contents[c1];
+  *idx = MAKE_CHAR (charset, c1, 0);
+  if (!SUB_CHAR_TABLE_P (elt))
+    return elt;
+  if (c2 < 32 || NILP (XCHAR_TABLE (elt)->contents[c2]))
+    return XCHAR_TABLE (elt)->defalt;
+  *idx = c;
+  return XCHAR_TABLE (elt)->contents[c2];
+}
+
 \f
 /* ARGSUSED */
 Lisp_Object
@@ -2443,7 +2587,7 @@ Only the last argument is not altered, and need not be a list.")
   register int argnum;
   register Lisp_Object tail, tem, val;
 
-  val = Qnil;
+  val = tail = Qnil;
 
   for (argnum = 0; argnum < nargs; argnum++)
     {
@@ -2645,7 +2789,7 @@ for more information.  In this case, the useful bindings are `act', `skip',\n\
 `recenter', and `quit'.\)\n\
 \n\
 Under a windowing system a dialog box will be used if `last-nonmenu-event'\n\
-is nil.")
+is nil and `use-dialog-box' is non-nil.")
   (prompt)
      Lisp_Object prompt;
 {
@@ -2668,7 +2812,7 @@ is nil.")
   if (display_busy_cursor_p)
     cancel_busy_cursor ();
 #endif
-  
+
   while (1)
     {
 
@@ -2782,7 +2926,7 @@ The user must confirm the answer with RET,\n\
 and can edit it until it has been confirmed.\n\
 \n\
 Under a windowing system a dialog box will be used if `last-nonmenu-event'\n\
-is nil.")
+is nil, and `use-dialog-box' is non-nil.")
   (prompt)
      Lisp_Object prompt;
 {
@@ -2916,7 +3060,9 @@ Normally the return value is FEATURE.")
   register Lisp_Object tem;
   CHECK_SYMBOL (feature, 0);
   tem = Fmemq (feature, Vfeatures);
+
   LOADHIST_ATTACH (Fcons (Qrequire, feature));
+  
   if (NILP (tem))
     {
       int count = specpdl_ptr - specpdl;
@@ -3028,7 +3174,7 @@ ARGS are passed as extra arguments to the function.")
   return result;
 }
 \f
-/* base64 encode/decode functions.
+/* base64 encode/decode functions (RFC 2045).
    Based on code from GNU recode. */
 
 #define MIME_LINE_LENGTH 76
@@ -3044,13 +3190,17 @@ ARGS are passed as extra arguments to the function.")
 /* Used by base64_decode_1 to retrieve a non-base64-ignorable
    character or return retval if there are no characters left to
    process. */
-#define READ_QUADRUPLET_BYTE(retval) \
-  do \
-    { \
-      if (i == length) \
-        return (retval); \
-      c = from[i++]; \
-    } \
+#define READ_QUADRUPLET_BYTE(retval)   \
+  do                                   \
+    {                                  \
+      if (i == length)                 \
+       {                               \
+         if (nchars_return)            \
+           *nchars_return = nchars;    \
+         return (retval);              \
+       }                               \
+      c = from[i++];                   \
+    }                                  \
   while (IS_BASE64_IGNORABLE (c))
 
 /* Don't use alloca for regions larger than this, lest we overflow
@@ -3107,7 +3257,7 @@ static short base64_char_to_value[128] =
 
 
 static int base64_encode_1 P_ ((const char *, char *, int, int, int));
-static int base64_decode_1 P_ ((const char *, char *, int));
+static int base64_decode_1 P_ ((const char *, char *, int, int, int *));
 
 DEFUN ("base64-encode-region", Fbase64_encode_region, Sbase64_encode_region,
        2, 3, "r",
@@ -3151,7 +3301,7 @@ into shorter lines.")
       /* The encoding wasn't possible. */
       if (length > MAX_ALLOCA)
        xfree (encoded);
-      error ("Base64 encoding failed");
+      error ("Multibyte character in data for base64 encoding");
     }
 
   /* Now we have encoded the region, so we insert the new contents
@@ -3212,7 +3362,7 @@ into shorter lines.")
       /* The encoding wasn't possible. */
       if (length > MAX_ALLOCA)
        xfree (encoded);
-      error ("Base64 encoding failed");
+      error ("Multibyte character in data for base64 encoding");
     }
 
   encoded_string = make_unibyte_string (encoded, encoded_length);
@@ -3232,7 +3382,7 @@ base64_encode_1 (from, to, length, line_break, multibyte)
 {
   int counter = 0, i = 0;
   char *e = to;
-  unsigned char c;
+  int c;
   unsigned int value;
   int bytes;
 
@@ -3241,7 +3391,7 @@ base64_encode_1 (from, to, length, line_break, multibyte)
       if (multibyte)
        {
          c = STRING_CHAR_AND_LENGTH (from + i, length - i, bytes);
-         if (!SINGLE_BYTE_CHAR_P (c))
+         if (c >= 256)
            return -1;
          i += bytes;
        }
@@ -3279,6 +3429,8 @@ base64_encode_1 (from, to, length, line_break, multibyte)
       if (multibyte)
        {
          c = STRING_CHAR_AND_LENGTH (from + i, length - i, bytes);
+         if (c >= 256)
+           return -1;
          i += bytes;
        }
       else
@@ -3299,6 +3451,8 @@ base64_encode_1 (from, to, length, line_break, multibyte)
       if (multibyte)
        {
          c = STRING_CHAR_AND_LENGTH (from + i, length - i, bytes);
+         if (c >= 256)
+           return -1;
          i += bytes;
        }
       else
@@ -3320,11 +3474,12 @@ If the region can't be decoded, signal an error and don't modify the buffer.")
      (beg, end)
      Lisp_Object beg, end;
 {
-  int ibeg, iend, length;
+  int ibeg, iend, length, allength;
   char *decoded;
   int old_pos = PT;
   int decoded_length;
   int inserted_chars;
+  int multibyte = !NILP (current_buffer->enable_multibyte_characters);
 
   validate_region (&beg, &end);
 
@@ -3332,34 +3487,35 @@ If the region can't be decoded, signal an error and don't modify the buffer.")
   iend = CHAR_TO_BYTE (XFASTINT (end));
 
   length = iend - ibeg;
-  /* We need to allocate enough room for decoding the text. */
-  if (length <= MAX_ALLOCA)
-    decoded = (char *) alloca (length);
+
+  /* We need to allocate enough room for decoding the text.  If we are
+     working on a multibyte buffer, each decoded code may occupy at
+     most two bytes.  */
+  allength = multibyte ? length * 2 : length;
+  if (allength <= MAX_ALLOCA)
+    decoded = (char *) alloca (allength);
   else
-    decoded = (char *) xmalloc (length);
+    decoded = (char *) xmalloc (allength);
 
   move_gap_both (XFASTINT (beg), ibeg);
-  decoded_length = base64_decode_1 (BYTE_POS_ADDR (ibeg), decoded, length);
-  if (decoded_length > length)
+  decoded_length = base64_decode_1 (BYTE_POS_ADDR (ibeg), decoded, length,
+                                   multibyte, &inserted_chars);
+  if (decoded_length > allength)
     abort ();
 
   if (decoded_length < 0)
     {
       /* The decoding wasn't possible. */
-      if (length > MAX_ALLOCA)
+      if (allength > MAX_ALLOCA)
        xfree (decoded);
-      error ("Base64 decoding failed");
+      error ("Invalid base64 data");
     }
 
-  inserted_chars = decoded_length;
-  if (!NILP (current_buffer->enable_multibyte_characters))
-    decoded_length = str_to_multibyte (decoded, length, decoded_length);
-
   /* Now we have decoded the region, so we insert the new contents
      and delete the old.  (Insert first in order to preserve markers.)  */
-  TEMP_SET_PT_BOTH (XFASTINT (beg), ibeg);  
+  TEMP_SET_PT_BOTH (XFASTINT (beg), ibeg);
   insert_1_both (decoded, inserted_chars, decoded_length, 0, 1, 0);
-  if (length > MAX_ALLOCA)
+  if (allength > MAX_ALLOCA)
     xfree (decoded);
   /* Delete the original text.  */
   del_range_both (PT, PT_BYTE, XFASTINT (end) + inserted_chars,
@@ -3395,7 +3551,9 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
   else
     decoded = (char *) xmalloc (length);
 
-  decoded_length = base64_decode_1 (XSTRING (string)->data, decoded, length);
+  /* The decoded result should be unibyte. */
+  decoded_length = base64_decode_1 (XSTRING (string)->data, decoded, length,
+                                   0, NULL);
   if (decoded_length > length)
     abort ();
   else if (decoded_length >= 0)
@@ -3406,21 +3564,29 @@ DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
   if (length > MAX_ALLOCA)
     xfree (decoded);
   if (!STRINGP (decoded_string))
-    error ("Base64 decoding failed");
+    error ("Invalid base64 data");
 
   return decoded_string;
 }
 
+/* Base64-decode the data at FROM of LENGHT bytes into TO.  If
+   MULTIBYTE is nonzero, the decoded result should be in multibyte
+   form.  If NCHARS_RETRUN is not NULL, store the number of produced
+   characters in *NCHARS_RETURN.  */
+
 static int
-base64_decode_1 (from, to, length)
+base64_decode_1 (from, to, length, multibyte, nchars_return)
      const char *from;
      char *to;
      int length;
+     int multibyte;
+     int *nchars_return;
 {
   int i = 0;
   char *e = to;
   unsigned char c;
   unsigned long value;
+  int nchars = 0;
 
   while (1)
     {
@@ -3440,16 +3606,21 @@ base64_decode_1 (from, to, length)
        return -1;
       value |= base64_char_to_value[c] << 12;
 
-      *e++ = (unsigned char) (value >> 16);
+      c = (unsigned char) (value >> 16);
+      if (multibyte)
+       e += CHAR_STRING (c, e);
+      else
+       *e++ = c;
+      nchars++;
 
       /* Process third byte of a quadruplet.  */
-      
+
       READ_QUADRUPLET_BYTE (-1);
 
       if (c == '=')
        {
          READ_QUADRUPLET_BYTE (-1);
-         
+
          if (c != '=')
            return -1;
          continue;
@@ -3459,7 +3630,12 @@ base64_decode_1 (from, to, length)
        return -1;
       value |= base64_char_to_value[c] << 6;
 
-      *e++ = (unsigned char) (0xff & value >> 8);
+      c = (unsigned char) (0xff & value >> 8);
+      if (multibyte)
+       e += CHAR_STRING (c, e);
+      else
+       *e++ = c;
+      nchars++;
 
       /* Process fourth byte of a quadruplet.  */
 
@@ -3472,7 +3648,12 @@ base64_decode_1 (from, to, length)
        return -1;
       value |= base64_char_to_value[c];
 
-      *e++ = (unsigned char) (0xff & value);
+      c = (unsigned char) (0xff & value);
+      if (multibyte)
+       e += CHAR_STRING (c, e);
+      else
+       *e++ = c;
+      nchars++;
     }
 }
 
@@ -3499,10 +3680,6 @@ base64_decode_1 (from, to, length)
    if a `:linear-search t' argument is given to make-hash-table.  */
 
 
-/* Return the contents of vector V at index IDX.  */
-
-#define AREF(V, IDX)       XVECTOR (V)->contents[IDX]
-
 /* Value is the key part of entry IDX in hash table H.  */
 
 #define HASH_KEY(H, IDX)   AREF ((H)->key_and_value, 2 * (IDX))
@@ -3537,7 +3714,7 @@ Lisp_Object Vweak_hash_tables;
 
 Lisp_Object Qhash_table_p, Qeq, Qeql, Qequal, Qkey, Qvalue;
 Lisp_Object QCtest, QCsize, QCrehash_size, QCrehash_threshold, QCweakness;
-Lisp_Object Qhash_table_test;
+Lisp_Object Qhash_table_test, Qkey_or_value, Qkey_and_value;
 
 /* Function prototypes.  */
 
@@ -3610,11 +3787,11 @@ get_key_arg (key, nargs, args, used)
      char *used;
 {
   int i;
-  
+
   for (i = 0; i < nargs - 1; ++i)
     if (!used[i] && EQ (args[i], key))
       break;
-  
+
   if (i >= nargs - 1)
     i = -1;
   else
@@ -3622,7 +3799,7 @@ get_key_arg (key, nargs, args, used)
       used[i++] = 1;
       used[i] = 1;
     }
-  
+
   return i;
 }
 
@@ -3688,7 +3865,7 @@ cmpfn_equal (h, key1, hash1, key2, hash2)
   return hash1 == hash2 && !NILP (Fequal (key1, key2));
 }
 
-  
+
 /* Compare KEY1 which has hash code HASH1, and KEY2 with hash code
    HASH2 in hash table H using H->user_cmp_function.  Value is non-zero
    if KEY1 and KEY2 are the same.  */
@@ -3702,7 +3879,7 @@ cmpfn_user_defined (h, key1, hash1, key2, hash2)
   if (hash1 == hash2)
     {
       Lisp_Object args[3];
-  
+
       args[0] = h->user_cmp_function;
       args[1] = key1;
       args[2] = key2;
@@ -3722,12 +3899,9 @@ hashfn_eq (h, key)
      struct Lisp_Hash_Table *h;
      Lisp_Object key;
 {
-  /* Lisp strings can change their address.  Don't try to compute a
-     hash code for a string from its address.  */
-  if (STRINGP (key))
-    return sxhash_string (XSTRING (key)->data, XSTRING (key)->size);
-  else
-    return XUINT (key) ^ XGCTYPE (key);
+  unsigned hash = XUINT (key) ^ XGCTYPE (key);
+  xassert ((hash & ~VALMASK) == 0);
+  return hash;
 }
 
 
@@ -3740,14 +3914,13 @@ hashfn_eql (h, key)
      struct Lisp_Hash_Table *h;
      Lisp_Object key;
 {
-  /* Lisp strings can change their address.  Don't try to compute a
-     hash code for a string from its address.  */
-  if (STRINGP (key))
-    return sxhash_string (XSTRING (key)->data, XSTRING (key)->size);
-  else if (FLOATP (key))
-    return sxhash (key, 0);
+  unsigned hash;
+  if (FLOATP (key))
+    hash = sxhash (key, 0);
   else
-    return XUINT (key) ^ XGCTYPE (key);
+    hash = XUINT (key) ^ XGCTYPE (key);
+  xassert ((hash & ~VALMASK) == 0);
+  return hash;
 }
 
 
@@ -3760,7 +3933,9 @@ hashfn_equal (h, key)
      struct Lisp_Hash_Table *h;
      Lisp_Object key;
 {
-  return sxhash (key, 0);
+  unsigned hash = sxhash (key, 0);
+  xassert ((hash & ~VALMASK) == 0);
+  return hash;
 }
 
 
@@ -3774,13 +3949,13 @@ hashfn_user_defined (h, key)
      Lisp_Object key;
 {
   Lisp_Object args[2], hash;
-  
+
   args[0] = h->user_hash_function;
   args[1] = key;
   hash = Ffuncall (2, args);
   if (!INTEGERP (hash))
     Fsignal (Qerror,
-            list2 (build_string ("Illegal hash code returned from \
+            list2 (build_string ("Invalid hash code returned from \
 user-supplied hash function"),
                    hash));
   return XUINT (hash);
@@ -3793,8 +3968,8 @@ user-supplied hash function"),
    It must be either one of the predefined tests `eq', `eql' or
    `equal' or a symbol denoting a user-defined test named TEST with
    test and hash functions USER_TEST and USER_HASH.
-   
-   Give the table initial capacity SIZE, SIZE > 0, an integer.
+
+   Give the table initial capacity SIZE, SIZE >= 0, an integer.
 
    If REHASH_SIZE is an integer, it must be > 0, and this hash table's
    new size when it becomes full is computed by adding REHASH_SIZE to
@@ -3807,7 +3982,7 @@ user-supplied hash function"),
    (table size) is >= REHASH_THRESHOLD.
 
    WEAK specifies the weakness of the table.  If non-nil, it must be
-   one of the symbols `key', `value' or t.  */
+   one of the symbols `key', `value', `key-or-value', or `key-and-value'.  */
 
 Lisp_Object
 make_hash_table (test, size, rehash_size, rehash_threshold, weak,
@@ -3822,13 +3997,16 @@ make_hash_table (test, size, rehash_size, rehash_threshold, weak,
 
   /* Preconditions.  */
   xassert (SYMBOLP (test));
-  xassert (INTEGERP (size) && XINT (size) > 0);
+  xassert (INTEGERP (size) && XINT (size) >= 0);
   xassert ((INTEGERP (rehash_size) && XINT (rehash_size) > 0)
           || (FLOATP (rehash_size) && XFLOATINT (rehash_size) > 1.0));
   xassert (FLOATP (rehash_threshold)
           && XFLOATINT (rehash_threshold) > 0
           && XFLOATINT (rehash_threshold) <= 1.0);
 
+  if (XFASTINT (size) == 0)
+    size = make_number (1);
+
   /* Allocate a vector, and initialize it.  */
   len = VECSIZE (struct Lisp_Hash_Table);
   v = allocate_vectorlike (len);
@@ -3839,7 +4017,7 @@ make_hash_table (test, size, rehash_size, rehash_threshold, weak,
   /* Initialize hash table slots.  */
   sz = XFASTINT (size);
   h = (struct Lisp_Hash_Table *) v;
-  
+
   h->test = test;
   if (EQ (test, Qeql))
     {
@@ -3863,7 +4041,7 @@ make_hash_table (test, size, rehash_size, rehash_threshold, weak,
       h->cmpfn = cmpfn_user_defined;
       h->hashfn = hashfn_user_defined;
     }
-  
+
   h->weak = weak;
   h->rehash_threshold = rehash_threshold;
   h->rehash_size = rehash_size;
@@ -3908,7 +4086,7 @@ copy_hash_table (h1)
   struct Lisp_Hash_Table *h2;
   struct Lisp_Vector *v, *next;
   int len;
-  
+
   len = VECSIZE (struct Lisp_Hash_Table);
   v = allocate_vectorlike (len);
   h2 = (struct Lisp_Hash_Table *) v;
@@ -3943,7 +4121,7 @@ maybe_resize_hash_table (h)
     {
       int old_size = HASH_TABLE_SIZE (h);
       int i, new_size, index_size;
+
       if (INTEGERP (h->rehash_size))
        new_size = old_size + XFASTINT (h->rehash_size);
       else
@@ -3965,16 +4143,16 @@ maybe_resize_hash_table (h)
          maphash faster.  */
       for (i = old_size; i < new_size - 1; ++i)
        HASH_NEXT (h, i) = make_number (i + 1);
-      
+
       if (!NILP (h->next_free))
        {
          Lisp_Object last, next;
-         
+
          last = h->next_free;
          while (next = HASH_NEXT (h, XFASTINT (last)),
                 !NILP (next))
            last = next;
-         
+
          HASH_NEXT (h, XFASTINT (last)) = make_number (old_size);
        }
       else
@@ -3989,7 +4167,7 @@ maybe_resize_hash_table (h)
            HASH_NEXT (h, i) = HASH_INDEX (h, start_of_bucket);
            HASH_INDEX (h, start_of_bucket) = make_number (i);
          }
-    }  
+    }
 }
 
 
@@ -4010,7 +4188,7 @@ hash_lookup (h, key, hash)
   hash_code = h->hashfn (h, key);
   if (hash)
     *hash = hash_code;
-  
+
   start_of_bucket = hash_code % XVECTOR (h->index)->size;
   idx = HASH_INDEX (h, start_of_bucket);
 
@@ -4047,7 +4225,7 @@ hash_put (h, key, value, hash)
   /* Increment count after resizing because resizing may fail.  */
   maybe_resize_hash_table (h);
   h->count = make_number (XFASTINT (h->count) + 1);
-  
+
   /* Store key/value in the key_and_value vector.  */
   i = XFASTINT (h->next_free);
   h->next_free = HASH_NEXT (h, i);
@@ -4158,10 +4336,10 @@ sweep_weak_table (h, remove_entries_p)
      int remove_entries_p;
 {
   int bucket, n, marked;
-  
+
   n = XVECTOR (h->index)->size & ~ARRAY_MARK_FLAG;
   marked = 0;
-  
+
   for (bucket = 0; bucket < n; ++bucket)
     {
       Lisp_Object idx, prev;
@@ -4175,17 +4353,22 @@ sweep_weak_table (h, remove_entries_p)
          int remove_p;
          int i = XFASTINT (idx);
          Lisp_Object next;
+         int key_known_to_survive_p, value_known_to_survive_p;
+
+         key_known_to_survive_p = survives_gc_p (HASH_KEY (h, i));
+         value_known_to_survive_p = survives_gc_p (HASH_VALUE (h, i));
 
          if (EQ (h->weak, Qkey))
-           remove_p = !survives_gc_p (HASH_KEY (h, i));
+           remove_p = !key_known_to_survive_p;
          else if (EQ (h->weak, Qvalue))
-           remove_p = !survives_gc_p (HASH_VALUE (h, i));
-         else if (EQ (h->weak, Qt))
-           remove_p = (!survives_gc_p (HASH_KEY (h, i))
-                       || !survives_gc_p (HASH_VALUE (h, i)));
+           remove_p = !value_known_to_survive_p;
+         else if (EQ (h->weak, Qkey_or_value))
+           remove_p = !(key_known_to_survive_p || value_known_to_survive_p);
+         else if (EQ (h->weak, Qkey_and_value))
+           remove_p = !(key_known_to_survive_p && value_known_to_survive_p);
          else
            abort ();
-                     
+
          next = HASH_NEXT (h, i);
 
          if (remove_entries_p)
@@ -4197,15 +4380,15 @@ sweep_weak_table (h, remove_entries_p)
                    HASH_INDEX (h, i) = next;
                  else
                    HASH_NEXT (h, XFASTINT (prev)) = next;
-                 
+
                  /* Add to free list.  */
                  HASH_NEXT (h, i) = h->next_free;
                  h->next_free = idx;
-                 
+
                  /* Clear key, value, and hash.  */
                  HASH_KEY (h, i) = HASH_VALUE (h, i) = Qnil;
                  HASH_HASH (h, i) = Qnil;
-                 
+
                  h->count = make_number (XFASTINT (h->count) - 1);
                }
            }
@@ -4214,9 +4397,17 @@ sweep_weak_table (h, remove_entries_p)
              if (!remove_p)
                {
                  /* Make sure key and value survive.  */
-                 mark_object (&HASH_KEY (h, i));
-                 mark_object (&HASH_VALUE (h, i));
-                 marked = 1;
+                 if (!key_known_to_survive_p)
+                   {
+                     mark_object (&HASH_KEY (h, i));
+                     marked = 1;
+                   }
+
+                 if (!value_known_to_survive_p)
+                   {
+                     mark_object (&HASH_VALUE (h, i));
+                     marked = 1;
+                   }
                }
            }
 
@@ -4234,8 +4425,8 @@ sweep_weak_table (h, remove_entries_p)
 void
 sweep_weak_hash_tables ()
 {
-  Lisp_Object table;
-  struct Lisp_Hash_Table *h, *prev;
+  Lisp_Object table, used, next;
+  struct Lisp_Hash_Table *h;
   int marked;
 
   /* Mark all keys and values that are in use.  Keep on marking until
@@ -4257,27 +4448,24 @@ sweep_weak_hash_tables ()
   while (marked);
 
   /* Remove tables and entries that aren't used.  */
-  prev = NULL;
-  for (table = Vweak_hash_tables; !GC_NILP (table); table = h->next_weak)
+  for (table = Vweak_hash_tables, used = Qnil; !GC_NILP (table); table = next)
     {
-      prev = h;
       h = XHASH_TABLE (table);
-       
+      next = h->next_weak;
+      
       if (h->size & ARRAY_MARK_FLAG)
        {
+         /* TABLE is marked as used.  Sweep its contents.  */
          if (XFASTINT (h->count) > 0)
            sweep_weak_table (h, 1);
-       }
-      else
-       {
-         /* Table is not marked, and will thus be freed.
-            Take it out of the list of weak hash tables.  */
-         if (prev)
-           prev->next_weak = h->next_weak;
-         else
-           Vweak_hash_tables = h->next_weak;
+
+         /* Add table to the list of used weak hash tables.  */
+         h->next_weak = used;
+         used = table;
        }
     }
+
+  Vweak_hash_tables = used;
 }
 
 
@@ -4302,7 +4490,8 @@ sweep_weak_hash_tables ()
       + (unsigned)(Y))
 
 
-/* Return a hash for string PTR which has length LEN.  */
+/* Return a hash for string PTR which has length LEN.  The hash
+   code returned is guaranteed to fit in a Lisp integer.  */
 
 static unsigned
 sxhash_string (ptr, len)
@@ -4321,8 +4510,8 @@ sxhash_string (ptr, len)
        c -= 40;
       hash = ((hash << 3) + (hash >> 28) + c);
     }
-  
-  return hash & 07777777777;
+
+  return hash & VALMASK;
 }
 
 
@@ -4336,7 +4525,7 @@ sxhash_list (list, depth)
 {
   unsigned hash = 0;
   int i;
-  
+
   if (depth < SXHASH_MAX_DEPTH)
     for (i = 0;
         CONSP (list) && i < SXHASH_MAX_LEN;
@@ -4401,7 +4590,7 @@ sxhash (obj, depth)
 
   if (depth > SXHASH_MAX_DEPTH)
     return 0;
-  
+
   switch (XTYPE (obj))
     {
     case Lisp_Int:
@@ -4479,27 +4668,29 @@ DEFUN ("make-hash-table", Fmake_hash_table, Smake_hash_table, 0, MANY, 0,
 Arguments are specified as keyword/argument pairs.  The following\n\
 arguments are defined:\n\
 \n\
-:TEST TEST -- TEST must be a symbol that specifies how to compare keys.\n\
+:test TEST -- TEST must be a symbol that specifies how to compare keys.\n\
 Default is `eql'.  Predefined are the tests `eq', `eql', and `equal'.\n\
 User-supplied test and hash functions can be specified via\n\
 `define-hash-table-test'.\n\
 \n\
-:SIZE SIZE -- A hint as to how many elements will be put in the table.\n\
+:size SIZE -- A hint as to how many elements will be put in the table.\n\
 Default is 65.\n\
 \n\
-:REHASH-SIZE REHASH-SIZE - Indicates how to expand the table when\n\
+:rehash-size REHASH-SIZE - Indicates how to expand the table when\n\
 it fills up.  If REHASH-SIZE is an integer, add that many space.\n\
 If it is a float, it must be > 1.0, and the new size is computed by\n\
 multiplying the old size with that factor.  Default is 1.5.\n\
 \n\
-:REHASH-THRESHOLD THRESHOLD -- THRESHOLD must a float > 0, and <= 1.0.\n\
+:rehash-threshold THRESHOLD -- THRESHOLD must a float > 0, and <= 1.0.\n\
 Resize the hash table when ratio of the number of entries in the table.\n\
 Default is 0.8.\n\
 \n\
-:WEAKNESS WEAK -- WEAK must be one of nil, t, `key', or `value'.\n\
-If WEAK is not nil, the table returned is a weak table.  Key/value\n\
-pairs are removed from a weak hash table when their key, value or both\n\
-(WEAK t) are otherwise unreferenced.  Default is nil.")
+:weakness WEAK -- WEAK must be one of nil, t, `key', `value',\n\
+`key-or-value', or `key-and-value'.  If WEAK is not nil, the table returned\n\
+is a weak table.  Key/value pairs are removed from a weak hash table when\n\
+there are no non-weak references pointing to their key, value, one of key\n\
+or value, or both key and value, depending on WEAK.  WEAK t is equivalent\n\
+to `key-and-value'.  Default value of WEAK is nil.")
   (nargs, args)
      int nargs;
      Lisp_Object *args;
@@ -4521,10 +4712,10 @@ pairs are removed from a weak hash table when their key, value or both\n\
     {
       /* See if it is a user-defined test.  */
       Lisp_Object prop;
-      
+
       prop = Fget (test, Qhash_table_test);
       if (!CONSP (prop) || XFASTINT (Flength (prop)) < 2)
-       Fsignal (Qerror, list2 (build_string ("Illegal hash table test"),
+       Fsignal (Qerror, list2 (build_string ("Invalid hash table test"),
                                test));
       user_test = Fnth (make_number (0), prop);
       user_hash = Fnth (make_number (1), prop);
@@ -4535,9 +4726,9 @@ pairs are removed from a weak hash table when their key, value or both\n\
   /* See if there's a `:size SIZE' argument.  */
   i = get_key_arg (QCsize, nargs, args, used);
   size = i < 0 ? make_number (DEFAULT_HASH_SIZE) : args[i];
-  if (!INTEGERP (size) || XINT (size) <= 0)
+  if (!INTEGERP (size) || XINT (size) < 0)
     Fsignal (Qerror,
-            list2 (build_string ("Illegal hash table size"),
+            list2 (build_string ("Invalid hash table size"),
                    size));
 
   /* Look for `:rehash-size SIZE'.  */
@@ -4547,9 +4738,9 @@ pairs are removed from a weak hash table when their key, value or both\n\
       || (INTEGERP (rehash_size) && XINT (rehash_size) <= 0)
       || XFLOATINT (rehash_size) <= 1.0)
     Fsignal (Qerror,
-            list2 (build_string ("Illegal hash table rehash size"),
+            list2 (build_string ("Invalid hash table rehash size"),
                    rehash_size));
-  
+
   /* Look for `:rehash-threshold THRESHOLD'.  */
   i = get_key_arg (QCrehash_threshold, nargs, args, used);
   rehash_threshold = i < 0 ? make_float (DEFAULT_REHASH_THRESHOLD) : args[i];
@@ -4557,19 +4748,22 @@ pairs are removed from a weak hash table when their key, value or both\n\
       || XFLOATINT (rehash_threshold) <= 0.0
       || XFLOATINT (rehash_threshold) > 1.0)
     Fsignal (Qerror,
-            list2 (build_string ("Illegal hash table rehash threshold"),
+            list2 (build_string ("Invalid hash table rehash threshold"),
                    rehash_threshold));
-  
+
   /* Look for `:weakness WEAK'.  */
   i = get_key_arg (QCweakness, nargs, args, used);
   weak = i < 0 ? Qnil : args[i];
+  if (EQ (weak, Qt))
+    weak = Qkey_and_value;
   if (!NILP (weak)
-      && !EQ (weak, Qt)
       && !EQ (weak, Qkey)
-      && !EQ (weak, Qvalue))
-    Fsignal (Qerror, list2 (build_string ("Illegal hash table weakness"), 
+      && !EQ (weak, Qvalue)
+      && !EQ (weak, Qkey_or_value)
+      && !EQ (weak, Qkey_and_value))
+    Fsignal (Qerror, list2 (build_string ("Invalid hash table weakness"),
                            weak));
-  
+
   /* Now, all args should have been used up, or there's a problem.  */
   for (i = 0; i < nargs; ++i)
     if (!used[i])
@@ -4600,7 +4794,7 @@ is `eql'.  New tests can be defined with `define-hash-table-test'.")
 {
   Lisp_Object args[2];
   args[0] = QCtest;
-  args[1] = test;
+  args[1] = NILP (test) ? Qeql : test;
   return Fmake_hash_table (2, args);
 }
 
@@ -4613,7 +4807,7 @@ DEFUN ("hash-table-count", Fhash_table_count, Shash_table_count, 1, 1, 0,
   return check_hash_table (table)->count;
 }
 
-  
+
 DEFUN ("hash-table-rehash-size", Fhash_table_rehash_size,
        Shash_table_rehash_size, 1, 1, 0,
   "Return the current rehash size of TABLE.")
@@ -4622,7 +4816,7 @@ DEFUN ("hash-table-rehash-size", Fhash_table_rehash_size,
 {
   return check_hash_table (table)->rehash_size;
 }
-  
+
 
 DEFUN ("hash-table-rehash-threshold", Fhash_table_rehash_threshold,
        Shash_table_rehash_threshold, 1, 1, 0,
@@ -4632,7 +4826,7 @@ DEFUN ("hash-table-rehash-threshold", Fhash_table_rehash_threshold,
 {
   return check_hash_table (table)->rehash_threshold;
 }
-  
+
 
 DEFUN ("hash-table-size", Fhash_table_size, Shash_table_size, 1, 1, 0,
   "Return the size of TABLE.\n\
@@ -4645,7 +4839,7 @@ without need for resizing.")
   struct Lisp_Hash_Table *h = check_hash_table (table);
   return make_number (HASH_TABLE_SIZE (h));
 }
-  
+
 
 DEFUN ("hash-table-test", Fhash_table_test, Shash_table_test, 1, 1, 0,
   "Return the test TABLE uses.")
@@ -4655,7 +4849,7 @@ DEFUN ("hash-table-test", Fhash_table_test, Shash_table_test, 1, 1, 0,
   return check_hash_table (table)->test;
 }
 
-  
+
 DEFUN ("hash-table-weakness", Fhash_table_weakness, Shash_table_weakness,
        1, 1, 0,
   "Return the weakness of TABLE.")
@@ -4665,7 +4859,7 @@ DEFUN ("hash-table-weakness", Fhash_table_weakness, Shash_table_weakness,
   return check_hash_table (table)->weak;
 }
 
-  
+
 DEFUN ("hash-table-p", Fhash_table_p, Shash_table_p, 1, 1, 0,
   "Return t if OBJ is a Lisp hash table object.")
   (obj)
@@ -4713,7 +4907,7 @@ VALUE.")
     HASH_VALUE (h, i) = value;
   else
     hash_put (h, key, value, hash);
-  
+
   return value;
 }
 
@@ -4747,7 +4941,7 @@ FUNCTION is called with 2 arguments KEY and VALUE.")
        args[2] = HASH_VALUE (h, i);
        Ffuncall (3, args);
       }
-  
+
   return Qnil;
 }
 
@@ -4770,6 +4964,211 @@ integers, including negative integers.")
 }
 
 
+\f
+/************************************************************************
+                                MD5
+ ************************************************************************/
+
+#include "md5.h"
+#include "coding.h"
+
+DEFUN ("md5", Fmd5, Smd5, 1, 5, 0,
+  "Return MD5 message digest of OBJECT, a buffer or string.\n\
+\n\
+The two optional arguments START and END are character positions\n\
+specifying for which part of OBJECT the message digest should be computed.\n\
+If nil or omitted, the digest is computed for the whole OBJECT.\n\
+\n\
+Third optional argument CODING-SYSTEM specifies the coding system text\n\
+should be converted to before computing the digest.  If nil or omitted,\n\
+the current format is used or a format is guessed.\n\
+\n\
+Fourth optional argument NOERROR is there for compatability with other\n\
+Emacsen and is ignored.")
+  (object, start, end, coding_system, noerror)
+     Lisp_Object object, start, end, coding_system, noerror;
+{
+  unsigned char digest[16];
+  unsigned char value[33];
+  int i;
+  int size;
+  int size_byte = 0;
+  int start_char = 0, end_char = 0;
+  int start_byte = 0, end_byte = 0;
+  register int b, e;
+  register struct buffer *bp;
+  int temp;
+
+  if (STRINGP (object))
+    {
+      if (NILP (coding_system))
+       {
+         /* Decide the coding-system to encode the data with.  */
+
+         if (STRING_MULTIBYTE (object))
+           /* use default, we can't guess correct value */
+           coding_system = XSYMBOL (XCAR (Vcoding_category_list))->value;
+         else 
+           coding_system = Qraw_text;
+       }
+      
+      if (NILP (Fcoding_system_p (coding_system)))
+       {
+         /* Invalid coding system.  */
+         
+         if (!NILP (noerror))
+           coding_system = Qraw_text;
+         else
+           while (1)
+             Fsignal (Qcoding_system_error, Fcons (coding_system, Qnil));
+       }
+
+      if (STRING_MULTIBYTE (object))
+       object = code_convert_string1 (object, coding_system, Qnil, 1);
+
+      size = XSTRING (object)->size;
+      size_byte = STRING_BYTES (XSTRING (object));
+
+      if (!NILP (start))
+       {
+         CHECK_NUMBER (start, 1);
+
+         start_char = XINT (start);
+
+         if (start_char < 0)
+           start_char += size;
+
+         start_byte = string_char_to_byte (object, start_char);
+       }
+
+      if (NILP (end))
+       {
+         end_char = size;
+         end_byte = size_byte;
+       }
+      else
+       {
+         CHECK_NUMBER (end, 2);
+         
+         end_char = XINT (end);
+
+         if (end_char < 0)
+           end_char += size;
+         
+         end_byte = string_char_to_byte (object, end_char);
+       }
+      
+      if (!(0 <= start_char && start_char <= end_char && end_char <= size))
+       args_out_of_range_3 (object, make_number (start_char),
+                            make_number (end_char));
+    }
+  else
+    {
+      CHECK_BUFFER (object, 0);
+
+      bp = XBUFFER (object);
+         
+      if (NILP (start))
+       b = BUF_BEGV (bp);
+      else
+       {
+         CHECK_NUMBER_COERCE_MARKER (start, 0);
+         b = XINT (start);
+       }
+
+      if (NILP (end))
+       e = BUF_ZV (bp);
+      else
+       {
+         CHECK_NUMBER_COERCE_MARKER (end, 1);
+         e = XINT (end);
+       }
+      
+      if (b > e)
+       temp = b, b = e, e = temp;
+      
+      if (!(BUF_BEGV (bp) <= b && e <= BUF_ZV (bp)))
+       args_out_of_range (start, end);
+      
+      if (NILP (coding_system))
+       {
+         /* Decide the coding-system to encode the data with. 
+            See fileio.c:Fwrite-region */
+
+         if (!NILP (Vcoding_system_for_write))
+           coding_system = Vcoding_system_for_write;
+         else
+           {
+             int force_raw_text = 0;
+
+             coding_system = XBUFFER (object)->buffer_file_coding_system;
+             if (NILP (coding_system)
+                 || NILP (Flocal_variable_p (Qbuffer_file_coding_system, Qnil)))
+               {
+                 coding_system = Qnil;
+                 if (NILP (current_buffer->enable_multibyte_characters))
+                   force_raw_text = 1;
+               }
+
+             if (NILP (coding_system) && !NILP (Fbuffer_file_name(object)))
+               {
+                 /* Check file-coding-system-alist.  */
+                 Lisp_Object args[4], val;
+                 
+                 args[0] = Qwrite_region; args[1] = start; args[2] = end;
+                 args[3] = Fbuffer_file_name(object);
+                 val = Ffind_operation_coding_system (4, args);
+                 if (CONSP (val) && !NILP (XCDR (val)))
+                   coding_system = XCDR (val);
+               }
+
+             if (NILP (coding_system)
+                 && !NILP (XBUFFER (object)->buffer_file_coding_system))
+               {
+                 /* If we still have not decided a coding system, use the
+                    default value of buffer-file-coding-system.  */
+                 coding_system = XBUFFER (object)->buffer_file_coding_system;
+               }
+
+             if (!force_raw_text
+                 && !NILP (Ffboundp (Vselect_safe_coding_system_function)))
+               /* Confirm that VAL can surely encode the current region.  */
+               coding_system = call3 (Vselect_safe_coding_system_function,
+                                      make_number (b), make_number (e),
+                                      coding_system);
+
+             if (force_raw_text)
+               coding_system = Qraw_text;
+           }
+
+         if (NILP (Fcoding_system_p (coding_system)))
+           {
+             /* Invalid coding system.  */
+
+             if (!NILP (noerror))
+               coding_system = Qraw_text;
+             else
+               while (1)
+                 Fsignal (Qcoding_system_error, Fcons (coding_system, Qnil));
+           }
+       }
+
+      object = make_buffer_string (b, e, 0);
+
+      if (STRING_MULTIBYTE (object))
+       object = code_convert_string1 (object, coding_system, Qnil, 1);
+    }
+
+  md5_buffer (XSTRING (object)->data + start_byte, 
+             STRING_BYTES(XSTRING (object)) - (size_byte - end_byte), 
+             digest);
+
+  for (i = 0; i < 16; i++)
+    sprintf (&value[2 * i], "%02x", digest[i]);
+  value[32] = '\0';
+
+  return make_string (value, 32);
+}
 
 \f
 void
@@ -4800,6 +5199,10 @@ syms_of_fns ()
   staticpro (&Qvalue);
   Qhash_table_test = intern ("hash-table-test");
   staticpro (&Qhash_table_test);
+  Qkey_or_value = intern ("key-or-value");
+  staticpro (&Qkey_or_value);
+  Qkey_and_value = intern ("key-and-value");
+  staticpro (&Qkey_and_value);
 
   defsubr (&Ssxhash);
   defsubr (&Smake_hash_table);
@@ -4818,7 +5221,7 @@ syms_of_fns ()
   defsubr (&Sremhash);
   defsubr (&Smaphash);
   defsubr (&Sdefine_hash_table_test);
-  
+
   Qstring_lessp = intern ("string-lessp");
   staticpro (&Qstring_lessp);
   Qprovide = intern ("provide");
@@ -4914,6 +5317,7 @@ invoked by mouse clicks and mouse menu items.");
   defsubr (&Sbase64_decode_region);
   defsubr (&Sbase64_encode_string);
   defsubr (&Sbase64_decode_string);
+  defsubr (&Smd5);
 }