(detect_coding_iso_2022): Fix handling of SS2 and SS3.
[bpt/emacs.git] / src / coding.c
index c4f9274..457b1ac 100644 (file)
@@ -1,8 +1,8 @@
 /* Coding system handler (conversion, detection, etc).
    Copyright (C) 1995, 1997, 1998 Electrotechnical Laboratory, JAPAN.
-   Licensed to the Free Software Foundation.
+     Licensed to the Free Software Foundation.
    Copyright (C) 2001, 2002 Free Software Foundation, Inc.
-   Copyright (C) 2001, 2002
+   Copyright (C) 2003
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H13PRO009
 
@@ -86,7 +86,7 @@ CODING SYSTEM
   variants of ISO2022.
 
   o SJIS (or Shift-JIS or MS-Kanji-Code)
-   
+
   A coding system to encode character sets: ASCII, JISX0201, and
   JISX0208.  Widely used for PC's in Japan.  Details are described in
   section 8.
@@ -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
@@ -310,16 +308,23 @@ 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;
+Lisp_Object QCcategory, QCmnemonic, QCdefalut_char;
+Lisp_Object QCdecode_translation_table, QCencode_translation_table;
+Lisp_Object QCpost_read_conversion, QCpre_write_conversion;
 
 extern Lisp_Object Qinsert_file_contents, Qwrite_region;
 Lisp_Object Qcall_process, Qcall_process_region, Qprocess_argument;
 Lisp_Object Qstart_process, Qopen_network_stream;
 Lisp_Object Qtarget_idx;
 
+Lisp_Object Qinsufficient_source, Qinconsistent_eol, Qinvalid_source;
+Lisp_Object Qinterrupted, Qinsufficient_memory;
+
+int coding_system_require_warning;
+
 Lisp_Object Vselect_safe_coding_system_function;
 
 /* Mnemonic string for each format of end-of-line.  */
@@ -337,6 +342,7 @@ Lisp_Object Qcoding_system_p, Qcoding_system_error;
 /* Coding system emacs-mule and raw-text are for converting only
    end-of-line format.  */
 Lisp_Object Qemacs_mule, Qraw_text;
+Lisp_Object Qutf_8_emacs;
 
 /* Coding-systems are handed between Emacs Lisp programs and C internal
    routines by the following three variables.  */
@@ -346,7 +352,8 @@ Lisp_Object Vcoding_system_for_read;
 Lisp_Object Vcoding_system_for_write;
 /* Coding-system actually used in the latest I/O.  */
 Lisp_Object Vlast_coding_system_used;
-
+/* Set to non-nil when an error is detected while code conversion.  */
+Lisp_Object Vlast_code_conversion_error;
 /* A vector of length 256 which contains information about special
    Latin codes (especially for dealing with Microsoft codes).  */
 Lisp_Object Vlatin_extra_code_table;
@@ -397,42 +404,47 @@ static Lisp_Object Vcharset_revision_table;
 /* Default coding systems used for process I/O.  */
 Lisp_Object Vdefault_process_coding_system;
 
-/* Global flag to tell that we can't call post-read-conversion and
-   pre-write-conversion functions.  Usually the value is zero, but it
-   is set to 1 temporarily while such functions are running.  This is
-   to avoid infinite recursive call.  */
-static int inhibit_pre_post_conversion;
+/* Char table for translating Quail and self-inserting input.  */
+Lisp_Object Vtranslation_table_for_input;
 
 /* Two special coding systems.  */
 Lisp_Object Vsjis_coding_system;
 Lisp_Object Vbig5_coding_system;
 
-
-static int detect_coding_utf_8 P_ ((struct coding_system *, int *));
+static void record_conversion_result (struct coding_system *coding,
+                                     enum coding_result_code result);
+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 *));
 
@@ -486,7 +498,6 @@ enum iso_code_class_type
     ISO_control_0,             /* Control codes in the range
                                   0x00..0x1F and 0x7F, except for the
                                   following 5 codes.  */
-    ISO_carriage_return,       /* ISO_CODE_CR (0x0D) */
     ISO_shift_out,             /* ISO_CODE_SO (0x0E) */
     ISO_shift_in,              /* ISO_CODE_SI (0x0F) */
     ISO_single_shift_2_7,      /* ISO_CODE_SS2_7 (0x19) */
@@ -584,8 +595,7 @@ enum iso_code_class_type
 #define CODING_CCL_ENCODER(coding)     \
   AREF (CODING_ID_ATTRS ((coding)->id), coding_attr_ccl_encoder)
 #define CODING_CCL_VALIDS(coding)                                         \
-  (XSTRING (AREF (CODING_ID_ATTRS ((coding)->id), coding_attr_ccl_valids)) \
-   ->data)
+  (SDATA (AREF (CODING_ID_ATTRS ((coding)->id), coding_attr_ccl_valids)))
 
 /* Index for each coding category in `coding_categories' */
 
@@ -622,6 +632,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)
@@ -631,6 +642,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.  */
@@ -695,26 +707,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
@@ -724,52 +716,61 @@ static int detected_mask[coding_category_raw_text] =
 #define max(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
-#define CODING_GET_INFO(coding, attrs, eol_type, charset_list) \
-  do {                                                         \
-    attrs = CODING_ID_ATTRS (coding->id);                      \
-    eol_type = CODING_ID_EOL_TYPE (coding->id);                        \
-    if (VECTORP (eol_type))                                    \
-      eol_type = Qunix;                                                \
-    charset_list = CODING_ATTR_CHARSET_LIST (attrs);           \
+#define CODING_GET_INFO(coding, attrs, charset_list)   \
+  do {                                                 \
+    (attrs) = CODING_ID_ATTRS ((coding)->id);          \
+    (charset_list) = CODING_ATTR_CHARSET_LIST (attrs); \
   } while (0)
 
 
 /* Safely get one byte from the source text pointed by SRC which ends
    at SRC_END, and set C to that byte.  If there are not enough bytes
-   in the source, it jumps to `no_more_source'.  The caller
-   should declare and set these variables appropriately in advance:
-       src, src_end, multibytep
-*/
+   in the source, it jumps to `no_more_source'.  If multibytep is
+   nonzero, and a multibyte character is found at SRC, set C to the
+   negative value of the character code.  The caller should declare
+   and set these variables appropriately in advance:
+       src, src_end, multibytep */
 
-#define ONE_MORE_BYTE(c)                                       \
-  do {                                                         \
-    if (src == src_end)                                                \
-      {                                                                \
-       if (src_base < src)                                     \
-         coding->result = CODING_RESULT_INSUFFICIENT_SRC;      \
-       goto no_more_source;                                    \
-      }                                                                \
-    c = *src++;                                                        \
-    if (multibytep && (c & 0x80))                              \
-      {                                                                \
-       if ((c & 0xFE) != 0xC0)                                 \
-         error ("Undecodable char found");                     \
-       c = ((c & 1) << 6) | *src++;                            \
-      }                                                                \
-    consumed_chars++;                                          \
+#define ONE_MORE_BYTE(c)                               \
+  do {                                                 \
+    if (src == src_end)                                        \
+      {                                                        \
+       if (src_base < src)                             \
+         record_conversion_result                      \
+           (coding, CODING_RESULT_INSUFFICIENT_SRC);   \
+       goto no_more_source;                            \
+      }                                                        \
+    c = *src++;                                                \
+    if (multibytep && (c & 0x80))                      \
+      {                                                        \
+       if ((c & 0xFE) == 0xC0)                         \
+         c = ((c & 1) << 6) | *src++;                  \
+       else                                            \
+         {                                             \
+           c = - string_char (--src, &src, NULL);      \
+           record_conversion_result                    \
+             (coding, CODING_RESULT_INVALID_SRC);      \
+         }                                             \
+      }                                                        \
+    consumed_chars++;                                  \
   } while (0)
 
 
-#define ONE_MORE_BYTE_NO_CHECK(c)              \
-  do {                                         \
-    c = *src++;                                        \
-    if (multibytep && (c & 0x80))              \
-      {                                                \
-       if ((c & 0xFE) != 0xC0)                 \
-         error ("Undecodable char found");     \
-       c = ((c & 1) << 6) | *src++;            \
-      }                                                \
-    consumed_chars++;                          \
+#define ONE_MORE_BYTE_NO_CHECK(c)                      \
+  do {                                                 \
+    c = *src++;                                                \
+    if (multibytep && (c & 0x80))                      \
+      {                                                        \
+       if ((c & 0xFE) == 0xC0)                         \
+         c = ((c & 1) << 6) | *src++;                  \
+       else                                            \
+         {                                             \
+           c = - string_char (--src, &src, NULL);      \
+           record_conversion_result                    \
+             (coding, CODING_RESULT_INVALID_SRC);      \
+         }                                             \
+      }                                                        \
+    consumed_chars++;                                  \
   } while (0)
 
 
@@ -857,13 +858,38 @@ static int detected_mask[coding_category_raw_text] =
   } while (0)
 
 
+static void
+record_conversion_result (struct coding_system *coding,
+                         enum coding_result_code result)
+{
+  coding->result = result;
+  switch (result)
+    {
+    case CODING_RESULT_INSUFFICIENT_SRC:
+      Vlast_code_conversion_error = Qinsufficient_source;
+      break;
+    case CODING_RESULT_INCONSISTENT_EOL:
+      Vlast_code_conversion_error = Qinconsistent_eol;
+      break;
+    case CODING_RESULT_INVALID_SRC:
+      Vlast_code_conversion_error = Qinvalid_source;
+      break;
+    case CODING_RESULT_INTERRUPT:
+      Vlast_code_conversion_error = Qinterrupted;
+      break;
+    case CODING_RESULT_INSUFFICIENT_MEM:
+      Vlast_code_conversion_error = Qinsufficient_memory;
+      break;
+    }
+}
+
 #define CODING_DECODE_CHAR(coding, src, src_base, src_end, charset, code, c) \
   do {                                                                      \
     charset_map_loaded = 0;                                                 \
     c = DECODE_CHAR (charset, code);                                        \
     if (charset_map_loaded)                                                 \
       {                                                                             \
-       unsigned char *orig = coding->source;                                \
+       const unsigned char *orig = coding->source;                          \
        EMACS_INT offset;                                                    \
                                                                             \
        coding_set_source (coding);                                          \
@@ -894,23 +920,16 @@ 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))
     {
-      coding->source = (XSTRING (coding->src_object)->data
-                       + coding->src_pos_byte);
+      coding->source = SDATA (coding->src_object) + coding->src_pos_byte;
     }
   else
     /* Otherwise, the source is C string and is never relocated
@@ -982,12 +1001,12 @@ coding_alloc_by_making_gap (coding, bytes)
       set_buffer_internal (XBUFFER (this_buffer));
     }
 }
-     
+
 
 static unsigned char *
 alloc_destination (coding, nbytes, dst)
      struct coding_system *coding;
-     int nbytes;
+     EMACS_INT nbytes;
      unsigned char *dst;
 {
   EMACS_INT offset = dst - coding->destination;
@@ -996,12 +1015,60 @@ alloc_destination (coding, nbytes, dst)
     coding_alloc_by_making_gap (coding, nbytes);
   else
     coding_alloc_by_realloc (coding, nbytes);
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding_set_destination (coding);
   dst = coding->destination + offset;
   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) ***/
 
@@ -1011,8 +1078,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)
@@ -1022,17 +1089,17 @@ 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;
+  const unsigned char *src = coding->source, *src_base;
+  const 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;
 
@@ -1040,55 +1107,55 @@ detect_coding_utf_8 (coding, mask)
     {
       int c, c1, c2, c3, c4;
 
-      incomplete = 0;
+      src_base = src;
       ONE_MORE_BYTE (c);
-      if (UTF_8_1_OCTET_P (c))
+      if (c < 0 || UTF_8_1_OCTET_P (c))
        continue;
-      incomplete = 1;
       ONE_MORE_BYTE (c1);
-      if (! UTF_8_EXTRA_OCTET_P (c1))
+      if (c1 < 0 || ! 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);
-      if (! UTF_8_EXTRA_OCTET_P (c2))
+      if (c2 < 0 || ! UTF_8_EXTRA_OCTET_P (c2))
        break;
       if (UTF_8_3_OCTET_LEADING_P (c))
        {
-         found++;
+         found = CATEGORY_MASK_UTF_8;
          continue;
        }
       ONE_MORE_BYTE (c3);
-      if (! UTF_8_EXTRA_OCTET_P (c3))
+      if (c3 < 0 || ! UTF_8_EXTRA_OCTET_P (c3))
        break;
       if (UTF_8_4_OCTET_LEADING_P (c))
        {
-         found++;
+         found = CATEGORY_MASK_UTF_8;
          continue;
        }
       ONE_MORE_BYTE (c4);
-      if (! UTF_8_EXTRA_OCTET_P (c4))
+      if (c4 < 0 || ! UTF_8_EXTRA_OCTET_P (c4))
        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 (incomplete && coding->mode & CODING_MODE_LAST_BLOCK)
+  if (src_base < src && coding->mode & CODING_MODE_LAST_BLOCK)
     {
-      *mask &= ~CATEGORY_MASK_UTF_8;
+      detect_info->rejected |= CATEGORY_MASK_UTF_8;
       return 0;
     }
-  return found;
+  detect_info->found |= found;
+  return 1;
 }
 
 
@@ -1096,16 +1163,16 @@ static void
 decode_coding_utf_8 (coding)
      struct coding_system *coding;
 {
-  unsigned char *src = coding->source + coding->consumed;
-  unsigned char *src_end = coding->source + coding->src_bytes;
-  unsigned char *src_base;
+  const unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  const unsigned char *src_base;
   int *charbuf = coding->charbuf;
   int *charbuf_end = charbuf + coding->charbuf_size;
   int consumed_chars = 0, consumed_chars_base;
   int multibytep = coding->src_multibyte;
-  Lisp_Object attr, eol_type, charset_list;
+  Lisp_Object attr, charset_list;
 
-  CODING_GET_INFO (coding, attr, eol_type, charset_list);
+  CODING_GET_INFO (coding, attr, charset_list);
 
   while (1)
     {
@@ -1118,26 +1185,18 @@ decode_coding_utf_8 (coding)
        break;
 
       ONE_MORE_BYTE (c1);
-      if (UTF_8_1_OCTET_P(c1))
+      if (c1 < 0)
+       {
+         c = - c1;
+       }
+      else if (UTF_8_1_OCTET_P(c1))
        {
          c = c1;
-         if (c == '\r')
-           {
-             if (EQ (eol_type, Qdos))
-               {
-                 if (src == src_end)
-                   goto no_more_source;
-                 if (*src == '\n')
-                   ONE_MORE_BYTE (c);
-               }
-             else if (EQ (eol_type, Qmac))
-               c = '\n';
-           }
        }
       else
        {
          ONE_MORE_BYTE (c2);
-         if (! UTF_8_EXTRA_OCTET_P (c2))
+         if (c2 < 0 || ! UTF_8_EXTRA_OCTET_P (c2))
            goto invalid_code;
          if (UTF_8_2_OCTET_LEADING_P (c1))
            {
@@ -1151,7 +1210,7 @@ decode_coding_utf_8 (coding)
          else
            {
              ONE_MORE_BYTE (c3);
-             if (! UTF_8_EXTRA_OCTET_P (c3))
+             if (c3 < 0 || ! UTF_8_EXTRA_OCTET_P (c3))
                goto invalid_code;
              if (UTF_8_3_OCTET_LEADING_P (c1))
                {
@@ -1164,7 +1223,7 @@ decode_coding_utf_8 (coding)
              else
                {
                  ONE_MORE_BYTE (c4);
-                 if (! UTF_8_EXTRA_OCTET_P (c4))
+                 if (c4 < 0 || ! UTF_8_EXTRA_OCTET_P (c4))
                    goto invalid_code;
                  if (UTF_8_4_OCTET_LEADING_P (c1))
                    {
@@ -1176,7 +1235,7 @@ decode_coding_utf_8 (coding)
                  else
                    {
                      ONE_MORE_BYTE (c5);
-                     if (! UTF_8_EXTRA_OCTET_P (c5))
+                     if (c5 < 0 || ! UTF_8_EXTRA_OCTET_P (c5))
                        goto invalid_code;
                      if (UTF_8_5_OCTET_LEADING_P (c1))
                        {
@@ -1230,7 +1289,7 @@ encode_coding_utf_8 (coding)
       while (charbuf < charbuf_end)
        {
          unsigned char str[MAX_MULTIBYTE_LENGTH], *p, *pend = str;
-         
+
          ASSURE_DESTINATION (safe_room);
          c = *charbuf++;
          if (CHAR_BYTE8_P (c))
@@ -1258,7 +1317,7 @@ encode_coding_utf_8 (coding)
          produced_chars++;
        }
     }
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->produced_char += produced_chars;
   coding->produced = dst - coding->destination;
   return 0;
@@ -1266,10 +1325,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)
@@ -1284,40 +1341,78 @@ 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;
+  const unsigned char *src = coding->source, *src_base = src;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
   int consumed_chars = 0;
   int c1, c2;
 
-  *mask &= ~CATEGORY_MASK_UTF_16;
+  detect_info->checked |= CATEGORY_MASK_UTF_16;
+  if (coding->mode & CODING_MODE_LAST_BLOCK
+      && (coding->src_chars & 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;
+    {
+      detect_info->found |= (CATEGORY_MASK_UTF_16_LE
+                            | CATEGORY_MASK_UTF_16_AUTO);
+      detect_info->rejected |= (CATEGORY_MASK_UTF_16_BE
+                               | CATEGORY_MASK_UTF_16_BE_NOSIG
+                               | CATEGORY_MASK_UTF_16_LE_NOSIG);
+    }
   else if ((c1 == 0xFE) && (c2 == 0xFF))
-    *mask |= CATEGORY_MASK_UTF_16_BE;
-  else
-    *mask |= CATEGORY_MASK_UTF_16_BE_NOSIG | CATEGORY_MASK_UTF_16_LE_NOSIG;
-  return 1;
+    {
+      detect_info->found |= (CATEGORY_MASK_UTF_16_BE
+                            | CATEGORY_MASK_UTF_16_AUTO);
+      detect_info->rejected |= (CATEGORY_MASK_UTF_16_LE
+                               | CATEGORY_MASK_UTF_16_BE_NOSIG
+                               | CATEGORY_MASK_UTF_16_LE_NOSIG);
+    }
+  else if (c1 >= 0 && c2 >= 0)
+    {
+      unsigned char b1[256], b2[256];
+      int b1_variants = 1, b2_variants = 1;
+      int n;
 
+      bzero (b1, 256), bzero (b2, 256);
+      b1[c1]++, b2[c2]++;
+      for (n = 0; n < 256 && src < src_end; n++)
+       {
+         src_base = src;
+         ONE_MORE_BYTE (c1);
+         ONE_MORE_BYTE (c2);
+         if (c1 < 0 || c2 < 0)
+           break;
+         if (! b1[c1++]) b1_variants++;
+         if (! b2[c2++]) b2_variants++;
+       }
+      if (b1_variants < b2_variants)
+       detect_info->found |= CATEGORY_MASK_UTF_16_BE_NOSIG;
+      else
+       detect_info->found |= CATEGORY_MASK_UTF_16_LE_NOSIG;      
+      detect_info->rejected
+       |= (CATEGORY_MASK_UTF_16_BE | CATEGORY_MASK_UTF_16_LE);
+    }
  no_more_source:
-  return 0;
+  return 1;
 }
 
 static void
 decode_coding_utf_16 (coding)
      struct coding_system *coding;
 {
-  unsigned char *src = coding->source + coding->consumed;
-  unsigned char *src_end = coding->source + coding->src_bytes;
-  unsigned char *src_base;
+  const unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  const unsigned char *src_base;
   int *charbuf = coding->charbuf;
   int *charbuf_end = charbuf + coding->charbuf_size;
   int consumed_chars = 0, consumed_chars_base;
@@ -1325,11 +1420,11 @@ decode_coding_utf_16 (coding)
   enum utf_16_bom_type bom = CODING_UTF_16_BOM (coding);
   enum utf_16_endian_type endian = CODING_UTF_16_ENDIAN (coding);
   int surrogate = CODING_UTF_16_SURROGATE (coding);
-  Lisp_Object attr, eol_type, charset_list;
+  Lisp_Object attr, charset_list;
 
-  CODING_GET_INFO (coding, attr, eol_type, charset_list);
+  CODING_GET_INFO (coding, attr, charset_list);
 
-  if (bom != utf_16_without_bom)
+  if (bom == utf_16_with_bom)
     {
       int c, c1, c2;
 
@@ -1337,33 +1432,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)
@@ -1377,7 +1461,18 @@ decode_coding_utf_16 (coding)
        break;
 
       ONE_MORE_BYTE (c1);
+      if (c1 < 0)
+       {
+         *charbuf++ = -c1;
+         continue;
+       }
       ONE_MORE_BYTE (c2);
+      if (c2 < 0)
+       {
+         *charbuf++ = ASCII_BYTE_P (c1) ? c1 : BYTE8_TO_CHAR (c1);
+         *charbuf++ = -c2;
+         continue;
+       }
       c = (endian == utf_16_big_endian
           ? ((c1 << 8) | c2) : ((c2 << 8) | c1));
       if (surrogate)
@@ -1409,7 +1504,7 @@ decode_coding_utf_16 (coding)
            CODING_UTF_16_SURROGATE (coding) = surrogate = c;
          else
            *charbuf++ = c;
-       }         
+       }
     }
 
  no_more_source:
@@ -1431,18 +1526,18 @@ encode_coding_utf_16 (coding)
   enum utf_16_bom_type bom = CODING_UTF_16_BOM (coding);
   int big_endian = CODING_UTF_16_ENDIAN (coding) == utf_16_big_endian;
   int produced_chars = 0;
-  Lisp_Object attrs, eol_type, charset_list;
+  Lisp_Object attrs, charset_list;
   int c;
 
-  CODING_GET_INFO (coding, attrs, eol_type, charset_list);
+  CODING_GET_INFO (coding, attrs, 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;
     }
 
@@ -1473,7 +1568,7 @@ encode_coding_utf_16 (coding)
            EMIT_FOUR_BYTES (c1 & 0xFF, c1 >> 8, c2 & 0xFF, c2 >> 8);
        }
     }
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->produced = dst - coding->destination;
   coding->produced_char += produced_chars;
   return 0;
@@ -1556,74 +1651,96 @@ encode_coding_utf_16 (coding)
 char emacs_mule_bytes[256];
 
 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;
+     const unsigned char *src;
+     int *nbytes, *nchars, *id;
 {
-  unsigned char *src_end = coding->source + coding->src_bytes;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  const unsigned char *src_base = src;
   int multibytep = coding->src_multibyte;
-  unsigned char *src_base = src;
   struct charset *charset;
   unsigned code;
   int c;
   int consumed_chars = 0;
 
   ONE_MORE_BYTE (c);
-  switch (emacs_mule_bytes[c])
+  if (c < 0)
     {
-    case 2:
-      if (! (charset = emacs_mule_charset[c]))
-       goto invalid_code;
-      ONE_MORE_BYTE (c);
-      code = c & 0x7F;
-      break;
-
-    case 3:
-      if (c == EMACS_MULE_LEADING_CODE_PRIVATE_11
-         || c == EMACS_MULE_LEADING_CODE_PRIVATE_12)
+      c = -c;
+      charset = emacs_mule_charset[0];
+    }
+  else
+    {
+      switch (emacs_mule_bytes[c])
        {
-         ONE_MORE_BYTE (c);
+       case 2:
          if (! (charset = emacs_mule_charset[c]))
            goto invalid_code;
          ONE_MORE_BYTE (c);
+         if (c < 0)
+           goto invalid_code;
          code = c & 0x7F;
-       }
-      else
-       {
-         if (! (charset = emacs_mule_charset[c]))
+         break;
+
+       case 3:
+         if (c == EMACS_MULE_LEADING_CODE_PRIVATE_11
+             || c == EMACS_MULE_LEADING_CODE_PRIVATE_12)
+           {
+             ONE_MORE_BYTE (c);
+             if (c < 0 || ! (charset = emacs_mule_charset[c]))
+               goto invalid_code;
+             ONE_MORE_BYTE (c);
+             if (c < 0)
+               goto invalid_code;
+             code = c & 0x7F;
+           }
+         else
+           {
+             if (! (charset = emacs_mule_charset[c]))
+               goto invalid_code;
+             ONE_MORE_BYTE (c);
+             if (c < 0)
+               goto invalid_code;
+             code = (c & 0x7F) << 8;
+             ONE_MORE_BYTE (c);
+             if (c < 0)
+               goto invalid_code;
+             code |= c & 0x7F;
+           }
+         break;
+
+       case 4:
+         ONE_MORE_BYTE (c);
+         if (c < 0 || ! (charset = emacs_mule_charset[c]))
            goto invalid_code;
          ONE_MORE_BYTE (c);
+         if (c < 0)
+           goto invalid_code;
          code = (c & 0x7F) << 8;
          ONE_MORE_BYTE (c);
+         if (c < 0)
+           goto invalid_code;
          code |= c & 0x7F;
-       }
-      break;
-
-    case 4:
-      ONE_MORE_BYTE (c);
-      if (! (charset = emacs_mule_charset[c]))
-       goto invalid_code;
-      ONE_MORE_BYTE (c);
-      code = (c & 0x7F) << 8;
-      ONE_MORE_BYTE (c);
-      code |= c & 0x7F;
-      break;
+         break;
 
-    case 1:
-      code = c;
-      charset = CHARSET_FROM_ID (ASCII_BYTE_P (code)
-                                ? charset_ascii : charset_eight_bit);
-      break;
+       case 1:
+         code = c;
+         charset = CHARSET_FROM_ID (ASCII_BYTE_P (code)
+                                    ? charset_ascii : charset_eight_bit);
+         break;
 
-    default:
-      abort ();
+       default:
+         abort ();
+       }
+      c = DECODE_CHAR (charset, code);
+      if (c < 0)
+       goto invalid_code;
     }
-  c = DECODE_CHAR (charset, code);
-  if (c < 0)
-    goto invalid_code;
   *nbytes = src - src_base;
   *nchars = consumed_chars;
+  if (id)
+    *id = charset->id;
   return c;
 
  no_more_source:
@@ -1635,37 +1752,38 @@ 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;
+  const unsigned char *src = coding->source, *src_base;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
   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;
+      src_base = src;
       ONE_MORE_BYTE (c);
-      incomplete = 1;
-
+      if (c < 0)
+       continue;
       if (c == 0x80)
        {
          /* Perhaps the start of composite character.  We simple skip
             it because analyzing it is too heavy for detecting.  But,
             at least, we check that the composite character
             constitues of more than 4 bytes.  */
-         unsigned char *src_base;
+         const unsigned char *src_base;
 
        repeat:
          src_base = src;
@@ -1677,7 +1795,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;
        }
@@ -1690,7 +1808,7 @@ detect_coding_emacs_mule (coding, mask)
        }
       else
        {
-         unsigned char *src_base = src - 1;
+         const unsigned char *src_base = src - 1;
 
          do
            {
@@ -1699,19 +1817,20 @@ 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 (incomplete && coding->mode & CODING_MODE_LAST_BLOCK)
+  if (src_base < src && coding->mode & CODING_MODE_LAST_BLOCK)
     {
-      *mask &= ~CATEGORY_MASK_EMACS_MULE;
+      detect_info->rejected |= CATEGORY_MASK_EMACS_MULE;
       return 0;
     }
-  return found;
+  detect_info->found |= found;
+  return 1;
 }
 
 
@@ -1732,7 +1851,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)                                          \
@@ -1789,16 +1908,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                \
@@ -1807,16 +1916,23 @@ 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;                                                        \
                                                                        \
     ONE_MORE_BYTE (c);                                                 \
+    if (c < 0)                                                         \
+      goto invalid_code;                                               \
     nbytes = c - 0xA0;                                                 \
     if (nbytes < 3)                                                    \
       goto invalid_code;                                               \
     ONE_MORE_BYTE (c);                                                 \
+    if (c < 0)                                                         \
+      goto invalid_code;                                               \
     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)                                        \
       {                                                                        \
@@ -1840,9 +1956,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 */                 \
@@ -1850,7 +1968,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)
@@ -1860,9 +1980,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++)           \
@@ -1874,7 +1996,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)                                 \
@@ -1886,17 +2010,19 @@ static void
 decode_coding_emacs_mule (coding)
      struct coding_system *coding;
 {
-  unsigned char *src = coding->source + coding->consumed;
-  unsigned char *src_end = coding->source + coding->src_bytes;
-  unsigned char *src_base;
+  const unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  const 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;
+  Lisp_Object attrs, 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);
+  CODING_GET_INFO (coding, attrs, charset_list);
 
   while (1)
     {
@@ -1909,29 +2035,21 @@ decode_coding_emacs_mule (coding)
        break;
 
       ONE_MORE_BYTE (c);
-
-      if (c < 0x80)
+      if (c < 0)
+       {
+         *charbuf++ = -c;
+         char_offset++;
+       }
+      else if (c < 0x80)
        {
-         if (c == '\r')
-           {
-             if (EQ (eol_type, Qdos))
-               {
-                 if (src == src_end)
-                   goto no_more_source;
-                 if (*src == '\n')
-                   ONE_MORE_BYTE (c);
-               }
-             else if (EQ (eol_type, Qmac))
-               c = '\n';
-           }
          *charbuf++ = c;
          char_offset++;
        }
       else if (c == 0x80)
        {
-         if (charbuf + 5 + (MAX_COMPOSITION_COMPONENTS * 2) - 1 > charbuf_end)
-           break;
          ONE_MORE_BYTE (c);
+         if (c < 0)
+           goto invalid_code;
          if (c - 0xF2 >= COMPOSITION_RELATIVE
              && c - 0xF2 <= COMPOSITION_WITH_RULE_ALTCHARS)
            DECODE_EMACS_MULE_21_COMPOSITION (c);
@@ -1941,20 +2059,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;
@@ -1967,10 +2093,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;
@@ -2003,15 +2132,44 @@ encode_coding_emacs_mule (coding)
   unsigned char *dst_end = coding->destination + coding->dst_bytes;
   int safe_room = 8;
   int produced_chars = 0;
-  Lisp_Object attrs, eol_type, charset_list;
+  Lisp_Object attrs, charset_list;
   int c;
+  int preferred_charset_id = -1;
 
-  CODING_GET_INFO (coding, attrs, eol_type, charset_list);
+  CODING_GET_INFO (coding, attrs, charset_list);
+  if (! EQ (charset_list, Vemacs_mule_charset_list))
+    {
+      CODING_ATTR_CHARSET_LIST (attrs)
+       = charset_list = Vemacs_mule_charset_list;
+    }
 
   while (charbuf < charbuf_end)
     {
       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))
@@ -2027,7 +2185,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;
@@ -2045,15 +2210,16 @@ encode_coding_emacs_mule (coding)
          if (leading_codes[1])
            EMIT_ONE_BYTE (leading_codes[1]);
          if (dimension == 1)
-           EMIT_ONE_BYTE (code);
+           EMIT_ONE_BYTE (code | 0x80);
          else
            {
+             code |= 0x8080;
              EMIT_ONE_BYTE (code >> 8);
              EMIT_ONE_BYTE (code & 0xFF);
            }
        }
     }
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->produced_char += produced_chars;
   coding->produced = dst - coding->destination;
   return 0;
@@ -2296,16 +2462,16 @@ setup_iso_safe_charsets (attrs)
       charset = CHARSET_FROM_ID (XINT (id));
       reg = Fcdr (Fassq (id, request));
       if (! NILP (reg))
-       XSTRING (safe_charsets)->data[XINT (id)] = XINT (reg);
+       SSET (safe_charsets, XINT (id), XINT (reg));
       else if (charset->iso_chars_96)
        {
          if (reg96 < 4)
-           XSTRING (safe_charsets)->data[XINT (id)] = reg96;
+           SSET (safe_charsets, XINT (id), reg96);
        }
       else
        {
          if (reg94 < 4)
-           XSTRING (safe_charsets)->data[XINT (id)] = reg94;
+           SSET (safe_charsets, XINT (id), reg94);
        }
     }
   ASET (attrs, coding_attr_safe_charsets, safe_charsets);
@@ -2313,32 +2479,26 @@ setup_iso_safe_charsets (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;
+  const unsigned char *src = coding->source, *src_base = src;
+  const 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++)
     {
@@ -2350,16 +2510,16 @@ detect_coding_iso_2022 (coding, mask)
          && ! EQ (CODING_ATTR_SAFE_CHARSETS (attrs), Viso_2022_charset_list))
        setup_iso_safe_charsets (attrs);
       val = CODING_ATTR_SAFE_CHARSETS (attrs);
-      this->max_charset_id = XSTRING (val)->size - 1;
-      this->safe_charsets = (char *) XSTRING (val)->data;
+      this->max_charset_id = SCHARS (val) - 1;
+      this->safe_charsets = (char *) SDATA (val);
     }
 
   /* 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)
     {
+      src_base = src;
       ONE_MORE_BYTE (c);
       switch (c)
        {
@@ -2376,7 +2536,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 == '$')
            {
@@ -2384,7 +2543,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);
@@ -2392,156 +2551,106 @@ 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:
+       case ISO_CODE_SI:
+         /* Locking shift out/in.  */
          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:
-         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;
-
-           mask_8bit_found = 1;
-           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 = 0;
+         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, single_shifting = 1;
+         if (CODING_ISO_FLAGS (&coding_categories[coding_category_iso_8_2])
+             & CODING_ISO_FLAG_SINGLE_SHIFT)
+           found |= CATEGORY_MASK_ISO_8_2, single_shifting = 1;
+         if (single_shifting)
+           break;
+         goto check_extra_latin;
 
        default:
+         if (c < 0)
+           continue;
          if (c < 0x80)
            {
              single_shifting = 0;
              break;
            }
-         else 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
+         if (c >= 0xA0)
            {
-             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)
@@ -2553,26 +2662,34 @@ 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;
+         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 &= ~CATEGORY_MASK_ISO;
-  *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;
 }
 
@@ -2652,7 +2769,7 @@ detect_coding_iso_2022 (coding, mask)
       }                                                                        \
     else                                                               \
       {                                                                        \
-       unsigned char *p;                                               \
+       const unsigned char *p;                                         \
                                                                        \
        MAYBE_FINISH_COMPOSITION ();                                    \
        if (charbuf + MAX_COMPOSITION_COMPONENTS > charbuf_end)         \
@@ -2688,8 +2805,10 @@ detect_coding_iso_2022 (coding, mask)
                  : (component_idx + 1) / 2);                           \
     int i;                                                             \
     int *saved_charbuf = charbuf;                                      \
+    int from = 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)                                         \
@@ -2742,13 +2861,13 @@ static void
 decode_coding_iso_2022 (coding)
      struct coding_system *coding;
 {
-  unsigned char *src = coding->source + coding->consumed;
-  unsigned char *src_end = coding->source + coding->src_bytes;
-  unsigned char *src_base;
+  const unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  const 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);
@@ -2767,9 +2886,12 @@ decode_coding_iso_2022 (coding)
   int components[MAX_COMPOSITION_COMPONENTS * 2 + 1];
   int component_idx;
   int component_len;
-  Lisp_Object attrs, eol_type, charset_list;
+  Lisp_Object attrs, 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);
+  CODING_GET_INFO (coding, attrs, charset_list);
   setup_iso_safe_charsets (attrs);
 
   while (1)
@@ -2783,8 +2905,10 @@ decode_coding_iso_2022 (coding)
        break;
 
       ONE_MORE_BYTE (c1);
+      if (c1 < 0)
+       goto invalid_code;
 
-      /* We produce no character or one character.  */
+      /* We produce at most one character.  */
       switch (iso_code_class [c1])
        {
        case ISO_0x20_or_0x7F:
@@ -2835,21 +2959,6 @@ decode_coding_iso_2022 (coding)
          charset = CHARSET_FROM_ID (charset_id_1);
          break;
 
-       case ISO_carriage_return:
-         if (c1 == '\r')
-           {
-             if (EQ (eol_type, Qdos))
-               {
-                 if (src == src_end)
-                   goto no_more_source;
-                 if (*src == '\n')
-                   ONE_MORE_BYTE (c1);
-               }
-             else if (EQ (eol_type, Qmac))
-               c1 = '\n';
-           }
-         /* fall through */
-
        case ISO_control_0:
          MAYBE_FINISH_COMPOSITION ();
          charset = CHARSET_FROM_ID (charset_ascii);
@@ -3020,6 +3129,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;
@@ -3042,6 +3215,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.  */
@@ -3095,11 +3277,18 @@ decode_coding_iso_2022 (coding)
       src = src_base;
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
-      *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      *charbuf++ = c < 0 ? -c : 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;
@@ -3521,11 +3710,17 @@ 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, charset_list);
+  eol_type = CODING_ID_EOL_TYPE (coding->id);
+  if (VECTORP (eol_type))
+    eol_type = Qunix;
 
-  CODING_GET_INFO (coding, attrs, eol_type, charset_list);
   setup_iso_safe_charsets (attrs);
-  coding->safe_charsets
-    = (char *) XSTRING (CODING_ATTR_SAFE_CHARSETS(attrs))->data;
+  /* Charset list may have been changed.  */
+  charset_list = CODING_ATTR_CHARSET_LIST (attrs);             \
+  coding->safe_charsets = (char *) SDATA (CODING_ATTR_SAFE_CHARSETS(attrs));
 
   ascii_compatible = ! NILP (CODING_ATTR_ASCII_COMPAT (attrs));
 
@@ -3546,6 +3741,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)
        {
@@ -3586,8 +3803,16 @@ encode_coding_iso_2022 (coding)
        }
       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)
@@ -3611,7 +3836,7 @@ encode_coding_iso_2022 (coding)
       ASSURE_DESTINATION (safe_room);
       ENCODE_RESET_PLANE_AND_REGISTER ();
     }
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   CODING_ISO_BOL (coding) = bol_designation;
   coding->produced_char += produced_chars;
   coding->produced = dst - coding->destination;
@@ -3660,26 +3885,25 @@ 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;
+  const unsigned char *src = coding->source, *src_base;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
   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;
+      src_base = src;
       ONE_MORE_BYTE (c);
-      incomplete = 1;
       if (c < 0x80)
        continue;
       if ((c >= 0x81 && c <= 0x9F) || (c >= 0xE0 && c <= 0xEF))
@@ -3687,23 +3911,24 @@ 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 (incomplete && coding->mode & CODING_MODE_LAST_BLOCK)
+  if (src_base < src && coding->mode & CODING_MODE_LAST_BLOCK)
     {
-      *mask &= ~CATEGORY_MASK_SJIS;
+      detect_info->rejected |= CATEGORY_MASK_SJIS;
       return 0;
     }
-  return found;
+  detect_info->found |= found;
+  return 1;
 }
 
 /* See the above "GENERAL NOTES on `detect_coding_XXX ()' functions".
@@ -3711,26 +3936,25 @@ 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;
+  const unsigned char *src = coding->source, *src_base;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
   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;
+      src_base = src;
       ONE_MORE_BYTE (c);
-      incomplete = 1;
       if (c < 0x80)
        continue;
       if (c >= 0xA1)
@@ -3738,21 +3962,22 @@ 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 (incomplete && coding->mode & CODING_MODE_LAST_BLOCK)
+  if (src_base < src && coding->mode & CODING_MODE_LAST_BLOCK)
     {
-      *mask &= ~CATEGORY_MASK_BIG5;
+      detect_info->rejected |= CATEGORY_MASK_BIG5;
       return 0;
     }
-  return found;
+  detect_info->found |= found;
+  return 1;
 }
 
 /* See the above "GENERAL NOTES on `decode_coding_XXX ()' functions".
@@ -3762,26 +3987,32 @@ static void
 decode_coding_sjis (coding)
      struct coding_system *coding;
 {
-  unsigned char *src = coding->source + coding->consumed;
-  unsigned char *src_end = coding->source + coding->src_bytes;
-  unsigned char *src_base;
+  const unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  const 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;
+  struct charset *charset_kanji2;
+  Lisp_Object attrs, 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);
+  CODING_GET_INFO (coding, attrs, charset_list);
 
   val = charset_list;
   charset_roman = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
   charset_kana = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
-  charset_kanji = CHARSET_FROM_ID (XINT (XCAR (val)));
+  charset_kanji = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
+  charset_kanji2 = NILP (val) ? NULL : CHARSET_FROM_ID (XINT (XCAR (val)));
 
   while (1)
     {
       int c, c1;
+      struct charset *charset;
 
       src_base = src;
       consumed_chars_base = consumed_chars;
@@ -3790,60 +4021,65 @@ decode_coding_sjis (coding)
        break;
 
       ONE_MORE_BYTE (c);
-
-      if (c == '\r')
+      if (c < 0)
+       goto invalid_code;
+      if (c < 0x80)
+       charset = charset_roman;
+      else if (c == 0x80 || c == 0xA0)
+       goto invalid_code;
+      else if (c >= 0xA1 && c <= 0xDF)
        {
-         if (EQ (eol_type, Qdos))
-           {
-             if (src == src_end)
-               goto no_more_source;
-             if (*src == '\n')
-               ONE_MORE_BYTE (c);
-           }
-         else if (EQ (eol_type, Qmac))
-           c = '\n';
+         /* SJIS -> JISX0201-Kana */
+         c &= 0x7F;
+         charset = charset_kana;
+       }
+      else if (c <= 0xEF)
+       {
+         /* SJIS -> JISX0208 */
+         ONE_MORE_BYTE (c1);
+         if (c1 < 0x40 || c1 == 0x7F || c1 > 0xFC)
+           goto invalid_code;
+         c = (c << 8) | c1;
+         SJIS_TO_JIS (c);
+         charset = charset_kanji;
+       }
+      else if (c <= 0xFC && charset_kanji2)
+       {
+         /* SJIS -> JISX0213-2 */
+         ONE_MORE_BYTE (c1);
+         if (c1 < 0x40 || c1 == 0x7F || c1 > 0xFC)
+           goto invalid_code;
+         c = (c << 8) | c1;
+         SJIS_TO_JIS2 (c);
+         charset = charset_kanji2;
        }
       else
+       goto invalid_code;
+      if (charset->id != charset_ascii
+         && last_id != charset->id)
        {
-         struct charset *charset;
-
-         if (c < 0x80)
-           charset = charset_roman;
-         else
-           {
-             if (c >= 0xF0)
-               goto invalid_code;
-             if (c < 0xA0 || c >= 0xE0)
-               {
-                 /* SJIS -> JISX0208 */
-                 ONE_MORE_BYTE (c1);
-                 if (c1 < 0x40 || c1 == 0x7F || c1 > 0xFC)
-                   goto invalid_code;
-                 c = (c << 8) | c1;
-                 SJIS_TO_JIS (c);
-                 charset = charset_kanji;
-               }
-             else
-               {
-                 /* SJIS -> JISX0201-Kana */
-                 c &= 0x7F;
-                 charset = charset_kana;
-               }
-           }
-         CODING_DECODE_CHAR (coding, src, src_base, src_end, charset, c, c);
+         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:
       src = src_base;
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
-      *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      *charbuf++ = c < 0 ? -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;
@@ -3853,17 +4089,20 @@ static void
 decode_coding_big5 (coding)
      struct coding_system *coding;
 {
-  unsigned char *src = coding->source + coding->consumed;
-  unsigned char *src_end = coding->source + coding->src_bytes;
-  unsigned char *src_base;
+  const unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  const 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;
+  Lisp_Object attrs, 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);
+  CODING_GET_INFO (coding, attrs, charset_list);
   val = charset_list;
   charset_roman = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
   charset_big5 = CHARSET_FROM_ID (XINT (XCAR (val)));
@@ -3871,6 +4110,7 @@ decode_coding_big5 (coding)
   while (1)
     {
       int c, c1;
+      struct charset *charset;
 
       src_base = src;
       consumed_chars_base = consumed_chars;
@@ -3880,49 +4120,46 @@ decode_coding_big5 (coding)
 
       ONE_MORE_BYTE (c);
 
-      if (c == '\r')
+      if (c < 0)
+       goto invalid_code;
+      if (c < 0x80)
+       charset = charset_roman;
+      else
        {
-         if (EQ (eol_type, Qdos))
-           {
-             if (src == src_end)
-               goto no_more_source;
-             if (*src == '\n')
-               ONE_MORE_BYTE (c);
-           }
-         else if (EQ (eol_type, Qmac))
-           c = '\n';
+         /* BIG5 -> Big5 */
+         if (c < 0xA1 || c > 0xFE)
+           goto invalid_code;
+         ONE_MORE_BYTE (c1);
+         if (c1 < 0x40 || (c1 > 0x7E && c1 < 0xA1) || c1 > 0xFE)
+           goto invalid_code;
+         c = c << 8 | c1;
+         charset = charset_big5;
        }
-      else
+      if (charset->id != charset_ascii
+         && last_id != charset->id)
        {
-         struct charset *charset;
-         if (c < 0x80)
-           charset = charset_roman;
-         else
-           {
-             /* BIG5 -> Big5 */
-             if (c < 0xA1 || c > 0xFE)
-               goto invalid_code;
-             ONE_MORE_BYTE (c1);
-             if (c1 < 0x40 || (c1 > 0x7E && c1 < 0xA1) || c1 > 0xFE)
-               goto invalid_code;
-             c = c << 8 | c1;
-             charset = charset_big5;
-           }
-         CODING_DECODE_CHAR (coding, src, src_base, src_end, charset, c, c);
+         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:
       src = src_base;
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
-      *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      *charbuf++ = c < 0 ? -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;
@@ -3947,16 +4184,18 @@ encode_coding_sjis (coding)
   unsigned char *dst_end = coding->destination + coding->dst_bytes;
   int safe_room = 4;
   int produced_chars = 0;
-  Lisp_Object attrs, eol_type, charset_list, val;
+  Lisp_Object attrs, charset_list, val;
   int ascii_compatible;
   struct charset *charset_roman, *charset_kanji, *charset_kana;
+  struct charset *charset_kanji2;
   int c;
 
-  CODING_GET_INFO (coding, attrs, eol_type, charset_list);
+  CODING_GET_INFO (coding, attrs, charset_list);
   val = charset_list;
   charset_roman = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
   charset_kana = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
-  charset_kanji = CHARSET_FROM_ID (XINT (XCAR (val)));
+  charset_kanji = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
+  charset_kanji2 = NILP (val) ? NULL : CHARSET_FROM_ID (XINT (XCAR (val)));
 
   ascii_compatible = ! NILP (CODING_ATTR_ASCII_COMPAT (attrs));
 
@@ -4001,11 +4240,26 @@ encode_coding_sjis (coding)
            }
          else if (charset == charset_kana)
            EMIT_ONE_BYTE (code | 0x80);
+         else if (charset_kanji2 && charset == charset_kanji2)
+           {
+             int c1, c2;
+
+             c1 = code >> 8;
+             if (c1 == 0x21 || (c1 >= 0x23 && c1 < 0x25)
+                 || (c1 >= 0x2C && c1 <= 0x2F) || c1 >= 0x6E)
+               {
+                 JIS_TO_SJIS2 (code);
+                 c1 = code >> 8, c2 = code & 0xFF;
+                 EMIT_TWO_BYTES (c1, c2);
+               }
+             else
+               EMIT_ONE_ASCII_BYTE (code & 0x7F);
+           }
          else
            EMIT_ONE_ASCII_BYTE (code & 0x7F);
        }
     }
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->produced_char += produced_chars;
   coding->produced = dst - coding->destination;
   return 0;
@@ -4022,12 +4276,12 @@ encode_coding_big5 (coding)
   unsigned char *dst_end = coding->destination + coding->dst_bytes;
   int safe_room = 4;
   int produced_chars = 0;
-  Lisp_Object attrs, eol_type, charset_list, val;
+  Lisp_Object attrs, charset_list, val;
   int ascii_compatible;
   struct charset *charset_roman, *charset_big5;
   int c;
 
-  CODING_GET_INFO (coding, attrs, eol_type, charset_list);
+  CODING_GET_INFO (coding, attrs, charset_list);
   val = charset_list;
   charset_roman = CHARSET_FROM_ID (XINT (XCAR (val))), val = XCDR (val);
   charset_big5 = CHARSET_FROM_ID (XINT (XCAR (val)));
@@ -4076,7 +4330,7 @@ encode_coding_big5 (coding)
            EMIT_ONE_ASCII_BYTE (code & 0x7F);
        }
     }
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->produced_char += produced_chars;
   coding->produced = dst - coding->destination;
   return 0;
@@ -4091,12 +4345,12 @@ 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;
+  const unsigned char *src = coding->source, *src_base;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
   int multibytep = coding->src_multibyte;
   int consumed_chars = 0;
   int found = 0;
@@ -4104,6 +4358,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)))
@@ -4112,17 +4368,20 @@ detect_coding_ccl (coding, mask)
   while (1)
     {
       int c;
+
+      src_base = src;
       ONE_MORE_BYTE (c);
-      if (! valids[c])
+      if (c < 0 || ! 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:
-  return found;
+  detect_info->found |= found;
+  return 1;
 }
 
 static void
@@ -4130,7 +4389,7 @@ decode_coding_ccl (coding)
      struct coding_system *coding;
 {
   const unsigned char *src = coding->source + coding->consumed;
-  unsigned char *src_end = coding->source + coding->src_bytes;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
   int *charbuf = coding->charbuf;
   int *charbuf_end = charbuf + coding->charbuf_size;
   int consumed_chars = 0;
@@ -4138,7 +4397,9 @@ decode_coding_ccl (coding)
   struct ccl_program ccl;
   int source_charbuf[1024];
   int source_byteidx[1024];
+  Lisp_Object attrs, charset_list;
 
+  CODING_GET_INFO (coding, attrs, charset_list);
   setup_ccl_program (&ccl, CODING_CCL_DECODER (coding));
 
   while (src < src_end)
@@ -4156,7 +4417,7 @@ decode_coding_ccl (coding)
       else
        while (i < 1024 && p < src_end)
          source_charbuf[i++] = *p++;
-      
+
       if (p == src_end && coding->mode & CODING_MODE_LAST_BLOCK)
        ccl.last_block = 1;
 
@@ -4165,7 +4426,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)
@@ -4185,16 +4447,16 @@ decode_coding_ccl (coding)
   switch (ccl.status)
     {
     case CCL_STAT_SUSPEND_BY_SRC:
-      coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+      record_conversion_result (coding, CODING_RESULT_INSUFFICIENT_SRC);
       break;
     case CCL_STAT_SUSPEND_BY_DST:
       break;
     case CCL_STAT_QUIT:
     case CCL_STAT_INVALID_CMD:
-      coding->result = CODING_RESULT_INTERRUPT;
+      record_conversion_result (coding, CODING_RESULT_INTERRUPT);
       break;
     default:
-      coding->result = CODING_RESULT_SUCCESS;
+      record_conversion_result (coding, CODING_RESULT_SUCCESS);
       break;
     }
   coding->consumed_char += consumed_chars;
@@ -4215,7 +4477,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, charset_list;
 
+  CODING_GET_INFO (coding, attrs, charset_list);
   setup_ccl_program (&ccl, CODING_CCL_ENCODER (coding));
 
   ccl.last_block = coding->mode & CODING_MODE_LAST_BLOCK;
@@ -4228,7 +4492,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++)
@@ -4244,17 +4508,17 @@ encode_coding_ccl (coding)
   switch (ccl.status)
     {
     case CCL_STAT_SUSPEND_BY_SRC:
-      coding->result = CODING_RESULT_INSUFFICIENT_SRC;
+      record_conversion_result (coding, CODING_RESULT_INSUFFICIENT_SRC);
       break;
     case CCL_STAT_SUSPEND_BY_DST:
-      coding->result = CODING_RESULT_INSUFFICIENT_DST;
+      record_conversion_result (coding, CODING_RESULT_INSUFFICIENT_DST);
       break;
     case CCL_STAT_QUIT:
     case CCL_STAT_INVALID_CMD:
-      coding->result = CODING_RESULT_INTERRUPT;
+      record_conversion_result (coding, CODING_RESULT_INTERRUPT);
       break;
     default:
-      coding->result = CODING_RESULT_SUCCESS;
+      record_conversion_result (coding, CODING_RESULT_SUCCESS);
       break;
     }
 
@@ -4276,7 +4540,7 @@ decode_coding_raw_text (coding)
   coding->chars_at_source = 1;
   coding->consumed_char = 0;
   coding->consumed = 0;
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
 }
 
 static int
@@ -4352,26 +4616,32 @@ encode_coding_raw_text (coding)
          while (charbuf < charbuf_end && dst < dst_end)
            *dst++ = *charbuf++;
          produced_chars = dst - (coding->destination + coding->dst_bytes);
-       } 
+       }
     }
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->produced_char += produced_chars;
   coding->produced = dst - coding->destination;
   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;
+  const unsigned char *src = coding->source, *src_base;
+  const 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);
   valids = AREF (attrs, coding_attr_charset_valids);
@@ -4383,38 +4653,50 @@ detect_coding_charset (coding, mask)
     {
       int c;
 
+      src_base = src;
       ONE_MORE_BYTE (c);
+      if (c < 0)
+       continue;
       if (NILP (AREF (valids, c)))
        break;
       if (c >= 0x80)
-       found = 1;
+       found = CATEGORY_MASK_CHARSET;
     }
-  *mask &= ~CATEGORY_MASK_CHARSET;
+  detect_info->rejected |= CATEGORY_MASK_CHARSET;
   return 0;
 
  no_more_source:
-  return (found || NILP (CODING_ATTR_ASCII_COMPAT (attrs)));
+  detect_info->found |= found;
+  return 1;
 }
 
 static void
 decode_coding_charset (coding)
      struct coding_system *coding;
 {
-  unsigned char *src = coding->source + coding->consumed;
-  unsigned char *src_end = coding->source + coding->src_bytes;
-  unsigned char *src_base;
+  const unsigned char *src = coding->source + coding->consumed;
+  const unsigned char *src_end = coding->source + coding->src_bytes;
+  const 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;
+  Lisp_Object attrs, 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);
+  CODING_GET_INFO (coding, attrs, charset_list);
   valids = AREF (attrs, coding_attr_charset_valids);
 
   while (1)
     {
       int c;
+      Lisp_Object val;
+      struct charset *charset;
+      int dim;
+      int len = 1;
+      unsigned code;
 
       src_base = src;
       consumed_chars_base = consumed_chars;
@@ -4423,33 +4705,34 @@ decode_coding_charset (coding)
        break;
 
       ONE_MORE_BYTE (c);
-      if (c == '\r')
+      if (c < 0)
+       goto invalid_code;
+      code = c;
+
+      val = AREF (valids, c);
+      if (NILP (val))
+       goto invalid_code;
+      if (INTEGERP (val))
        {
-         /* Here we assume that no charset maps '\r' to something
-            else.  */
-         if (EQ (eol_type, Qdos))
+         charset = CHARSET_FROM_ID (XFASTINT (val));
+         dim = CHARSET_DIMENSION (charset);
+         while (len < dim)
            {
-             if (src < src_end
-                 && *src == '\n')
-               ONE_MORE_BYTE (c);
+             ONE_MORE_BYTE (c);
+             code = (code << 8) | c;
+             len++;
            }
-         else if (EQ (eol_type, Qmac))
-           c = '\n';
+         CODING_DECODE_CHAR (coding, src, src_base, src_end,
+                             charset, code, c);
        }
       else
        {
-         Lisp_Object val;
-         struct charset *charset;
-         int dim;
-         int len = 1;
-         unsigned code = c;
-
-         val = AREF (valids, c);
-         if (NILP (val))
-           goto invalid_code;
-         if (INTEGERP (val))
+         /* VAL is a list of charset IDs.  It is assured that the
+            list is sorted by charset dimensions (smaller one
+            comes first).  */
+         while (CONSP (val))
            {
-             charset = CHARSET_FROM_ID (XFASTINT (val));
+             charset = CHARSET_FROM_ID (XFASTINT (XCAR (val)));
              dim = CHARSET_DIMENSION (charset);
              while (len < dim)
                {
@@ -4457,46 +4740,40 @@ decode_coding_charset (coding)
                  code = (code << 8) | c;
                  len++;
                }
-             CODING_DECODE_CHAR (coding, src, src_base, src_end,
-                                 charset, code, c);
-           }
-         else
-           {
-             /* VAL is a list of charset IDs.  It is assured that the
-                list is sorted by charset dimensions (smaller one
-                comes first).  */
-             while (CONSP (val))
-               {
-                 charset = CHARSET_FROM_ID (XFASTINT (XCAR (val)));
-                 dim = CHARSET_DIMENSION (charset);
-                 while (len < dim)
-                   {
-                     ONE_MORE_BYTE (c);
-                     code = (code << 8) | c;
-                     len++;
-                   }
-                 CODING_DECODE_CHAR (coding, src, src_base,
-                                     src_end, charset, code, c);
-                 if (c >= 0)
-                   break;
-                 val = XCDR (val);
-               }
+             CODING_DECODE_CHAR (coding, src, src_base,
+                                 src_end, charset, code, c);
+             if (c >= 0)
+               break;
+             val = XCDR (val);
            }
-         if (c < 0)
-           goto invalid_code;
        }
+      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:
       src = src_base;
       consumed_chars = consumed_chars_base;
       ONE_MORE_BYTE (c);
-      *charbuf++ = ASCII_BYTE_P (c) ? c : BYTE8_TO_CHAR (c);
+      *charbuf++ = c < 0 ? -c : 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;
@@ -4513,18 +4790,18 @@ encode_coding_charset (coding)
   unsigned char *dst_end = coding->destination + coding->dst_bytes;
   int safe_room = MAX_MULTIBYTE_LENGTH;
   int produced_chars = 0;
-  Lisp_Object attrs, eol_type, charset_list;
+  Lisp_Object attrs, charset_list;
   int ascii_compatible;
   int c;
 
-  CODING_GET_INFO (coding, attrs, eol_type, charset_list);
+  CODING_GET_INFO (coding, attrs, charset_list);
   ascii_compatible = ! NILP (CODING_ATTR_ASCII_COMPAT (attrs));
 
   while (charbuf < charbuf_end)
     {
       struct charset *charset;
       unsigned code;
-      
+
       ASSURE_DESTINATION (safe_room);
       c = *charbuf++;
       if (ascii_compatible && ASCII_CHAR_P (c))
@@ -4560,7 +4837,7 @@ encode_coding_charset (coding)
        }
     }
 
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->produced_char += produced_chars;
   coding->produced = dst - coding->destination;
   return 0;
@@ -4595,10 +4872,16 @@ setup_coding_system (coding_system, coding)
   coding->head_ascii = -1;
   coding->common_flags
     = (VECTORP (eol_type) ? CODING_REQUIRE_DETECTION_MASK : 0);
+  if (! NILP (CODING_ATTR_POST_READ (attrs)))
+    coding->common_flags |= CODING_REQUIRE_DECODING_MASK;
+  if (! NILP (CODING_ATTR_PRE_WRITE (attrs)))
+    coding->common_flags |= CODING_REQUIRE_ENCODING_MASK;
+  if (! NILP (CODING_ATTR_FOR_UNIBYTE (attrs)))
+    coding->common_flags |= CODING_FOR_UNIBYTE_MASK;
 
   val = CODING_ATTR_SAFE_CHARSETS (attrs);
-  coding->max_charset_id = XSTRING (val)->size - 1;
-  coding->safe_charsets = (char *) XSTRING (val)->data;
+  coding->max_charset_id = SCHARS (val) - 1;
+  coding->safe_charsets = (char *) SDATA (val);
   coding->default_char = XINT (CODING_ATTR_DEFAULT_CHAR (attrs));
 
   coding_type = CODING_ATTR_TYPE (attrs);
@@ -4636,12 +4919,14 @@ 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);
          val = CODING_ATTR_SAFE_CHARSETS (attrs);
-         coding->max_charset_id = XSTRING (val)->size - 1;
-         coding->safe_charsets = (char *) XSTRING (val)->data;
+         coding->max_charset_id = SCHARS (val) - 1;
+         coding->safe_charsets = (char *) SDATA (val);
        }
       CODING_ISO_FLAGS (coding) = flags;
     }
@@ -4668,7 +4953,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;
@@ -4676,6 +4961,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))
     {
@@ -4707,9 +4994,9 @@ setup_coding_system (coding_system, coding)
                                        make_number (255));
          for (tail = Vemacs_mule_charset_list; CONSP (tail);
               tail = XCDR (tail))
-           XSTRING (safe_charsets)->data[XFASTINT (XCAR (tail))] = 0;
+           SSET (safe_charsets, XFASTINT (XCAR (tail)), 0);
          coding->max_charset_id = max_charset_id;
-         coding->safe_charsets = (char *) XSTRING (safe_charsets)->data;
+         coding->safe_charsets = (char *) SDATA (safe_charsets);
        }
     }
   else if (EQ (coding_type, Qshift_jis))
@@ -4733,7 +5020,6 @@ setup_coding_system (coding_system, coding)
       coding->detector = NULL;
       coding->decoder = decode_coding_raw_text;
       coding->encoder = encode_coding_raw_text;
-      coding->common_flags |= CODING_FOR_UNIBYTE_MASK;
     }
 
   return;
@@ -4749,9 +5035,11 @@ raw_text_coding_system (coding_system)
   Lisp_Object spec, attrs;
   Lisp_Object eol_type, raw_text_eol_type;
 
+  if (NILP (coding_system))
+    return Qraw_text;
   spec = CODING_SYSTEM_SPEC (coding_system);
   attrs = AREF (spec, 0);
-  
+
   if (EQ (CODING_ATTR_TYPE (attrs), Qraw_text))
     return coding_system;
 
@@ -4774,12 +5062,14 @@ Lisp_Object
 coding_inherit_eol_type (coding_system, parent)
      Lisp_Object coding_system, parent;
 {
-  Lisp_Object spec, attrs, eol_type;
+  Lisp_Object spec, eol_type;
 
+  if (NILP (coding_system))
+    coding_system = Qraw_text;
   spec = CODING_SYSTEM_SPEC (coding_system);
-  attrs = AREF (spec, 0);
   eol_type = AREF (spec, 2);
-  if (VECTORP (eol_type))
+  if (VECTORP (eol_type)
+      && ! NILP (parent))
     {
       Lisp_Object parent_spec;
       Lisp_Object parent_eol_type;
@@ -4911,9 +5201,12 @@ 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
 
@@ -4950,7 +5243,7 @@ detect_eol (source, src_bytes, category)
                       || src[lsb + 2] != '\n')
                this_eol = EOL_SEEN_CR;
              else
-               this_eol = EOL_SEEN_CRLF; 
+               this_eol = EOL_SEEN_CRLF;
 
              if (eol_seen == EOL_SEEN_NONE)
                /* This is the first end-of-line.  */
@@ -4966,7 +5259,7 @@ detect_eol (source, src_bytes, category)
            }
          src += 2;
        }
-    }   
+    }
   else
     {
       while (src < src_end)
@@ -5001,20 +5294,30 @@ detect_eol (source, src_bytes, category)
 }
 
 
-static void
+static Lisp_Object
 adjust_coding_eol_type (coding, eol_seen)
      struct coding_system *coding;
      int eol_seen;
 {
   Lisp_Object eol_type;
-  
+
   eol_type = CODING_ID_EOL_TYPE (coding->id);
   if (eol_seen & EOL_SEEN_LF)
-    coding->id = CODING_SYSTEM_ID (AREF (eol_type, 0));
+    {
+      coding->id = CODING_SYSTEM_ID (AREF (eol_type, 0));
+      eol_type = Qunix;
+    }
   else if (eol_seen & EOL_SEEN_CRLF)
-    coding->id = CODING_SYSTEM_ID (AREF (eol_type, 1));
+    {
+      coding->id = CODING_SYSTEM_ID (AREF (eol_type, 1));
+      eol_type = Qdos;
+    }
   else if (eol_seen & EOL_SEEN_CR)
-    coding->id = CODING_SYSTEM_ID (AREF (eol_type, 2));
+    {
+      coding->id = CODING_SYSTEM_ID (AREF (eol_type, 2));
+      eol_type = Qmac;
+    }
+  return eol_type;
 }
 
 /* Detect how a text specified in CODING is encoded.  If a coding
@@ -5025,7 +5328,7 @@ void
 detect_coding (coding)
      struct coding_system *coding;
 {
-  unsigned char *src, *src_end;
+  const unsigned char *src, *src_end;
   Lisp_Object attrs, coding_type;
 
   coding->consumed = coding->consumed_char = 0;
@@ -5038,10 +5341,9 @@ 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++)
+      for (i = 0, src = coding->source; src < src_end; i++, src++)
        {
          c = *src;
          if (c & 0x80 || (c < 0x20 && (c == ISO_CODE_ESC
@@ -5053,63 +5355,71 @@ 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;
-
+             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 if (category >= coding_category_raw_text
-                      || detected & (1 << category))
+             else if (category >= coding_category_raw_text)
                continue;
-             else
+             else if (detect_info.checked & (1 << category))
+               {
+                 if (detect_info.found & (1 << category))
+                   break;
+               }
+             else if ((*(this->detector)) (coding, &detect_info)
+                      && detect_info.found & (1 << category))
                {
-                 detected |= detected_mask[category];
-                 if ((*(this->detector)) (coding, &mask)
-                     && (mask & (1 << category)))
+                 if (category == coding_category_utf_16_auto)
                    {
-                     mask = 1 << category;
-                     break;
+                     if (detect_info.found & CATEGORY_MASK_UTF_16_LE)
+                       category = coding_category_utf_16_le;
+                     else
+                       category = coding_category_utf_16_be;
                    }
+                 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 (mask & (1 << category))
-                 {
-                   setup_coding_system (CODING_ID_NAME (this->id), coding);
-                   break;
-                 }
-             }
+             if (! (detect_info.rejected & (1 << coding_priorities[i])))
+               {
+                 this = coding_categories + coding_priorities[i];
+                 setup_coding_system (CODING_ID_NAME (this->id), coding);
+                 break;
+               }
        }
     }
-
-  attrs = CODING_ID_ATTRS (coding->id);
-  coding_type = CODING_ATTR_TYPE (attrs);
-
-  /* If we have not yet decided the EOL type, detect it now.  But, the
-     detection is impossible for a CCL based coding system, in which
-     case, we detct the EOL type after decoding.  */
-  if (VECTORP (CODING_ID_EOL_TYPE (coding->id))
-      && ! EQ (coding_type, Qccl))
+  else if (XINT (CODING_ATTR_CATEGORY (CODING_ID_ATTRS (coding->id)))
+          == coding_category_utf_16_auto)
     {
-      int eol_seen = detect_eol (coding->source, coding->src_bytes,
-                                XINT (CODING_ATTR_CATEGORY (attrs)));
+      Lisp_Object coding_systems;
+      struct coding_detection_info detect_info;
 
-      if (eol_seen != EOL_SEEN_NONE)
-       adjust_coding_eol_type (coding, eol_seen);
+      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))
+       {
+         if (detect_info.found & CATEGORY_MASK_UTF_16_LE)
+           setup_coding_system (XCAR (coding_systems), coding);
+         else if (detect_info.found & CATEGORY_MASK_UTF_16_BE)
+           setup_coding_system (XCDR (coding_systems), coding);
+       }
     }
 }
 
@@ -5118,13 +5428,24 @@ static void
 decode_eol (coding)
      struct coding_system *coding;
 {
-  if (VECTORP (CODING_ID_EOL_TYPE (coding->id)))
+  Lisp_Object eol_type;
+  unsigned char *p, *pbeg, *pend;
+  
+  eol_type = CODING_ID_EOL_TYPE (coding->id);
+  if (EQ (eol_type, Qunix))
+    return;
+
+  if (NILP (coding->dst_object))
+    pbeg = coding->destination;
+  else
+    pbeg = BYTE_POS_ADDR (coding->dst_pos_byte);
+  pend = pbeg + coding->produced;
+
+  if (VECTORP (eol_type))
     {
-      unsigned char *p = CHAR_POS_ADDR (coding->dst_pos);
-      unsigned char *pend = p + coding->produced;
       int eol_seen = EOL_SEEN_NONE;
 
-      for (; p < pend; p++)
+      for (p = pbeg; p < pend; p++)
        {
          if (*p == '\n')
            eol_seen |= EOL_SEEN_LF;
@@ -5139,45 +5460,91 @@ decode_eol (coding)
                eol_seen |= EOL_SEEN_CR;
            }
        }
+      if (eol_seen != EOL_SEEN_NONE
+         && eol_seen != EOL_SEEN_LF
+         && eol_seen != EOL_SEEN_CRLF
+         && eol_seen != EOL_SEEN_CR)
+       eol_seen = EOL_SEEN_LF;
       if (eol_seen != EOL_SEEN_NONE)
-       adjust_coding_eol_type (coding, eol_seen);
+       eol_type = adjust_coding_eol_type (coding, eol_seen);
     }
 
-  if (EQ (CODING_ID_EOL_TYPE (coding->id), Qmac))
+  if (EQ (eol_type, Qmac))
     {
-      unsigned char *p = CHAR_POS_ADDR (coding->dst_pos);
-      unsigned char *pend = p + coding->produced;
-      
-      for (; p < pend; p++)
+      for (p = pbeg; p < pend; p++)
        if (*p == '\r')
          *p = '\n';
     }
-  else if (EQ (CODING_ID_EOL_TYPE (coding->id), Qdos))
+  else if (EQ (eol_type, Qdos))
     {
-      unsigned char *p, *pbeg, *pend;
-      Lisp_Object undo_list;
+      int n = 0;
 
-      move_gap_both (coding->dst_pos + coding->produced_char,
-                    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, 0);
-      current_buffer->undo_list = undo_list;
-      pbeg = GPT_ADDR;
-      pend = pbeg + coding->produced;
+      if (NILP (coding->dst_object))
+       {
+         for (p = pend - 2; p >= pbeg; p--)
+           if (*p == '\r')
+             {
+               safe_bcopy ((char *) (p + 1), (char *) p, pend-- - p - 1);
+               n++;
+             }
+       }
+      else
+       {
+         for (p = pend - 2; p >= pbeg; p--)
+           if (*p == '\r')
+             {
+               int pos_byte = coding->dst_pos_byte + (p - pbeg);
+               int pos = BYTE_TO_CHAR (pos_byte);
+               
+               del_range_2 (pos, pos_byte, pos + 1, pos_byte + 1, 0);
+               n++;
+             }
+       }
+      coding->produced -= n;
+      coding->produced_char -= n;
+    }
+}
 
-      for (p = pend - 1; p >= pbeg; p--)
-       if (*p == '\r')
-         {
-           safe_bcopy ((char *) (p + 1), (char *) p, pend - p - 1);
-           pend--;
-         }
-      coding->produced_char -= coding->produced - (pend - pbeg);
-      coding->produced = pend - pbeg;
-      insert_from_gap (coding->produced_char, coding->produced);
+
+/* Return a translation table (or list of them) from coding system
+   attribute vector ATTRS for encoding (ENCODEP is nonzero) or
+   decoding (ENCODEP is zero). */
+
+static INLINE
+get_translation_table (attrs, encodep)
+{
+  Lisp_Object standard, translation_table;
+
+  if (encodep)
+    translation_table = CODING_ATTR_ENCODE_TBL (attrs),
+      standard = Vstandard_translation_table_for_encode;
+  else
+    translation_table = CODING_ATTR_DECODE_TBL (attrs),
+      standard = Vstandard_translation_table_for_decode;
+  if (NILP (translation_table))
+    return standard;
+  if (SYMBOLP (translation_table))
+    translation_table = Fget (translation_table, Qtranslation_table);
+  else if (CONSP (translation_table))
+    {
+      Lisp_Object val;
+
+      translation_table = Fcopy_sequence (translation_table);
+      for (val = translation_table; CONSP (val); val = XCDR (val))
+       if (SYMBOLP (XCAR (val)))
+         XSETCAR (val, Fget (XCAR (val), Qtranslation_table));
     }
+  if (! NILP (standard))
+    {
+      if (CONSP (translation_table))
+       translation_table = nconc2 (translation_table, Fcons (standard, Qnil));
+      else
+       translation_table = Fcons (translation_table, Fcons (standard, Qnil));
+    }
+  return translation_table;
 }
 
+
 static void
 translate_chars (coding, table)
      struct coding_system *coding;
@@ -5194,7 +5561,7 @@ translate_chars (coding, table)
     {
       c = *charbuf;
       if (c < 0)
-       charbuf += c;
+       charbuf += -c;
       else
        *charbuf++ = translate_char (table, c);
     }
@@ -5218,13 +5585,13 @@ produce_chars (coding)
 
       if (BUFFERP (coding->src_object)
          && EQ (coding->src_object, coding->dst_object))
-       dst_end = coding->source + coding->consumed;
+       dst_end = ((unsigned char *) coding->source) + coding->consumed;
       adjusted_dst_end = dst_end - MAX_MULTIBYTE_LENGTH;
 
       while (buf < buf_end)
        {
          int c = *buf++;
-         
+
          if (dst >= adjusted_dst_end)
            {
              dst = alloc_destination (coding,
@@ -5243,14 +5610,15 @@ produce_chars (coding)
              produced_chars++;
            }
          else
-           /* This is an annotation data.  */
-           buf -= c + 1;
+           /* This is an annotation datum.  (-C) is the length of
+              it.  */
+           buf += -c - 1;
        }
     }
   else
     {
-      unsigned char *src = coding->source;
-      unsigned char *src_end = src + coding->src_bytes;
+      const unsigned char *src = coding->source;
+      const unsigned char *src_end = src + coding->src_bytes;
       Lisp_Object eol_type;
 
       eol_type = CODING_ID_EOL_TYPE (coding->id);
@@ -5264,7 +5632,7 @@ produce_chars (coding)
 
              while (1)
                {
-                 unsigned char *src_base = src;
+                 const unsigned char *src_base = src;
                  int c;
 
                  ONE_MORE_BYTE (c);
@@ -5272,8 +5640,13 @@ produce_chars (coding)
                    {
                      if (EQ (eol_type, Qdos))
                        {
-                         if (src < src_end
-                             && *src == '\n')
+                         if (src == src_end)
+                           {
+                             record_conversion_result
+                               (coding, CODING_RESULT_INSUFFICIENT_SRC);
+                             goto no_more_source;
+                           }
+                         if (*src == '\n')
                            c = *src++;
                        }
                      else if (EQ (eol_type, Qmac))
@@ -5284,7 +5657,7 @@ produce_chars (coding)
                      coding->consumed = src - coding->source;
 
                    if (EQ (coding->src_object, coding->dst_object))
-                     dst_end = src;
+                     dst_end = (unsigned char *) src;
                    if (dst == dst_end)
                      {
                        dst = alloc_destination (coding, src_end - src + 1,
@@ -5323,7 +5696,7 @@ produce_chars (coding)
                    coding->consumed = src - coding->source;
 
                    if (EQ (coding->src_object, coding->dst_object))
-                     dst_end = src;
+                     dst_end = (unsigned char *) src;
                    if (dst >= dst_end - 1)
                      {
                        dst = alloc_destination (coding, src_end - src + 2,
@@ -5385,9 +5758,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
@@ -5395,18 +5768,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;
@@ -5422,65 +5792,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)                             \
@@ -5497,7 +5832,7 @@ save_composition_data (buf, buf_end, prop)
       }                                                                        \
     if (! coding->charbuf)                                             \
       {                                                                        \
-       coding->result = CODING_RESULT_INSUFFICIENT_MEM;                \
+       record_conversion_result (coding, CODING_RESULT_INSUFFICIENT_MEM); \
        return coding->result;                                          \
       }                                                                        \
     coding->charbuf_size = size;                                       \
@@ -5511,6 +5846,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)
@@ -5518,11 +5856,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 ();
            }
@@ -5559,6 +5900,8 @@ decode_coding (coding)
      struct coding_system *coding;
 {
   Lisp_Object attrs;
+  Lisp_Object undo_list;
+  Lisp_Object translation_table;
 
   if (BUFFERP (coding->src_object)
       && coding->src_pos > 0
@@ -5566,33 +5909,35 @@ decode_coding (coding)
       && coding->src_pos + coding->src_chars > GPT)
     move_gap_both (coding->src_pos, coding->src_pos_byte);
 
+  undo_list = Qt;
   if (BUFFERP (coding->dst_object))
     {
       if (current_buffer != XBUFFER (coding->dst_object))
        set_buffer_internal (XBUFFER (coding->dst_object));
       if (GPT != PT)
        move_gap_both (PT, PT_BYTE);
+      undo_list = current_buffer->undo_list;
+      current_buffer->undo_list = Qt;
     }
 
   coding->consumed = coding->consumed_char = 0;
   coding->produced = coding->produced_char = 0;
   coding->chars_at_source = 0;
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->errors = 0;
 
   ALLOC_CONVERSION_WORK_AREA (coding);
 
   attrs = CODING_ID_ATTRS (coding->id);
+  translation_table = get_translation_table (attrs, 0);
 
   do
     {
       coding_set_source (coding);
       coding->annotated = 0;
       (*(coding->decoder)) (coding);
-      if (!NILP (CODING_ATTR_DECODE_TBL (attrs)))
-       translate_chars (coding, CODING_ATTR_DECODE_TBL (attrs));
-      else if (!NILP (Vstandard_translation_table_for_decode))
-       translate_chars (coding, Vstandard_translation_table_for_decode);
+      if (!NILP (translation_table))
+       translate_chars (coding, translation_table);
       coding_set_destination (coding);
       produce_chars (coding);
       if (coding->annotated)
@@ -5601,16 +5946,11 @@ decode_coding (coding)
   while (coding->consumed < coding->src_bytes
         && ! coding->result);
 
-  if (EQ (CODING_ATTR_TYPE (CODING_ID_ATTRS (coding->id)), Qccl)
-      && SYMBOLP (CODING_ID_EOL_TYPE (coding->id))
-      && ! EQ (CODING_ID_EOL_TYPE (coding->id), Qunix))
-    decode_eol (coding);
-
   coding->carryover_bytes = 0;
   if (coding->consumed < coding->src_bytes)
     {
       int nbytes = coding->src_bytes - coding->consumed;
-      unsigned char *src;
+      const unsigned char *src;
 
       coding_set_source (coding);
       coding_set_destination (coding);
@@ -5621,90 +5961,225 @@ 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;
+         coding->charbuf_used = 0;
+         while (nbytes-- > 0)
+           {
+             int c = *src++;
 
+             coding->charbuf[coding->charbuf_used++] = (c & 0x80 ? - c : c);
+           }
+         produce_chars (coding);
+       }
+      else
+       {
+         /* Record unprocessed bytes in coding->carryover.  We are
+            sure that the number of data is less than the size of
+            coding->carryover.  */
+         unsigned char *p = coding->carryover;
+
+         coding->carryover_bytes = nbytes;
          while (nbytes-- > 0)
+           *p++ = *src++;
+       }
+      coding->consumed = coding->src_bytes;
+    }
+
+  if (BUFFERP (coding->dst_object))
+    {
+      current_buffer->undo_list = undo_list;
+      record_insert (coding->dst_pos, coding->produced_char);
+    }
+  if (! EQ (CODING_ID_EOL_TYPE (coding->id), Qunix))
+    decode_eol (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)
            {
-             int c = *src++;
-             *charbuf++ =  (c & 0x80 ? - c : c);
+             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 = SCHARS (components);
+                 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;
            }
-         produce_chars (coding);
        }
-      else
-       {
-         /* Record unprocessed bytes in coding->carryover.  We are
-            sure that the number of data is less than the size of
-            coding->carryover.  */
-         unsigned char *p = coding->carryover;
 
-         coding->carryover_bytes = nbytes;
-         while (nbytes-- > 0)
-           *p++ = *src++;
-       }
-      coding->consumed = coding->src_bytes;
+      if (find_composition (end, limit, &start, &end, &prop,
+                           coding->src_object)
+         && end <= limit)
+       *stop = start;
+      else
+       *stop = limit;
     }
+  return buf;
+}
 
-  return coding->result;
+
+/* 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;
+  int *buf_end = coding->charbuf + coding->charbuf_size;
   const unsigned char *src = coding->source + coding->consumed;
-  int pos = coding->src_pos + coding->consumed_char;
-  int end_pos = coding->src_pos + coding->src_chars;
+  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 (NILP (coding->src_object))
+    stop = stop_composition = stop_charset = end_pos;
   else
-    stop = end_pos;
+    {
+      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_charset = end_pos;
+    }
 
+  /* Compensate for CRLF and conversion.  */
+  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;
+
+         if (! CODING_FOR_UNIBYTE (coding)
+             && (bytes = MULTIBYTE_LENGTH (src, src_end)) > 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))
@@ -5718,7 +6193,6 @@ consume_chars (coding)
            }
        }
       *buf++ = c;
-      pos++;
     }
 
   coding->consumed = src - coding->source;
@@ -5754,8 +6228,10 @@ encode_coding (coding)
      struct coding_system *coding;
 {
   Lisp_Object attrs;
+  Lisp_Object translation_table;
 
   attrs = CODING_ID_ATTRS (coding->id);
+  translation_table = get_translation_table (attrs, 1);
 
   if (BUFFERP (coding->dst_object))
     {
@@ -5766,7 +6242,7 @@ encode_coding (coding)
 
   coding->consumed = coding->consumed_char = 0;
   coding->produced = coding->produced_char = 0;
-  coding->result = CODING_RESULT_SUCCESS;
+  record_conversion_result (coding, CODING_RESULT_SUCCESS);
   coding->errors = 0;
 
   ALLOC_CONVERSION_WORK_AREA (coding);
@@ -5775,10 +6251,8 @@ encode_coding (coding)
     coding_set_source (coding);
     consume_chars (coding);
 
-    if (!NILP (CODING_ATTR_ENCODE_TBL (attrs)))
-      translate_chars (coding, CODING_ATTR_ENCODE_TBL (attrs));
-    else if (!NILP (Vstandard_translation_table_for_encode))
-      translate_chars (coding, Vstandard_translation_table_for_encode);
+    if (!NILP (translation_table))
+      translate_chars (coding, translation_table);
 
     coding_set_destination (coding);
     (*(coding->encoder)) (coding);
@@ -5790,88 +6264,93 @@ encode_coding (coding)
   return (coding->result);
 }
 
-/* Work buffer */
 
-/* List of currently used working buffer.  */
-Lisp_Object Vcode_conversion_work_buf_list;
+/* Name (or base name) of work buffer for code conversion.  */
+static Lisp_Object Vcode_conversion_workbuf_name;
 
-/* A working buffer used by the top level conversion.  */
-Lisp_Object Vcode_conversion_reused_work_buf;
+/* A working buffer used by the top level conversion.  Once it is
+   created, it is never destroyed.  It has the name
+   Vcode_conversion_workbuf_name.  The other working buffers are
+   destroyed after the use is finished, and their names are modified
+   versions of Vcode_conversion_workbuf_name.  */
+static Lisp_Object Vcode_conversion_reused_workbuf;
 
+/* 1 iff Vcode_conversion_reused_workbuf is already in use.  */
+static int reused_workbuf_in_use;
 
-/* Return a working buffer that can be freely used by the following
-   code conversion.  MULTIBYTEP specifies the multibyteness of the
-   buffer.  */
+
+/* Return a working buffer of code convesion.  MULTIBYTE specifies the
+   multibyteness of returning buffer.  */
 
 Lisp_Object
-make_conversion_work_buffer (multibytep)
-     int multibytep;
+make_conversion_work_buffer (multibyte)
 {
-  struct buffer *current = current_buffer;
-  Lisp_Object buf;
+  Lisp_Object name, workbuf;
+  struct buffer *current;
 
-  if (NILP (Vcode_conversion_work_buf_list))
+  if (reused_workbuf_in_use++)
     {
-      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);
+      name = Fgenerate_new_buffer_name (Vcode_conversion_workbuf_name, Qnil);
+      workbuf = Fget_buffer_create (name);
     }
   else
     {
-      int depth = XINT (Flength (Vcode_conversion_work_buf_list));
-      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);
+      name = Vcode_conversion_workbuf_name;
+      workbuf = Fget_buffer_create (name);
+      if (NILP (Vcode_conversion_reused_workbuf))
+       Vcode_conversion_reused_workbuf = workbuf;
     }
-
-  buf = XCAR (Vcode_conversion_work_buf_list);
-  set_buffer_internal (XBUFFER (buf));
+  current = current_buffer;
+  set_buffer_internal (XBUFFER (workbuf));
+  Ferase_buffer ();      
   current_buffer->undo_list = Qt;
-  Ferase_buffer ();
-  Fset_buffer_multibyte (multibytep ? Qt : Qnil, Qnil);
+  current_buffer->enable_multibyte_characters = multibyte ? Qt : Qnil;
   set_buffer_internal (current);
-  return buf;
+  return workbuf;
 }
 
-static struct coding_system *saved_coding;
 
-Lisp_Object
-code_conversion_restore (info)
-     Lisp_Object info;
+static Lisp_Object
+code_conversion_restore (arg)
+     Lisp_Object arg;
 {
-  int depth = XINT (Flength (Vcode_conversion_work_buf_list));
-  Lisp_Object buf;
+  Lisp_Object current, workbuf;
 
-  if (depth > 0)
+  current = XCAR (arg);
+  workbuf = XCDR (arg);
+  if (! NILP (workbuf))
     {
-      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);
+      if (EQ (workbuf, Vcode_conversion_reused_workbuf))
+       reused_workbuf_in_use = 0;
+      else if (! NILP (Fbuffer_live_p (workbuf)))
+       Fkill_buffer (workbuf);
     }
+  set_buffer_internal (XBUFFER (current));
+  return Qnil;
+}
 
-  if (EQ (saved_coding->dst_object, Qt)
-      && saved_coding->destination)
-    xfree (saved_coding->destination);
+Lisp_Object
+code_conversion_save (with_work_buf, multibyte)
+     int with_work_buf, multibyte;
+{
+  Lisp_Object workbuf = Qnil;
 
-  return save_excursion_restore (info);
+  if (with_work_buf)
+    workbuf = make_conversion_work_buffer (multibyte);
+  record_unwind_protect (code_conversion_restore,
+                        Fcons (Fcurrent_buffer (), workbuf));
+  return workbuf;
 }
 
-
 int
 decode_coding_gap (coding, chars, bytes)
      struct coding_system *coding;
      EMACS_INT chars, bytes;
 {
   int count = specpdl_ptr - specpdl;
+  Lisp_Object attrs;
 
-  saved_coding = coding;
-  record_unwind_protect (code_conversion_restore, save_excursion_save ());
+  code_conversion_save (0, 0);
 
   coding->src_object = Fcurrent_buffer ();
   coding->src_chars = chars;
@@ -5883,12 +6362,27 @@ 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);
-    
+
   decode_coding (coding);
 
+  attrs = CODING_ID_ATTRS (coding->id);
+  if (! NILP (CODING_ATTR_POST_READ (attrs)))
+    {
+      EMACS_INT prev_Z = Z, prev_Z_BYTE = Z_BYTE;
+      Lisp_Object val;
+
+      TEMP_SET_PT_BOTH (coding->dst_pos, coding->dst_pos_byte);
+      val = call1 (CODING_ATTR_POST_READ (attrs),
+                  make_number (coding->produced_char));
+      CHECK_NATNUM (val);
+      coding->produced_char += Z - prev_Z;
+      coding->produced += Z_BYTE - prev_Z_BYTE;
+    }
+
   unbind_to (count, Qnil);
   return coding->result;
 }
@@ -5899,13 +6393,10 @@ encode_coding_gap (coding, chars, bytes)
      EMACS_INT chars, bytes;
 {
   int count = specpdl_ptr - specpdl;
-  Lisp_Object buffer;
 
-  saved_coding = coding;
-  record_unwind_protect (code_conversion_restore, save_excursion_save ());
+  code_conversion_save (0, 0);
 
-  buffer = Fcurrent_buffer ();
-  coding->src_object = buffer;
+  coding->src_object = Fcurrent_buffer ();
   coding->src_chars = chars;
   coding->src_bytes = bytes;
   coding->src_pos = -chars;
@@ -5946,7 +6437,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.
  */
@@ -5965,9 +6456,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))
     {
@@ -5992,6 +6484,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;
@@ -6008,16 +6501,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 (1, 1);
       coding->dst_pos = BEG;
       coding->dst_pos_byte = BEG_BYTE;
       coding->dst_multibyte = 1;
     }
   else if (BUFFERP (dst_object))
     {
+      code_conversion_save (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));
@@ -6026,6 +6521,7 @@ decode_coding_object (coding, src_object, from, from_byte, to, to_byte,
     }
   else
     {
+      code_conversion_save (0, 0);
       coding->dst_object = Qnil;
       coding->dst_multibyte = 1;
     }
@@ -6064,7 +6560,8 @@ decode_coding_object (coding, src_object, from, from_byte, to, to_byte,
            = (unsigned char *) xrealloc (destination, coding->produced);
          if (! destination)
            {
-             coding->result = CODING_RESULT_INSUFFICIENT_DST;
+             record_conversion_result (coding,
+                                       CODING_RESULT_INSUFFICIENT_DST);
              unbind_to (count, Qnil);
              return;
            }
@@ -6075,7 +6572,26 @@ decode_coding_object (coding, src_object, from, from_byte, to, to_byte,
        }
     }
 
-  unbind_to (count, Qnil);
+  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, coding->dst_object);
 }
 
 
@@ -6091,9 +6607,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;
@@ -6104,7 +6621,7 @@ 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 (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);
@@ -6116,6 +6633,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));
        }
@@ -6133,26 +6651,31 @@ encode_coding_object (coding, src_object, from, from_byte, to, to_byte,
     }
   else if (STRINGP (src_object))
     {
+      code_conversion_save (0, 0);
       coding->src_pos = from;
       coding->src_pos_byte = from_byte;
     }
   else if (BUFFERP (src_object))
     {
+      code_conversion_save (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 (0, 0);
 
   if (BUFFERP (dst_object))
     {
@@ -6200,6 +6723,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);
 }
 
@@ -6238,7 +6780,7 @@ DEFUN ("read-non-nil-coding-system", Fread_non_nil_coding_system,
       val = Fcompleting_read (prompt, Vcoding_system_alist, Qnil,
                              Qt, Qnil, Qcoding_system_history, Qnil, Qnil);
     }
-  while (XSTRING (val)->size == 0);
+  while (SCHARS (val) == 0);
   return (Fintern (val, Qnil));
 }
 
@@ -6250,11 +6792,11 @@ If the user enters null input, return second argument DEFAULT-CODING-SYSTEM.  */
 {
   Lisp_Object val;
   if (SYMBOLP (default_coding_system))
-    XSETSTRING (default_coding_system, XSYMBOL (default_coding_system)->name);
+    XSETSTRING (default_coding_system, XPNTR (SYMBOL_NAME (default_coding_system)));
   val = Fcompleting_read (prompt, Vcoding_system_alist, Qnil,
                          Qt, Qnil, Qcoding_system_history,
                          default_coding_system, Qnil);
-  return (XSTRING (val)->size == 0 ? Qnil : Fintern (val, Qnil));
+  return (SCHARS (val) == 0 ? Qnil : Fintern (val, Qnil));
 }
 
 DEFUN ("check-coding-system", Fcheck_coding_system, Scheck_coding_system,
@@ -6289,20 +6831,20 @@ If valid, return CODING-SYSTEM, else signal a `coding-system-error' error.  */)
    detect only text-format.  */
 
 Lisp_Object
-detect_coding_system (src, src_bytes, highest, multibytep, coding_system)
-     unsigned char *src;
-     int src_bytes, highest;
+detect_coding_system (src, src_chars, src_bytes, highest, multibytep,
+                     coding_system)
+     const unsigned char *src;
+     int src_chars, src_bytes, highest;
      int multibytep;
      Lisp_Object coding_system;
 {
-  unsigned char *src_end = src + src_bytes;
-  int mask = CATEGORY_MASK_ANY;
-  int detected = 0;
-  int c, i;
+  const unsigned char *src_end = src + src_bytes;
   Lisp_Object attrs, eol_type;
   Lisp_Object val;
   struct coding_system coding;
   int id;
+  struct coding_detection_info detect_info;
+  enum coding_category base_category;
 
   if (NILP (coding_system))
     coding_system = Qundecided;
@@ -6312,21 +6854,29 @@ detect_coding_system (src, src_bytes, highest, multibytep, coding_system)
   coding_system = CODING_ATTR_BASE_NAME (attrs);
 
   coding.source = src;
+  coding.src_chars = src_chars;
   coding.src_bytes = src_bytes;
   coding.src_multibyte = multibytep;
   coding.consumed = 0;
   coding.mode |= CODING_MODE_LAST_BLOCK;
 
+  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)
+  base_category = XINT (CODING_ATTR_CATEGORY (attrs));
+  if (base_category == coding_category_undecided)
     {
-      for (; src < src_end; src++)
+      enum coding_category category;
+      struct coding_system *this;
+      int c, i;
+
+      /* Skip all ASCII bytes except for a few ISO2022 controls.  */
+      for (i = 0; src < src_end; i++, 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;
@@ -6334,64 +6884,118 @@ 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 (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
-                    || detected & (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)
+               if ((*(this->detector)) (&coding, &detect_info)
                    && highest
-                   && (mask & (1 << category)))
+                   && (detect_info.found & (1 << category)))
                  {
-                   mask = 1 << category;
+                   if (category == coding_category_utf_16_auto)
+                     {
+                       if (detect_info.found & CATEGORY_MASK_UTF_16_LE)
+                         category = coding_category_utf_16_le;
+                       else
+                         category = coding_category_utf_16_be;
+                     }
                    break;
                  }
              }
          }
 
-      if (!mask)
+      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 (mask == CATEGORY_MASK_ANY)
+      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)
        {
-         for (i = 0; i < coding_category_raw_text; i++)
-           if (mask & (1 << coding_priorities[i]))
-             {
-               id = coding_categories[coding_priorities[i]].id;
-               val = Fcons (make_number (id), Qnil);
-               break;
-             }
-       }       
+         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--)
-           if (mask & (1 << coding_priorities[i]))
-             {
-               id = coding_categories[coding_priorities[i]].id;
-               val = Fcons (make_number (id), val);
-             }
+           {
+             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 if (base_category == coding_category_utf_16_auto)
+    {
+      if (detect_coding_utf_16 (&coding, &detect_info))
+       {
+         enum coding_category category;
+         struct coding_system *this;
+
+         if (detect_info.found & CATEGORY_MASK_UTF_16_LE)
+           this = coding_categories + coding_category_utf_16_le;
+         else if (detect_info.found & CATEGORY_MASK_UTF_16_BE)
+           this = coding_categories + coding_category_utf_16_be;
+         else if (detect_info.rejected & CATEGORY_MASK_UTF_16_LE_NOSIG)
+           this = coding_categories + coding_category_utf_16_be_nosig;
+         else
+           this = coding_categories + coding_category_utf_16_le_nosig;
+         val = Fcons (make_number (this->id), Qnil);
        }
     }
   else
     {
-      mask = 1 << XINT (CODING_ATTR_CATEGORY (attrs));
+      detect_info.found = 1 << XINT (CODING_ATTR_CATEGORY (attrs));
       val = Fcons (make_number (coding.id), Qnil);
     }
 
@@ -6402,13 +7006,15 @@ detect_coding_system (src, src_bytes, highest, multibytep, coding_system)
 
     if (VECTORP (eol_type))
       {
-       if (mask & ~CATEGORY_MASK_UTF_16)
+       if (detect_info.found & ~CATEGORY_MASK_UTF_16)
          normal_eol = detect_eol (coding.source, src_bytes,
                                   coding_category_raw_text);
-       if (mask & (CATEGORY_MASK_UTF_16_BE | CATEGORY_MASK_UTF_16_BE_NOSIG))
+       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 (mask & (CATEGORY_MASK_UTF_16_LE | CATEGORY_MASK_UTF_16_LE_NOSIG))
+       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);
       }
@@ -6489,7 +7095,7 @@ highest priority.  */)
     move_gap_both (to, to_byte);
 
   return detect_coding_system (BYTE_POS_ADDR (from_byte),
-                              to_byte - from_byte,
+                              to - from, to_byte - from_byte,
                               !NILP (highest),
                               !NILP (current_buffer
                                      ->enable_multibyte_characters),
@@ -6512,10 +7118,9 @@ highest priority.  */)
 {
   CHECK_STRING (string);
 
-  return detect_coding_system (XSTRING (string)->data,
-                              STRING_BYTES (XSTRING (string)),
-                              !NILP (highest),
-                              STRING_MULTIBYTE (string),
+  return detect_coding_system (SDATA (string),
+                              SCHARS (string), SBYTES (string),
+                              !NILP (highest), STRING_MULTIBYTE (string),
                               Qnil);
 }
 
@@ -6527,7 +7132,11 @@ char_encodable_p (c, attrs)
 {
   Lisp_Object tail;
   struct charset *charset;
+  Lisp_Object translation_table;
 
+  translation_table = CODING_ATTR_TRANS_TBL (attrs);
+  if (! NILP (translation_table))
+    c = translate_char (translation_table, c);
   for (tail = CODING_ATTR_CHARSET_LIST (attrs);
        CONSP (tail); tail = XCDR (tail))
     {
@@ -6561,10 +7170,10 @@ DEFUN ("find-coding-systems-region-internal",
   if (STRINGP (start))
     {
       if (!STRING_MULTIBYTE (start)
-         || XSTRING (start)->size == STRING_BYTES (XSTRING (start)))
+         || SCHARS (start) == SBYTES (start))
        return Qt;
       start_byte = 0;
-      end_byte = STRING_BYTES (XSTRING (start));
+      end_byte = SBYTES (start);
     }
   else
     {
@@ -6579,12 +7188,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);
        }
     }
 
@@ -6598,11 +7207,15 @@ DEFUN ("find-coding-systems-region-internal",
        attrs = AREF (CODING_SYSTEM_SPEC (XCAR (tail)), 0);
        if (EQ (XCAR (tail), CODING_ATTR_BASE_NAME (attrs))
            && ! EQ (CODING_ATTR_TYPE (attrs), Qundecided))
-         coding_attrs_list = Fcons (attrs, coding_attrs_list);
+         {
+           ASET (attrs, coding_attr_trans_tbl,
+                 get_translation_table (attrs, 1));
+           coding_attrs_list = Fcons (attrs, coding_attrs_list);
+         }
       }
 
   if (STRINGP (start))
-    p = pbeg = XSTRING (start)->data;
+    p = pbeg = SDATA (start);
   else
     p = pbeg = BYTE_POS_ADDR (start_byte);
   pend = p + (end_byte - start_byte);
@@ -6642,7 +7255,7 @@ DEFUN ("find-coding-systems-region-internal",
              EMACS_INT p_offset = p - pbeg, pend_offset = pend - pbeg;
 
              if (STRINGP (start))
-               pbeg = XSTRING (start)->data;
+               pbeg = SDATA (start);
              else
                pbeg = BYTE_POS_ADDR (start_byte);
              p = pbeg + p_offset;
@@ -6660,6 +7273,115 @@ DEFUN ("find-coding-systems-region-internal",
 }
 
 
+DEFUN ("unencodable-char-position", Funencodable_char_position,
+       Sunencodable_char_position, 3, 5, 0,
+       doc: /*
+Return position of first un-encodable character in a region.
+START and END specfiy the region and CODING-SYSTEM specifies the
+encoding to check.  Return nil if CODING-SYSTEM does encode the region.
+
+If optional 4th argument COUNT is non-nil, it specifies at most how
+many un-encodable characters to search.  In this case, the value is a
+list of positions.
+
+If optional 5th argument STRING is non-nil, it is a string to search
+for un-encodable characters.  In that case, START and END are indexes
+to the string.  */)
+     (start, end, coding_system, count, string)
+     Lisp_Object start, end, coding_system, count, string;
+{
+  int n;
+  struct coding_system coding;
+  Lisp_Object attrs, charset_list, translation_table;
+  Lisp_Object positions;
+  int from, to;
+  const unsigned char *p, *stop, *pend;
+  int ascii_compatible;
+
+  setup_coding_system (Fcheck_coding_system (coding_system), &coding);
+  attrs = CODING_ID_ATTRS (coding.id);
+  if (EQ (CODING_ATTR_TYPE (attrs), Qraw_text))
+    return Qnil;
+  ascii_compatible = ! NILP (CODING_ATTR_ASCII_COMPAT (attrs));
+  charset_list = CODING_ATTR_CHARSET_LIST (attrs);
+  translation_table = get_translation_table (attrs, 1);
+
+  if (NILP (string))
+    {
+      validate_region (&start, &end);
+      from = XINT (start);
+      to = XINT (end);
+      if (NILP (current_buffer->enable_multibyte_characters)
+         || (ascii_compatible
+             && (to - from) == (CHAR_TO_BYTE (to) - (CHAR_TO_BYTE (from)))))
+       return Qnil;
+      p = CHAR_POS_ADDR (from);
+      pend = CHAR_POS_ADDR (to);
+      if (from < GPT && to >= GPT)
+       stop = GPT_ADDR;
+      else
+       stop = pend;
+    }
+  else
+    {
+      CHECK_STRING (string);
+      CHECK_NATNUM (start);
+      CHECK_NATNUM (end);
+      from = XINT (start);
+      to = XINT (end);
+      if (from > to
+         || to > SCHARS (string))
+       args_out_of_range_3 (string, start, end);
+      if (! STRING_MULTIBYTE (string))
+       return Qnil;
+      p = SDATA (string) + string_char_to_byte (string, from);
+      stop = pend = SDATA (string) + string_char_to_byte (string, to);
+      if (ascii_compatible && (to - from) == (pend - p))
+       return Qnil;
+    }
+
+  if (NILP (count))
+    n = 1;
+  else
+    {
+      CHECK_NATNUM (count);
+      n = XINT (count);
+    }
+
+  positions = Qnil;
+  while (1)
+    {
+      int c;
+
+      if (ascii_compatible)
+       while (p < stop && ASCII_BYTE_P (*p))
+         p++, from++;
+      if (p >= stop)
+       {
+         if (p >= pend)
+           break;
+         stop = pend;
+         p = GAP_END_ADDR;
+       }
+
+      c = STRING_CHAR_ADVANCE (p);
+      if (! (ASCII_CHAR_P (c) && ascii_compatible)
+         && ! char_charset (translate_char (translation_table, c),
+                            charset_list, NULL))
+       {
+         positions = Fcons (make_number (from), positions);
+         n--;
+         if (n == 0)
+           break;
+       }
+
+      from++;
+    }
+
+  return (NILP (count) ? Fcar (positions) : Fnreverse (positions));
+}
+
+
 DEFUN ("check-coding-systems-region", Fcheck_coding_systems_region,
        Scheck_coding_systems_region, 3, 3, 0,
        doc: /* Check if the region is encodable by coding systems.
@@ -6686,15 +7408,15 @@ buffer positions.  END is ignored.  */)
   int pos;
   const unsigned char *p, *pbeg, *pend;
   int c;
-  Lisp_Object tail, elt;
+  Lisp_Object tail, elt, attrs;
 
   if (STRINGP (start))
     {
       if (!STRING_MULTIBYTE (start)
-         && XSTRING (start)->size != STRING_BYTES (XSTRING (start)))
+         && SCHARS (start) != SBYTES (start))
        return Qnil;
       start_byte = 0;
-      end_byte = STRING_BYTES (XSTRING (start));
+      end_byte = SBYTES (start);
       pos = 0;
     }
   else
@@ -6710,27 +7432,27 @@ 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;
   for (tail = coding_system_list; CONSP (tail); tail = XCDR (tail))
     {
       elt = XCAR (tail);
-      list = Fcons (Fcons (elt, Fcons (AREF (CODING_SYSTEM_SPEC (elt), 0),
-                                      Qnil)),
-                   list);
+      attrs = AREF (CODING_SYSTEM_SPEC (elt), 0);
+      ASET (attrs, coding_attr_trans_tbl, get_translation_table (attrs, 1));
+      list = Fcons (Fcons (elt, Fcons (attrs, Qnil)), list);
     }
 
   if (STRINGP (start))
-    p = pbeg = XSTRING (start)->data;
+    p = pbeg = SDATA (start);
   else
     p = pbeg = BYTE_POS_ADDR (start_byte);
   pend = p + (end_byte - start_byte);
@@ -6758,7 +7480,7 @@ buffer positions.  END is ignored.  */)
              EMACS_INT p_offset = p - pbeg, pend_offset = pend - pbeg;
 
              if (STRINGP (start))
-               pbeg = XSTRING (start)->data;
+               pbeg = SDATA (start);
              else
                pbeg = BYTE_POS_ADDR (start_byte);
              p = pbeg + p_offset;
@@ -6782,7 +7504,6 @@ buffer positions.  END is ignored.  */)
 }
 
 
-
 Lisp_Object
 code_convert_region (start, end, coding_system, dst_object, encodep, norecord)
      Lisp_Object start, end, coding_system, dst_object;
@@ -6822,9 +7543,6 @@ code_convert_region (start, end, coding_system, dst_object, encodep, norecord)
   if (! norecord)
     Vlast_coding_system_used = CODING_ID_NAME (coding.id);
 
-  if (coding.result != CODING_RESULT_SUCCESS)
-    error ("Code conversion error: %d", coding.result);
-
   return (BUFFERP (dst_object)
          ? make_number (coding.produced_char)
          : coding.dst_object);
@@ -6903,8 +7621,8 @@ code_convert_string (string, coding_system, dst_object,
 
   setup_coding_system (coding_system, &coding);
   coding.mode |= CODING_MODE_LAST_BLOCK;
-  chars = XSTRING (string)->size;
-  bytes = STRING_BYTES (XSTRING (string));
+  chars = SCHARS (string);
+  bytes = SBYTES (string);
   if (encodep)
     encode_coding_object (&coding, string, 0, 0, chars, bytes, dst_object);
   else
@@ -6912,9 +7630,6 @@ code_convert_string (string, coding_system, dst_object,
   if (! norecord)
     Vlast_coding_system_used = CODING_ID_NAME (coding.id);
 
-  if (coding.result != CODING_RESULT_SUCCESS)
-    error ("Code conversion error: %d", coding.result);
-
   return (BUFFERP (dst_object)
          ? make_number (coding.produced_char)
          : coding.dst_object);
@@ -7133,7 +7848,7 @@ DEFUN ("set-terminal-coding-system-internal",
   CHECK_SYMBOL (coding_system);
   setup_coding_system (Fcheck_coding_system (coding_system),
                        &terminal_coding);
-  
+
   /* We had better not send unsafe characters to terminal.  */
   terminal_coding.mode |= CODING_MODE_SAFE_ENCODING;
   /* Characer composition should be disabled.  */
@@ -7239,7 +7954,7 @@ usage: (find-operation-coding-system OPERATION ARGUMENTS ...)  */)
     error ("Invalid first arguement");
   if (nargs < 1 + XINT (target_idx))
     error ("Too few arguments for operation: %s",
-          XSYMBOL (operation)->name->data);
+          SDATA (SYMBOL_NAME (operation)));
   target = args[XINT (target_idx) + 1];
   if (!(STRINGP (target)
        || (EQ (operation, Qopen_network_stream) && INTEGERP (target))))
@@ -7291,7 +8006,10 @@ usage: (find-operation-coding-system OPERATION ARGUMENTS ...)  */)
 DEFUN ("set-coding-system-priority", Fset_coding_system_priority,
        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 ...)  */)
+If multiple coding systems belongs to the same category,
+all but the first one are ignored.
+
+usage: (set-coding-system-priority ...)  */)
      (nargs, args)
      int nargs;
      Lisp_Object *args;
@@ -7319,6 +8037,7 @@ usage: (set-coding-system-priority CODING-SYSTEM ...)  */)
       if (coding_categories[category].id >= 0
          && ! EQ (args[i], CODING_ID_NAME (coding_categories[category].id)))
        setup_coding_system (args[i], &coding_categories[category]);
+      Fset (AREF (Vcoding_category_table, category), args[i]);
     }
 
   /* Now we have decided top J priorities.  Reflect the order of the
@@ -7335,6 +8054,14 @@ usage: (set-coding-system-priority CODING-SYSTEM ...)  */)
     }
 
   bcopy (priorities, coding_priorities, sizeof priorities);
+
+  /* Update `coding-category-list'.  */
+  Vcoding_category_list = Qnil;
+  for (i = coding_category_max - 1; i >= 0; i--)
+    Vcoding_category_list
+      = Fcons (AREF (Vcoding_category_table, priorities[i]),
+              Vcoding_category_list);
+
   return Qnil;
 }
 
@@ -7371,11 +8098,11 @@ make_subsidiaries (base)
      Lisp_Object base;
 {
   Lisp_Object subsidiaries;
-  int base_name_len = STRING_BYTES (XSYMBOL (base)->name);
+  int base_name_len = SBYTES (SYMBOL_NAME (base));
   char *buf = (char *) alloca (base_name_len + 6);
   int i;
-      
-  bcopy (XSYMBOL (base)->name->data, buf, base_name_len);
+
+  bcopy (SDATA (SYMBOL_NAME (base)), buf, base_name_len);
   subsidiaries = Fmake_vector (make_number (3), Qnil);
   for (i = 0; i < 3; i++)
     {
@@ -7457,9 +8184,9 @@ usage: (define-coding-system-internal ...)  */)
              ? CHARSET_EMACS_MULE_ID (charset) < 0
              : 0)
            error ("Can't handle charset `%s'",
-                  XSYMBOL (CHARSET_NAME (charset))->name->data);
+                  SDATA (SYMBOL_NAME (CHARSET_NAME (charset))));
 
-         XCAR (tail) = make_number (charset->id);
+         XSETCAR (tail, make_number (charset->id));
          if (max_charset_id < charset->id)
            max_charset_id = charset->id;
        }
@@ -7469,19 +8196,19 @@ usage: (define-coding-system-internal ...)  */)
   safe_charsets = Fmake_string (make_number (max_charset_id + 1),
                                make_number (255));
   for (tail = charset_list; CONSP (tail); tail = XCDR (tail))
-    XSTRING (safe_charsets)->data[XFASTINT (XCAR (tail))] = 0;
+    SSET (safe_charsets, 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);
+  if (! CHAR_TABLE_P (val) && ! CONSP (val))
+    CHECK_SYMBOL (val);
   CODING_ATTR_DECODE_TBL (attrs) = val;
 
   val = args[coding_arg_encode_translation_table];
-  if (! NILP (val))
-    CHECK_CHAR_TABLE (val);
+  if (! CHAR_TABLE_P (val) && ! CONSP (val))
+    CHECK_SYMBOL (val);
   CODING_ATTR_ENCODE_TBL (attrs) = val;
 
   val = args[coding_arg_post_read_conversion];
@@ -7497,10 +8224,13 @@ usage: (define-coding-system-internal ...)  */)
     CODING_ATTR_DEFAULT_CHAR (attrs) = make_number (' ');
   else
     {
-      CHECK_CHARACTER (val); 
+      CHECK_CHARACTER (val);
       CODING_ATTR_DEFAULT_CHAR (attrs) = val;
     }
 
+  val = args[coding_arg_for_unibyte];
+  CODING_ATTR_FOR_UNIBYTE (attrs) = NILP (val) ? Qnil : Qt;
+
   val = args[coding_arg_plist];
   CHECK_LIST (val);
   CODING_ATTR_PLIST (attrs) = val;
@@ -7518,8 +8248,7 @@ usage: (define-coding-system-internal ...)  */)
 
         If Nth element is a list of charset IDs, N is the first byte
         of one of them.  The list is sorted by dimensions of the
-        charsets.  A charset of smaller dimension comes firtst.
-      */
+        charsets.  A charset of smaller dimension comes firtst. */
       val = Fmake_vector (make_number (256), Qnil);
 
       for (tail = charset_list; CONSP (tail); tail = XCDR (tail))
@@ -7527,7 +8256,7 @@ usage: (define-coding-system-internal ...)  */)
          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;
 
@@ -7573,7 +8302,7 @@ usage: (define-coding-system-internal ...)  */)
   else if (EQ (coding_type, Qccl))
     {
       Lisp_Object valids;
-      
+
       if (nargs < coding_arg_ccl_max)
        goto short_args;
 
@@ -7593,24 +8322,34 @@ 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), make_number (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));
+             CHECK_NATNUM_CAR (val);
+             CHECK_NATNUM_CDR (val);
              from = XINT (XCAR (val));
+             if (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, make_number (1));
+             if (to < from || to > 255)
+               args_out_of_range_3 (XCDR (val),
+                                    XCAR (val), make_number (255));
            }
+         for (i = from; i <= to; i++)
+           SSET (valids, i, 1);
        }
       ASET (attrs, coding_attr_ccl_valids, valids);
-      
+
       category = coding_category_ccl;
     }
   else if (EQ (coding_type, Qutf_16))
@@ -7626,28 +8365,35 @@ usage: (define-coding-system-internal ...)  */)
       if (! NILP (bom) && ! EQ (bom, Qt))
        {
          CHECK_CONS (bom);
-         CHECK_CODING_SYSTEM (XCAR (bom));
-         CHECK_CODING_SYSTEM (XCDR (bom));
+         val = XCAR (bom);
+         CHECK_CODING_SYSTEM (val);
+         val = XCDR (bom);
+         CHECK_CODING_SYSTEM (val);
        }
       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", SDATA (SYMBOL_NAME (endian)));
       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;
@@ -7672,21 +8418,23 @@ usage: (define-coding-system-internal ...)  */)
 
       reg_usage = args[coding_arg_iso2022_reg_usage];
       CHECK_CONS (reg_usage);
-      CHECK_NATNUM (XCAR (reg_usage));
-      CHECK_NATNUM (XCDR (reg_usage));
+      CHECK_NUMBER_CAR (reg_usage);
+      CHECK_NUMBER_CDR (reg_usage);
 
       request = Fcopy_sequence (args[coding_arg_iso2022_request]);
       for (tail = request; ! NILP (tail); tail = Fcdr (tail))
        {
          int id;
+         Lisp_Object tmp;
 
          val = Fcar (tail);
          CHECK_CONS (val);
-         CHECK_CHARSET_GET_ID (XCAR (val), id);
-         CHECK_NATNUM (XCDR (val));
+         tmp = XCAR (val);
+         CHECK_CHARSET_GET_ID (tmp, id);
+         CHECK_NATNUM_CDR (val);
          if (XINT (XCDR (val)) >= 4)
            error ("Invalid graphic register number: %d", XINT (XCDR (val)));
-         XCAR (val) = make_number (id);
+         XSETCAR (val, make_number (id));
        }
 
       flags = args[coding_arg_iso2022_flags];
@@ -7736,13 +8484,14 @@ usage: (define-coding-system-internal ...)  */)
 
       struct charset *charset;
 
-      if (XINT (Flength (charset_list)) != 3)
-       error ("There should be just three charsets");
+      if (XINT (Flength (charset_list)) != 3
+         && XINT (Flength (charset_list)) != 4)
+       error ("There should be three or four charsets");
 
       charset = CHARSET_FROM_ID (XINT (XCAR (charset_list)));
       if (CHARSET_DIMENSION (charset) != 1)
        error ("Dimension of charset %s is not one",
-              XSYMBOL (CHARSET_NAME (charset))->name->data);
+              SDATA (SYMBOL_NAME (CHARSET_NAME (charset))));
       if (CHARSET_ASCII_COMPATIBLE_P (charset))
        CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
 
@@ -7750,13 +8499,22 @@ usage: (define-coding-system-internal ...)  */)
       charset = CHARSET_FROM_ID (XINT (XCAR (charset_list)));
       if (CHARSET_DIMENSION (charset) != 1)
        error ("Dimension of charset %s is not one",
-              XSYMBOL (CHARSET_NAME (charset))->name->data);
+              SDATA (SYMBOL_NAME (CHARSET_NAME (charset))));
 
       charset_list = XCDR (charset_list);
       charset = CHARSET_FROM_ID (XINT (XCAR (charset_list)));
       if (CHARSET_DIMENSION (charset) != 2)
        error ("Dimension of charset %s is not two",
-              XSYMBOL (CHARSET_NAME (charset))->name->data);
+              SDATA (SYMBOL_NAME (CHARSET_NAME (charset))));
+
+      charset_list = XCDR (charset_list);
+      if (! NILP (charset_list))
+       {
+         charset = CHARSET_FROM_ID (XINT (XCAR (charset_list)));
+         if (CHARSET_DIMENSION (charset) != 2)
+           error ("Dimension of charset %s is not two",
+                  SDATA (SYMBOL_NAME (CHARSET_NAME (charset))));
+       }
 
       category = coding_category_sjis;
       Vsjis_coding_system = name;
@@ -7771,7 +8529,7 @@ usage: (define-coding-system-internal ...)  */)
       charset = CHARSET_FROM_ID (XINT (XCAR (charset_list)));
       if (CHARSET_DIMENSION (charset) != 1)
        error ("Dimension of charset %s is not one",
-              XSYMBOL (CHARSET_NAME (charset))->name->data);
+              SDATA (SYMBOL_NAME (CHARSET_NAME (charset))));
       if (CHARSET_ASCII_COMPATIBLE_P (charset))
        CODING_ATTR_ASCII_COMPAT (attrs) = Qt;
 
@@ -7779,7 +8537,7 @@ usage: (define-coding-system-internal ...)  */)
       charset = CHARSET_FROM_ID (XINT (XCAR (charset_list)));
       if (CHARSET_DIMENSION (charset) != 2)
        error ("Dimension of charset %s is not two",
-              XSYMBOL (CHARSET_NAME (charset))->name->data);
+              SDATA (SYMBOL_NAME (CHARSET_NAME (charset))));
 
       category = coding_category_big5;
       Vbig5_coding_system = name;
@@ -7798,9 +8556,12 @@ usage: (define-coding-system-internal ...)  */)
     category = coding_category_undecided;
   else
     error ("Invalid coding system type: %s",
-          XSYMBOL (coding_type)->name->data);
+          SDATA (SYMBOL_NAME (coding_type)));
 
   CODING_ATTR_CATEGORY (attrs) = make_number (category);
+  CODING_ATTR_PLIST (attrs)
+    = Fcons (QCcategory, Fcons (AREF (Vcoding_category_table, category),
+                               CODING_ATTR_PLIST (attrs)));
 
   eol_type = args[coding_arg_eol_type];
   if (! NILP (eol_type)
@@ -7855,8 +8616,60 @@ usage: (define-coding-system-internal ...)  */)
                         make_number (nargs)));
 }
 
-/* Fixme: should this record the alias relationships for
-   diagnostics?  */
+
+DEFUN ("coding-system-put", Fcoding_system_put, Scoding_system_put,
+       3, 3, 0,
+       doc: /* Change value in CODING-SYSTEM's property list PROP to VAL.  */)
+  (coding_system, prop, val)
+     Lisp_Object coding_system, prop, val;
+{
+  Lisp_Object spec, attrs, plist;
+
+  CHECK_CODING_SYSTEM_GET_SPEC (coding_system, spec);
+  attrs = AREF (spec, 0);
+  if (EQ (prop, QCmnemonic))
+    {
+      if (! STRINGP (val))
+       CHECK_CHARACTER (val);
+      CODING_ATTR_MNEMONIC (attrs) = val;
+    }
+  else if (EQ (prop, QCdefalut_char))
+    {
+      if (NILP (val))
+       val = make_number (' ');
+      else
+       CHECK_CHARACTER (val);
+      CODING_ATTR_DEFAULT_CHAR (attrs) = val;
+    }
+  else if (EQ (prop, QCdecode_translation_table))
+    {
+      if (! CHAR_TABLE_P (val) && ! CONSP (val))
+       CHECK_SYMBOL (val);
+      CODING_ATTR_DECODE_TBL (attrs) = val;
+    }
+  else if (EQ (prop, QCencode_translation_table))
+    {
+      if (! CHAR_TABLE_P (val) && ! CONSP (val))
+       CHECK_SYMBOL (val);
+      CODING_ATTR_ENCODE_TBL (attrs) = val;
+    }
+  else if (EQ (prop, QCpost_read_conversion))
+    {
+      CHECK_SYMBOL (val);
+      CODING_ATTR_POST_READ (attrs) = val;
+    }
+  else if (EQ (prop, QCpre_write_conversion))
+    {
+      CHECK_SYMBOL (val);
+      CODING_ATTR_PRE_WRITE (attrs) = val;
+    }
+
+  CODING_ATTR_PLIST (attrs)
+    = Fplist_put (CODING_ATTR_PLIST (attrs), prop, val);
+  return val;
+}
+
+
 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.  */)
@@ -7868,9 +8681,12 @@ DEFUN ("define-coding-system-alias", Fdefine_coding_system_alias,
   CHECK_SYMBOL (alias);
   CHECK_CODING_SYSTEM_GET_SPEC (coding_system, spec);
   aliases = AREF (spec, 1);
+  /* ALISES should be a list of length more than zero, and the first
+     element is a base coding system.  Append ALIAS at the tail of the
+     list.  */
   while (!NILP (XCDR (aliases)))
     aliases = XCDR (aliases);
-  XCDR (aliases) = Fcons (alias, Qnil);
+  XSETCDR (aliases, Fcons (alias, Qnil));
 
   eol_type = AREF (spec, 2);
   if (VECTORP (eol_type))
@@ -7882,11 +8698,10 @@ DEFUN ("define-coding-system-alias", Fdefine_coding_system_alias,
       for (i = 0; i < 3; i++)
        Fdefine_coding_system_alias (AREF (subsidiaries, i),
                                     AREF (eol_type, i));
-
-      ASET (spec, 2, subsidiaries);
     }
 
   Fputhash (alias, spec, Vcoding_system_hash_table);
+  Vcoding_system_list = Fcons (alias, Vcoding_system_list);
   Vcoding_system_alist = Fcons (Fcons (Fsymbol_name (alias), Qnil),
                                Vcoding_system_alist);
 
@@ -7995,7 +8810,6 @@ init_coding_once ()
     iso_code_class[i] = ISO_graphic_plane_1;
   iso_code_class[0x20] = iso_code_class[0x7F] = ISO_0x20_or_0x7F;
   iso_code_class[0xA0] = iso_code_class[0xFF] = ISO_0xA0_or_0xFF;
-  iso_code_class[ISO_CODE_CR] = ISO_carriage_return;
   iso_code_class[ISO_CODE_SO] = ISO_shift_out;
   iso_code_class[ISO_CODE_SI] = ISO_shift_in;
   iso_code_class[ISO_CODE_SS2_7] = ISO_single_shift_2_7;
@@ -8004,8 +8818,6 @@ init_coding_once ()
   iso_code_class[ISO_CODE_SS3] = ISO_single_shift_3;
   iso_code_class[ISO_CODE_CSI] = ISO_control_sequence_introducer;
 
-  inhibit_pre_post_conversion = 0;
-
   for (i = 0; i < 256; i++)
     {
       emacs_mule_bytes[i] = 1;
@@ -8022,7 +8834,12 @@ void
 syms_of_coding ()
 {
   staticpro (&Vcoding_system_hash_table);
-  Vcoding_system_hash_table = Fmakehash (Qeq);
+  {
+    Lisp_Object args[2];
+    args[0] = QCtest;
+    args[1] = Qeq;
+    Vcoding_system_hash_table = Fmake_hash_table (2, args);
+  }
 
   staticpro (&Vsjis_coding_system);
   Vsjis_coding_system = Qnil;
@@ -8030,11 +8847,13 @@ syms_of_coding ()
   staticpro (&Vbig5_coding_system);
   Vbig5_coding_system = Qnil;
 
-  staticpro (&Vcode_conversion_work_buf_list);
-  Vcode_conversion_work_buf_list = Qnil;
+  staticpro (&Vcode_conversion_reused_workbuf);
+  Vcode_conversion_reused_workbuf = Qnil;
+
+  staticpro (&Vcode_conversion_workbuf_name);
+  Vcode_conversion_workbuf_name = build_string (" *code-conversion-work*");
 
-  staticpro (&Vcode_conversion_reused_work_buf);
-  Vcode_conversion_reused_work_buf = Qnil;
+  reused_workbuf_in_use = 0;
 
   DEFSYM (Qcharset, "charset");
   DEFSYM (Qtarget_idx, "target-idx");
@@ -8080,14 +8899,9 @@ syms_of_coding ()
   DEFSYM (Qiso_2022, "iso-2022");
 
   DEFSYM (Qutf_8, "utf-8");
+  DEFSYM (Qutf_8_emacs, "utf-8-emacs");
 
   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");
 
@@ -8117,6 +8931,14 @@ syms_of_coding ()
 
   DEFSYM (Qemacs_mule, "emacs-mule");
 
+  DEFSYM (QCcategory, ":category");
+  DEFSYM (QCmnemonic, ":mnemonic");
+  DEFSYM (QCdefalut_char, ":default-char");
+  DEFSYM (QCdecode_translation_table, ":decode-translation-table");
+  DEFSYM (QCencode_translation_table, ":encode-translation-table");
+  DEFSYM (QCpost_read_conversion, ":post-read-conversion");
+  DEFSYM (QCpre_write_conversion, ":pre-write-conversion");
+
   Vcoding_category_table
     = Fmake_vector (make_number (coding_category_max), Qnil);
   staticpro (&Vcoding_category_table);
@@ -8137,6 +8959,8 @@ syms_of_coding ()
        intern ("coding-category-utf-8"));
   ASET (Vcoding_category_table, coding_category_utf_16_be,
        intern ("coding-category-utf-16-be"));
+  ASET (Vcoding_category_table, coding_category_utf_16_auto,
+       intern ("coding-category-utf-16-auto"));
   ASET (Vcoding_category_table, coding_category_utf_16_le,
        intern ("coding-category-utf-16-le"));
   ASET (Vcoding_category_table, coding_category_utf_16_be_nosig,
@@ -8159,6 +8983,12 @@ syms_of_coding ()
   ASET (Vcoding_category_table, coding_category_undecided,
        intern ("coding-category-undecided"));
 
+  DEFSYM (Qinsufficient_source, "insufficient-source");
+  DEFSYM (Qinconsistent_eol, "inconsistent-eol");
+  DEFSYM (Qinvalid_source, "invalid-source");
+  DEFSYM (Qinterrupted, "interrupted");
+  DEFSYM (Qinsufficient_memory, "insufficient-memory");
+
   defsubr (&Scoding_system_p);
   defsubr (&Sread_coding_system);
   defsubr (&Sread_non_nil_coding_system);
@@ -8166,6 +8996,7 @@ syms_of_coding ()
   defsubr (&Sdetect_coding_region);
   defsubr (&Sdetect_coding_string);
   defsubr (&Sfind_coding_systems_region_internal);
+  defsubr (&Sunencodable_char_position);
   defsubr (&Scheck_coding_systems_region);
   defsubr (&Sdecode_coding_region);
   defsubr (&Sencode_coding_region);
@@ -8184,6 +9015,7 @@ syms_of_coding ()
   defsubr (&Sset_coding_system_priority);
   defsubr (&Sdefine_coding_system_internal);
   defsubr (&Sdefine_coding_system_alias);
+  defsubr (&Scoding_system_put);
   defsubr (&Scoding_system_base);
   defsubr (&Scoding_system_plist);
   defsubr (&Scoding_system_aliases);
@@ -8253,6 +9085,23 @@ the value of `buffer-file-coding-system' is used.  */);
 Coding system used in the latest file or process I/O.  */);
   Vlast_coding_system_used = Qnil;
 
+  DEFVAR_LISP ("last-code-conversion-error", &Vlast_code_conversion_error,
+              doc: /*
+Error status of the last code conversion.
+
+When an error was detected in the last code conversion, this variable
+is set to one of the following symbols.
+  `insufficient-source'
+  `inconsistent-eol'
+  `invalid-source'
+  `interrupted'
+  `insufficient-memory'
+When no error was detected, the value doesn't change.  So, to check
+the error status of a code conversion by this variable, you must
+explicitly set this variable to nil before performing code
+conversion.  */);
+  Vlast_code_conversion_error = Qnil;
+
   DEFVAR_BOOL ("inhibit-eol-conversion", &inhibit_eol_conversion,
               doc: /*
 *Non-nil means always inhibit code conversion of end-of-line format.
@@ -8397,6 +9246,15 @@ coding system used in each operation can't encode the text.
 The default value is `select-safe-coding-system' (which see).  */);
   Vselect_safe_coding_system_function = Qnil;
 
+  DEFVAR_BOOL ("coding-system-require-warning",
+              &coding_system_require_warning,
+              doc: /* Internal use only.
+If non-nil, on writing a file, `select-safe-coding-system-function' is
+called even if `coding-system-for-write' is non-nil.  The command
+`universal-coding-system-argument' binds this variable to t temporarily.  */);
+  coding_system_require_warning = 0;
+
+
   DEFVAR_BOOL ("inhibit-iso-escape-detection",
               &inhibit_iso_escape_detection,
               doc: /*
@@ -8425,9 +9283,15 @@ to explicitly specify some coding system that doesn't use ISO2022's
 escape sequence (e.g `latin-1') on reading by \\[universal-coding-system-argument].  */);
   inhibit_iso_escape_detection = 0;
 
+  DEFVAR_LISP ("translation-table-for-input", &Vtranslation_table_for_input,
+              doc: /* Char table for translating self-inserting characters.
+This is applied to the result of input methods, not their input.  See also
+`keyboard-translate-table'.  */);
+    Vtranslation_table_for_input = Qnil;
+
   {
     Lisp_Object args[coding_arg_max];
-    Lisp_Object plist[14];
+    Lisp_Object plist[16];
     int i;
 
     for (i = 0; i < coding_arg_max; i++)
@@ -8443,21 +9307,30 @@ escape sequence (e.g `latin-1') on reading by \\[universal-coding-system-argumen
     plist[7] = args[coding_arg_ascii_compatible_p] = Qt;
     plist[8] = intern (":default-char");
     plist[9] = args[coding_arg_default_char] = make_number (0);
-    plist[10] = intern (":docstring");
-    plist[11] = build_string ("Do no conversion.\n\
+    plist[10] = intern (":for-unibyte");
+    plist[11] = args[coding_arg_for_unibyte] = Qt;
+    plist[12] = intern (":docstring");
+    plist[13] = build_string ("Do no conversion.\n\
 \n\
 When you visit a file with this coding, the file is read into a\n\
 unibyte buffer as is, thus each byte of a file is treated as a\n\
 character.");
-    plist[12] = intern (":eol-type");
-    plist[13] = args[coding_arg_eol_type] = Qunix;
-    args[coding_arg_plist] = Flist (14, plist);
+    plist[14] = intern (":eol-type");
+    plist[15] = args[coding_arg_eol_type] = Qunix;
+    args[coding_arg_plist] = Flist (16, plist);
     Fdefine_coding_system_internal (coding_arg_max, args);
   }
 
   setup_coding_system (Qno_conversion, &keyboard_coding);
   setup_coding_system (Qno_conversion, &terminal_coding);
   setup_coding_system (Qno_conversion, &safe_terminal_coding);
+
+  {
+    int i;
+
+    for (i = 0; i < coding_category_max; i++)
+      Fset (AREF (Vcoding_category_table, i), Qno_conversion);
+  }
 }
 
 char *
@@ -8474,7 +9347,7 @@ emacs_strerror (error_number)
       Lisp_Object dec = code_convert_string_norecord (build_string (str),
                                                      Vlocale_coding_system,
                                                      0);
-      str = (char *) XSTRING (dec)->data;
+      str = (char *) SDATA (dec);
     }
 
   return str;