(detected_mask): Delete unused variable.
[bpt/emacs.git] / src / coding.c
index 4553cae..e2b5ed6 100644 (file)
@@ -1,7 +1,7 @@
-/* Coding system handler (conversion, detection, and etc).
+/* Coding system handler (conversion, detection, etc).
    Copyright (C) 1995, 1997, 1998 Electrotechnical Laboratory, JAPAN.
    Licensed to the Free Software Foundation.
-   Copyright (C) 2001 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2002 Free Software Foundation, Inc.
    Copyright (C) 2001, 2002
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H13PRO009
@@ -94,7 +94,7 @@ CODING SYSTEM
   o BIG5
 
   A coding system to encode character sets: ASCII and Big5.  Widely
-  used by Chinese (mainly in Taiwan and Hong Kong).  Details are
+  used for Chinese (mainly in Taiwan and Hong Kong).  Details are
   described in section 8.  In this file, when we write "big5" (all
   lowercase), we mean the coding system, and when we write "Big5"
   (capitalized), we mean the character set.
@@ -108,7 +108,7 @@ CODING SYSTEM
 
   o Raw-text
 
-  A coding system for text containing raw eight-bit data.  Emacs
+  A coding system for text containing raw eight-bit data.  Emacs
   treats each byte of source text as a character (except for
   end-of-line conversion).
 
@@ -144,26 +144,23 @@ STRUCT CODING_SYSTEM
 /*** GENERAL NOTES on `detect_coding_XXX ()' functions ***
 
   These functions check if a byte sequence specified as a source in
-  CODING conforms to the format of XXX.  Return 1 if the data contains
-  a byte sequence which can be decoded into non-ASCII characters by
-  the coding system.  Otherwize (i.e. the data contains only ASCII
-  characters or invalid sequence) return 0.
+  CODING conforms to the format of XXX, and update the members of
+  DETECT_INFO.
 
-  It also resets some bits of an integer pointed by MASK.  The macros
-  CATEGORY_MASK_XXX specifies each bit of this integer.
+  Return 1 if the byte sequence conforms to XXX, otherwise return 0.
 
   Below is the template of these functions.  */
 
 #if 0
 static int
-detect_coding_XXX (coding, mask)
+detect_coding_XXX (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source;
   unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
-  int c;
+  int consumed_chars = 0;
   int found = 0;
   ...;
 
@@ -172,18 +169,19 @@ detect_coding_XXX (coding, mask)
       /* Get one byte from the source.  If the souce is exausted, jump
         to no_more_source:.  */
       ONE_MORE_BYTE (c);
-      /* Check if it conforms to XXX.  If not, break the loop.  */
+
+      if (! __C_conforms_to_XXX___ (c))
+       break;
+      if (! __C_strongly_suggests_XXX__ (c))
+       found = CATEGORY_MASK_XXX;
     }
-  /* As the data is invalid for XXX, reset a proper bits.  */
-  *mask &= ~CODING_CATEGORY_XXX;
+  /* The byte sequence is invalid for XXX.  */
+  detect_info->rejected |= CATEGORY_MASK_XXX;
   return 0;
+
  no_more_source:
-  /* The source exausted.  */
-  if (!found)
-    /* ASCII characters only. */
-    return 0;
-  /* Some data should be decoded into non-ASCII characters.  */
-  *mask &= CODING_CATEGORY_XXX;
+  /* The source exausted successfully.  */
+  detect_info->found |= found;
   return 1;
 }
 #endif
@@ -303,14 +301,14 @@ encode_coding_XXX (coding)
 Lisp_Object Vcoding_system_hash_table;
 
 Lisp_Object Qcoding_system, Qcoding_aliases, Qeol_type;
-Lisp_Object Qunix, Qdos, Qmac;
+Lisp_Object Qunix, Qdos;
+extern Lisp_Object Qmac;       /* frame.c */
 Lisp_Object Qbuffer_file_coding_system;
 Lisp_Object Qpost_read_conversion, Qpre_write_conversion;
 Lisp_Object Qdefault_char;
 Lisp_Object Qno_conversion, Qundecided;
 Lisp_Object Qcharset, Qiso_2022, Qutf_8, Qutf_16, Qshift_jis, Qbig5;
-Lisp_Object Qutf_16_be_nosig, Qutf_16_be, Qutf_16_le_nosig, Qutf_16_le;
-Lisp_Object Qsignature, Qendian, Qbig, Qlittle;
+Lisp_Object Qbig, Qlittle;
 Lisp_Object Qcoding_system_history;
 Lisp_Object Qvalid_codes;
 
@@ -407,31 +405,38 @@ Lisp_Object Vsjis_coding_system;
 Lisp_Object Vbig5_coding_system;
 
 
-static int detect_coding_utf_8 P_ ((struct coding_system *, int *));
+static int detect_coding_utf_8 P_ ((struct coding_system *,
+                                   struct coding_detection_info *info));
 static void decode_coding_utf_8 P_ ((struct coding_system *));
 static int encode_coding_utf_8 P_ ((struct coding_system *));
 
-static int detect_coding_utf_16 P_ ((struct coding_system *, int *));
+static int detect_coding_utf_16 P_ ((struct coding_system *,
+                                    struct coding_detection_info *info));
 static void decode_coding_utf_16 P_ ((struct coding_system *));
 static int encode_coding_utf_16 P_ ((struct coding_system *));
 
-static int detect_coding_iso_2022 P_ ((struct coding_system *, int *));
+static int detect_coding_iso_2022 P_ ((struct coding_system *,
+                                      struct coding_detection_info *info));
 static void decode_coding_iso_2022 P_ ((struct coding_system *));
 static int encode_coding_iso_2022 P_ ((struct coding_system *));
 
-static int detect_coding_emacs_mule P_ ((struct coding_system *, int *));
+static int detect_coding_emacs_mule P_ ((struct coding_system *,
+                                        struct coding_detection_info *info));
 static void decode_coding_emacs_mule P_ ((struct coding_system *));
 static int encode_coding_emacs_mule P_ ((struct coding_system *));
 
-static int detect_coding_sjis P_ ((struct coding_system *, int *));
+static int detect_coding_sjis P_ ((struct coding_system *,
+                                  struct coding_detection_info *info));
 static void decode_coding_sjis P_ ((struct coding_system *));
 static int encode_coding_sjis P_ ((struct coding_system *));
 
-static int detect_coding_big5 P_ ((struct coding_system *, int *));
+static int detect_coding_big5 P_ ((struct coding_system *,
+                                  struct coding_detection_info *info));
 static void decode_coding_big5 P_ ((struct coding_system *));
 static int encode_coding_big5 P_ ((struct coding_system *));
 
-static int detect_coding_ccl P_ ((struct coding_system *, int *));
+static int detect_coding_ccl P_ ((struct coding_system *,
+                                 struct coding_detection_info *info));
 static void decode_coding_ccl P_ ((struct coding_system *));
 static int encode_coding_ccl P_ ((struct coding_system *));
 
@@ -555,7 +560,11 @@ enum iso_code_class_type
 
 #define CODING_ISO_FLAG_EUC_TW_SHIFT   0x4000
 
-#define CODING_ISO_FLAG_FULL_SUPPORT   0x8000
+#define CODING_ISO_FLAG_USE_ROMAN      0x8000
+
+#define CODING_ISO_FLAG_USE_OLDJIS     0x10000
+
+#define CODING_ISO_FLAG_FULL_SUPPORT   0x100000
 
 /* A character to be produced on output if encoding of the original
    character is prohibited by CODING_ISO_FLAG_SAFE.  */
@@ -582,7 +591,7 @@ enum iso_code_class_type
   (XSTRING (AREF (CODING_ID_ATTRS ((coding)->id), coding_attr_ccl_valids)) \
    ->data)
 
-/* Index for each coding category in `coding_category_table' */
+/* Index for each coding category in `coding_categories' */
 
 enum coding_category
   {
@@ -617,6 +626,7 @@ enum coding_category
 #define CATEGORY_MASK_ISO_7_ELSE       (1 << coding_category_iso_7_else)
 #define CATEGORY_MASK_ISO_8_ELSE       (1 << coding_category_iso_8_else)
 #define CATEGORY_MASK_UTF_8            (1 << coding_category_utf_8)
+#define CATEGORY_MASK_UTF_16_AUTO      (1 << coding_category_utf_16_auto)
 #define CATEGORY_MASK_UTF_16_BE                (1 << coding_category_utf_16_be)
 #define CATEGORY_MASK_UTF_16_LE                (1 << coding_category_utf_16_le)
 #define CATEGORY_MASK_UTF_16_BE_NOSIG  (1 << coding_category_utf_16_be_nosig)
@@ -626,6 +636,7 @@ enum coding_category
 #define CATEGORY_MASK_BIG5             (1 << coding_category_big5)
 #define CATEGORY_MASK_CCL              (1 << coding_category_ccl)
 #define CATEGORY_MASK_EMACS_MULE       (1 << coding_category_emacs_mule)
+#define CATEGORY_MASK_RAW_TEXT         (1 << coding_category_raw_text)
 
 /* This value is returned if detect_coding_mask () find nothing other
    than ASCII characters.  */
@@ -690,26 +701,6 @@ static enum coding_category coding_priorities[coding_category_max];
    Nth coding category.  */
 static struct coding_system coding_categories[coding_category_max];
 
-static int detected_mask[coding_category_raw_text] =
-  { CATEGORY_MASK_ISO,
-    CATEGORY_MASK_ISO,
-    CATEGORY_MASK_ISO,
-    CATEGORY_MASK_ISO,
-    CATEGORY_MASK_ISO,
-    CATEGORY_MASK_ISO,
-    CATEGORY_MASK_UTF_8,
-    CATEGORY_MASK_UTF_16,
-    CATEGORY_MASK_UTF_16,
-    CATEGORY_MASK_UTF_16,
-    CATEGORY_MASK_UTF_16,
-    CATEGORY_MASK_UTF_16,
-    CATEGORY_MASK_CHARSET,
-    CATEGORY_MASK_SJIS,
-    CATEGORY_MASK_BIG5,
-    CATEGORY_MASK_CCL,
-    CATEGORY_MASK_EMACS_MULE
-  };
-
 /*** Commonly used macros and functions ***/
 
 #ifndef min
@@ -889,18 +880,12 @@ coding_set_source (coding)
 {
   if (BUFFERP (coding->src_object))
     {
+      struct buffer *buf = XBUFFER (coding->src_object);
+
       if (coding->src_pos < 0)
-       coding->source = GAP_END_ADDR + coding->src_pos_byte;
+       coding->source = BUF_GAP_END_ADDR (buf) + coding->src_pos_byte;
       else
-       {
-         struct buffer *buf = XBUFFER (coding->src_object);
-         EMACS_INT gpt_byte = BUF_GPT_BYTE (buf);
-         unsigned char *beg_addr = BUF_BEG_ADDR (buf);
-
-         coding->source = beg_addr + coding->src_pos_byte - 1;
-         if (coding->src_pos_byte >= gpt_byte)
-           coding->source += BUF_GAP_SIZE (buf);
-       }
+       coding->source = BUF_BYTE_ADDRESS (buf, coding->src_pos_byte);
     }
   else if (STRINGP (coding->src_object))
     {
@@ -919,17 +904,22 @@ coding_set_destination (coding)
 {
   if (BUFFERP (coding->dst_object))
     {
-      /* We are sure that coding->dst_pos_byte is before the gap of the
-        buffer. */
-      coding->destination = (BUF_BEG_ADDR (XBUFFER (coding->dst_object))
-                            + coding->dst_pos_byte - 1);
       if (coding->src_pos < 0)
-       coding->dst_bytes = (GAP_END_ADDR
-                            - (coding->src_bytes - coding->consumed)
-                            - coding->destination);
+       {
+         coding->destination = BEG_ADDR + coding->dst_pos_byte - 1;
+         coding->dst_bytes = (GAP_END_ADDR
+                              - (coding->src_bytes - coding->consumed)
+                              - coding->destination);
+       }
       else
-       coding->dst_bytes = (BUF_GAP_END_ADDR (XBUFFER (coding->dst_object))
-                            - coding->destination);
+       {
+         /* We are sure that coding->dst_pos_byte is before the gap
+            of the buffer. */
+         coding->destination = (BUF_BEG_ADDR (XBUFFER (coding->dst_object))
+                                + coding->dst_pos_byte - 1);
+         coding->dst_bytes = (BUF_GAP_END_ADDR (XBUFFER (coding->dst_object))
+                              - coding->destination);
+       }
     }
   else
     /* Otherwise, the destination is C string and is never relocated
@@ -992,6 +982,54 @@ alloc_destination (coding, nbytes, dst)
   return dst;
 }
 
+/** Macros for annotations.  */
+
+/* Maximum length of annotation data (sum of annotations for
+   composition and charset).  */
+#define MAX_ANNOTATION_LENGTH (5 + (MAX_COMPOSITION_COMPONENTS * 2) - 1 + 5)
+
+/* An annotation data is stored in the array coding->charbuf in this
+   format:
+     [ -LENGTH ANNOTATION_MASK FROM TO ... ]
+   LENGTH is the number of elements in the annotation.
+   ANNOTATION_MASK is one of CODING_ANNOTATE_XXX_MASK.
+   FROM and TO specify the range of text annotated.  They are relative
+   to coding->src_pos (on encoding) or coding->dst_pos (on decoding).
+
+   The format of the following elements depend on ANNOTATION_MASK.
+
+   In the case of CODING_ANNOTATE_COMPOSITION_MASK, these elements
+   follows:
+     ... METHOD [ COMPOSITION-COMPONENTS ... ]
+   METHOD is one of enum composition_method.
+   Optionnal COMPOSITION-COMPONENTS are characters and composition
+   rules.
+
+   In the case of CODING_ANNOTATE_CHARSET_MASK, one element CHARSET-ID
+   follows.  */
+
+#define ADD_ANNOTATION_DATA(buf, len, mask, from, to)  \
+  do {                                                 \
+    *(buf)++ = -(len);                                 \
+    *(buf)++ = (mask);                                 \
+    *(buf)++ = (from);                                 \
+    *(buf)++ = (to);                                   \
+    coding->annotated = 1;                             \
+  } while (0);
+
+#define ADD_COMPOSITION_DATA(buf, from, to, method)                          \
+  do {                                                                       \
+    ADD_ANNOTATION_DATA (buf, 5, CODING_ANNOTATE_COMPOSITION_MASK, from, to); \
+    *buf++ = method;                                                         \
+  } while (0)
+
+
+#define ADD_CHARSET_DATA(buf, from, to, id)                              \
+  do {                                                                   \
+    ADD_ANNOTATION_DATA (buf, 5, CODING_ANNOTATE_CHARSET_MASK, from, to); \
+    *buf++ = id;                                                         \
+  } while (0)
+
 \f
 /*** 2. Emacs' internal format (emacs-utf-8) ***/
 
@@ -1001,8 +1039,8 @@ alloc_destination (coding, nbytes, dst)
 /*** 3. UTF-8 ***/
 
 /* See the above "GENERAL NOTES on `detect_coding_XXX ()' functions".
-   Check if a text is encoded in UTF-8.  If it is, return
-   CATEGORY_MASK_UTF_8, else return 0.  */
+   Check if a text is encoded in UTF-8.  If it is, return 1, else
+   return 0.  */
 
 #define UTF_8_1_OCTET_P(c)         ((c) < 0x80)
 #define UTF_8_EXTRA_OCTET_P(c)     (((c) & 0xC0) == 0x80)
@@ -1012,16 +1050,18 @@ alloc_destination (coding, nbytes, dst)
 #define UTF_8_5_OCTET_LEADING_P(c) (((c) & 0xFC) == 0xF8)
 
 static int
-detect_coding_utf_8 (coding, mask)
+detect_coding_utf_8 (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source, *src_base = src;
   unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
   int consumed_chars = 0;
   int found = 0;
+  int incomplete;
 
+  detect_info->checked |= CATEGORY_MASK_UTF_8;
   /* A coding system of this category is always ASCII compatible.  */
   src += coding->head_ascii;
 
@@ -1029,15 +1069,17 @@ detect_coding_utf_8 (coding, mask)
     {
       int c, c1, c2, c3, c4;
 
+      incomplete = 0;
       ONE_MORE_BYTE (c);
       if (UTF_8_1_OCTET_P (c))
        continue;
+      incomplete = 1;
       ONE_MORE_BYTE (c1);
       if (! UTF_8_EXTRA_OCTET_P (c1))
        break;
       if (UTF_8_2_OCTET_LEADING_P (c))
        {
-         found++;
+         found = CATEGORY_MASK_UTF_8;
          continue;
        }
       ONE_MORE_BYTE (c2);
@@ -1045,7 +1087,7 @@ detect_coding_utf_8 (coding, mask)
        break;
       if (UTF_8_3_OCTET_LEADING_P (c))
        {
-         found++;
+         found = CATEGORY_MASK_UTF_8;
          continue;
        }
       ONE_MORE_BYTE (c3);
@@ -1053,7 +1095,7 @@ detect_coding_utf_8 (coding, mask)
        break;
       if (UTF_8_4_OCTET_LEADING_P (c))
        {
-         found++;
+         found = CATEGORY_MASK_UTF_8;
          continue;
        }
       ONE_MORE_BYTE (c4);
@@ -1061,18 +1103,21 @@ detect_coding_utf_8 (coding, mask)
        break;
       if (UTF_8_5_OCTET_LEADING_P (c))
        {
-         found++;
+         found = CATEGORY_MASK_UTF_8;
          continue;
        }
       break;
     }
-  *mask &= ~CATEGORY_MASK_UTF_8;
+  detect_info->rejected |= CATEGORY_MASK_UTF_8;
   return 0;
 
  no_more_source:
-  if (! found)
-    return 0;
-  *mask &= CATEGORY_MASK_UTF_8;
+  if (incomplete && coding->mode & CODING_MODE_LAST_BLOCK)
+    {
+      detect_info->rejected |= CATEGORY_MASK_UTF_8;
+      return 0;
+    }
+  detect_info->found |= found;
   return 1;
 }
 
@@ -1111,7 +1156,10 @@ decode_coding_utf_8 (coding)
              if (EQ (eol_type, Qdos))
                {
                  if (src == src_end)
-                   goto no_more_source;
+                   {
+                     coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+                     goto no_more_source;
+                   }
                  if (*src == '\n')
                    ONE_MORE_BYTE (c);
                }
@@ -1125,23 +1173,39 @@ decode_coding_utf_8 (coding)
          if (! UTF_8_EXTRA_OCTET_P (c2))
            goto invalid_code;
          if (UTF_8_2_OCTET_LEADING_P (c1))
-           c = ((c1 & 0x1F) << 6) | (c2 & 0x3F);
+           {
+             c = ((c1 & 0x1F) << 6) | (c2 & 0x3F);
+             /* Reject overlong sequences here and below.  Encoders
+                producing them are incorrect, they can be misleading,
+                and they mess up read/write invariance.  */
+             if (c < 128)
+               goto invalid_code;
+           }
          else
            {
              ONE_MORE_BYTE (c3);
              if (! UTF_8_EXTRA_OCTET_P (c3))
                goto invalid_code;
              if (UTF_8_3_OCTET_LEADING_P (c1))
-               c = (((c1 & 0xF) << 12)
-                    | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+               {
+                 c = (((c1 & 0xF) << 12)
+                      | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+                 if (c < 0x800
+                     || (c >= 0xd800 && c < 0xe000)) /* surrogates (invalid) */
+                   goto invalid_code;
+               }
              else
                {
                  ONE_MORE_BYTE (c4);
                  if (! UTF_8_EXTRA_OCTET_P (c4))
                    goto invalid_code;
                  if (UTF_8_4_OCTET_LEADING_P (c1))
+                   {
                    c = (((c1 & 0x7) << 18) | ((c2 & 0x3F) << 12)
                         | ((c3 & 0x3F) << 6) | (c4 & 0x3F));
+                   if (c < 0x10000)
+                     goto invalid_code;
+                   }
                  else
                    {
                      ONE_MORE_BYTE (c5);
@@ -1152,7 +1216,7 @@ decode_coding_utf_8 (coding)
                          c = (((c1 & 0x3) << 24) | ((c2 & 0x3F) << 18)
                               | ((c3 & 0x3F) << 12) | ((c4 & 0x3F) << 6)
                               | (c5 & 0x3F));
-                         if (c > MAX_CHAR)
+                         if ((c > MAX_CHAR) || (c < 0x200000))
                            goto invalid_code;
                        }
                      else
@@ -1202,9 +1266,17 @@ encode_coding_utf_8 (coding)
          
          ASSURE_DESTINATION (safe_room);
          c = *charbuf++;
-         CHAR_STRING_ADVANCE (c, pend);
-         for (p = str; p < pend; p++)
-           EMIT_ONE_BYTE (*p);
+         if (CHAR_BYTE8_P (c))
+           {
+             c = CHAR_TO_BYTE8 (c);
+             EMIT_ONE_BYTE (c);
+           }
+         else
+           {
+             CHAR_STRING_ADVANCE (c, pend);
+             for (p = str; p < pend; p++)
+               EMIT_ONE_BYTE (*p);
+           }
        }
     }
   else
@@ -1227,10 +1299,8 @@ encode_coding_utf_8 (coding)
 
 
 /* See the above "GENERAL NOTES on `detect_coding_XXX ()' functions".
-   Check if a text is encoded in UTF-16 Big Endian (endian == 1) or
-   Little Endian (otherwise).  If it is, return
-   CATEGORY_MASK_UTF_16_BE or CATEGORY_MASK_UTF_16_LE,
-   else return 0.  */
+   Check if a text is encoded in one of UTF-16 based coding systems.
+   If it is, return 1, else return 0.  */
 
 #define UTF_16_HIGH_SURROGATE_P(val) \
   (((val) & 0xFC00) == 0xD800)
@@ -1245,9 +1315,9 @@ encode_coding_utf_8 (coding)
 
 
 static int
-detect_coding_utf_16 (coding, mask)
+detect_coding_utf_16 (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source, *src_base = src;
   unsigned char *src_end = coding->source + coding->src_bytes;
@@ -1255,21 +1325,31 @@ detect_coding_utf_16 (coding, mask)
   int consumed_chars = 0;
   int c1, c2;
 
+  detect_info->checked |= CATEGORY_MASK_UTF_16;
+
+  if (coding->mode & CODING_MODE_LAST_BLOCK
+      && (coding->src_bytes & 1))
+    {
+      detect_info->rejected |= CATEGORY_MASK_UTF_16;
+      return 0;
+    }
   ONE_MORE_BYTE (c1);
   ONE_MORE_BYTE (c2);
 
   if ((c1 == 0xFF) && (c2 == 0xFE))
     {
-      *mask &= CATEGORY_MASK_UTF_16_LE;
-      return 1;
+      detect_info->found |= (CATEGORY_MASK_UTF_16_LE
+                            | CATEGORY_MASK_UTF_16_AUTO);
+      detect_info->rejected |= CATEGORY_MASK_UTF_16_BE;
     }
   else if ((c1 == 0xFE) && (c2 == 0xFF))
     {
-      *mask &= CATEGORY_MASK_UTF_16_BE;
-      return 1;
+      detect_info->found |= (CATEGORY_MASK_UTF_16_BE
+                            | CATEGORY_MASK_UTF_16_AUTO);
+      detect_info->rejected |= CATEGORY_MASK_UTF_16_LE;
     }
  no_more_source:
-  return 0;
+  return 1;
 }
 
 static void
@@ -1290,7 +1370,7 @@ decode_coding_utf_16 (coding)
 
   CODING_GET_INFO (coding, attr, eol_type, charset_list);
 
-  if (bom != utf_16_without_bom)
+  if (bom == utf_16_with_bom)
     {
       int c, c1, c2;
 
@@ -1298,33 +1378,22 @@ decode_coding_utf_16 (coding)
       ONE_MORE_BYTE (c1);
       ONE_MORE_BYTE (c2);
       c = (c1 << 8) | c2;
-      if (bom == utf_16_with_bom)
-       {
-         if (endian == utf_16_big_endian
-             ? c != 0xFFFE : c != 0xFEFF)
-           {
-             /* We are sure that there's enouph room at CHARBUF.  */
-             *charbuf++ = c1;
-             *charbuf++ = c2;
-             coding->errors++;
-           }
-       }
-      else
+
+      if (endian == utf_16_big_endian
+         ? c != 0xFEFF : c != 0xFFFE)
        {
-         if (c == 0xFFFE)
-           CODING_UTF_16_ENDIAN (coding)
-             = endian = utf_16_big_endian;
-         else if (c == 0xFEFF)
-           CODING_UTF_16_ENDIAN (coding)
-             = endian = utf_16_little_endian;
-         else
-           {
-             CODING_UTF_16_ENDIAN (coding)
-               = endian = utf_16_big_endian;
-             src = src_base;
-           }
+         /* The first two bytes are not BOM.  Treat them as bytes
+            for a normal character.  */
+         src = src_base;
+         coding->errors++;
        }
-      CODING_UTF_16_BOM (coding) = utf_16_with_bom;
+      CODING_UTF_16_BOM (coding) = utf_16_without_bom;
+    }
+  else if (bom == utf_16_detect_bom)
+    {
+      /* We have already tried to detect BOM and failed in
+        detect_coding.  */
+      CODING_UTF_16_BOM (coding) = utf_16_without_bom;
     }
 
   while (1)
@@ -1397,13 +1466,13 @@ encode_coding_utf_16 (coding)
 
   CODING_GET_INFO (coding, attrs, eol_type, charset_list);
 
-  if (bom == utf_16_with_bom)
+  if (bom != utf_16_without_bom)
     {
       ASSURE_DESTINATION (safe_room);
       if (big_endian)
-       EMIT_TWO_BYTES (0xFF, 0xFE);
-      else
        EMIT_TWO_BYTES (0xFE, 0xFF);
+      else
+       EMIT_TWO_BYTES (0xFF, 0xFE);
       CODING_UTF_16_BOM (coding) = utf_16_without_bom;
     }
 
@@ -1516,18 +1585,11 @@ encode_coding_utf_16 (coding)
 
 char emacs_mule_bytes[256];
 
-/* Leading-code followed by extended leading-code.  */
-#define LEADING_CODE_PRIVATE_11        0x9A /* for private DIMENSION1 of 1-column */
-#define LEADING_CODE_PRIVATE_12        0x9B /* for private DIMENSION1 of 2-column */
-#define LEADING_CODE_PRIVATE_21        0x9C /* for private DIMENSION2 of 1-column */
-#define LEADING_CODE_PRIVATE_22        0x9D /* for private DIMENSION2 of 2-column */
-
-
 int
-emacs_mule_char (coding, src, nbytes, nchars)
+emacs_mule_char (coding, src, nbytes, nchars, id)
      struct coding_system *coding;
      unsigned char *src;
-     int *nbytes, *nchars;
+     int *nbytes, *nchars, *id;
 {
   unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
@@ -1548,8 +1610,8 @@ emacs_mule_char (coding, src, nbytes, nchars)
       break;
 
     case 3:
-      if (c == LEADING_CODE_PRIVATE_11
-         || c == LEADING_CODE_PRIVATE_12)
+      if (c == EMACS_MULE_LEADING_CODE_PRIVATE_11
+         || c == EMACS_MULE_LEADING_CODE_PRIVATE_12)
        {
          ONE_MORE_BYTE (c);
          if (! (charset = emacs_mule_charset[c]))
@@ -1580,9 +1642,8 @@ emacs_mule_char (coding, src, nbytes, nchars)
 
     case 1:
       code = c;
-      charset = CHARSET_FROM_ID (ASCII_BYTE_P (code) ? charset_ascii
-                                : code < 0xA0 ? charset_8_bit_control
-                                : charset_8_bit_graphic);
+      charset = CHARSET_FROM_ID (ASCII_BYTE_P (code)
+                                ? charset_ascii : charset_eight_bit);
       break;
 
     default:
@@ -1593,6 +1654,8 @@ emacs_mule_char (coding, src, nbytes, nchars)
     goto invalid_code;
   *nbytes = src - src_base;
   *nchars = consumed_chars;
+  if (id)
+    *id = charset->id;
   return c;
 
  no_more_source:
@@ -1604,12 +1667,13 @@ emacs_mule_char (coding, src, nbytes, nchars)
 
 
 /* See the above "GENERAL NOTES on `detect_coding_XXX ()' functions".
-   Check if a text is encoded in `emacs-mule'.  */
+   Check if a text is encoded in `emacs-mule'.  If it is, return 1,
+   else return 0.  */
 
 static int
-detect_coding_emacs_mule (coding, mask)
+detect_coding_emacs_mule (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source, *src_base = src;
   unsigned char *src_end = coding->source + coding->src_bytes;
@@ -1617,13 +1681,17 @@ detect_coding_emacs_mule (coding, mask)
   int consumed_chars = 0;
   int c;
   int found = 0;
+  int incomplete;
 
+  detect_info->checked |= CATEGORY_MASK_EMACS_MULE;
   /* A coding system of this category is always ASCII compatible.  */
   src += coding->head_ascii;
 
   while (1)
     {
+      incomplete = 0;
       ONE_MORE_BYTE (c);
+      incomplete = 1;
 
       if (c == 0x80)
        {
@@ -1643,7 +1711,7 @@ detect_coding_emacs_mule (coding, mask)
 
          if (src - src_base <= 4)
            break;
-         found = 1;
+         found = CATEGORY_MASK_EMACS_MULE;
          if (c == 0x80)
            goto repeat;
        }
@@ -1665,16 +1733,19 @@ detect_coding_emacs_mule (coding, mask)
          while (c >= 0xA0);
          if (src - src_base != emacs_mule_bytes[*src_base])
            break;
-         found = 1;
+         found = CATEGORY_MASK_EMACS_MULE;
        }
     }
-  *mask &= ~CATEGORY_MASK_EMACS_MULE;
+  detect_info->rejected |= CATEGORY_MASK_EMACS_MULE;
   return 0;
 
  no_more_source:
-  if (!found)
-    return 0;
-  *mask &= CATEGORY_MASK_EMACS_MULE;
+  if (incomplete && coding->mode & CODING_MODE_LAST_BLOCK)
+    {
+      detect_info->rejected |= CATEGORY_MASK_EMACS_MULE;
+      return 0;
+    }
+  detect_info->found |= found;
   return 1;
 }
 
@@ -1696,7 +1767,7 @@ detect_coding_emacs_mule (coding, mask)
                                                                \
       if (src == src_end)                                      \
        break;                                                  \
-      c = emacs_mule_char (coding, src, &nbytes, &nchars);     \
+      c = emacs_mule_char (coding, src, &nbytes, &nchars, NULL);\
       if (c < 0)                                               \
        {                                                       \
          if (c == -2)                                          \
@@ -1753,16 +1824,6 @@ detect_coding_emacs_mule (coding, mask)
   } while (0)
 
 
-#define ADD_COMPOSITION_DATA(buf, method, nchars)      \
-  do {                                                 \
-    *buf++ = -5;                                       \
-    *buf++ = coding->produced_char + char_offset;      \
-    *buf++ = CODING_ANNOTATE_COMPOSITION_MASK;         \
-    *buf++ = method;                                   \
-    *buf++ = nchars;                                   \
-  } while (0)
-
-
 #define DECODE_EMACS_MULE_21_COMPOSITION(c)                            \
   do {                                                                 \
     /* Emacs 21 style format.  The first three bytes at SRC are                \
@@ -1771,6 +1832,7 @@ detect_coding_emacs_mule (coding, mask)
        number of characters composed by this composition.  */          \
     enum composition_method method = c - 0xF2;                         \
     int *charbuf_base = charbuf;                                       \
+    int from, to;                                                      \
     int consumed_chars_limit;                                          \
     int nbytes, nchars;                                                        \
                                                                        \
@@ -1780,7 +1842,9 @@ detect_coding_emacs_mule (coding, mask)
       goto invalid_code;                                               \
     ONE_MORE_BYTE (c);                                                 \
     nchars = c - 0xA0;                                                 \
-    ADD_COMPOSITION_DATA (charbuf, method, nchars);                    \
+    from = coding->produced + char_offset;                             \
+    to = from + nchars;                                                        \
+    ADD_COMPOSITION_DATA (charbuf, from, to, method);                  \
     consumed_chars_limit = consumed_chars_base + nbytes;               \
     if (method != COMPOSITION_RELATIVE)                                        \
       {                                                                        \
@@ -1804,9 +1868,11 @@ detect_coding_emacs_mule (coding, mask)
   do {                                                         \
     /* Emacs 20 style format for relative composition.  */     \
     /* Store multibyte form of characters to be composed.  */  \
+    enum composition_method method = COMPOSITION_RELATIVE;     \
     int components[MAX_COMPOSITION_COMPONENTS * 2 - 1];                \
     int *buf = components;                                     \
     int i, j;                                                  \
+    int from, to;                                              \
                                                                \
     src = src_base;                                            \
     ONE_MORE_BYTE (c);         /* skip 0x80 */                 \
@@ -1814,7 +1880,9 @@ detect_coding_emacs_mule (coding, mask)
       DECODE_EMACS_MULE_COMPOSITION_CHAR (buf);                        \
     if (i < 2)                                                 \
       goto invalid_code;                                       \
-    ADD_COMPOSITION_DATA (charbuf, COMPOSITION_RELATIVE, i);   \
+    from = coding->produced_char + char_offset;                        \
+    to = from + i;                                             \
+    ADD_COMPOSITION_DATA (charbuf, from, to, method);          \
     for (j = 0; j < i; j++)                                    \
       *charbuf++ = components[j];                              \
   } while (0)
@@ -1824,9 +1892,11 @@ detect_coding_emacs_mule (coding, mask)
   do {                                                         \
     /* Emacs 20 style format for rule-base composition.  */    \
     /* Store multibyte form of characters to be composed.  */  \
+    enum composition_method method = COMPOSITION_WITH_RULE;    \
     int components[MAX_COMPOSITION_COMPONENTS * 2 - 1];                \
     int *buf = components;                                     \
     int i, j;                                                  \
+    int from, to;                                              \
                                                                \
     DECODE_EMACS_MULE_COMPOSITION_CHAR (buf);                  \
     for (i = 0; i < MAX_COMPOSITION_COMPONENTS; i++)           \
@@ -1838,7 +1908,9 @@ detect_coding_emacs_mule (coding, mask)
       goto invalid_code;                                       \
     if (charbuf + i + (i / 2) + 1 < charbuf_end)               \
       goto no_more_source;                                     \
-    ADD_COMPOSITION_DATA (buf, COMPOSITION_WITH_RULE, i);      \
+    from = coding->produced_char + char_offset;                        \
+    to = from + i;                                             \
+    ADD_COMPOSITION_DATA (buf, from, to, method);              \
     for (j = 0; j < i; j++)                                    \
       *charbuf++ = components[j];                              \
     for (j = 0; j < i; j += 2)                                 \
@@ -1854,11 +1926,13 @@ decode_coding_emacs_mule (coding)
   unsigned char *src_end = coding->source + coding->src_bytes;
   unsigned char *src_base;
   int *charbuf = coding->charbuf;
-  int *charbuf_end = charbuf + coding->charbuf_size;
+  int *charbuf_end = charbuf + coding->charbuf_size - MAX_ANNOTATION_LENGTH;
   int consumed_chars = 0, consumed_chars_base;
-  int char_offset = 0;
   int multibytep = coding->src_multibyte;
   Lisp_Object attrs, eol_type, charset_list;
+  int char_offset = coding->produced_char;
+  int last_offset = char_offset;
+  int last_id = charset_ascii;
 
   CODING_GET_INFO (coding, attrs, eol_type, charset_list);
 
@@ -1881,7 +1955,10 @@ decode_coding_emacs_mule (coding)
              if (EQ (eol_type, Qdos))
                {
                  if (src == src_end)
-                   goto no_more_source;
+                   {
+                     coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+                     goto no_more_source;
+                   }
                  if (*src == '\n')
                    ONE_MORE_BYTE (c);
                }
@@ -1893,8 +1970,6 @@ decode_coding_emacs_mule (coding)
        }
       else if (c == 0x80)
        {
-         if (charbuf + 5 + (MAX_COMPOSITION_COMPONENTS * 2) - 1 > charbuf_end)
-           break;
          ONE_MORE_BYTE (c);
          if (c - 0xF2 >= COMPOSITION_RELATIVE
              && c - 0xF2 <= COMPOSITION_WITH_RULE_ALTCHARS)
@@ -1905,20 +1980,28 @@ decode_coding_emacs_mule (coding)
            DECODE_EMACS_MULE_20_RULEBASE_COMPOSITION (c);
          else
            goto invalid_code;
-         coding->annotated = 1;
        }
       else if (c < 0xA0 && emacs_mule_bytes[c] > 1)
        {
          int nbytes, nchars;
+         int id;
+
          src = src_base;
          consumed_chars = consumed_chars_base;
-         c = emacs_mule_char (coding, src, &nbytes, &nchars);
+         c = emacs_mule_char (coding, src, &nbytes, &nchars, &id);
          if (c < 0)
            {
              if (c == -2)
                break;
              goto invalid_code;
            }
+         if (last_id != id)
+           {
+             if (last_id != charset_ascii)
+               ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
+             last_id = id;
+             last_offset = char_offset;
+           }
          *charbuf++ = c;
          src += nbytes;
          consumed_chars += nchars;
@@ -1931,10 +2014,13 @@ decode_coding_emacs_mule (coding)
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
       *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      char_offset++;
       coding->errors++;
     }
 
  no_more_source:
+  if (last_id != charset_ascii)
+    ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
   coding->consumed_char += consumed_chars_base;
   coding->consumed = src_base - coding->source;
   coding->charbuf_used = charbuf - coding->charbuf;
@@ -1969,6 +2055,7 @@ encode_coding_emacs_mule (coding)
   int produced_chars = 0;
   Lisp_Object attrs, eol_type, charset_list;
   int c;
+  int preferred_charset_id = -1;
 
   CODING_GET_INFO (coding, attrs, eol_type, charset_list);
 
@@ -1976,8 +2063,36 @@ encode_coding_emacs_mule (coding)
     {
       ASSURE_DESTINATION (safe_room);
       c = *charbuf++;
+
+      if (c < 0)
+       {
+         /* Handle an annotation.  */
+         switch (*charbuf)
+           {
+           case CODING_ANNOTATE_COMPOSITION_MASK:
+             /* Not yet implemented.  */
+             break;
+           case CODING_ANNOTATE_CHARSET_MASK:
+             preferred_charset_id = charbuf[3];
+             if (preferred_charset_id >= 0
+                 && NILP (Fmemq (make_number (preferred_charset_id),
+                                 charset_list)))
+               preferred_charset_id = -1;
+             break;
+           default:
+             abort ();
+           }
+         charbuf += -c - 1;
+         continue;
+       }
+
       if (ASCII_CHAR_P (c))
        EMIT_ONE_ASCII_BYTE (c);
+      else if (CHAR_BYTE8_P (c))
+       {
+         c = CHAR_TO_BYTE8 (c);
+         EMIT_ONE_BYTE (c);
+       }
       else
        {
          struct charset *charset;
@@ -1986,7 +2101,14 @@ encode_coding_emacs_mule (coding)
          int emacs_mule_id;
          unsigned char leading_codes[2];
 
-         charset = char_charset (c, charset_list, &code);
+         if (preferred_charset_id >= 0)
+           {
+             charset = CHARSET_FROM_ID (preferred_charset_id);
+             if (! CHAR_CHARSET_P (c, charset))
+               charset = char_charset (c, charset_list, NULL);
+           }
+         else
+           charset = char_charset (c, charset_list, &code);
          if (! charset)
            {
              c = coding->default_char;
@@ -2023,21 +2145,23 @@ encode_coding_emacs_mule (coding)
 
 /* The following note describes the coding system ISO2022 briefly.
    Since the intention of this note is to help understand the
-   functions in this file, some parts are NOT ACCURATE or OVERLY
+   functions in this file, some parts are NOT ACCURATE or are OVERLY
    SIMPLIFIED.  For thorough understanding, please refer to the
-   original document of ISO2022.
+   original document of ISO2022.  This is equivalent to the standard
+   ECMA-35, obtainable from <URL:http://www.ecma.ch/> (*).
 
    ISO2022 provides many mechanisms to encode several character sets
-   in 7-bit and 8-bit environments.  For 7-bite environments, all text
+   in 7-bit and 8-bit environments.  For 7-bit environments, all text
    is encoded using bytes less than 128.  This may make the encoded
    text a little bit longer, but the text passes more easily through
-   several gateways, some of which strip off MSB (Most Signigant Bit).
+   several types of gateway, some of which strip off the MSB (Most
+   Significant Bit).
 
-   There are two kinds of character sets: control character set and
-   graphic character set.  The former contains control characters such
+   There are two kinds of character sets: control character sets and
+   graphic character sets.  The former contain control characters such
    as `newline' and `escape' to provide control functions (control
    functions are also provided by escape sequences).  The latter
-   contains graphic characters such as 'A' and '-'.  Emacs recognizes
+   contain graphic characters such as 'A' and '-'.  Emacs recognizes
    two control character sets and many graphic character sets.
 
    Graphic character sets are classified into one of the following
@@ -2049,14 +2173,14 @@ encode_coding_emacs_mule (coding)
    - DIMENSION2_CHARS96
 
    In addition, each character set is assigned an identification tag,
-   unique for each set, called "final character" (denoted as <F>
+   unique for each set, called the "final character" (denoted as <F>
    hereafter).  The <F> of each character set is decided by ECMA(*)
    when it is registered in ISO.  The code range of <F> is 0x30..0x7F
    (0x30..0x3F are for private use only).
 
    Note (*): ECMA = European Computer Manufacturers Association
 
-   Here are examples of graphic character set [NAME(<F>)]:
+   Here are examples of graphic character sets [NAME(<F>)]:
        o DIMENSION1_CHARS94 -- ASCII('B'), right-half-of-JISX0201('I'), ...
        o DIMENSION1_CHARS96 -- right-half-of-ISO8859-1('A'), ...
        o DIMENSION2_CHARS94 -- GB2312('A'), JISX0208('B'), ...
@@ -2149,11 +2273,11 @@ encode_coding_emacs_mule (coding)
    Note (**): If <F> is '@', 'A', or 'B', the intermediate character
    '(' must be omitted.  We refer to this as "short-form" hereafter.
 
-   Now you may notice that there are a lot of ways for encoding the
+   Now you may notice that there are a lot of ways of encoding the
    same multilingual text in ISO2022.  Actually, there exist many
    coding systems such as Compound Text (used in X11's inter client
-   communication, ISO-2022-JP (used in Japanese internet), ISO-2022-KR
-   (used in Korean internet), EUC (Extended UNIX Code, used in Asian
+   communication, ISO-2022-JP (used in Japanese Internet), ISO-2022-KR
+   (used in Korean Internet), EUC (Extended UNIX Code, used in Asian
    localized platforms), and all of these are variants of ISO2022.
 
    In addition to the above, Emacs handles two more kinds of escape
@@ -2175,19 +2299,19 @@ encode_coding_emacs_mule (coding)
        o ESC '3' -- start relative composition with alternate chars  (**)
        o ESC '4' -- start rule-base composition with alternate chars  (**)
   Since these are not standard escape sequences of any ISO standard,
-  the use of them for these meaning is restricted to Emacs only.
+  the use of them with these meanings is restricted to Emacs only.
 
-  (*) This form is used only in Emacs 20.5 and the older versions,
-  but the newer versions can safely decode it.
-  (**) This form is used only in Emacs 21.1 and the newer versions,
-  and the older versions can't decode it.
+  (*) This form is used only in Emacs 20.7 and older versions,
+  but newer versions can safely decode it.
+  (**) This form is used only in Emacs 21.1 and newer versions,
+  and older versions can't decode it.
 
-  Here's a list of examples usages of these composition escape
+  Here's a list of example usages of these composition escape
   sequences (categorized by `enum composition_method').
 
   COMPOSITION_RELATIVE:
        ESC 0 CHAR [ CHAR ] ESC 1
-  COMPOSITOIN_WITH_RULE:
+  COMPOSITION_WITH_RULE:
        ESC 2 CHAR [ RULE CHAR ] ESC 1
   COMPOSITION_WITH_ALTCHARS:
        ESC 3 ALTCHAR [ ALTCHAR ] ESC 0 CHAR [ CHAR ] ESC 1
@@ -2205,7 +2329,8 @@ enum iso_code_class_type iso_code_class[256];
   (CODING_ISO_INITIAL (&coding_categories[category], 1) >= 0)
 
 static void
-setup_iso_safe_charsets (Lisp_Object attrs)
+setup_iso_safe_charsets (attrs)
+     Lisp_Object attrs;
 {
   Lisp_Object charset_list, safe_charsets;
   Lisp_Object request;
@@ -2250,7 +2375,7 @@ setup_iso_safe_charsets (Lisp_Object attrs)
 
       id = XCAR (tail);
       charset = CHARSET_FROM_ID (XINT (id));
-      reg = Fcdr (Fassq (request, id));
+      reg = Fcdr (Fassq (id, request));
       if (! NILP (reg))
        XSTRING (safe_charsets)->data[XINT (id)] = XINT (reg);
       else if (charset->iso_chars_96)
@@ -2269,32 +2394,26 @@ setup_iso_safe_charsets (Lisp_Object attrs)
 
 
 /* See the above "GENERAL NOTES on `detect_coding_XXX ()' functions".
-   Check if a text is encoded in ISO2022.  If it is, returns an
-   integer in which appropriate flag bits any of:
-       CATEGORY_MASK_ISO_7
-       CATEGORY_MASK_ISO_7_TIGHT
-       CATEGORY_MASK_ISO_8_1
-       CATEGORY_MASK_ISO_8_2
-       CATEGORY_MASK_ISO_7_ELSE
-       CATEGORY_MASK_ISO_8_ELSE
-   are set.  If a code which should never appear in ISO2022 is found,
-   returns 0.  */
+   Check if a text is encoded in one of ISO-2022 based codig systems.
+   If it is, return 1, else return 0.  */
 
 static int
-detect_coding_iso_2022 (coding, mask)
+detect_coding_iso_2022 (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source, *src_base = src;
   unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
-  int mask_iso = CATEGORY_MASK_ISO;
-  int mask_found = 0, mask_8bit_found = 0;
-  int reg[4], shift_out = 0, single_shifting = 0;
+  int single_shifting = 0;
   int id;
   int c, c1;
   int consumed_chars = 0;
   int i;
+  int rejected = 0;
+  int found = 0;
+
+  detect_info->checked |= CATEGORY_MASK_ISO;
 
   for (i = coding_category_iso_7; i <= coding_category_iso_8_else; i++)
     {
@@ -2313,8 +2432,7 @@ detect_coding_iso_2022 (coding, mask)
   /* A coding system of this category is always ASCII compatible.  */
   src += coding->head_ascii;
 
-  reg[0] = charset_ascii, reg[1] = reg[2] = reg[3] = -1;
-  while (mask_iso && src < src_end)
+  while (rejected != CATEGORY_MASK_ISO)
     {
       ONE_MORE_BYTE (c);
       switch (c)
@@ -2332,7 +2450,6 @@ detect_coding_iso_2022 (coding, mask)
                  || (id = iso_charset_table[0][c >= ','][c1]) < 0)
                /* Invalid designation sequence.  Just ignore.  */
                break;
-             reg[(c - '(') % 4] = id;
            }
          else if (c == '$')
            {
@@ -2340,7 +2457,7 @@ detect_coding_iso_2022 (coding, mask)
              ONE_MORE_BYTE (c);
              if (c >= '@' && c <= 'B')
                /* Designation for JISX0208.1978, GB2312, or JISX0208.  */
-               reg[0] = id = iso_charset_table[1][0][c];
+               id = iso_charset_table[1][0][c];
              else if (c >= '(' && c <= '/')
                {
                  ONE_MORE_BYTE (c1);
@@ -2348,115 +2465,86 @@ detect_coding_iso_2022 (coding, mask)
                      || (id = iso_charset_table[1][c >= ','][c1]) < 0)
                    /* Invalid designation sequence.  Just ignore.  */
                    break;
-                 reg[(c - '(') % 4] = id;
                }
              else
-               /* Invalid designation sequence.  Just ignore.  */
+               /* Invalid designation sequence.  Just ignore it.  */
                break;
            }
          else if (c == 'N' || c == 'O')
            {
              /* ESC <Fe> for SS2 or SS3.  */
-             mask_iso &= CATEGORY_MASK_ISO_7_ELSE;
+             single_shifting = 1;
+             rejected |= CATEGORY_MASK_ISO_7BIT | CATEGORY_MASK_ISO_8BIT;
              break;
            }
          else if (c >= '0' && c <= '4')
            {
              /* ESC <Fp> for start/end composition.  */
-             mask_found |= CATEGORY_MASK_ISO;
+             found |= CATEGORY_MASK_ISO;
              break;
            }
          else
            {
-             /* Invalid escape sequence.  */
-             mask_iso &= ~CATEGORY_MASK_ISO_ESCAPE;
+             /* Invalid escape sequence.  Just ignore it.  */
              break;
            }
 
          /* We found a valid designation sequence for CHARSET.  */
-         mask_iso &= ~CATEGORY_MASK_ISO_8BIT;
+         rejected |= CATEGORY_MASK_ISO_8BIT;
          if (SAFE_CHARSET_P (&coding_categories[coding_category_iso_7],
                              id))
-           mask_found |= CATEGORY_MASK_ISO_7;
+           found |= CATEGORY_MASK_ISO_7;
          else
-           mask_iso &= ~CATEGORY_MASK_ISO_7;
+           rejected |= CATEGORY_MASK_ISO_7;
          if (SAFE_CHARSET_P (&coding_categories[coding_category_iso_7_tight],
                              id))
-           mask_found |= CATEGORY_MASK_ISO_7_TIGHT;
+           found |= CATEGORY_MASK_ISO_7_TIGHT;
          else
-           mask_iso &= ~CATEGORY_MASK_ISO_7_TIGHT;
+           rejected |= CATEGORY_MASK_ISO_7_TIGHT;
          if (SAFE_CHARSET_P (&coding_categories[coding_category_iso_7_else],
                              id))
-           mask_found |= CATEGORY_MASK_ISO_7_ELSE;
+           found |= CATEGORY_MASK_ISO_7_ELSE;
          else
-           mask_iso &= ~CATEGORY_MASK_ISO_7_ELSE;
+           rejected |= CATEGORY_MASK_ISO_7_ELSE;
          if (SAFE_CHARSET_P (&coding_categories[coding_category_iso_8_else],
                              id))
-           mask_found |= CATEGORY_MASK_ISO_8_ELSE;
+           found |= CATEGORY_MASK_ISO_8_ELSE;
          else
-           mask_iso &= ~CATEGORY_MASK_ISO_8_ELSE;
+           rejected |= CATEGORY_MASK_ISO_8_ELSE;
          break;
 
        case ISO_CODE_SO:
-         if (inhibit_iso_escape_detection)
-           break;
-         single_shifting = 0;
-         if (shift_out == 0
-             && (reg[1] >= 0
-                 || SHIFT_OUT_OK (coding_category_iso_7_else)
-                 || SHIFT_OUT_OK (coding_category_iso_8_else)))
-           {
-             /* Locking shift out.  */
-             mask_iso &= ~CATEGORY_MASK_ISO_7BIT;
-             mask_found |= CATEGORY_MASK_ISO_ELSE;
-           }
-         break;
-         
        case ISO_CODE_SI:
+         /* Locking shift out/in.  */
          if (inhibit_iso_escape_detection)
            break;
          single_shifting = 0;
-         if (shift_out == 1)
-           {
-             /* Locking shift in.  */
-             mask_iso &= ~CATEGORY_MASK_ISO_7BIT;
-             mask_found |= CATEGORY_MASK_ISO_ELSE;
-           }
+         rejected |= CATEGORY_MASK_ISO_7BIT | CATEGORY_MASK_ISO_8BIT;
+         found |= CATEGORY_MASK_ISO_ELSE;
          break;
-
+         
        case ISO_CODE_CSI:
+         /* Control sequence introducer.  */
          single_shifting = 0;
+         rejected |= CATEGORY_MASK_ISO_7BIT | CATEGORY_MASK_ISO_7_ELSE;
+         found |= CATEGORY_MASK_ISO_8_ELSE;
+         goto check_extra_latin;
+
+
        case ISO_CODE_SS2:
        case ISO_CODE_SS3:
-         {
-           int newmask = CATEGORY_MASK_ISO_8_ELSE;
-
-           if (inhibit_iso_escape_detection)
-             break;
-           if (c != ISO_CODE_CSI)
-             {
-               if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_1])
-                   & CODING_ISO_FLAG_SINGLE_SHIFT)
-                 newmask |= CATEGORY_MASK_ISO_8_1;
-               if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_2])
-                   & CODING_ISO_FLAG_SINGLE_SHIFT)
-                 newmask |= CATEGORY_MASK_ISO_8_2;
-               single_shifting = 1;
-             }
-           if (VECTORP (Vlatin_extra_code_table)
-               && !NILP (XVECTOR (Vlatin_extra_code_table)->contents[c]))
-             {
-               if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_1])
-                   & CODING_ISO_FLAG_LATIN_EXTRA)
-                 newmask |= CATEGORY_MASK_ISO_8_1;
-               if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_2])
-                   & CODING_ISO_FLAG_LATIN_EXTRA)
-                 newmask |= CATEGORY_MASK_ISO_8_2;
-             }
-           mask_iso &= newmask;
-           mask_found |= newmask;
-         }
-         break;
+         /* Single shift.   */
+         if (inhibit_iso_escape_detection)
+           break;
+         single_shifting = 1;
+         rejected |= CATEGORY_MASK_ISO_7BIT;
+         if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_1])
+             & CODING_ISO_FLAG_SINGLE_SHIFT)
+           found |= CATEGORY_MASK_ISO_8_1;
+         if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_2])
+             & CODING_ISO_FLAG_SINGLE_SHIFT)
+           found |= CATEGORY_MASK_ISO_8_2;
+         goto check_extra_latin;
 
        default:
          if (c < 0x80)
@@ -2464,39 +2552,16 @@ detect_coding_iso_2022 (coding, mask)
              single_shifting = 0;
              break;
            }
-         else if (c < 0xA0)
+         if (c >= 0xA0)
            {
-             single_shifting = 0;
-             mask_8bit_found = 1;
-             if (VECTORP (Vlatin_extra_code_table)
-                 && !NILP (XVECTOR (Vlatin_extra_code_table)->contents[c]))
-               {
-                 int newmask = 0;
-
-                 if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_1])
-                     & CODING_ISO_FLAG_LATIN_EXTRA)
-                   newmask |= CATEGORY_MASK_ISO_8_1;
-                 if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_2])
-                     & CODING_ISO_FLAG_LATIN_EXTRA)
-                   newmask |= CATEGORY_MASK_ISO_8_2;
-                 mask_iso &= newmask;
-                 mask_found |= newmask;
-               }
-             else
-               return 0;
-           }
-         else
-           {
-             mask_iso &= ~(CATEGORY_MASK_ISO_7BIT
-                           | CATEGORY_MASK_ISO_7_ELSE);
-             mask_found |= CATEGORY_MASK_ISO_8_1;
-             mask_8bit_found = 1;
+             rejected |= CATEGORY_MASK_ISO_7BIT | CATEGORY_MASK_ISO_7_ELSE;
+             found |= CATEGORY_MASK_ISO_8_1;
              /* Check the length of succeeding codes of the range
-                 0xA0..0FF.  If the byte length is odd, we exclude
-                 CATEGORY_MASK_ISO_8_2.  We can check this only
-                 when we are not single shifting.  */
-             if (!single_shifting
-                 && mask_iso & CATEGORY_MASK_ISO_8_2)
+                 0xA0..0FF.  If the byte length is even, we include
+                 CATEGORY_MASK_ISO_8_2 in `found'.  We can check this
+                 only when we are not single shifting.  */
+             if (! single_shifting
+                 && ! (rejected & CATEGORY_MASK_ISO_8_2))
                {
                  int i = 1;
                  while (src < src_end)
@@ -2508,25 +2573,38 @@ detect_coding_iso_2022 (coding, mask)
                    }
 
                  if (i & 1 && src < src_end)
-                   mask_iso &= ~CATEGORY_MASK_ISO_8_2;
+                   rejected |= CATEGORY_MASK_ISO_8_2;
                  else
-                   mask_found |= CATEGORY_MASK_ISO_8_2;
+                   found |= CATEGORY_MASK_ISO_8_2;
                }
+             break;
            }
-         break;
+       check_extra_latin:
+         single_shifting = 0;
+         if (! VECTORP (Vlatin_extra_code_table)
+             || NILP (XVECTOR (Vlatin_extra_code_table)->contents[c]))
+           {
+             rejected = CATEGORY_MASK_ISO;
+             break;
+           }
+         if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_1])
+             & CODING_ISO_FLAG_LATIN_EXTRA)
+           found |= CATEGORY_MASK_ISO_8_1;
+         else
+           rejected |= CATEGORY_MASK_ISO_8_1;
+         if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_2])
+             & CODING_ISO_FLAG_LATIN_EXTRA)
+           found |= CATEGORY_MASK_ISO_8_2;
+         else
+           rejected |= CATEGORY_MASK_ISO_8_2;
        }
     }
+  detect_info->rejected |= CATEGORY_MASK_ISO;
+  return 0;
+
  no_more_source:
-  if (!mask_iso)
-    {
-      *mask &= ~CATEGORY_MASK_ISO;
-      return 0;
-    }
-  if (!mask_found)
-    return 0;
-  *mask &= mask_iso & mask_found; 
-  if (! mask_8bit_found)
-    *mask &= ~(CATEGORY_MASK_ISO_8BIT | CATEGORY_MASK_ISO_8_ELSE);
+  detect_info->rejected |= rejected;
+  detect_info->found |= (found & ~rejected);
   return 1;
 }
 
@@ -2544,6 +2622,16 @@ detect_coding_iso_2022 (coding, mask)
        goto invalid_code;                                              \
       }                                                                        \
     prev = CODING_ISO_DESIGNATION (coding, reg);                       \
+    if (id == charset_jisx0201_roman)                                  \
+      {                                                                        \
+       if (CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_USE_ROMAN)      \
+         id = charset_ascii;                                           \
+      }                                                                        \
+    else if (id == charset_jisx0208_1978)                              \
+      {                                                                        \
+       if (CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_USE_OLDJIS)     \
+         id = charset_jisx0208;                                        \
+      }                                                                        \
     CODING_ISO_DESIGNATION (coding, reg) = id;                         \
     /* If there was an invalid designation to REG previously, and this \
        designation is ASCII to REG, we should keep this designation    \
@@ -2632,8 +2720,10 @@ detect_coding_iso_2022 (coding, mask)
                  : (component_idx + 1) / 2);                           \
     int i;                                                             \
     int *saved_charbuf = charbuf;                                      \
+    int from = coding->produced_char + char_offset;                    \
+    int to = from + nchars;                                            \
                                                                        \
-    ADD_COMPOSITION_DATA (charbuf, method, nchars);                    \
+    ADD_COMPOSITION_DATA (charbuf, from, to, method);                  \
     if (method != COMPOSITION_RELATIVE)                                        \
       {                                                                        \
        if (component_len == 0)                                         \
@@ -2690,9 +2780,9 @@ decode_coding_iso_2022 (coding)
   unsigned char *src_end = coding->source + coding->src_bytes;
   unsigned char *src_base;
   int *charbuf = coding->charbuf;
-  int *charbuf_end = charbuf + coding->charbuf_size - 4;
+  int *charbuf_end
+    = charbuf + coding->charbuf_size - 4 - MAX_ANNOTATION_LENGTH;
   int consumed_chars = 0, consumed_chars_base;
-  int char_offset = 0;
   int multibytep = coding->src_multibyte;
   /* Charsets invoked to graphic plane 0 and 1 respectively.  */
   int charset_id_0 = CODING_ISO_INVOKED_CHARSET (coding, 0);
@@ -2712,6 +2802,9 @@ decode_coding_iso_2022 (coding)
   int component_idx;
   int component_len;
   Lisp_Object attrs, eol_type, charset_list;
+  int char_offset = coding->produced_char;
+  int last_offset = char_offset;
+  int last_id = charset_ascii;
 
   CODING_GET_INFO (coding, attrs, eol_type, charset_list);
   setup_iso_safe_charsets (attrs);
@@ -2728,7 +2821,7 @@ decode_coding_iso_2022 (coding)
 
       ONE_MORE_BYTE (c1);
 
-      /* We produce no character or one character.  */
+      /* We produce at most one character.  */
       switch (iso_code_class [c1])
        {
        case ISO_0x20_or_0x7F:
@@ -2785,7 +2878,10 @@ decode_coding_iso_2022 (coding)
              if (EQ (eol_type, Qdos))
                {
                  if (src == src_end)
-                   goto no_more_source;
+                   {
+                     coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+                     goto no_more_source;
+                   }                 
                  if (*src == '\n')
                    ONE_MORE_BYTE (c1);
                }
@@ -2964,6 +3060,70 @@ decode_coding_iso_2022 (coding)
                }
              continue;
 
+           case '%':
+             ONE_MORE_BYTE (c1);
+             if (c1 == '/')
+               {
+                 /* CTEXT extended segment:
+                    ESC % / [0-4] M L --ENCODING-NAME-- \002 --BYTES--
+                    We keep these bytes as is for the moment.
+                    They may be decoded by post-read-conversion.  */
+                 int dim, M, L;
+                 int size;
+                 
+                 ONE_MORE_BYTE (dim);
+                 ONE_MORE_BYTE (M);
+                 ONE_MORE_BYTE (L);
+                 size = ((M - 128) * 128) + (L - 128);
+                 if (charbuf + 8 + size > charbuf_end)
+                   goto break_loop;
+                 *charbuf++ = ISO_CODE_ESC;
+                 *charbuf++ = '%';
+                 *charbuf++ = '/';
+                 *charbuf++ = dim;
+                 *charbuf++ = BYTE8_TO_CHAR (M);
+                 *charbuf++ = BYTE8_TO_CHAR (L);
+                 while (size-- > 0)
+                   {
+                     ONE_MORE_BYTE (c1);
+                     *charbuf++ = ASCII_BYTE_P (c1) ? c1 : BYTE8_TO_CHAR (c1);
+                   }
+               }
+             else if (c1 == 'G')
+               {
+                 /* XFree86 extension for embedding UTF-8 in CTEXT:
+                    ESC % G --UTF-8-BYTES-- ESC % @
+                    We keep these bytes as is for the moment.
+                    They may be decoded by post-read-conversion.  */
+                 int *p = charbuf;
+
+                 if (p + 6 > charbuf_end)
+                   goto break_loop;
+                 *p++ = ISO_CODE_ESC;
+                 *p++ = '%';
+                 *p++ = 'G';
+                 while (p < charbuf_end)
+                   {
+                     ONE_MORE_BYTE (c1);
+                     if (c1 == ISO_CODE_ESC
+                         && src + 1 < src_end
+                         && src[0] == '%'
+                         && src[1] == '@')
+                       break;
+                     *p++ = ASCII_BYTE_P (c1) ? c1 : BYTE8_TO_CHAR (c1);
+                   }
+                 if (p + 3 > charbuf_end)
+                   goto break_loop;
+                 *p++ = ISO_CODE_ESC;
+                 *p++ = '%';
+                 *p++ = '@';
+                 charbuf = p;
+               }
+             else
+               goto invalid_code;
+             continue;
+             break;
+
            default:
              if (! (CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_DESIGNATION))
                goto invalid_code;
@@ -2986,6 +3146,15 @@ decode_coding_iso_2022 (coding)
            }
        }
 
+      if (charset->id != charset_ascii
+         && last_id != charset->id)
+       {
+         if (last_id != charset_ascii)
+           ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
+         last_id = charset->id;
+         last_offset = char_offset;
+       }
+
       /* Now we know CHARSET and 1st position code C1 of a character.
          Produce a decoded character while getting 2nd position code
          C2 if necessary.  */
@@ -3017,6 +3186,7 @@ decode_coding_iso_2022 (coding)
                *charbuf++ = *src_base;
              else
                *charbuf++ = BYTE8_TO_CHAR (*src_base);
+             char_offset++;
            }
        }
       else if (composition_state == COMPOSING_NO)
@@ -3040,10 +3210,17 @@ decode_coding_iso_2022 (coding)
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
       *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      char_offset++;
       coding->errors++;
+      continue;
+
+    break_loop:
+      break;
     }
 
  no_more_source:
+  if (last_id != charset_ascii)
+    ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
   coding->consumed_char += consumed_chars_base;
   coding->consumed = src_base - coding->source;
   coding->charbuf_used = charbuf - coding->charbuf;
@@ -3085,7 +3262,7 @@ decode_coding_iso_2022 (coding)
     int c;                                                             \
                                                                        \
     if (CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_REVISION)          \
-      revision = XINT (CHARSET_ISO_REVISION (charset));                        \
+      revision = CHARSET_ISO_REVISION (charset);                       \
                                                                        \
     if (revision >= 0)                                                 \
       {                                                                        \
@@ -3183,6 +3360,14 @@ decode_coding_iso_2022 (coding)
 #define ENCODE_ISO_CHARACTER_DIMENSION1(charset, c1)                   \
   do {                                                                 \
     int id = CHARSET_ID (charset);                                     \
+                                                                       \
+    if ((CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_USE_ROMAN)                \
+       && id == charset_ascii)                                         \
+      {                                                                        \
+       id = charset_jisx0201_roman;                                    \
+       charset = CHARSET_FROM_ID (id);                                 \
+      }                                                                        \
+                                                                       \
     if (CODING_ISO_SINGLE_SHIFTING (coding))                           \
       {                                                                        \
        if (CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_SEVEN_BITS)     \
@@ -3219,6 +3404,14 @@ decode_coding_iso_2022 (coding)
 #define ENCODE_ISO_CHARACTER_DIMENSION2(charset, c1, c2)               \
   do {                                                                 \
     int id = CHARSET_ID (charset);                                     \
+                                                                       \
+    if ((CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_USE_OLDJIS)       \
+       && id == charset_jisx0208)                                      \
+      {                                                                        \
+       id = charset_jisx0208_1978;                                     \
+       charset = CHARSET_FROM_ID (id);                                 \
+      }                                                                        \
+                                                                       \
     if (CODING_ISO_SINGLE_SHIFTING (coding))                           \
       {                                                                        \
        if (CODING_ISO_FLAGS (coding) & CODING_ISO_FLAG_SEVEN_BITS)     \
@@ -3449,9 +3642,12 @@ encode_coding_iso_2022 (coding)
   Lisp_Object attrs, eol_type, charset_list;
   int ascii_compatible;
   int c;
+  int preferred_charset_id = -1;
 
   CODING_GET_INFO (coding, attrs, eol_type, charset_list);
   setup_iso_safe_charsets (attrs);
+  /* Charset list may have been changed.  */
+  charset_list = CODING_ATTR_CHARSET_LIST (attrs);             \
   coding->safe_charsets
     = (char *) XSTRING (CODING_ATTR_SAFE_CHARSETS(attrs))->data;
 
@@ -3474,6 +3670,28 @@ encode_coding_iso_2022 (coding)
 
       c = *charbuf++;
 
+      if (c < 0)
+       {
+         /* Handle an annotation.  */
+         switch (*charbuf)
+           {
+           case CODING_ANNOTATE_COMPOSITION_MASK:
+             /* Not yet implemented.  */
+             break;
+           case CODING_ANNOTATE_CHARSET_MASK:
+             preferred_charset_id = charbuf[3];
+             if (preferred_charset_id >= 0
+                 && NILP (Fmemq (make_number (preferred_charset_id),
+                                 charset_list)))
+               preferred_charset_id = -1;
+             break;
+           default:
+             abort ();
+           }
+         charbuf += -c - 1;
+         continue;
+       }
+
       /* Now encode the character C.  */
       if (c < 0x20 || c == 0x7F)
        {
@@ -3502,12 +3720,28 @@ encode_coding_iso_2022 (coding)
          if (ascii_compatible)
            EMIT_ONE_ASCII_BYTE (c);
          else
-           ENCODE_ISO_CHARACTER (CHARSET_FROM_ID (charset_ascii), c);
+           {
+             struct charset *charset = CHARSET_FROM_ID (charset_ascii);
+             ENCODE_ISO_CHARACTER (charset, c);
+           }
+       }
+      else if (CHAR_BYTE8_P (c))
+       {
+         c = CHAR_TO_BYTE8 (c);
+         EMIT_ONE_BYTE (c);
        }
       else
        {
-         struct charset *charset = char_charset (c, charset_list, NULL);
+         struct charset *charset;
 
+         if (preferred_charset_id >= 0)
+           {
+             charset = CHARSET_FROM_ID (preferred_charset_id);
+             if (! CHAR_CHARSET_P (c, charset))
+               charset = char_charset (c, charset_list, NULL);
+           }
+         else
+           charset = char_charset (c, charset_list, NULL);
          if (!charset)
            {
              if (coding->mode & CODING_MODE_SAFE_ENCODING)
@@ -3580,9 +3814,9 @@ encode_coding_iso_2022 (coding)
    CATEGORY_MASK_SJIS, else return 0.  */
 
 static int
-detect_coding_sjis (coding, mask)
+detect_coding_sjis (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source, *src_base = src;
   unsigned char *src_end = coding->source + coding->src_bytes;
@@ -3590,13 +3824,17 @@ detect_coding_sjis (coding, mask)
   int consumed_chars = 0;
   int found = 0;
   int c;
+  int incomplete;
 
+  detect_info->checked |= CATEGORY_MASK_SJIS;
   /* A coding system of this category is always ASCII compatible.  */
   src += coding->head_ascii;
 
   while (1)
     {
+      incomplete = 0;
       ONE_MORE_BYTE (c);
+      incomplete = 1;
       if (c < 0x80)
        continue;
       if ((c >= 0x81 && c <= 0x9F) || (c >= 0xE0 && c <= 0xEF))
@@ -3604,20 +3842,23 @@ detect_coding_sjis (coding, mask)
          ONE_MORE_BYTE (c);
          if (c < 0x40 || c == 0x7F || c > 0xFC)
            break;
-         found = 1;
+         found = CATEGORY_MASK_SJIS;
        }
       else if (c >= 0xA0 && c < 0xE0)
-       found = 1;
+       found = CATEGORY_MASK_SJIS;
       else
        break;
     }
-  *mask &= ~CATEGORY_MASK_SJIS;
+  detect_info->rejected |= CATEGORY_MASK_SJIS;
   return 0;
 
  no_more_source:
-  if (!found)
-    return 0;
-  *mask &= CATEGORY_MASK_SJIS;
+  if (incomplete && coding->mode & CODING_MODE_LAST_BLOCK)
+    {
+      detect_info->rejected |= CATEGORY_MASK_SJIS;
+      return 0;
+    }
+  detect_info->found |= found;
   return 1;
 }
 
@@ -3626,9 +3867,9 @@ detect_coding_sjis (coding, mask)
    CATEGORY_MASK_BIG5, else return 0.  */
 
 static int
-detect_coding_big5 (coding, mask)
+detect_coding_big5 (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source, *src_base = src;
   unsigned char *src_end = coding->source + coding->src_bytes;
@@ -3636,13 +3877,17 @@ detect_coding_big5 (coding, mask)
   int consumed_chars = 0;
   int found = 0;
   int c;
+  int incomplete;
 
+  detect_info->checked |= CATEGORY_MASK_BIG5;
   /* A coding system of this category is always ASCII compatible.  */
   src += coding->head_ascii;
 
   while (1)
     {
+      incomplete = 0;
       ONE_MORE_BYTE (c);
+      incomplete = 1;
       if (c < 0x80)
        continue;
       if (c >= 0xA1)
@@ -3650,18 +3895,21 @@ detect_coding_big5 (coding, mask)
          ONE_MORE_BYTE (c);
          if (c < 0x40 || (c >= 0x7F && c <= 0xA0))
            return 0;
-         found = 1;
+         found = CATEGORY_MASK_BIG5;
        }
       else
        break;
     }
-  *mask &= ~CATEGORY_MASK_BIG5;
+  detect_info->rejected |= CATEGORY_MASK_BIG5;
   return 0;
 
  no_more_source:
-  if (!found)
-    return 0;
-  *mask &= CATEGORY_MASK_BIG5;
+  if (incomplete && coding->mode & CODING_MODE_LAST_BLOCK)
+    {
+      detect_info->rejected |= CATEGORY_MASK_BIG5;
+      return 0;
+    }
+  detect_info->found |= found;
   return 1;
 }
 
@@ -3676,18 +3924,21 @@ decode_coding_sjis (coding)
   unsigned char *src_end = coding->source + coding->src_bytes;
   unsigned char *src_base;
   int *charbuf = coding->charbuf;
-  int *charbuf_end = charbuf + coding->charbuf_size;
+  int *charbuf_end = charbuf + coding->charbuf_size - MAX_ANNOTATION_LENGTH;
   int consumed_chars = 0, consumed_chars_base;
   int multibytep = coding->src_multibyte;
   struct charset *charset_roman, *charset_kanji, *charset_kana;
   Lisp_Object attrs, eol_type, charset_list, val;
+  int char_offset = coding->produced_char;
+  int last_offset = char_offset;
+  int last_id = charset_ascii;
 
   CODING_GET_INFO (coding, attrs, eol_type, charset_list);
 
   val = charset_list;
   charset_roman = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
-  charset_kanji = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
-  charset_kana = CHARSET_FROM_ID (XINT (XCAR (val)));
+  charset_kana = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
+  charset_kanji = CHARSET_FROM_ID (XINT (XCAR (val)));
 
   while (1)
     {
@@ -3706,7 +3957,10 @@ decode_coding_sjis (coding)
          if (EQ (eol_type, Qdos))
            {
              if (src == src_end)
-               goto no_more_source;
+               {
+                 coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+                 goto no_more_source;
+               }
              if (*src == '\n')
                ONE_MORE_BYTE (c);
            }
@@ -3734,12 +3988,24 @@ decode_coding_sjis (coding)
                  charset = charset_kanji;
                }
              else
-               /* SJIS -> JISX0201-Kana */
-               charset = charset_kana;
+               {
+                 /* SJIS -> JISX0201-Kana */
+                 c &= 0x7F;
+                 charset = charset_kana;
+               }
+           }
+         if (charset->id != charset_ascii
+             && last_id != charset->id)
+           {
+             if (last_id != charset_ascii)
+               ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
+             last_id = charset->id;
+             last_offset = char_offset;
            }
          CODING_DECODE_CHAR (coding, src, src_base, src_end, charset, c, c);
        }
       *charbuf++ = c;
+      char_offset++;
       continue;
 
     invalid_code:
@@ -3747,10 +4013,13 @@ decode_coding_sjis (coding)
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
       *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      char_offset++;
       coding->errors++;
     }
 
  no_more_source:
+  if (last_id != charset_ascii)
+    ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
   coding->consumed_char += consumed_chars_base;
   coding->consumed = src_base - coding->source;
   coding->charbuf_used = charbuf - coding->charbuf;
@@ -3764,11 +4033,14 @@ decode_coding_big5 (coding)
   unsigned char *src_end = coding->source + coding->src_bytes;
   unsigned char *src_base;
   int *charbuf = coding->charbuf;
-  int *charbuf_end = charbuf + coding->charbuf_size;
+  int *charbuf_end = charbuf + coding->charbuf_size - MAX_ANNOTATION_LENGTH;
   int consumed_chars = 0, consumed_chars_base;
   int multibytep = coding->src_multibyte;
   struct charset *charset_roman, *charset_big5;
   Lisp_Object attrs, eol_type, charset_list, val;
+  int char_offset = coding->produced_char;
+  int last_offset = char_offset;
+  int last_id = charset_ascii;
 
   CODING_GET_INFO (coding, attrs, eol_type, charset_list);
   val = charset_list;
@@ -3792,7 +4064,10 @@ decode_coding_big5 (coding)
          if (EQ (eol_type, Qdos))
            {
              if (src == src_end)
-               goto no_more_source;
+               {
+                 coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+                 goto no_more_source;
+               }
              if (*src == '\n')
                ONE_MORE_BYTE (c);
            }
@@ -3815,10 +4090,19 @@ decode_coding_big5 (coding)
              c = c << 8 | c1;
              charset = charset_big5;
            }
+         if (charset->id != charset_ascii
+             && last_id != charset->id)
+           {
+             if (last_id != charset_ascii)
+               ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
+             last_id = charset->id;
+             last_offset = char_offset;
+           }
          CODING_DECODE_CHAR (coding, src, src_base, src_end, charset, c, c);
        }
 
       *charbuf++ = c;
+      char_offset++;
       continue;
 
     invalid_code:
@@ -3826,10 +4110,13 @@ decode_coding_big5 (coding)
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
       *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      char_offset++;
       coding->errors++;
     }
 
  no_more_source:
+  if (last_id != charset_ascii)
+    ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
   coding->consumed_char += consumed_chars_base;
   coding->consumed = src_base - coding->source;
   coding->charbuf_used = charbuf - coding->charbuf;
@@ -3874,6 +4161,11 @@ encode_coding_sjis (coding)
       /* Now encode the character C.  */
       if (ASCII_CHAR_P (c) && ascii_compatible)
        EMIT_ONE_ASCII_BYTE (c);
+      else if (CHAR_BYTE8_P (c))
+       {
+         c = CHAR_TO_BYTE8 (c);
+         EMIT_ONE_BYTE (c);
+       }
       else
        {
          unsigned code;
@@ -3942,6 +4234,11 @@ encode_coding_big5 (coding)
       /* Now encode the character C.  */
       if (ASCII_CHAR_P (c) && ascii_compatible)
        EMIT_ONE_ASCII_BYTE (c);
+      else if (CHAR_BYTE8_P (c))
+       {
+         c = CHAR_TO_BYTE8 (c);
+         EMIT_ONE_BYTE (c);
+       }
       else
        {
          unsigned code;
@@ -3988,9 +4285,9 @@ encode_coding_big5 (coding)
    CATEGORY_MASK_CCL, else return 0.  */
 
 static int
-detect_coding_ccl (coding, mask)
+detect_coding_ccl (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source, *src_base = src;
   unsigned char *src_end = coding->source + coding->src_bytes;
@@ -4001,6 +4298,8 @@ detect_coding_ccl (coding, mask)
   int head_ascii = coding->head_ascii;
   Lisp_Object attrs;
 
+  detect_info->checked |= CATEGORY_MASK_CCL;
+
   coding = &coding_categories[coding_category_ccl];
   attrs = CODING_ID_ATTRS (coding->id);
   if (! NILP (CODING_ATTR_ASCII_COMPAT (attrs)))
@@ -4012,16 +4311,14 @@ detect_coding_ccl (coding, mask)
       ONE_MORE_BYTE (c);
       if (! valids[c])
        break;
-      if (!found && valids[c] > 1)
-       found = 1;
+      if ((valids[c] > 1))
+       found = CATEGORY_MASK_CCL;
     }
-  *mask &= ~CATEGORY_MASK_CCL;
+  detect_info->rejected |= CATEGORY_MASK_CCL;
   return 0;
 
  no_more_source:
-  if (!found)
-    return 0;
-  *mask &= CATEGORY_MASK_CCL;
+  detect_info->found |= found;
   return 1;
 }
 
@@ -4029,7 +4326,7 @@ static void
 decode_coding_ccl (coding)
      struct coding_system *coding;
 {
-  unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src = coding->source + coding->consumed;
   unsigned char *src_end = coding->source + coding->src_bytes;
   int *charbuf = coding->charbuf;
   int *charbuf_end = charbuf + coding->charbuf_size;
@@ -4038,12 +4335,14 @@ decode_coding_ccl (coding)
   struct ccl_program ccl;
   int source_charbuf[1024];
   int source_byteidx[1024];
+  Lisp_Object attrs, eol_type, charset_list;
 
+  CODING_GET_INFO (coding, attrs, eol_type, charset_list);
   setup_ccl_program (&ccl, CODING_CCL_DECODER (coding));
 
   while (src < src_end)
     {
-      unsigned char *p = src;
+      const unsigned char *p = src;
       int *source, *source_end;
       int i = 0;
 
@@ -4065,7 +4364,8 @@ decode_coding_ccl (coding)
       while (source < source_end)
        {
          ccl_driver (&ccl, source, charbuf,
-                     source_end - source, charbuf_end - charbuf);
+                     source_end - source, charbuf_end - charbuf,
+                     charset_list);
          source += ccl.consumed;
          charbuf += ccl.produced;
          if (ccl.status != CCL_STAT_SUSPEND_BY_DST)
@@ -4115,7 +4415,9 @@ encode_coding_ccl (coding)
   unsigned char *adjusted_dst_end = dst_end - 1;
   int destination_charbuf[1024];
   int i, produced_chars = 0;
+  Lisp_Object attrs, eol_type, charset_list;
 
+  CODING_GET_INFO (coding, attrs, eol_type, charset_list);
   setup_ccl_program (&ccl, CODING_CCL_ENCODER (coding));
 
   ccl.last_block = coding->mode & CODING_MODE_LAST_BLOCK;
@@ -4128,7 +4430,7 @@ encode_coding_ccl (coding)
        dst_bytes = 1024;
 
       ccl_driver (&ccl, charbuf, destination_charbuf,
-                 charbuf_end - charbuf, dst_bytes);
+                 charbuf_end - charbuf, dst_bytes, charset_list);
       charbuf += ccl.consumed;
       if (multibytep)
        for (i = 0; i < ccl.produced; i++)
@@ -4213,7 +4515,10 @@ encode_coding_raw_text (coding)
 
                CHAR_STRING_ADVANCE (c, p1);
                while (p0 < p1)
-                 EMIT_ONE_BYTE (*p0);
+                 {
+                   EMIT_ONE_BYTE (*p0);
+                   p0++;
+                 }
              }
          }
       else
@@ -4257,16 +4562,23 @@ encode_coding_raw_text (coding)
   return 0;
 }
 
+/* See the above "GENERAL NOTES on `detect_coding_XXX ()' functions".
+   Check if a text is encoded in a charset-based coding system.  If it
+   is, return 1, else return 0.  */
+
 static int
-detect_coding_charset (coding, mask)
+detect_coding_charset (coding, detect_info)
      struct coding_system *coding;
-     int *mask;
+     struct coding_detection_info *detect_info;
 {
   unsigned char *src = coding->source, *src_base = src;
   unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
   int consumed_chars = 0;
   Lisp_Object attrs, valids;
+  int found = 0;
+
+  detect_info->checked |= CATEGORY_MASK_CHARSET;
 
   coding = &coding_categories[coding_category_charset];
   attrs = CODING_ID_ATTRS (coding->id);
@@ -4282,12 +4594,14 @@ detect_coding_charset (coding, mask)
       ONE_MORE_BYTE (c);
       if (NILP (AREF (valids, c)))
        break;
+      if (c >= 0x80)
+       found = CATEGORY_MASK_CHARSET;
     }
-  *mask &= ~CATEGORY_MASK_CHARSET;
+  detect_info->rejected |= CATEGORY_MASK_CHARSET;
   return 0;
 
  no_more_source:
-  *mask &= CATEGORY_MASK_CHARSET;
+  detect_info->found |= found;
   return 1;
 }
 
@@ -4299,10 +4613,13 @@ decode_coding_charset (coding)
   unsigned char *src_end = coding->source + coding->src_bytes;
   unsigned char *src_base;
   int *charbuf = coding->charbuf;
-  int *charbuf_end = charbuf + coding->charbuf_size;
+  int *charbuf_end = charbuf + coding->charbuf_size - MAX_ANNOTATION_LENGTH;
   int consumed_chars = 0, consumed_chars_base;
   int multibytep = coding->src_multibyte;
   Lisp_Object attrs, eol_type, charset_list, valids;
+  int char_offset = coding->produced_char;
+  int last_offset = char_offset;
+  int last_id = charset_ascii;
 
   CODING_GET_INFO (coding, attrs, eol_type, charset_list);
   valids = AREF (attrs, coding_attr_charset_valids);
@@ -4324,8 +4641,12 @@ decode_coding_charset (coding)
             else.  */
          if (EQ (eol_type, Qdos))
            {
-             if (src < src_end
-                 && *src == '\n')
+             if (src == src_end)
+               {
+                 coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+                 goto no_more_source;
+               }
+             if (*src == '\n')
                ONE_MORE_BYTE (c);
            }
          else if (EQ (eol_type, Qmac))
@@ -4336,8 +4657,8 @@ decode_coding_charset (coding)
          Lisp_Object val;
          struct charset *charset;
          int dim;
-         unsigned code;
-         int c1;
+         int len = 1;
+         unsigned code = c;
 
          val = AREF (valids, c);
          if (NILP (val))
@@ -4346,21 +4667,11 @@ decode_coding_charset (coding)
            {
              charset = CHARSET_FROM_ID (XFASTINT (val));
              dim = CHARSET_DIMENSION (charset);
-             code = c;
-             if (dim > 1)
+             while (len < dim)
                {
-                 ONE_MORE_BYTE (c1);
-                 code = (code << 8) | c1;
-                 if (dim > 2)
-                   {
-                     ONE_MORE_BYTE (c1);
-                     code = (code << 8) | c1;
-                     if (dim > 3)
-                       {
-                         ONE_MORE_BYTE (c1);
-                         code = (c << 8) | c1;
-                       }
-                   }
+                 ONE_MORE_BYTE (c);
+                 code = (code << 8) | c;
+                 len++;
                }
              CODING_DECODE_CHAR (coding, src, src_base, src_end,
                                  charset, code, c);
@@ -4370,28 +4681,16 @@ decode_coding_charset (coding)
              /* VAL is a list of charset IDs.  It is assured that the
                 list is sorted by charset dimensions (smaller one
                 comes first).  */
-             int b[4];
-             int len = 1;
-
-             b[0] = c;
-             /* VAL is a list of charset IDs.  */
              while (CONSP (val))
                {
                  charset = CHARSET_FROM_ID (XFASTINT (XCAR (val)));
                  dim = CHARSET_DIMENSION (charset);
                  while (len < dim)
                    {
-                     ONE_MORE_BYTE (c1);
-                     b[len++] = c1;
+                     ONE_MORE_BYTE (c);
+                     code = (code << 8) | c;
+                     len++;
                    }
-                 if (dim == 1)
-                   code = b[0];
-                 else if (dim == 2)
-                   code = (b[0] << 8) | b[1];
-                 else if (dim == 3)
-                   code = (b[0] << 16) | (b[1] << 8) | b[2];
-                 else
-                   code = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
                  CODING_DECODE_CHAR (coding, src, src_base,
                                      src_end, charset, code, c);
                  if (c >= 0)
@@ -4401,8 +4700,17 @@ decode_coding_charset (coding)
            }
          if (c < 0)
            goto invalid_code;
+         if (charset->id != charset_ascii
+             && last_id != charset->id)
+           {
+             if (last_id != charset_ascii)
+               ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
+             last_id = charset->id;
+             last_offset = char_offset;
+           }
        }
       *charbuf++ = c;
+      char_offset++;
       continue;
 
     invalid_code:
@@ -4410,10 +4718,13 @@ decode_coding_charset (coding)
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
       *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      char_offset++;
       coding->errors++;
     }
 
  no_more_source:
+  if (last_id != charset_ascii)
+    ADD_CHARSET_DATA (charbuf, last_offset, char_offset, last_id);
   coding->consumed_char += consumed_chars_base;
   coding->consumed = src_base - coding->source;
   coding->charbuf_used = charbuf - coding->charbuf;
@@ -4446,6 +4757,11 @@ encode_coding_charset (coding)
       c = *charbuf++;
       if (ascii_compatible && ASCII_CHAR_P (c))
        EMIT_ONE_ASCII_BYTE (c);
+      else if (CHAR_BYTE8_P (c))
+       {
+         c = CHAR_TO_BYTE8 (c);
+         EMIT_ONE_BYTE (c);
+       }
       else
        {
          charset = char_charset (c, charset_list, &code);
@@ -4481,66 +4797,6 @@ encode_coding_charset (coding)
 \f
 /*** 7. C library functions ***/
 
-/* In Emacs Lisp, coding system is represented by a Lisp symbol which
-   has a property `coding-system'.  The value of this property is a
-   vector of length 5 (called as coding-vector).  Among elements of
-   this vector, the first (element[0]) and the fifth (element[4])
-   carry important information for decoding/encoding.  Before
-   decoding/encoding, this information should be set in fields of a
-   structure of type `coding_system'.
-
-   A value of property `coding-system' can be a symbol of another
-   subsidiary coding-system.  In that case, Emacs gets coding-vector
-   from that symbol.
-
-   `element[0]' contains information to be set in `coding->type'.  The
-   value and its meaning is as follows:
-
-   0 -- coding_type_emacs_mule
-   1 -- coding_type_sjis
-   2 -- coding_type_iso_2022
-   3 -- coding_type_big5
-   4 -- coding_type_ccl encoder/decoder written in CCL
-   nil -- coding_type_no_conversion
-   t -- coding_type_undecided (automatic conversion on decoding,
-                              no-conversion on encoding)
-
-   `element[4]' contains information to be set in `coding->flags' and
-   `coding->spec'.  The meaning varies by `coding->type'.
-
-   If `coding->type' is `coding_type_iso_2022', element[4] is a vector
-   of length 32 (of which the first 13 sub-elements are used now).
-   Meanings of these sub-elements are:
-
-   sub-element[N] where N is 0 through 3: to be set in `coding->spec.iso_2022'
-       If the value is an integer of valid charset, the charset is
-       assumed to be designated to graphic register N initially.
-
-       If the value is minus, it is a minus value of charset which
-       reserves graphic register N, which means that the charset is
-       not designated initially but should be designated to graphic
-       register N just before encoding a character in that charset.
-
-       If the value is nil, graphic register N is never used on
-       encoding.
-   
-   sub-element[N] where N is 4 through 11: to be set in `coding->flags'
-       Each value takes t or nil.  See the section ISO2022 of
-       `coding.h' for more information.
-
-   If `coding->type' is `coding_type_big5', element[4] is t to denote
-   BIG5-ETen or nil to denote BIG5-HKU.
-
-   If `coding->type' takes the other value, element[4] is ignored.
-
-   Emacs Lisp's coding system also carries information about format of
-   end-of-line in a value of property `eol-type'.  If the value is
-   integer, 0 means eol_lf, 1 means eol_crlf, and 2 means eol_cr.  If
-   it is not integer, it should be a vector of subsidiary coding
-   systems of which property `eol-type' has one of above values.
-
-*/
-
 /* Setup coding context CODING from information about CODING_SYSTEM.
    If CODING_SYSTEM is nil, `no-conversion' is assumed.  If
    CODING_SYSTEM is invalid, signal an error.  */
@@ -4608,6 +4864,8 @@ setup_coding_system (coding_system, coding)
            | CODING_REQUIRE_FLUSHING_MASK);
       if (flags & CODING_ISO_FLAG_COMPOSITION)
        coding->common_flags |= CODING_ANNOTATE_COMPOSITION_MASK;
+      if (flags & CODING_ISO_FLAG_DESIGNATION)
+       coding->common_flags |= CODING_ANNOTATE_CHARSET_MASK;
       if (flags & CODING_ISO_FLAG_FULL_SUPPORT)
        {
          setup_iso_safe_charsets (attrs);
@@ -4640,7 +4898,7 @@ setup_coding_system (coding_system, coding)
                                    : EQ (val, Qt) ? utf_16_with_bom
                                    : utf_16_without_bom);
       val = AREF (attrs, coding_attr_utf_16_endian);
-      CODING_UTF_16_ENDIAN (coding) = (NILP (val) ? utf_16_big_endian
+      CODING_UTF_16_ENDIAN (coding) = (EQ (val, Qbig) ? utf_16_big_endian
                                       : utf_16_little_endian);
       CODING_UTF_16_SURROGATE (coding) = 0;
       coding->detector = detect_coding_utf_16;
@@ -4648,6 +4906,8 @@ setup_coding_system (coding_system, coding)
       coding->encoder = encode_coding_utf_16;
       coding->common_flags
        |= (CODING_REQUIRE_DECODING_MASK | CODING_REQUIRE_ENCODING_MASK);
+      if (CODING_UTF_16_BOM (coding) == utf_16_detect_bom)
+       coding->common_flags |= CODING_REQUIRE_DETECTION_MASK;
     }
   else if (EQ (coding_type, Qccl))
     {
@@ -4883,32 +5143,32 @@ coding_inherit_eol_type (coding_system, parent)
 #define EOL_SEEN_CR    2
 #define EOL_SEEN_CRLF  4
 
-/* Detect how end-of-line of a text of length CODING->src_bytes
-   pointed by CODING->source is encoded.  Return one of
-   EOL_SEEN_XXX.  */
+/* Detect how end-of-line of a text of length SRC_BYTES pointed by
+   SOURCE is encoded.  If CATEGORY is one of
+   coding_category_utf_16_XXXX, assume that CR and LF are encoded by
+   two-byte, else they are encoded by one-byte.
+
+   Return one of EOL_SEEN_XXX.  */
 
 #define MAX_EOL_CHECK_COUNT 3
 
 static int
-detect_eol (coding, source, src_bytes)
-     struct coding_system *coding;
+detect_eol (source, src_bytes, category)
      unsigned char *source;
      EMACS_INT src_bytes;
+     enum coding_category category;
 {
-  Lisp_Object attrs, coding_type;
   unsigned char *src = source, *src_end = src + src_bytes;
   unsigned char c;
   int total  = 0;
   int eol_seen = EOL_SEEN_NONE;
 
-  attrs = CODING_ID_ATTRS (coding->id);
-  coding_type = CODING_ATTR_TYPE (attrs);
-
-  if (EQ (coding_type, Qccl))
+  if ((1 << category) & CATEGORY_MASK_UTF_16)
     {
       int msb, lsb;
 
-      msb = coding->spec.utf_16.endian == utf_16_little_endian;
+      msb = category == (coding_category_utf_16_le
+                        | coding_category_utf_16_le_nosig);
       lsb = 1 - msb;
 
       while (src + 1 < src_end)
@@ -4986,9 +5246,9 @@ adjust_coding_eol_type (coding, eol_seen)
   eol_type = CODING_ID_EOL_TYPE (coding->id);
   if (eol_seen & EOL_SEEN_LF)
     coding->id = CODING_SYSTEM_ID (AREF (eol_type, 0));
-  else if (eol_type & EOL_SEEN_CRLF)
+  else if (eol_seen & EOL_SEEN_CRLF)
     coding->id = CODING_SYSTEM_ID (AREF (eol_type, 1));
-  else if (eol_type & EOL_SEEN_CR)
+  else if (eol_seen & EOL_SEEN_CR)
     coding->id = CODING_SYSTEM_ID (AREF (eol_type, 2));
 }
 
@@ -5013,7 +5273,6 @@ detect_coding (coding)
      now.  */
   if (EQ (CODING_ATTR_TYPE (CODING_ID_ATTRS (coding->id)), Qundecided))
     {
-      int mask = CATEGORY_MASK_ANY;
       int c, i;
 
       for (src = coding->source; src < src_end; src++)
@@ -5028,43 +5287,62 @@ detect_coding (coding)
 
       if (coding->head_ascii < coding->src_bytes)
        {
-         int detected = 0;
+         struct coding_detection_info detect_info;
+         enum coding_category category;
+         struct coding_system *this;
 
+         detect_info.checked = detect_info.found = detect_info.rejected = 0;
          for (i = 0; i < coding_category_raw_text; i++)
            {
-             enum coding_category category = coding_priorities[i];
-             struct coding_system *this = coding_categories + category;
-
-             if (category >= coding_category_raw_text
-                 || detected & (1 << category))
-               continue;
-
+             category = coding_priorities[i];
+             this = coding_categories + category;
              if (this->id < 0)
                {
                  /* No coding system of this category is defined.  */
-                 mask &= ~(1 << category);
+                 detect_info.rejected |= (1 << category);
                }
-             else
+             else if (category >= coding_category_raw_text)
+               continue;
+             else if (detect_info.checked & (1 << category))
                {
-                 detected |= detected_mask[category];
-                 if ((*(this->detector)) (coding, &mask))
+                 if (detect_info.found & (1 << category))
                    break;
                }
+             else if ((*(this->detector)) (coding, &detect_info)
+                      && detect_info.found & (1 << category))
+               break;
            }
-         if (! mask)
+         if (i < coding_category_raw_text)
+           setup_coding_system (CODING_ID_NAME (this->id), coding);
+         else if (detect_info.rejected == CATEGORY_MASK_ANY)
            setup_coding_system (Qraw_text, coding);
-         else if (mask != CATEGORY_MASK_ANY)
+         else if (detect_info.rejected)
            for (i = 0; i < coding_category_raw_text; i++)
-             {
-               enum coding_category category = coding_priorities[i];
-               struct coding_system *this = coding_categories + category;
+             if (! (detect_info.rejected & (1 << coding_priorities[i])))
+               {
+                 this = coding_categories + coding_priorities[i];
+                 setup_coding_system (CODING_ID_NAME (this->id), coding);
+                 break;
+               }
+       }
+    }
+  else if (EQ (CODING_ATTR_TYPE (CODING_ID_ATTRS (coding->id)), Qutf_16))
+    {
+      Lisp_Object coding_systems;
+      struct coding_detection_info detect_info;
 
-               if (mask & (1 << category))
-                 {
-                   setup_coding_system (CODING_ID_NAME (this->id), coding);
-                   break;
-                 }
-             }
+      coding_systems
+       = AREF (CODING_ID_ATTRS (coding->id), coding_attr_utf_16_bom);
+      detect_info.found = detect_info.rejected = 0;
+      if (CONSP (coding_systems)
+         && detect_coding_utf_16 (coding, &detect_info)
+         && (detect_info.found & (CATEGORY_MASK_UTF_16_LE
+                                  | CATEGORY_MASK_UTF_16_BE)))
+       {
+         if (detect_info.found & CATEGORY_MASK_UTF_16_LE)
+           setup_coding_system (XCAR (coding_systems), coding);
+         else
+           setup_coding_system (XCDR (coding_systems), coding);
        }
     }
 
@@ -5077,7 +5355,8 @@ detect_coding (coding)
   if (VECTORP (CODING_ID_EOL_TYPE (coding->id))
       && ! EQ (coding_type, Qccl))
     {
-      int eol_seen = detect_eol (coding, coding->source, coding->src_bytes);
+      int eol_seen = detect_eol (coding->source, coding->src_bytes,
+                                XINT (CODING_ATTR_CATEGORY (attrs)));
 
       if (eol_seen != EOL_SEEN_NONE)
        adjust_coding_eol_type (coding, eol_seen);
@@ -5132,7 +5411,7 @@ decode_eol (coding)
                     coding->dst_pos_byte + coding->produced);
       undo_list = current_buffer->undo_list;
       current_buffer->undo_list = Qt;
-      del_range_2 (coding->dst_pos, coding->dst_pos_byte, GPT, GPT_BYTE, Qnil);
+      del_range_2 (coding->dst_pos, coding->dst_pos_byte, GPT, GPT_BYTE, 0);
       current_buffer->undo_list = undo_list;
       pbeg = GPT_ADDR;
       pend = pbeg + coding->produced;
@@ -5214,7 +5493,7 @@ produce_chars (coding)
              produced_chars++;
            }
          else
-           /* This is an annotation data.  */
+           /* This is an annotation datum.  */
            buf -= c + 1;
        }
     }
@@ -5243,8 +5522,12 @@ produce_chars (coding)
                    {
                      if (EQ (eol_type, Qdos))
                        {
-                         if (src < src_end
-                             && *src == '\n')
+                         if (src == src_end)
+                           {
+                             coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+                             goto no_more_source;
+                           }
+                         if (*src == '\n')
                            c = *src++;
                        }
                      else if (EQ (eol_type, Qmac))
@@ -5356,9 +5639,9 @@ produce_chars (coding)
   return produced_chars;
 }
 
-/* [ -LENGTH CHAR_POS_OFFSET MASK METHOD COMP_LEN ]
-       or
-   [ -LENGTH CHAR_POS_OFFSET MASK METHOD COMP_LEN COMPONENTS... ]
+/* Compose text in CODING->object according to the annotation data at
+   CHARBUF.  CHARBUF is an array:
+     [ -LENGTH ANNOTATION_MASK FROM TO METHOD COMP_LEN [ COMPONENTS... ] ]
  */
 
 static INLINE void
@@ -5366,18 +5649,15 @@ produce_composition (coding, charbuf)
      struct coding_system *coding;
      int *charbuf;
 {
-  Lisp_Object buffer;
   int len;
-  EMACS_INT pos;
+  EMACS_INT from, to;
   enum composition_method method;
-  int cmp_len;
   Lisp_Object components;
 
-  buffer = coding->dst_object;
   len = -charbuf[0];
-  pos = coding->dst_pos + charbuf[1];
-  method = (enum composition_method) (charbuf[3]);
-  cmp_len = charbuf[4];
+  from = coding->dst_pos + charbuf[2];
+  to = coding->dst_pos + charbuf[3];
+  method = (enum composition_method) (charbuf[4]);
 
   if (method == COMPOSITION_RELATIVE)
     components = Qnil;
@@ -5393,65 +5673,30 @@ produce_composition (coding, charbuf)
       components = (method == COMPOSITION_WITH_ALTCHARS
                    ? Fstring (len, args) : Fvector (len, args));
     }
-  compose_text (pos, pos + cmp_len, components, Qnil, Qnil);
+  compose_text (from, to, components, Qnil, coding->dst_object);
 }
 
-static int *
-save_composition_data (buf, buf_end, prop)
-     int *buf, *buf_end;
-     Lisp_Object prop;
-{
-  enum composition_method method = COMPOSITION_METHOD (prop);
-  int cmp_len = COMPOSITION_LENGTH (prop);
-
-  if (buf + 4 + (MAX_COMPOSITION_COMPONENTS * 2 - 1) > buf_end)
-    return NULL;
 
-  buf[1] = CODING_ANNOTATE_COMPOSITION_MASK;
-  buf[2] = method;
-  buf[3] = cmp_len;
-
-  if (method == COMPOSITION_RELATIVE)
-    buf[0] = 4;
-  else
-    {
-      Lisp_Object components;
-      int len, i;
+/* Put `charset' property on text in CODING->object according to
+   the annotation data at CHARBUF.  CHARBUF is an array:
+     [ -LENGTH ANNOTATION_MASK FROM TO CHARSET-ID ]
+ */
 
-      components = COMPOSITION_COMPONENTS (prop);
-      if (VECTORP (components))
-       {
-         len = XVECTOR (components)->size;
-         for (i = 0; i < len; i++)
-           buf[4 + i] = XINT (AREF (components, i));
-       }
-      else if (STRINGP (components))
-       {
-         int i_byte;
+static INLINE void
+produce_charset (coding, charbuf)
+     struct coding_system *coding;
+     int *charbuf;
+{
+  EMACS_INT from = coding->dst_pos + charbuf[2];
+  EMACS_INT to = coding->dst_pos + charbuf[3];
+  struct charset *charset = CHARSET_FROM_ID (charbuf[4]);
 
-         len = XSTRING (components)->size;
-         i = i_byte = 0;
-         while (i < len)
-           FETCH_STRING_CHAR_ADVANCE (buf[4 + i], components, i, i_byte);
-       }
-      else if (INTEGERP (components))
-       {
-         len = 1;
-         buf[4] = XINT (components);
-       }
-      else if (CONSP (components))
-       {
-         for (len = 0; CONSP (components);
-              len++, components = XCDR (components))
-           buf[4 + len] = XINT (XCAR (components));
-       }
-      else
-       abort ();
-      buf[0] = 4 + len;
-    }
-  return (buf + buf[0]);
+  Fput_text_property (make_number (from), make_number (to),
+                     Qcharset, CHARSET_NAME (charset),
+                     coding->dst_object);
 }
 
+
 #define CHARBUF_SIZE 0x4000
 
 #define ALLOC_CONVERSION_WORK_AREA(coding)                             \
@@ -5482,6 +5727,9 @@ produce_annotation (coding)
   int *charbuf = coding->charbuf;
   int *charbuf_end = charbuf + coding->charbuf_used;
 
+  if (NILP (coding->dst_object))
+    return;
+
   while (charbuf < charbuf_end)
     {
       if (*charbuf >= 0)
@@ -5489,11 +5737,14 @@ produce_annotation (coding)
       else
        {
          int len = -*charbuf;
-         switch (charbuf[2])
+         switch (charbuf[1])
            {
            case CODING_ANNOTATE_COMPOSITION_MASK:
              produce_composition (coding, charbuf);
              break;
+           case CODING_ANNOTATE_CHARSET_MASK:
+             produce_charset (coding, charbuf);
+             break;
            default:
              abort ();
            }
@@ -5561,7 +5812,9 @@ decode_coding (coding)
       coding->annotated = 0;
       (*(coding->decoder)) (coding);
       if (!NILP (CODING_ATTR_DECODE_TBL (attrs)))
-       translate_chars (CODING_ATTR_DECODE_TBL (attrs), coding);
+       translate_chars (coding, CODING_ATTR_DECODE_TBL (attrs));
+      else if (!NILP (Vstandard_translation_table_for_decode))
+       translate_chars (coding, Vstandard_translation_table_for_decode);
       coding_set_destination (coding);
       produce_chars (coding);
       if (coding->annotated)
@@ -5590,12 +5843,11 @@ decode_coding (coding)
          /* Flush out unprocessed data as binary chars.  We are sure
             that the number of data is less than the size of
             coding->charbuf.  */
-         int *charbuf = coding->charbuf;
-
          while (nbytes-- > 0)
            {
              int c = *src++;
-             *charbuf++ =  (c & 0x80 ? - c : c);
+
+             coding->charbuf[coding->charbuf_used++] = (c & 0x80 ? - c : c);
            }
          produce_chars (coding);
        }
@@ -5616,64 +5868,186 @@ decode_coding (coding)
   return coding->result;
 }
 
+
+/* Extract an annotation datum from a composition starting at POS and
+   ending before LIMIT of CODING->src_object (buffer or string), store
+   the data in BUF, set *STOP to a starting position of the next
+   composition (if any) or to LIMIT, and return the address of the
+   next element of BUF.
+
+   If such an annotation is not found, set *STOP to a starting
+   position of a composition after POS (if any) or to LIMIT, and
+   return BUF.  */
+
+static INLINE int *
+handle_composition_annotation (pos, limit, coding, buf, stop)
+     EMACS_INT pos, limit;
+     struct coding_system *coding;
+     int *buf;
+     EMACS_INT *stop;
+{
+  EMACS_INT start, end;
+  Lisp_Object prop;
+
+  if (! find_composition (pos, limit, &start, &end, &prop, coding->src_object)
+      || end > limit)
+    *stop = limit;
+  else if (start > pos)
+    *stop = start;
+  else
+    {
+      if (start == pos)
+       {
+         /* We found a composition.  Store the corresponding
+            annotation data in BUF.  */
+         int *head = buf;
+         enum composition_method method = COMPOSITION_METHOD (prop);
+         int nchars = COMPOSITION_LENGTH (prop);
+
+         ADD_COMPOSITION_DATA (buf, 0, nchars, method);
+         if (method != COMPOSITION_RELATIVE)
+           {
+             Lisp_Object components;
+             int len, i, i_byte;
+
+             components = COMPOSITION_COMPONENTS (prop);
+             if (VECTORP (components))
+               {
+                 len = XVECTOR (components)->size;
+                 for (i = 0; i < len; i++)
+                   *buf++ = XINT (AREF (components, i));
+               }
+             else if (STRINGP (components))
+               {
+                 len = XSTRING (components)->size;
+                 i = i_byte = 0;
+                 while (i < len)
+                   {
+                     FETCH_STRING_CHAR_ADVANCE (*buf, components, i, i_byte);
+                     buf++;
+                   }
+               }
+             else if (INTEGERP (components))
+               {
+                 len = 1;
+                 *buf++ = XINT (components);
+               }
+             else if (CONSP (components))
+               {
+                 for (len = 0; CONSP (components);
+                      len++, components = XCDR (components))
+                   *buf++ = XINT (XCAR (components));
+               }
+             else
+               abort ();
+             *head -= len;
+           }
+       }
+
+      if (find_composition (end, limit, &start, &end, &prop,
+                           coding->src_object)
+         && end <= limit)
+       *stop = start;
+      else
+       *stop = limit;
+    }
+  return buf;
+}
+
+
+/* Extract an annotation datum from a text property `charset' at POS of
+   CODING->src_object (buffer of string), store the data in BUF, set
+   *STOP to the position where the value of `charset' property changes
+   (limiting by LIMIT), and return the address of the next element of
+   BUF.
+
+   If the property value is nil, set *STOP to the position where the
+   property value is non-nil (limiting by LIMIT), and return BUF.  */
+
+static INLINE int *
+handle_charset_annotation (pos, limit, coding, buf, stop)
+     EMACS_INT pos, limit;
+     struct coding_system *coding;
+     int *buf;
+     EMACS_INT *stop;
+{
+  Lisp_Object val, next;
+  int id;
+
+  val = Fget_text_property (make_number (pos), Qcharset, coding->src_object);
+  if (! NILP (val) && CHARSETP (val))
+    id = XINT (CHARSET_SYMBOL_ID (val));
+  else
+    id = -1;
+  ADD_CHARSET_DATA (buf, 0, 0, id);
+  next = Fnext_single_property_change (make_number (pos), Qcharset,
+                                      coding->src_object,
+                                      make_number (limit));
+  *stop = XINT (next);
+  return buf;
+}
+
+
 static void
 consume_chars (coding)
      struct coding_system *coding;
 {
   int *buf = coding->charbuf;
-  /* -1 is to compensate for CRLF.  */
-  int *buf_end = coding->charbuf + coding->charbuf_size - 1;
-  unsigned char *src = coding->source + coding->consumed;
-  int pos = coding->src_pos + coding->consumed_char;
-  int end_pos = coding->src_pos + coding->src_chars;
+  int *buf_end = coding->charbuf + coding->charbuf_size;
+  const unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  EMACS_INT pos = coding->src_pos + coding->consumed_char;
+  EMACS_INT end_pos = coding->src_pos + coding->src_chars;
   int multibytep = coding->src_multibyte;
   Lisp_Object eol_type;
   int c;
-  int start, end, stop;
-  Lisp_Object object, prop;
+  EMACS_INT stop, stop_composition, stop_charset;
 
   eol_type = CODING_ID_EOL_TYPE (coding->id);
   if (VECTORP (eol_type))
     eol_type = Qunix;
 
-  object = coding->src_object;
-
   /* Note: composition handling is not yet implemented.  */
   coding->common_flags &= ~CODING_ANNOTATE_COMPOSITION_MASK;
 
-  if (coding->common_flags & CODING_ANNOTATE_COMPOSITION_MASK
-      && find_composition (pos, end_pos, &start, &end, &prop, object)
-      && end <= end_pos
-      && (start >= pos
-         || (find_composition (end, end_pos, &start, &end, &prop, object)
-             && end <= end_pos)))
-    stop = start;
+  if (coding->common_flags & CODING_ANNOTATE_COMPOSITION_MASK)
+    stop = stop_composition = pos;
+  else
+    stop = stop_composition = end_pos;
+  if (coding->common_flags & CODING_ANNOTATE_CHARSET_MASK)
+    stop = stop_charset = pos;
   else
-    stop = end_pos;
+    stop_charset = end_pos;
 
+  /* Compensate for CRLF and annotation.  */
+  buf_end -= 1 + MAX_ANNOTATION_LENGTH;
   while (buf < buf_end)
     {
       if (pos == stop)
        {
-         int *p;
-
          if (pos == end_pos)
            break;
-         p = save_composition_data (buf, buf_end, prop);
-         if (p == NULL)
-           break;
-         buf = p;
-         if (find_composition (end, end_pos, &start, &end, &prop, object)
-             && end <= end_pos)
-           stop = start;
-         else
-           stop = end_pos;
+         if (pos == stop_composition)
+           buf = handle_composition_annotation (pos, end_pos, coding,
+                                                buf, &stop_composition);
+         if (pos == stop_charset)
+           buf = handle_charset_annotation (pos, end_pos, coding,
+                                            buf, &stop_charset);
+         stop = (stop_composition < stop_charset
+                 ? stop_composition : stop_charset);
        }
 
       if (! multibytep)
-       c = *src++;
+       {
+         EMACS_INT bytes = MULTIBYTE_LENGTH (src, src_end);
+
+         if (bytes > 0)
+           c = STRING_CHAR_ADVANCE (src), pos += bytes;
+         else
+           c = *src++, pos++;
+       }
       else
-       c = STRING_CHAR_ADVANCE (src);
+       c = STRING_CHAR_ADVANCE (src), pos++;
       if ((c == '\r') && (coding->mode & CODING_MODE_SELECTIVE_DISPLAY))
        c = '\n';
       if (! EQ (eol_type, Qunix))
@@ -5687,7 +6061,6 @@ consume_chars (coding)
            }
        }
       *buf++ = c;
-      pos++;
     }
 
   coding->consumed = src - coding->source;
@@ -5745,7 +6118,9 @@ encode_coding (coding)
     consume_chars (coding);
 
     if (!NILP (CODING_ATTR_ENCODE_TBL (attrs)))
-      translate_chars (CODING_ATTR_ENCODE_TBL (attrs), coding);
+      translate_chars (coding, CODING_ATTR_ENCODE_TBL (attrs));
+    else if (!NILP (Vstandard_translation_table_for_encode))
+      translate_chars (coding, Vstandard_translation_table_for_encode);
 
     coding_set_destination (coding);
     (*(coding->encoder)) (coding);
@@ -5757,9 +6132,10 @@ encode_coding (coding)
   return (coding->result);
 }
 
-/* Work buffer */
 
-/* List of currently used working buffer.  */
+/* Stack of working buffers used in code conversion.  An nil element
+   means that the code conversion of that level is not using a working
+   buffer.  */
 Lisp_Object Vcode_conversion_work_buf_list;
 
 /* A working buffer used by the top level conversion.  */
@@ -5771,76 +6147,92 @@ Lisp_Object Vcode_conversion_reused_work_buf;
    buffer.  */
 
 Lisp_Object
-make_conversion_work_buffer (multibytep)
-     int multibytep;
+make_conversion_work_buffer (multibytep, depth)
+     int multibytep, depth;
 {
   struct buffer *current = current_buffer;
-  Lisp_Object buf;
+  Lisp_Object buf, name;
 
-  if (NILP (Vcode_conversion_work_buf_list))
+  if (depth == 0)
     {
       if (NILP (Vcode_conversion_reused_work_buf))
        Vcode_conversion_reused_work_buf
-         = Fget_buffer_create (build_string (" *code-conversion-work*"));
-      Vcode_conversion_work_buf_list
-       = Fcons (Vcode_conversion_reused_work_buf, Qnil);
+         = Fget_buffer_create (build_string (" *code-conversion-work<0>*"));
+      buf = Vcode_conversion_reused_work_buf;
     }
   else
     {
-      int depth = Flength (Vcode_conversion_work_buf_list);
-      char str[128];
+      if (depth < 0)
+       {
+         name = build_string (" *code-conversion-work*");
+         name = Fgenerate_new_buffer_name (name, Qnil);
+       }
+      else
+       {
+         char str[128];
 
-      sprintf (str, " *code-conversion-work*<%d>", depth);
-      Vcode_conversion_work_buf_list
-       = Fcons (Fget_buffer_create (build_string (str)),
-                Vcode_conversion_work_buf_list);
+         sprintf (str, " *code-conversion-work*<%d>", depth);
+         name = build_string (str);
+       }
+      buf = Fget_buffer_create (name);
     }
-
-  buf = XCAR (Vcode_conversion_work_buf_list);
   set_buffer_internal (XBUFFER (buf));
   current_buffer->undo_list = Qt;
   Ferase_buffer ();
-  Fset_buffer_multibyte (multibytep ? Qt : Qnil);
+  Fset_buffer_multibyte (multibytep ? Qt : Qnil, Qnil);
   set_buffer_internal (current);
   return buf;
 }
 
-static struct coding_system *saved_coding;
+static Lisp_Object
+code_conversion_restore (buffer)
+     Lisp_Object buffer;
+{
+  Lisp_Object workbuf;
+
+  workbuf = XCAR (Vcode_conversion_work_buf_list);
+  if (! NILP (workbuf)
+      && ! EQ (workbuf, Vcode_conversion_reused_work_buf)
+      && ! NILP (Fbuffer_live_p (workbuf)))
+    Fkill_buffer (workbuf);
+  Vcode_conversion_work_buf_list = XCDR (Vcode_conversion_work_buf_list);
+  set_buffer_internal (XBUFFER (buffer));
+  return Qnil;
+}
 
-Lisp_Object
-code_conversion_restore (info)
-     Lisp_Object info;
+static Lisp_Object
+code_conversion_save (buffer, with_work_buf, multibyte)
+     Lisp_Object buffer;
+     int with_work_buf, multibyte;
 {
-  int depth = Flength (Vcode_conversion_work_buf_list);
-  Lisp_Object buf;
+  Lisp_Object workbuf;
 
-  if (depth > 0)
+  if (with_work_buf)
     {
-      buf = XCAR (Vcode_conversion_work_buf_list);
-      Vcode_conversion_work_buf_list = XCDR (Vcode_conversion_work_buf_list);
-      if (depth > 1 && !NILP (Fbuffer_live_p (buf)))
-       Fkill_buffer (buf);
-    }
+      int depth = XINT (Flength (Vcode_conversion_work_buf_list));
 
-  if (saved_coding->dst_object == Qt
-      && saved_coding->destination)
-    xfree (saved_coding->destination);
-
-  return save_excursion_restore (info);
+      workbuf = make_conversion_work_buffer (multibyte, depth);
+    }
+  else
+    workbuf = Qnil;
+  Vcode_conversion_work_buf_list
+    = Fcons (workbuf, Vcode_conversion_work_buf_list);
+  record_unwind_protect (code_conversion_restore, buffer);
+  return workbuf;
 }
 
-
 int
 decode_coding_gap (coding, chars, bytes)
      struct coding_system *coding;
      EMACS_INT chars, bytes;
 {
   int count = specpdl_ptr - specpdl;
+  Lisp_Object buffer;
 
-  saved_coding = coding;
-  record_unwind_protect (code_conversion_restore, save_excursion_save ());
+  buffer = Fcurrent_buffer ();
+  code_conversion_save (buffer, 0, 0);
 
-  coding->src_object = Fcurrent_buffer ();
+  coding->src_object = buffer;
   coding->src_chars = chars;
   coding->src_bytes = bytes;
   coding->src_pos = -chars;
@@ -5850,6 +6242,7 @@ decode_coding_gap (coding, chars, bytes)
   coding->dst_pos = PT;
   coding->dst_pos_byte = PT_BYTE;
   coding->dst_multibyte = ! NILP (current_buffer->enable_multibyte_characters);
+  coding->mode |= CODING_MODE_LAST_BLOCK;
 
   if (CODING_REQUIRE_DETECTION (coding))
     detect_coding (coding);
@@ -5868,10 +6261,9 @@ encode_coding_gap (coding, chars, bytes)
   int count = specpdl_ptr - specpdl;
   Lisp_Object buffer;
 
-  saved_coding = coding;
-  record_unwind_protect (code_conversion_restore, save_excursion_save ());
-
   buffer = Fcurrent_buffer ();
+  code_conversion_save (buffer, 0, 0);
+
   coding->src_object = buffer;
   coding->src_chars = chars;
   coding->src_bytes = bytes;
@@ -5913,7 +6305,7 @@ encode_coding_gap (coding, chars, bytes)
    set in CODING->dst_object.
 
    If it is Qnil, the decoded text is stored at CODING->destination.
-   The called must allocate CODING->dst_bytes bytes at
+   The caller must allocate CODING->dst_bytes bytes at
    CODING->destination by xmalloc.  If the decoded text is longer than
    CODING->dst_bytes, CODING->destination is relocated by xrealloc.
  */
@@ -5932,9 +6324,10 @@ decode_coding_object (coding, src_object, from, from_byte, to, to_byte,
   EMACS_INT chars = to - from;
   EMACS_INT bytes = to_byte - from_byte;
   Lisp_Object attrs;
+  Lisp_Object buffer;
+  int saved_pt = -1, saved_pt_byte;
 
-  saved_coding = coding;
-  record_unwind_protect (code_conversion_restore, save_excursion_save ());
+  buffer = Fcurrent_buffer ();
 
   if (NILP (dst_object))
     {
@@ -5959,6 +6352,7 @@ decode_coding_object (coding, src_object, from, from_byte, to, to_byte,
        move_gap_both (from, from_byte);
       if (EQ (src_object, dst_object))
        {
+         saved_pt = PT, saved_pt_byte = PT_BYTE;
          TEMP_SET_PT_BOTH (from, from_byte);
          del_range_both (from, from_byte, to, to_byte, 1);
          coding->src_pos = -chars;
@@ -5975,16 +6369,18 @@ decode_coding_object (coding, src_object, from, from_byte, to, to_byte,
     detect_coding (coding);
   attrs = CODING_ID_ATTRS (coding->id);
 
-  if (! NILP (CODING_ATTR_POST_READ (attrs))
-      || EQ (dst_object, Qt))
+  if (EQ (dst_object, Qt)
+      || (! NILP (CODING_ATTR_POST_READ (attrs))
+         && NILP (dst_object)))
     {
-      coding->dst_object = make_conversion_work_buffer (1);
+      coding->dst_object = code_conversion_save (buffer, 1, 1);
       coding->dst_pos = BEG;
       coding->dst_pos_byte = BEG_BYTE;
       coding->dst_multibyte = 1;
     }
   else if (BUFFERP (dst_object))
     {
+      code_conversion_save (buffer, 0, 0);
       coding->dst_object = dst_object;
       coding->dst_pos = BUF_PT (XBUFFER (dst_object));
       coding->dst_pos_byte = BUF_PT_BYTE (XBUFFER (dst_object));
@@ -5993,6 +6389,7 @@ decode_coding_object (coding, src_object, from, from_byte, to, to_byte,
     }
   else
     {
+      code_conversion_save (buffer, 0, 0);
       coding->dst_object = Qnil;
       coding->dst_multibyte = 1;
     }
@@ -6042,6 +6439,25 @@ decode_coding_object (coding, src_object, from, from_byte, to, to_byte,
        }
     }
 
+  if (saved_pt >= 0)
+    {
+      /* This is the case of:
+        (BUFFERP (src_object) && EQ (src_object, dst_object))
+        As we have moved PT while replacing the original buffer
+        contents, we must recover it now.  */
+      set_buffer_internal (XBUFFER (src_object));
+      if (saved_pt < from)
+       TEMP_SET_PT_BOTH (saved_pt, saved_pt_byte);
+      else if (saved_pt < from + chars)
+       TEMP_SET_PT_BOTH (from, from_byte);
+      else if (! NILP (current_buffer->enable_multibyte_characters))
+       TEMP_SET_PT_BOTH (saved_pt + (coding->produced_char - chars),
+                         saved_pt_byte + (coding->produced - bytes));
+      else
+       TEMP_SET_PT_BOTH (saved_pt + (coding->produced - bytes),
+                         saved_pt_byte + (coding->produced - bytes));
+    }
+
   unbind_to (count, Qnil);
 }
 
@@ -6058,9 +6474,10 @@ encode_coding_object (coding, src_object, from, from_byte, to, to_byte,
   EMACS_INT chars = to - from;
   EMACS_INT bytes = to_byte - from_byte;
   Lisp_Object attrs;
+  Lisp_Object buffer;
+  int saved_pt = -1, saved_pt_byte;
 
-  saved_coding = coding;
-  record_unwind_protect (code_conversion_restore, save_excursion_save ());
+  buffer = Fcurrent_buffer ();
 
   coding->src_object = src_object;
   coding->src_chars = chars;
@@ -6071,7 +6488,8 @@ encode_coding_object (coding, src_object, from, from_byte, to, to_byte,
 
   if (! NILP (CODING_ATTR_PRE_WRITE (attrs)))
     {
-      coding->src_object = make_conversion_work_buffer (coding->src_multibyte);
+      coding->src_object = code_conversion_save (buffer, 1,
+                                                coding->src_multibyte);
       set_buffer_internal (XBUFFER (coding->src_object));
       if (STRINGP (src_object))
        insert_from_string (src_object, from, from_byte, chars, bytes, 0);
@@ -6083,6 +6501,7 @@ encode_coding_object (coding, src_object, from, from_byte, to, to_byte,
       if (EQ (src_object, dst_object))
        {
          set_buffer_internal (XBUFFER (src_object));
+         saved_pt = PT, saved_pt_byte = PT_BYTE;
          del_range_both (from, from_byte, to, to_byte, 1);
          set_buffer_internal (XBUFFER (coding->src_object));
        }
@@ -6100,32 +6519,45 @@ encode_coding_object (coding, src_object, from, from_byte, to, to_byte,
     }
   else if (STRINGP (src_object))
     {
+      code_conversion_save (buffer, 0, 0);
       coding->src_pos = from;
       coding->src_pos_byte = from_byte;
     }
   else if (BUFFERP (src_object))
     {
+      code_conversion_save (buffer, 0, 0);
       set_buffer_internal (XBUFFER (src_object));
-      if (from != GPT)
-       move_gap_both (from, from_byte);
       if (EQ (src_object, dst_object))
        {
-         del_range_both (from, from_byte, to, to_byte, 1);
-         coding->src_pos = -chars;
-         coding->src_pos_byte = -bytes;
+         saved_pt = PT, saved_pt_byte = PT_BYTE;
+         coding->src_object = del_range_1 (from, to, 1, 1);
+         coding->src_pos = 0;
+         coding->src_pos_byte = 0;
        }
       else
        {
+         if (from < GPT && to >= GPT)
+           move_gap_both (from, from_byte);
          coding->src_pos = from;
          coding->src_pos_byte = from_byte;
        }
     }
+  else
+    code_conversion_save (buffer, 0, 0);
 
   if (BUFFERP (dst_object))
     {
       coding->dst_object = dst_object;
-      coding->dst_pos = BUF_PT (XBUFFER (dst_object));
-      coding->dst_pos_byte = BUF_PT_BYTE (XBUFFER (dst_object));
+      if (EQ (src_object, dst_object))
+       {
+         coding->dst_pos = from;
+         coding->dst_pos_byte = from_byte;
+       }
+      else
+       {
+         coding->dst_pos = BUF_PT (XBUFFER (dst_object));
+         coding->dst_pos_byte = BUF_PT_BYTE (XBUFFER (dst_object));
+       }
       coding->dst_multibyte
        = ! NILP (XBUFFER (dst_object)->enable_multibyte_characters);
     }
@@ -6159,6 +6591,25 @@ encode_coding_object (coding, src_object, from, from_byte, to, to_byte,
        }
     }
 
+  if (saved_pt >= 0)
+    {
+      /* This is the case of:
+        (BUFFERP (src_object) && EQ (src_object, dst_object))
+        As we have moved PT while replacing the original buffer
+        contents, we must recover it now.  */
+      set_buffer_internal (XBUFFER (src_object));
+      if (saved_pt < from)
+       TEMP_SET_PT_BOTH (saved_pt, saved_pt_byte);
+      else if (saved_pt < from + chars)
+       TEMP_SET_PT_BOTH (from, from_byte);
+      else if (! NILP (current_buffer->enable_multibyte_characters))
+       TEMP_SET_PT_BOTH (saved_pt + (coding->produced_char - chars),
+                         saved_pt_byte + (coding->produced - bytes));
+      else
+       TEMP_SET_PT_BOTH (saved_pt + (coding->produced - bytes),
+                         saved_pt_byte + (coding->produced - bytes));
+    }
+
   unbind_to (count, Qnil);
 }
 
@@ -6219,9 +6670,7 @@ If the user enters null input, return second argument DEFAULT-CODING-SYSTEM.  */
 DEFUN ("check-coding-system", Fcheck_coding_system, Scheck_coding_system,
        1, 1, 0,
        doc: /* Check validity of CODING-SYSTEM.
-If valid, return CODING-SYSTEM, else signal a `coding-system-error' error.
-It is valid if it is a symbol with a non-nil `coding-system' property.
-The value of property should be a vector of length 5.  */)
+If valid, return CODING-SYSTEM, else signal a `coding-system-error' error.  */)
   (coding_system)
      Lisp_Object coding_system;
 {
@@ -6233,6 +6682,22 @@ The value of property should be a vector of length 5.  */)
 }
 
 \f
+/* Detect how the bytes at SRC of length SRC_BYTES are encoded.  If
+   HIGHEST is nonzero, return the coding system of the highest
+   priority among the detected coding systems.  Otherwize return a
+   list of detected coding systems sorted by their priorities.  If
+   MULTIBYTEP is nonzero, it is assumed that the bytes are in correct
+   multibyte form but contains only ASCII and eight-bit chars.
+   Otherwise, the bytes are raw bytes.
+
+   CODING-SYSTEM controls the detection as below:
+
+   If it is nil, detect both text-format and eol-format.  If the
+   text-format part of CODING-SYSTEM is already specified
+   (e.g. `iso-latin-1'), detect only eol-format.  If the eol-format
+   part of CODING-SYSTEM is already specified (e.g. `undecided-unix'),
+   detect only text-format.  */
+
 Lisp_Object
 detect_coding_system (src, src_bytes, highest, multibytep, coding_system)
      unsigned char *src;
@@ -6241,37 +6706,41 @@ detect_coding_system (src, src_bytes, highest, multibytep, coding_system)
      Lisp_Object coding_system;
 {
   unsigned char *src_end = src + src_bytes;
-  int mask = CATEGORY_MASK_ANY;
-  int detected = 0;
-  int c, i;
   Lisp_Object attrs, eol_type;
   Lisp_Object val;
   struct coding_system coding;
+  int id;
+  struct coding_detection_info detect_info;
 
   if (NILP (coding_system))
     coding_system = Qundecided;
   setup_coding_system (coding_system, &coding);
   attrs = CODING_ID_ATTRS (coding.id);
   eol_type = CODING_ID_EOL_TYPE (coding.id);
+  coding_system = CODING_ATTR_BASE_NAME (attrs);
 
   coding.source = src;
   coding.src_bytes = src_bytes;
   coding.src_multibyte = multibytep;
   coding.consumed = 0;
+  coding.mode |= CODING_MODE_LAST_BLOCK;
 
-  if (XINT (CODING_ATTR_CATEGORY (attrs)) != coding_category_undecided)
-    {
-      mask = 1 << XINT (CODING_ATTR_CATEGORY (attrs));
-    }
-  else
+  detect_info.checked = detect_info.found = detect_info.rejected = 0;
+
+  /* At first, detect text-format if necessary.  */
+  if (XINT (CODING_ATTR_CATEGORY (attrs)) == coding_category_undecided)
     {
-      coding_system = Qnil;
+      enum coding_category category;
+      struct coding_system *this;
+      int c, i;
+
       for (; src < src_end; src++)
        {
          c = *src;
-         if (c & 0x80 || (c < 0x20 && (c == ISO_CODE_ESC
-                                       || c == ISO_CODE_SI
-                                       || c == ISO_CODE_SO)))
+         if (c & 0x80
+             || (c < 0x20 && (c == ISO_CODE_ESC
+                              || c == ISO_CODE_SI
+                              || c == ISO_CODE_SO)))
            break;
        }
       coding.head_ascii = src - coding.source;
@@ -6279,87 +6748,155 @@ detect_coding_system (src, src_bytes, highest, multibytep, coding_system)
       if (src < src_end)
        for (i = 0; i < coding_category_raw_text; i++)
          {
-           enum coding_category category = coding_priorities[i];
-           struct coding_system *this = coding_categories + category;
+           category = coding_priorities[i];
+           this = coding_categories + category;
 
-           if (category >= coding_category_raw_text
-               || detected & (1 << category))
-             continue;
-       
            if (this->id < 0)
              {
                /* No coding system of this category is defined.  */
-               mask &= ~(1 << category);
+               detect_info.rejected |= (1 << category);
+             }
+           else if (category >= coding_category_raw_text)
+             continue;
+           else if (detect_info.checked & (1 << category))
+             {
+               if (highest
+                   && (detect_info.found & (1 << category)))
+                 break;
              }
            else
              {
-               detected |= detected_mask[category];
-               if ((*(coding_categories[category].detector)) (&coding, &mask)
-                   && highest)
-                 {
-                   mask &= detected_mask[category];
-                   break;
-                 }
+               if ((*(this->detector)) (&coding, &detect_info)
+                   && highest
+                   && (detect_info.found & (1 << category)))
+                 break;
              }
          }
-    }
 
-  if (!mask)
-    val = Fcons (make_number (coding_category_raw_text), Qnil);
-  else if (mask == CATEGORY_MASK_ANY)
-    val = Fcons (make_number (coding_category_undecided), Qnil);
-  else if (highest)
-    {
-      for (i = 0; i < coding_category_raw_text; i++)
-       if (mask & (1 << coding_priorities[i]))
-         {
-           val = Fcons (make_number (coding_priorities[i]), Qnil);
-           break;
-         }
-    }  
+
+      if (detect_info.rejected == CATEGORY_MASK_ANY)
+       {
+         detect_info.found = CATEGORY_MASK_RAW_TEXT;
+         id = coding_categories[coding_category_raw_text].id;
+         val = Fcons (make_number (id), Qnil);
+       }
+      else if (! detect_info.rejected && ! detect_info.found)
+       {
+         detect_info.found = CATEGORY_MASK_ANY;
+         id = coding_categories[coding_category_undecided].id;
+         val = Fcons (make_number (id), Qnil);
+       }
+      else if (highest)
+       {
+         if (detect_info.found)
+           {
+             detect_info.found = 1 << category;
+             val = Fcons (make_number (this->id), Qnil);
+           }
+         else
+           for (i = 0; i < coding_category_raw_text; i++)
+             if (! (detect_info.rejected & (1 << coding_priorities[i])))
+               {
+                 detect_info.found = 1 << coding_priorities[i];
+                 id = coding_categories[coding_priorities[i]].id;
+                 val = Fcons (make_number (id), Qnil);
+                 break;
+               }
+       }
+      else
+       {
+         int mask = detect_info.rejected | detect_info.found;
+         int found = 0;
+         val = Qnil;
+
+         for (i = coding_category_raw_text - 1; i >= 0; i--)
+           {
+             category = coding_priorities[i];
+             if (! (mask & (1 << category)))
+               {
+                 found |= 1 << category;
+                 id = coding_categories[category].id;
+                 val = Fcons (make_number (id), val);
+               }
+           }
+         for (i = coding_category_raw_text - 1; i >= 0; i--)
+           {
+             category = coding_priorities[i];
+             if (detect_info.found & (1 << category))
+               {
+                 id = coding_categories[category].id;
+                 val = Fcons (make_number (id), val);
+               }
+           }
+         detect_info.found |= found;
+       }
+    }
   else
     {
-      val = Qnil;
-      for (i = coding_category_raw_text - 1; i >= 0; i--)
-       if (mask & (1 << coding_priorities[i]))
-         val = Fcons (make_number (coding_priorities[i]), val);
+      detect_info.found = 1 << XINT (CODING_ATTR_CATEGORY (attrs));
+      val = Fcons (make_number (coding.id), Qnil);
     }
 
+  /* Then, detect eol-format if necessary.  */
   {
-    int one_byte_eol = -1, two_byte_eol = -1;
+    int normal_eol = -1, utf_16_be_eol = -1, utf_16_le_eol;
     Lisp_Object tail;
 
+    if (VECTORP (eol_type))
+      {
+       if (detect_info.found & ~CATEGORY_MASK_UTF_16)
+         normal_eol = detect_eol (coding.source, src_bytes,
+                                  coding_category_raw_text);
+       if (detect_info.found & (CATEGORY_MASK_UTF_16_BE
+                                | CATEGORY_MASK_UTF_16_BE_NOSIG))
+         utf_16_be_eol = detect_eol (coding.source, src_bytes,
+                                     coding_category_utf_16_be);
+       if (detect_info.found & (CATEGORY_MASK_UTF_16_LE
+                                | CATEGORY_MASK_UTF_16_LE_NOSIG))
+         utf_16_le_eol = detect_eol (coding.source, src_bytes,
+                                     coding_category_utf_16_le);
+      }
+    else
+      {
+       if (EQ (eol_type, Qunix))
+         normal_eol = utf_16_be_eol = utf_16_le_eol = EOL_SEEN_LF;
+       else if (EQ (eol_type, Qdos))
+         normal_eol = utf_16_be_eol = utf_16_le_eol = EOL_SEEN_CRLF;
+       else
+         normal_eol = utf_16_be_eol = utf_16_le_eol = EOL_SEEN_CR;
+      }
+
     for (tail = val; CONSP (tail); tail = XCDR (tail))
       {
-       struct coding_system *this
-         = (NILP (coding_system) ? coding_categories + XINT (XCAR (tail))
-            : &coding);
+       enum coding_category category;
        int this_eol;
-       
-       attrs = CODING_ID_ATTRS (this->id);
-       eol_type = CODING_ID_EOL_TYPE (this->id);
-       XSETCAR (tail, CODING_ID_NAME (this->id));
+
+       id = XINT (XCAR (tail));
+       attrs = CODING_ID_ATTRS (id);
+       category = XINT (CODING_ATTR_CATEGORY (attrs));
+       eol_type = CODING_ID_EOL_TYPE (id);
        if (VECTORP (eol_type))
          {
-           if (EQ (CODING_ATTR_TYPE (attrs), Qutf_16))
-             {
-               if (two_byte_eol < 0)
-                 two_byte_eol = detect_eol (this, coding.source, src_bytes);
-               this_eol = two_byte_eol;
-             }
+           if (category == coding_category_utf_16_be
+               || category == coding_category_utf_16_be_nosig)
+             this_eol = utf_16_be_eol;
+           else if (category == coding_category_utf_16_le
+                    || category == coding_category_utf_16_le_nosig)
+             this_eol = utf_16_le_eol;
            else
-             {
-               if (one_byte_eol < 0)
-                 one_byte_eol =detect_eol (this, coding.source, src_bytes);
-               this_eol = one_byte_eol;
-             }
+             this_eol = normal_eol;
+
            if (this_eol == EOL_SEEN_LF)
              XSETCAR (tail, AREF (eol_type, 0));
            else if (this_eol == EOL_SEEN_CRLF)
              XSETCAR (tail, AREF (eol_type, 1));
            else if (this_eol == EOL_SEEN_CR)
              XSETCAR (tail, AREF (eol_type, 2));
+           else
+             XSETCAR (tail, CODING_ID_NAME (id));
          }
+       else
+         XSETCAR (tail, CODING_ID_NAME (id));
       }
   }
 
@@ -6449,7 +6986,7 @@ char_encodable_p (c, attrs)
 /* Return a list of coding systems that safely encode the text between
    START and END.  If EXCLUDE is non-nil, it is a list of coding
    systems not to check.  The returned list doesn't contain any such
-   coding systems.  In any case, If the text contains only ASCII or is
+   coding systems.  In any case, if the text contains only ASCII or is
    unibyte, return t.  */
 
 DEFUN ("find-coding-systems-region-internal",
@@ -6461,14 +6998,14 @@ DEFUN ("find-coding-systems-region-internal",
 {
   Lisp_Object coding_attrs_list, safe_codings;
   EMACS_INT start_byte, end_byte;
-  unsigned char *p, *pbeg, *pend;
+  const unsigned char *p, *pbeg, *pend;
   int c;
   Lisp_Object tail, elt;
 
   if (STRINGP (start))
     {
       if (!STRING_MULTIBYTE (start)
-         && XSTRING (start)->size != STRING_BYTES (XSTRING (start)))
+         || XSTRING (start)->size == STRING_BYTES (XSTRING (start)))
        return Qt;
       start_byte = 0;
       end_byte = STRING_BYTES (XSTRING (start));
@@ -6486,12 +7023,12 @@ DEFUN ("find-coding-systems-region-internal",
       if (XINT (end) - XINT (start) == end_byte - start_byte)
        return Qt;
 
-      if (start < GPT && end > GPT)
+      if (XINT (start) < GPT && XINT (end) > GPT)
        {
-         if ((GPT - start) < (end - GPT))
-           move_gap_both (start, start_byte);
+         if ((GPT - XINT (start)) < (XINT (end) - GPT))
+           move_gap_both (XINT (start), start_byte);
          else
-           move_gap_both (end, end_byte);
+           move_gap_both (XINT (end), end_byte);
        }
     }
 
@@ -6591,7 +7128,7 @@ buffer positions.  END is ignored.  */)
   Lisp_Object list;
   EMACS_INT start_byte, end_byte;
   int pos;
-  unsigned char *p, *pbeg, *pend;
+  const unsigned char *p, *pbeg, *pend;
   int c;
   Lisp_Object tail, elt;
 
@@ -6617,14 +7154,14 @@ buffer positions.  END is ignored.  */)
       if (XINT (end) - XINT (start) == end_byte - start_byte)
        return Qt;
 
-      if (start < GPT && end > GPT)
+      if (XINT (start) < GPT && XINT (end) > GPT)
        {
-         if ((GPT - start) < (end - GPT))
-           move_gap_both (start, start_byte);
+         if ((GPT - XINT (start)) < (XINT (end) - GPT))
+           move_gap_both (XINT (start), start_byte);
          else
-           move_gap_both (end, end_byte);
+           move_gap_both (XINT (end), end_byte);
        }
-      pos = start;
+      pos = XINT (start);
     }
 
   list = Qnil;
@@ -6851,7 +7388,7 @@ Optional third arg NOCOPY non-nil means it is OK to return STRING itself
 if the decoding operation is trivial.
 
 Optional fourth arg BUFFER non-nil meant that the decoded text is
-inserted in BUFFER instead of returned as a astring.  In this case,
+inserted in BUFFER instead of returned as a string.  In this case,
 the return value is BUFFER.
 
 This function sets `last-coding-system-used' to the precise coding system
@@ -6872,7 +7409,7 @@ Optional third arg NOCOPY non-nil means it is OK to return STRING
 itself if the encoding operation is trivial.
 
 Optional fourth arg BUFFER non-nil meant that the encoded text is
-inserted in BUFFER instead of returned as a astring.  In this case,
+inserted in BUFFER instead of returned as a string.  In this case,
 the return value is BUFFER.
 
 This function sets `last-coding-system-used' to the precise coding system
@@ -6882,7 +7419,7 @@ not fully specified.)  */)
      Lisp_Object string, coding_system, nocopy, buffer;
 {
   return code_convert_string (string, coding_system, buffer,
-                             nocopy, ! NILP (nocopy), 1);
+                             1, ! NILP (nocopy), 1);
 }
 
 \f
@@ -7196,8 +7733,8 @@ usage: (find-operation-coding-system OPERATION ARGUMENTS ...)  */)
 }
 
 DEFUN ("set-coding-system-priority", Fset_coding_system_priority,
-       Sset_coding_system_priority, 1, MANY, 0,
-       doc: /* Assign higher priority to coding systems given as arguments.
+       Sset_coding_system_priority, 0, MANY, 0,
+       doc: /* Assign higher priority to the coding systems given as arguments.
 usage: (set-coding-system-priority CODING-SYSTEM ...)  */)
      (nargs, args)
      int nargs;
@@ -7247,7 +7784,8 @@ usage: (set-coding-system-priority CODING-SYSTEM ...)  */)
 
 DEFUN ("coding-system-priority-list", Fcoding_system_priority_list,
        Scoding_system_priority_list, 0, 1, 0,
-       doc: /* Return a list of coding systems ordered by their priorities.  */)
+       doc: /* Return a list of coding systems ordered by their priorities.
+HIGHESTP non-nil means just return the highest priority one.  */)
      (highestp)
      Lisp_Object highestp;
 {
@@ -7270,12 +7808,13 @@ DEFUN ("coding-system-priority-list", Fcoding_system_priority_list,
   return Fnreverse (val);
 }
 
+static char *suffixes[] = { "-unix", "-dos", "-mac" };
+
 static Lisp_Object
 make_subsidiaries (base)
      Lisp_Object base;
 {
   Lisp_Object subsidiaries;
-  char *suffixes[] = { "-unix", "-dos", "-mac" };
   int base_name_len = STRING_BYTES (XSYMBOL (base)->name);
   char *buf = (char *) alloca (base_name_len + 6);
   int i;
@@ -7377,6 +7916,8 @@ usage: (define-coding-system-internal ...)  */)
     XSTRING (safe_charsets)->data[XFASTINT (XCAR (tail))] = 0;
   CODING_ATTR_SAFE_CHARSETS (attrs) = safe_charsets;
 
+  CODING_ATTR_ASCII_COMPAT (attrs) = args[coding_arg_ascii_compatible_p];
+
   val = args[coding_arg_decode_translation_table];
   if (! NILP (val))
     CHECK_CHAR_TABLE (val);
@@ -7410,6 +7951,7 @@ usage: (define-coding-system-internal ...)  */)
 
   if (EQ (coding_type, Qcharset))
     {
+      Lisp_Object list;
       /* Generate a lisp vector of 256 elements.  Each element is nil,
         integer, or a list of charset IDs.
 
@@ -7423,14 +7965,31 @@ usage: (define-coding-system-internal ...)  */)
         of one of them.  The list is sorted by dimensions of the
         charsets.  A charset of smaller dimension comes firtst.
       */
+      for (list = Qnil, tail = charset_list; CONSP (tail); tail = XCDR (tail))
+       {
+         struct charset *charset = CHARSET_FROM_ID (XFASTINT (XCAR (tail)));
+
+         if (charset->method == CHARSET_METHOD_SUPERSET)
+           {
+             val = CHARSET_SUPERSET (charset);
+             for (; CONSP (val); val = XCDR (val))
+               list = Fcons (XCAR (XCAR (val)), list); 
+           }
+         else
+           list = Fcons (XCAR (tail), list);
+       }
+
       val = Fmake_vector (make_number (256), Qnil);
 
-      for (tail = charset_list; CONSP (tail); tail = XCDR (tail))
+      for (tail = Fnreverse (list); CONSP (tail); tail = XCDR (tail))
        {
          struct charset *charset = CHARSET_FROM_ID (XFASTINT (XCAR (tail)));
          int dim = CHARSET_DIMENSION (charset);
          int idx = (dim - 1) * 4;
          
+         if (CHARSET_ASCII_COMPATIBLE_P (charset))
+           CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
+
          for (i = charset->code_space[idx];
               i <= charset->code_space[idx + 1]; i++)
            {
@@ -7444,9 +8003,9 @@ usage: (define-coding-system-internal ...)  */)
                {
                  dim2 = CHARSET_DIMENSION (CHARSET_FROM_ID (XFASTINT (tmp)));
                  if (dim < dim2)
-                   tmp = Fcons (tmp, Fcons (XCAR (tail), Qnil));
-                 else
                    tmp = Fcons (XCAR (tail), Fcons (tmp, Qnil));
+                 else
+                   tmp = Fcons (tmp, Fcons (XCAR (tail), Qnil));
                }
              else
                {
@@ -7493,21 +8052,31 @@ usage: (define-coding-system-internal ...)  */)
       valids = Fmake_string (make_number (256), make_number (0));
       for (tail = val; !NILP (tail); tail = Fcdr (tail))
        {
+         int from, to;
+
          val = Fcar (tail);
          if (INTEGERP (val))
-           ASET (valids, XINT (val), 1);
+           {
+             from = to = XINT (val);
+             if (from < 0 || from > 255)
+               args_out_of_range_3 (val, make_number (0), make_number (255));
+           }
          else
            {
-             int from, to;
-
              CHECK_CONS (val);
              CHECK_NUMBER (XCAR (val));
              CHECK_NUMBER (XCDR (val));
              from = XINT (XCAR (val));
+             if (from < 0 || from > 255)
+               args_out_of_range_3 (XCAR (val),
+                                    make_number (0), make_number (255));
              to = XINT (XCDR (val));
-             for (i = from; i <= to; i++)
-               ASET (valids, i, 1);
+             if (to < from || to > 255)
+               args_out_of_range_3 (XCDR (val),
+                                    XCAR (val), make_number (255));
            }
+         for (i = from; i <= to; i++)
+           XSTRING (valids)->data[i] = 1;
        }
       ASET (attrs, coding_attr_ccl_valids, valids);
       
@@ -7517,6 +8086,8 @@ usage: (define-coding-system-internal ...)  */)
     {
       Lisp_Object bom, endian;
 
+      CODING_ATTR_ASCII_COMPAT (attrs) = Qnil;
+
       if (nargs < coding_arg_utf16_max)
        goto short_args;
 
@@ -7530,22 +8101,27 @@ usage: (define-coding-system-internal ...)  */)
       ASET (attrs, coding_attr_utf_16_bom, bom);
 
       endian = args[coding_arg_utf16_endian];
+      CHECK_SYMBOL (endian);
+      if (NILP (endian))
+       endian = Qbig;
+      else if (! EQ (endian, Qbig) && ! EQ (endian, Qlittle))
+       error ("Invalid endian: %s", XSYMBOL (endian)->name->data);
       ASET (attrs, coding_attr_utf_16_endian, endian);
 
       category = (CONSP (bom)
                  ? coding_category_utf_16_auto
                  : NILP (bom)
-                 ? (NILP (endian)
+                 ? (EQ (endian, Qbig)
                     ? coding_category_utf_16_be_nosig
                     : coding_category_utf_16_le_nosig)
-                 : (NILP (endian)
+                 : (EQ (endian, Qbig)
                     ? coding_category_utf_16_be
                     : coding_category_utf_16_le));
     }
   else if (EQ (coding_type, Qiso_2022))
     {
       Lisp_Object initial, reg_usage, request, flags;
-      int i, id;
+      int i;
 
       if (nargs < coding_arg_iso2022_max)
        goto short_args;
@@ -7557,8 +8133,12 @@ usage: (define-coding-system-internal ...)  */)
          val = Faref (initial, make_number (i));
          if (! NILP (val))
            {
-             CHECK_CHARSET_GET_ID (val, id);
-             ASET (initial, i, make_number (id));
+             struct charset *charset;
+
+             CHECK_CHARSET_GET_CHARSET (val, charset);
+             ASET (initial, i, make_number (CHARSET_ID (charset)));
+             if (i == 0 && CHARSET_ASCII_COMPATIBLE_P (charset))
+               CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
            }
          else
            ASET (initial, i, make_number (-1));
@@ -7606,8 +8186,7 @@ usage: (define-coding-system-internal ...)  */)
        {
          int id = XINT (AREF (initial, 1));
 
-         category = (((i & (CODING_ISO_FLAG_LOCKING_SHIFT
-                            | CODING_ISO_FLAG_SINGLE_SHIFT))
+         category = (((i & CODING_ISO_FLAG_LOCKING_SHIFT)
                       || EQ (args[coding_arg_charset_list], Qiso_2022)
                       || id < 0)
                      ? coding_category_iso_8_else
@@ -7615,12 +8194,15 @@ usage: (define-coding-system-internal ...)  */)
                      ? coding_category_iso_8_1
                      : coding_category_iso_8_2);
        }
+      if (category != coding_category_iso_8_1
+         && category != coding_category_iso_8_2)
+       CODING_ATTR_ASCII_COMPAT (attrs) = Qnil;
     }
   else if (EQ (coding_type, Qemacs_mule))
     {
       if (EQ (args[coding_arg_charset_list], Qemacs_mule))
        ASET (attrs, coding_attr_emacs_mule_full, Qt);
-
+      CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
       category = coding_category_emacs_mule;
     }
   else if (EQ (coding_type, Qshift_jis))
@@ -7635,6 +8217,8 @@ usage: (define-coding-system-internal ...)  */)
       if (CHARSET_DIMENSION (charset) != 1)
        error ("Dimension of charset %s is not one",
               XSYMBOL (CHARSET_NAME (charset))->name->data);
+      if (CHARSET_ASCII_COMPATIBLE_P (charset))
+       CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
 
       charset_list = XCDR (charset_list);
       charset = CHARSET_FROM_ID (XINT (XCAR (charset_list)));
@@ -7662,6 +8246,8 @@ usage: (define-coding-system-internal ...)  */)
       if (CHARSET_DIMENSION (charset) != 1)
        error ("Dimension of charset %s is not one",
               XSYMBOL (CHARSET_NAME (charset))->name->data);
+      if (CHARSET_ASCII_COMPATIBLE_P (charset))
+       CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
 
       charset_list = XCDR (charset_list);
       charset = CHARSET_FROM_ID (XINT (XCAR (charset_list)));
@@ -7673,9 +8259,15 @@ usage: (define-coding-system-internal ...)  */)
       Vbig5_coding_system = name;
     }
   else if (EQ (coding_type, Qraw_text))
-    category = coding_category_raw_text;
+    {
+      category = coding_category_raw_text;
+      CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
+    }
   else if (EQ (coding_type, Qutf_8))
-    category = coding_category_utf_8;
+    {
+      category = coding_category_utf_8;
+      CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
+    }
   else if (EQ (coding_type, Qundecided))
     category = coding_category_undecided;
   else
@@ -7737,6 +8329,8 @@ usage: (define-coding-system-internal ...)  */)
                         make_number (nargs)));
 }
 
+/* Fixme: should this record the alias relationships for
+   diagnostics?  Should it update coding-system-list?  */
 DEFUN ("define-coding-system-alias", Fdefine_coding_system_alias,
        Sdefine_coding_system_alias, 2, 2, 0,
        doc: /* Define ALIAS as an alias for CODING-SYSTEM.  */)
@@ -7776,7 +8370,7 @@ DEFUN ("define-coding-system-alias", Fdefine_coding_system_alias,
 DEFUN ("coding-system-base", Fcoding_system_base, Scoding_system_base,
        1, 1, 0,
        doc: /* Return the base of CODING-SYSTEM.
-Any alias or subsidiary coding systems are not base coding system.  */)
+Any alias or subsidiary coding system is not a base coding system.  */)
   (coding_system)
      Lisp_Object coding_system;
 {
@@ -7807,9 +8401,7 @@ DEFUN ("coding-system-plist", Fcoding_system_plist, Scoding_system_plist,
 
 DEFUN ("coding-system-aliases", Fcoding_system_aliases, Scoding_system_aliases,
        1, 1, 0,
-       doc: /* Return the list of aliases of CODING-SYSTEM.
-A base coding system is what made by `define-coding-system'.
-Any alias nor subsidiary coding systems are not base coding system.  */)
+       doc: /* Return the list of aliases of CODING-SYSTEM.  */)
      (coding_system)
      Lisp_Object coding_system;
 {
@@ -7818,7 +8410,7 @@ Any alias nor subsidiary coding systems are not base coding system.  */)
   if (NILP (coding_system))
     coding_system = Qno_conversion;
   CHECK_CODING_SYSTEM_GET_SPEC (coding_system, spec);
-  return AREF (spec, 2);
+  return AREF (spec, 1);
 }
 
 DEFUN ("coding-system-eol-type", Fcoding_system_eol_type,
@@ -7892,10 +8484,10 @@ init_coding_once ()
     {
       emacs_mule_bytes[i] = 1;
     }
-  emacs_mule_bytes[LEADING_CODE_PRIVATE_11] = 3;
-  emacs_mule_bytes[LEADING_CODE_PRIVATE_12] = 3;
-  emacs_mule_bytes[LEADING_CODE_PRIVATE_21] = 4;
-  emacs_mule_bytes[LEADING_CODE_PRIVATE_22] = 4;
+  emacs_mule_bytes[EMACS_MULE_LEADING_CODE_PRIVATE_11] = 3;
+  emacs_mule_bytes[EMACS_MULE_LEADING_CODE_PRIVATE_12] = 3;
+  emacs_mule_bytes[EMACS_MULE_LEADING_CODE_PRIVATE_21] = 4;
+  emacs_mule_bytes[EMACS_MULE_LEADING_CODE_PRIVATE_22] = 4;
 }
 
 #ifdef emacs
@@ -7950,7 +8542,6 @@ syms_of_coding ()
   DEFSYM (Qeol_type, "eol-type");
   DEFSYM (Qunix, "unix");
   DEFSYM (Qdos, "dos");
-  DEFSYM (Qmac, "mac");
 
   DEFSYM (Qbuffer_file_coding_system, "buffer-file-coding-system");
   DEFSYM (Qpost_read_conversion, "post-read-conversion");
@@ -7965,12 +8556,6 @@ syms_of_coding ()
   DEFSYM (Qutf_8, "utf-8");
 
   DEFSYM (Qutf_16, "utf-16");
-  DEFSYM (Qutf_16_be, "utf-16-be");
-  DEFSYM (Qutf_16_be_nosig, "utf-16-be-nosig");
-  DEFSYM (Qutf_16_le, "utf-16-l3");
-  DEFSYM (Qutf_16_le_nosig, "utf-16-le-nosig");
-  DEFSYM (Qsignature, "signature");
-  DEFSYM (Qendian, "endian");
   DEFSYM (Qbig, "big");
   DEFSYM (Qlittle, "little");