(Fset_buffer_multibyte): Fix arg for chars_in_text.
[bpt/emacs.git] / src / fns.c
index 157918b..1299f3d 100644 (file)
--- a/src/fns.c
+++ b/src/fns.c
@@ -1,5 +1,5 @@
 /* Random utility Lisp functions.
-   Copyright (C) 1985, 86, 87, 93, 94, 95 Free Software Foundation, Inc.
+   Copyright (C) 1985, 86, 87, 93, 94, 95, 97, 1998 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -123,7 +123,9 @@ To get the number of characters, use `chars-in-string'")
   else if (VECTORP (sequence))
     XSETFASTINT (val, XVECTOR (sequence)->size);
   else if (CHAR_TABLE_P (sequence))
-    XSETFASTINT (val, CHAR_TABLE_ORDINARY_SLOTS);
+    XSETFASTINT (val, (MIN_CHAR_COMPOSITION
+                      + (CHAR_FIELD2_MASK | CHAR_FIELD3_MASK)
+                      - 1));
   else if (BOOL_VECTOR_P (sequence))
     XSETFASTINT (val, XBOOL_VECTOR (sequence)->size);
   else if (COMPILEDP (sequence))
@@ -177,8 +179,18 @@ which is at least the number of distinct elements.")
   return length;
 }
 
+DEFUN ("string-bytes", Fstring_bytes, Sstring_bytes, 1, 1, 0,
+  "Return the number of bytes in STRING.\n\
+If STRING is a multibyte string, this is greater than the length of STRING.")
+  (string)
+     Lisp_Object string;
+{
+  CHECK_STRING (string, 1);
+  return make_number (XSTRING (string)->size_byte);
+}
+
 DEFUN ("string-equal", Fstring_equal, Sstring_equal, 2, 2, 0,
-  "T if two strings have identical contents.\n\
+  "Return t if two strings have identical contents.\n\
 Case is significant, but text properties are ignored.\n\
 Symbols are also allowed; their print names are used instead.")
   (s1, s2)
@@ -191,22 +203,22 @@ Symbols are also allowed; their print names are used instead.")
   CHECK_STRING (s1, 0);
   CHECK_STRING (s2, 1);
 
-  if (XSTRING (s1)->size != XSTRING (s2)->size ||
-      bcmp (XSTRING (s1)->data, XSTRING (s2)->data, XSTRING (s1)->size))
+  if (XSTRING (s1)->size != XSTRING (s2)->size
+      || XSTRING (s1)->size_byte != XSTRING (s2)->size_byte
+      || bcmp (XSTRING (s1)->data, XSTRING (s2)->data, XSTRING (s1)->size_byte))
     return Qnil;
   return Qt;
 }
 
 DEFUN ("string-lessp", Fstring_lessp, Sstring_lessp, 2, 2, 0,
-  "T if first arg string is less than second in lexicographic order.\n\
+  "Return t if first arg string is less than second in lexicographic order.\n\
 Case is significant.\n\
 Symbols are also allowed; their print names are used instead.")
   (s1, s2)
      register Lisp_Object s1, s2;
 {
-  register int i;
-  register unsigned char *p1, *p2;
   register int end;
+  register int i1, i1_byte, i2, i2_byte;
 
   if (SYMBOLP (s1))
     XSETSTRING (s1, XSYMBOL (s1)->name);
@@ -215,18 +227,32 @@ Symbols are also allowed; their print names are used instead.")
   CHECK_STRING (s1, 0);
   CHECK_STRING (s2, 1);
 
-  p1 = XSTRING (s1)->data;
-  p2 = XSTRING (s2)->data;
+  i1 = i1_byte = i2 = i2_byte = 0;
+
   end = XSTRING (s1)->size;
   if (end > XSTRING (s2)->size)
     end = XSTRING (s2)->size;
 
-  for (i = 0; i < end; i++)
+  while (i1 < end)
     {
-      if (p1[i] != p2[i])
-       return p1[i] < p2[i] ? Qt : Qnil;
+      /* When we find a mismatch, we must compare the
+        characters, not just the bytes.  */
+      int c1, c2;
+
+      if (STRING_MULTIBYTE (s1))
+       FETCH_STRING_CHAR_ADVANCE (c1, s1, i1, i1_byte);
+      else
+       c1 = XSTRING (s1)->data[i1++];
+
+      if (STRING_MULTIBYTE (s2))
+       FETCH_STRING_CHAR_ADVANCE (c2, s2, i2, i2_byte);
+      else
+       c2 = XSTRING (s2)->data[i2++];
+
+      if (c1 != c2)
+       return c1 < c2 ? Qt : Qnil;
     }
-  return i < XSTRING (s2)->size ? Qt : Qnil;
+  return i1 < XSTRING (s2)->size ? Qt : Qnil;
 }
 \f
 static Lisp_Object concat ();
@@ -379,14 +405,16 @@ concat (nargs, args, target_type, last_special)
      int last_special;
 {
   Lisp_Object val;
-  Lisp_Object len;
   register Lisp_Object tail;
   register Lisp_Object this;
   int toindex;
-  register int leni;
+  int toindex_byte;
+  register int result_len;
+  register int result_len_byte;
   register int argnum;
   Lisp_Object last_tail;
   Lisp_Object prev;
+  int some_multibyte;
 
   /* In append, the last arg isn't treated like the others */
   if (last_special && nargs > 0)
@@ -397,6 +425,7 @@ concat (nargs, args, target_type, last_special)
   else
     last_tail = Qnil;
 
+  /* Canonicalize each argument.  */
   for (argnum = 0; argnum < nargs; argnum++)
     {
       this = args[argnum];
@@ -410,56 +439,86 @@ concat (nargs, args, target_type, last_special)
        }
     }
 
-  for (argnum = 0, leni = 0; argnum < nargs; argnum++)
+  /* Compute total length in chars of arguments in RESULT_LEN.
+     If desired output is a string, also compute length in bytes
+     in RESULT_LEN_BYTE, and determine in SOME_MULTIBYTE
+     whether the result should be a multibyte string.  */
+  result_len_byte = 0;
+  result_len = 0;
+  some_multibyte = 0;
+  for (argnum = 0; argnum < nargs; argnum++)
     {
+      int len;
       this = args[argnum];
-      len = Flength (this);
-      if ((VECTORP (this) || CONSP (this)) && target_type == Lisp_String)
-
+      len = XFASTINT (Flength (this));
+      if (target_type == Lisp_String)
        {
-         /* We must pay attention to a multibyte character which
-             takes more than one byte in string.  */
+         /* We must count the number of bytes needed in the string
+            as well as the number of characters.  */
          int i;
          Lisp_Object ch;
+         int this_len_byte;
 
          if (VECTORP (this))
-           for (i = 0; i < XFASTINT (len); i++)
+           for (i = 0; i < len; i++)
              {
                ch = XVECTOR (this)->contents[i];
                if (! INTEGERP (ch))
                  wrong_type_argument (Qintegerp, ch);
-               leni += XFASTINT (Fchar_bytes (ch));
+               this_len_byte = XFASTINT (Fchar_bytes (ch));
+               result_len_byte += this_len_byte;
+               if (this_len_byte > 1)
+                 some_multibyte = 1;
              }
-         else
+         else if (BOOL_VECTOR_P (this) && XBOOL_VECTOR (this)->size > 0)
+           wrong_type_argument (Qintegerp, Faref (this, make_number (0)));
+         else if (CONSP (this))
            for (; CONSP (this); this = XCONS (this)->cdr)
              {
                ch = XCONS (this)->car;
                if (! INTEGERP (ch))
                  wrong_type_argument (Qintegerp, ch);
-               leni += XFASTINT (Fchar_bytes (ch));
+               this_len_byte = XFASTINT (Fchar_bytes (ch));
+               result_len_byte += this_len_byte;
+               if (this_len_byte > 1)
+                 some_multibyte = 1;
              }
+         else if (STRINGP (this))
+           {
+             if (STRING_MULTIBYTE (this))
+               {
+                 some_multibyte = 1;
+                 result_len_byte += XSTRING (this)->size_byte;
+               }
+             else
+               result_len_byte += count_size_as_multibyte (XSTRING (this)->data,
+                                                           XSTRING (this)->size);
+           }
        }
-      else
-       leni += XFASTINT (len);
+
+      result_len += len;
     }
 
-  XSETFASTINT (len, leni);
+  if (! some_multibyte)
+    result_len_byte = result_len;
 
+  /* Create the output object.  */
   if (target_type == Lisp_Cons)
-    val = Fmake_list (len, Qnil);
+    val = Fmake_list (make_number (result_len), Qnil);
   else if (target_type == Lisp_Vectorlike)
-    val = Fmake_vector (len, Qnil);
+    val = Fmake_vector (make_number (result_len), Qnil);
   else
-    val = Fmake_string (len, len);
+    val = make_uninit_multibyte_string (result_len, result_len_byte);
 
-  /* In append, if all but last arg are nil, return last arg */
+  /* In `append', if all but last arg are nil, return last arg.  */
   if (target_type == Lisp_Cons && EQ (val, Qnil))
     return last_tail;
 
+  /* Copy the contents of the args into the result.  */
   if (CONSP (val))
     tail = val, toindex = -1;          /* -1 in toindex is flag we are making a list */
   else
-    toindex = 0;
+    toindex = 0, toindex_byte = 0;
 
   prev = Qnil;
 
@@ -468,6 +527,7 @@ concat (nargs, args, target_type, last_special)
       Lisp_Object thislen;
       int thisleni;
       register unsigned int thisindex = 0;
+      register unsigned int thisindex_byte = 0;
 
       this = args[argnum];
       if (!CONSP (this))
@@ -475,71 +535,108 @@ concat (nargs, args, target_type, last_special)
 
       if (STRINGP (this) && STRINGP (val)
          && ! NULL_INTERVAL_P (XSTRING (this)->intervals))
+       copy_text_properties (make_number (0), thislen, this,
+                             make_number (toindex), val, Qnil);
+
+      /* Between strings of the same kind, copy fast.  */
+      if (STRINGP (this) && STRINGP (val)
+         && STRING_MULTIBYTE (this) == some_multibyte)
        {
-         copy_text_properties (make_number (0), thislen, this,
-                               make_number (toindex), val, Qnil);
+         int thislen_byte = XSTRING (this)->size_byte;
+         bcopy (XSTRING (this)->data, XSTRING (val)->data + toindex_byte,
+                XSTRING (this)->size_byte);
+         toindex_byte += thislen_byte;
+         toindex += thisleni;
        }
-
-      while (1)
+      /* Copy a single-byte string to a multibyte string.  */
+      else if (STRINGP (this) && STRINGP (val))
        {
-         register Lisp_Object elt;
-
-         /* Fetch next element of `this' arg into `elt', or break if
-             `this' is exhausted. */
-         if (NILP (this)) break;
-         if (CONSP (this))
-           elt = XCONS (this)->car, this = XCONS (this)->cdr;
-         else
-           {
-             if (thisindex >= thisleni) break;
-             if (STRINGP (this))
-               XSETFASTINT (elt, XSTRING (this)->data[thisindex++]);
-             else if (BOOL_VECTOR_P (this))
-               {
-                 int size_in_chars
-                   = ((XBOOL_VECTOR (this)->size + BITS_PER_CHAR - 1)
-                      / BITS_PER_CHAR);
-                 int byte;
-                 byte = XBOOL_VECTOR (val)->data[thisindex / BITS_PER_CHAR];
-                 if (byte & (1 << (thisindex % BITS_PER_CHAR)))
-                   elt = Qt;
-                 else
-                   elt = Qnil;
-               }
-             else
-               elt = XVECTOR (this)->contents[thisindex++];
-           }
+         toindex_byte += copy_text (XSTRING (this)->data,
+                                    XSTRING (val)->data + toindex_byte,
+                                    XSTRING (this)->size, 0, 1);
+         toindex += thisleni;
+       }
+      else
+       /* Copy element by element.  */
+       while (1)
+         {
+           register Lisp_Object elt;
+
+           /* Fetch next element of `this' arg into `elt', or break if
+              `this' is exhausted. */
+           if (NILP (this)) break;
+           if (CONSP (this))
+             elt = XCONS (this)->car, this = XCONS (this)->cdr;
+           else if (thisindex >= thisleni)
+             break;
+           else if (STRINGP (this))
+             {
+               int c;
+               if (STRING_MULTIBYTE (this))
+                 {
+                   FETCH_STRING_CHAR_ADVANCE (c, this,
+                                              thisindex,
+                                              thisindex_byte);
+                   XSETFASTINT (elt, c);
+                 }
+               else
+                 {
+                   XSETFASTINT (elt, XSTRING (this)->data[thisindex++]);
+                   if (some_multibyte && XINT (elt) >= 0200
+                       && XINT (elt) < 0400)
+                     {
+                       c = unibyte_char_to_multibyte (XINT (elt));
+                       XSETINT (elt, c);
+                     }
+                 }
+             }
+           else if (BOOL_VECTOR_P (this))
+             {
+               int byte;
+               byte = XBOOL_VECTOR (this)->data[thisindex / BITS_PER_CHAR];
+               if (byte & (1 << (thisindex % BITS_PER_CHAR)))
+                 elt = Qt;
+               else
+                 elt = Qnil;
+               thisindex++;
+             }
+           else
+             elt = XVECTOR (this)->contents[thisindex++];
 
-         /* Store into result */
-         if (toindex < 0)
-           {
-             XCONS (tail)->car = elt;
-             prev = tail;
-             tail = XCONS (tail)->cdr;
-           }
-         else if (VECTORP (val))
-           XVECTOR (val)->contents[toindex++] = elt;
-         else
-           {
-             while (!INTEGERP (elt))
-               elt = wrong_type_argument (Qintegerp, elt);
+           /* Store this element into the result.  */
+           if (toindex < 0)
              {
-               int c = XINT (elt);
-               unsigned char work[4], *str;
-               int i = CHAR_STRING (c, work, str);
-
-#ifdef MASSC_REGISTER_BUG
-               /* Even removing all "register"s doesn't disable this bug!
-                  Nothing simpler than this seems to work. */
-               unsigned char *p = & XSTRING (val)->data[toindex];
-               bcopy (str, p, i);
-#else
-               bcopy (str, & XSTRING (val)->data[toindex], i);
-#endif
-               toindex += i;
+               XCONS (tail)->car = elt;
+               prev = tail;
+               tail = XCONS (tail)->cdr;
              }
-           }
-       }
+           else if (VECTORP (val))
+             XVECTOR (val)->contents[toindex++] = elt;
+           else
+             {
+               CHECK_NUMBER (elt, 0);
+               if (SINGLE_BYTE_CHAR_P (XINT (elt)))
+                 {
+                   XSTRING (val)->data[toindex++] = XINT (elt);
+                   toindex_byte++;
+                 }
+               else
+                 /* If we have any multibyte characters,
+                    we already decided to make a multibyte string.  */
+                 {
+                   int c = XINT (elt);
+                   unsigned char work[4], *str;
+                   int i = CHAR_STRING (c, work, str);
+
+                   /* P exists as a variable
+                      to avoid a bug on the Masscomp C compiler.  */
+                   unsigned char *p = & XSTRING (val)->data[toindex_byte];
+                   bcopy (str, p, i);
+                   toindex_byte += i;
+                   toindex++;
+                 }
+             }
+         }
     }
   if (!NILP (prev))
     XCONS (prev)->cdr = last_tail;
@@ -547,6 +644,243 @@ concat (nargs, args, target_type, last_special)
   return val;
 }
 \f
+static Lisp_Object string_char_byte_cache_string;
+static int string_char_byte_cache_charpos;
+static int string_char_byte_cache_bytepos;
+
+/* Return the character index corresponding to CHAR_INDEX in STRING.  */
+
+int
+string_char_to_byte (string, char_index)
+     Lisp_Object string;
+     int char_index;
+{
+  int i, i_byte;
+  int best_below, best_below_byte;
+  int best_above, best_above_byte;
+
+  if (! STRING_MULTIBYTE (string))
+    return char_index;
+
+  best_below = best_below_byte = 0;
+  best_above = XSTRING (string)->size;
+  best_above_byte = XSTRING (string)->size_byte;
+
+  if (EQ (string, string_char_byte_cache_string))
+    {
+      if (string_char_byte_cache_charpos < char_index)
+       {
+         best_below = string_char_byte_cache_charpos;
+         best_below_byte = string_char_byte_cache_bytepos;
+       }
+      else
+       {
+         best_above = string_char_byte_cache_charpos;
+         best_above_byte = string_char_byte_cache_bytepos;
+       }
+    }
+
+  if (char_index - best_below < best_above - char_index)
+    {
+      while (best_below < char_index)
+       {
+         int c;
+         FETCH_STRING_CHAR_ADVANCE (c, string, best_below, best_below_byte);
+       }
+      i = best_below;
+      i_byte = best_below_byte;
+    }
+  else
+    {
+      while (best_above > char_index)
+       {
+         int best_above_byte_saved = --best_above_byte;
+
+         while (best_above_byte > 0
+                && !CHAR_HEAD_P (XSTRING (string)->data[best_above_byte]))
+           best_above_byte--;
+         if (XSTRING (string)->data[best_above_byte] < 0x80)
+           best_above_byte = best_above_byte_saved;
+         best_above--;
+       }
+      i = best_above;
+      i_byte = best_above_byte;
+    }
+
+  string_char_byte_cache_bytepos = i_byte;
+  string_char_byte_cache_charpos = i;
+  string_char_byte_cache_string = string;
+
+  return i_byte;
+}
+\f
+/* Return the character index corresponding to BYTE_INDEX in STRING.  */
+
+int
+string_byte_to_char (string, byte_index)
+     Lisp_Object string;
+     int byte_index;
+{
+  int i, i_byte;
+  int best_below, best_below_byte;
+  int best_above, best_above_byte;
+
+  if (! STRING_MULTIBYTE (string))
+    return byte_index;
+
+  best_below = best_below_byte = 0;
+  best_above = XSTRING (string)->size;
+  best_above_byte = XSTRING (string)->size_byte;
+
+  if (EQ (string, string_char_byte_cache_string))
+    {
+      if (string_char_byte_cache_bytepos < byte_index)
+       {
+         best_below = string_char_byte_cache_charpos;
+         best_below_byte = string_char_byte_cache_bytepos;
+       }
+      else
+       {
+         best_above = string_char_byte_cache_charpos;
+         best_above_byte = string_char_byte_cache_bytepos;
+       }
+    }
+
+  if (byte_index - best_below_byte < best_above_byte - byte_index)
+    {
+      while (best_below_byte < byte_index)
+       {
+         int c;
+         FETCH_STRING_CHAR_ADVANCE (c, string, best_below, best_below_byte);
+       }
+      i = best_below;
+      i_byte = best_below_byte;
+    }
+  else
+    {
+      while (best_above_byte > byte_index)
+       {
+         int best_above_byte_saved = --best_above_byte;
+
+         while (best_above_byte > 0
+                && !CHAR_HEAD_P (XSTRING (string)->data[best_above_byte]))
+           best_above_byte--;
+         if (XSTRING (string)->data[best_above_byte] < 0x80)
+           best_above_byte = best_above_byte_saved;
+         best_above--;
+       }
+      i = best_above;
+      i_byte = best_above_byte;
+    }
+
+  string_char_byte_cache_bytepos = i_byte;
+  string_char_byte_cache_charpos = i;
+  string_char_byte_cache_string = string;
+
+  return i;
+}
+\f
+/* Convert STRING to a multibyte string.
+   Single-byte characters 0240 through 0377 are converted
+   by adding nonascii_insert_offset to each.  */
+
+Lisp_Object
+string_make_multibyte (string)
+     Lisp_Object string;
+{
+  unsigned char *buf;
+  int nbytes;
+
+  if (STRING_MULTIBYTE (string))
+    return string;
+
+  nbytes = count_size_as_multibyte (XSTRING (string)->data,
+                                   XSTRING (string)->size);
+  /* If all the chars are ASCII, they won't need any more bytes
+     once converted.  In that case, we can return STRING itself.  */
+  if (nbytes == XSTRING (string)->size_byte)
+    return string;
+
+  buf = (unsigned char *) alloca (nbytes);
+  copy_text (XSTRING (string)->data, buf, XSTRING (string)->size_byte,
+            0, 1);
+
+  return make_multibyte_string (buf, XSTRING (string)->size, nbytes);
+}
+
+/* Convert STRING to a single-byte string.  */
+
+Lisp_Object
+string_make_unibyte (string)
+     Lisp_Object string;
+{
+  unsigned char *buf;
+
+  if (! STRING_MULTIBYTE (string))
+    return string;
+
+  buf = (unsigned char *) alloca (XSTRING (string)->size);
+
+  copy_text (XSTRING (string)->data, buf, XSTRING (string)->size_byte,
+            1, 0);
+
+  return make_unibyte_string (buf, XSTRING (string)->size);
+}
+
+DEFUN ("string-make-multibyte", Fstring_make_multibyte, Sstring_make_multibyte,
+       1, 1, 0,
+  "Return the multibyte equivalent of STRING.")
+  (string)
+     Lisp_Object string;
+{
+  return string_make_multibyte (string);
+}
+
+DEFUN ("string-make-unibyte", Fstring_make_unibyte, Sstring_make_unibyte,
+       1, 1, 0,
+  "Return the unibyte equivalent of STRING.")
+  (string)
+     Lisp_Object string;
+{
+  return string_make_unibyte (string);
+}
+
+DEFUN ("string-as-unibyte", Fstring_as_unibyte, Sstring_as_unibyte,
+       1, 1, 0,
+  "Return a unibyte string with the same individual bytes as STRING.\n\
+If STRING is unibyte, the result is STRING itself.")
+  (string)
+     Lisp_Object string;
+{
+  if (STRING_MULTIBYTE (string))
+    {
+      string = Fcopy_sequence (string);
+      XSTRING (string)->size = XSTRING (string)->size_byte;
+    }
+  return string;
+}
+
+DEFUN ("string-as-multibyte", Fstring_as_multibyte, Sstring_as_multibyte,
+       1, 1, 0,
+  "Return a multibyte string with the same individual bytes as STRING.\n\
+If STRING is multibyte, the result is STRING itself.")
+  (string)
+     Lisp_Object string;
+{
+  if (! STRING_MULTIBYTE (string))
+    {
+      int newlen = chars_in_text (XSTRING (string)->data,
+                                 XSTRING (string)->size_byte);
+      /* If all the chars are ASCII, STRING is already suitable.  */
+      if (newlen != XSTRING (string)->size_byte)
+       {
+         string = Fcopy_sequence (string);
+         XSTRING (string)->size = newlen;
+       }
+    }
+  return string;
+}
+\f
 DEFUN ("copy-alist", Fcopy_alist, Scopy_alist, 1, 1, 0,
   "Return a copy of ALIST.\n\
 This is an alist which represents the same mapping from objects to objects,\n\
@@ -586,6 +920,9 @@ This function allows vectors as well as strings.")
 {
   Lisp_Object res;
   int size;
+  int size_byte;
+  int from_char, to_char;
+  int from_byte, to_byte;
 
   if (! (STRINGP (string) || VECTORP (string)))
     wrong_type_argument (Qarrayp, string);
@@ -593,32 +930,89 @@ This function allows vectors as well as strings.")
   CHECK_NUMBER (from, 1);
 
   if (STRINGP (string))
-    size = XSTRING (string)->size;
+    {
+      size = XSTRING (string)->size;
+      size_byte = XSTRING (string)->size_byte;
+    }
   else
     size = XVECTOR (string)->size;
 
   if (NILP (to))
-    XSETINT (to, size);
+    {
+      to_char = size;
+      to_byte = size_byte;
+    }
+  else
+    {
+      CHECK_NUMBER (to, 2);
+
+      to_char = XINT (to);
+      if (to_char < 0)
+       to_char += size;
+
+      if (STRINGP (string))
+       to_byte = string_char_to_byte (string, to_char);
+    }
+
+  from_char = XINT (from);
+  if (from_char < 0)
+    from_char += size;
+  if (STRINGP (string))
+    from_byte = string_char_to_byte (string, from_char);
+
+  if (!(0 <= from_char && from_char <= to_char && to_char <= size))
+    args_out_of_range_3 (string, make_number (from_char),
+                        make_number (to_char));
+
+  if (STRINGP (string))
+    {
+      res = make_multibyte_string (XSTRING (string)->data + from_byte,
+                                  to_char - from_char, to_byte - from_byte);
+      copy_text_properties (from_char, to_char, string,
+                           make_number (0), res, Qnil);
+    }
+  else
+    res = Fvector (to_char - from_char,
+                  XVECTOR (string)->contents + from_char);
+
+  return res;
+}
+
+/* Extract a substring of STRING, giving start and end positions
+   both in characters and in bytes.  */
+
+Lisp_Object
+substring_both (string, from, from_byte, to, to_byte)
+     Lisp_Object string;
+     int from, from_byte, to, to_byte;
+{
+  Lisp_Object res;
+  int size;
+  int size_byte;
+
+  if (! (STRINGP (string) || VECTORP (string)))
+    wrong_type_argument (Qarrayp, string);
+
+  if (STRINGP (string))
+    {
+      size = XSTRING (string)->size;
+      size_byte = XSTRING (string)->size_byte;
+    }
   else
-    CHECK_NUMBER (to, 2);
+    size = XVECTOR (string)->size;
 
-  if (XINT (from) < 0)
-    XSETINT (from, XINT (from) + size);
-  if (XINT (to) < 0)
-    XSETINT (to, XINT (to) + size);
-  if (!(0 <= XINT (from) && XINT (from) <= XINT (to)
-        && XINT (to) <= size))
-    args_out_of_range_3 (string, from, to);
+  if (!(0 <= from && from <= to && to <= size))
+    args_out_of_range_3 (string, make_number (from), make_number (to));
 
   if (STRINGP (string))
     {
-      res = make_string (XSTRING (string)->data + XINT (from),
-                        XINT (to) - XINT (from));
+      res = make_multibyte_string (XSTRING (string)->data + from_byte,
+                                  to - from, to_byte - from_byte);
       copy_text_properties (from, to, string, make_number (0), res, Qnil);
     }
   else
-    res = Fvector (XINT (to) - XINT (from),
-                  XVECTOR (string)->contents + XINT (from));
+    res = Fvector (to - from,
+                  XVECTOR (string)->contents + from);
 
   return res;
 }
@@ -1081,7 +1475,7 @@ It can be retrieved with `(get SYMBOL PROPNAME)'.")
 }
 
 DEFUN ("equal", Fequal, Sequal, 2, 2, 0,
-  "T if two Lisp objects have similar structure and contents.\n\
+  "Return t if two Lisp objects have similar structure and contents.\n\
 They must have the same data type.\n\
 Conses are compared by comparing the cars and the cdrs.\n\
 Vectors and strings are compared element by element.\n\
@@ -1141,7 +1535,7 @@ internal_equal (o1, o2, depth)
        {
          return (XMARKER (o1)->buffer == XMARKER (o2)->buffer
                  && (XMARKER (o1)->buffer == 0
-                     || XMARKER (o1)->bufpos == XMARKER (o2)->bufpos));
+                     || XMARKER (o1)->bytepos == XMARKER (o2)->bytepos));
        }
       break;
 
@@ -1167,6 +1561,8 @@ internal_equal (o1, o2, depth)
              return 0;
            return 1;
          }
+       if (WINDOW_CONFIGURATIONP (o1))
+         return compare_window_configurations (o1, o2, 0);
 
        /* Aside from them, only true vectors, char-tables, and compiled
           functions are sensible to compare, so eliminate the others now.  */
@@ -1191,8 +1587,10 @@ internal_equal (o1, o2, depth)
     case Lisp_String:
       if (XSTRING (o1)->size != XSTRING (o2)->size)
        return 0;
+      if (XSTRING (o1)->size_byte != XSTRING (o2)->size_byte)
+       return 0;
       if (bcmp (XSTRING (o1)->data, XSTRING (o2)->data,
-               XSTRING (o1)->size))
+               XSTRING (o1)->size_byte))
        return 0;
       return 1;
     }
@@ -1250,7 +1648,7 @@ ARRAY is a vector, string, char-table, or bool-vector.")
     }
   return array;
 }
-
+\f
 DEFUN ("char-table-subtype", Fchar_table_subtype, Schar_table_subtype,
        1, 1, 0,
   "Return the subtype of char-table CHAR-TABLE.   The value is a symbol.")
@@ -1332,13 +1730,13 @@ DEFUN ("set-char-table-extra-slot", Fset_char_table_extra_slot,
 
   return XCHAR_TABLE (char_table)->extras[XINT (n)] = value;
 }
-
+\f
 DEFUN ("char-table-range", Fchar_table_range, Schar_table_range,
        2, 2, 0,
   "Return the value in CHAR-TABLE for a range of characters RANGE.\n\
-RANGE should be t (for all characters), nil (for the default value)\n\
+RANGE should be nil (for the default value)\n\
 a vector which identifies a character set or a row of a character set,\n\
-or a character code.")
+a character set name, or a character code.")
   (char_table, range)
      Lisp_Object char_table, range;
 {
@@ -1350,10 +1748,19 @@ or a character code.")
     return XCHAR_TABLE (char_table)->defalt;
   else if (INTEGERP (range))
     return Faref (char_table, range);
+  else if (SYMBOLP (range))
+    {
+      Lisp_Object charset_info;
+
+      charset_info = Fget (range, Qcharset);
+      CHECK_VECTOR (charset_info, 0);
+
+      return Faref (char_table, XVECTOR (charset_info)->contents[0] + 128);
+    }
   else if (VECTORP (range))
     {
       if (XVECTOR (range)->size == 1)
-       return Faref (char_table, XVECTOR (range)->contents[0]);
+       return Faref (char_table, XVECTOR (range)->contents[0] + 128);
       else
        {
          int size = XVECTOR (range)->size;
@@ -1373,7 +1780,7 @@ DEFUN ("set-char-table-range", Fset_char_table_range, Sset_char_table_range,
   "Set the value in CHAR-TABLE for a range of characters RANGE to VALUE.\n\
 RANGE should be t (for all characters), nil (for the default value)\n\
 a vector which identifies a character set or a row of a character set,\n\
-or a character code.")
+a coding system, or a character code.")
   (char_table, range, value)
      Lisp_Object char_table, range, value;
 {
@@ -1386,12 +1793,22 @@ or a character code.")
       XCHAR_TABLE (char_table)->contents[i] = value;
   else if (EQ (range, Qnil))
     XCHAR_TABLE (char_table)->defalt = value;
+  else if (SYMBOLP (range))
+    {
+      Lisp_Object charset_info;
+
+      charset_info = Fget (range, Qcharset);
+      CHECK_VECTOR (charset_info, 0);
+
+      return Faset (char_table, XVECTOR (charset_info)->contents[0] + 128,
+                   value);
+    }
   else if (INTEGERP (range))
     Faset (char_table, range, value);
   else if (VECTORP (range))
     {
       if (XVECTOR (range)->size == 1)
-       return Faset (char_table, XVECTOR (range)->contents[0], value);
+       return Faset (char_table, XVECTOR (range)->contents[0] + 128, value);
       else
        {
          int size = XVECTOR (range)->size;
@@ -1425,7 +1842,7 @@ See also the documentation of make-char.")
   c = XINT (ch);
   SPLIT_NON_ASCII_CHAR (c, charset, code1, code2);
   if (! CHARSET_DEFINED_P (charset))
-    error ("Invalid character: %d", c);
+    invalid_character (c);
 
   if (charset == CHARSET_ASCII)
     return (XCHAR_TABLE (char_table)->defalt = value);
@@ -1454,7 +1871,6 @@ See also the documentation of make-char.")
     XCHAR_TABLE (char_table)->contents[code1] = value;
   return value;
 }
-
 \f
 /* Map C_FUNCTION or FUNCTION over SUBTABLE, calling it for each
    character or group of characters that share a value.
@@ -1483,8 +1899,11 @@ map_char_table (c_function, function, subtable, arg, depth, indices)
          else
            call2 (function, make_number (i), elt);
        }
+#if 0 /* If the char table has entries for higher characters,
+        we should report them.  */
       if (NILP (current_buffer->enable_multibyte_characters))
        return;
+#endif
       to = CHAR_TABLE_ORDINARY_SLOTS;
     }
   else
@@ -1597,9 +2016,9 @@ Only the last argument is not altered, and need not be a list.")
 }
 \f
 /* This is the guts of all mapping functions.
- Apply fn to each element of seq, one by one,
- storing the results into elements of vals, a C vector of Lisp_Objects.
leni is the length of vals, which should also be the length of seq. */
+ Apply FN to each element of SEQ, one by one,
+ storing the results into elements of VALS, a C vector of Lisp_Objects.
LENI is the length of VALS, which should also be the length of SEQ.  */
 
 static void
 mapcar1 (leni, vals, fn, seq)
@@ -1630,14 +2049,45 @@ mapcar1 (leni, vals, fn, seq)
          vals[i] = call1 (fn, dummy);
        }
     }
-  else if (STRINGP (seq))
+  else if (BOOL_VECTOR_P (seq))
     {
+      for (i = 0; i < leni; i++)
+       {
+         int byte;
+         byte = XBOOL_VECTOR (seq)->data[i / BITS_PER_CHAR];
+         if (byte & (1 << (i % BITS_PER_CHAR)))
+           dummy = Qt;
+         else
+           dummy = Qnil;
+
+         vals[i] = call1 (fn, dummy);
+       }
+    }
+  else if (STRINGP (seq) && ! STRING_MULTIBYTE (seq))
+    {
+      /* Single-byte string.  */
       for (i = 0; i < leni; i++)
        {
          XSETFASTINT (dummy, XSTRING (seq)->data[i]);
          vals[i] = call1 (fn, dummy);
        }
     }
+  else if (STRINGP (seq))
+    {
+      /* Multi-byte string.  */
+      int len_byte = XSTRING (seq)->size_byte;
+      int i_byte;
+
+      for (i = 0, i_byte = 0; i < leni;)
+       {
+         int c;
+         int i_before = i;
+
+         FETCH_STRING_CHAR_ADVANCE (c, seq, i, i_byte);
+         XSETFASTINT (dummy, c);
+         vals[i_before] = call1 (fn, dummy);
+       }
+    }
   else   /* Must be a list, since Flength did not get an error */
     {
       tail = seq;
@@ -1654,7 +2104,8 @@ mapcar1 (leni, vals, fn, seq)
 DEFUN ("mapconcat", Fmapconcat, Smapconcat, 3, 3, 0,
   "Apply FUNCTION to each element of SEQUENCE, and concat the results as strings.\n\
 In between each pair of results, stick in SEPARATOR.  Thus, \" \" as\n\
-SEPARATOR results in spaces between the values returned by FUNCTION.")
+SEPARATOR results in spaces between the values returned by FUNCTION.\n\
+SEQUENCE may be a list, a vector, a bool-vector, or a string.")
   (function, sequence, separator)
      Lisp_Object function, sequence, separator;
 {
@@ -1688,7 +2139,7 @@ SEPARATOR results in spaces between the values returned by FUNCTION.")
 DEFUN ("mapcar", Fmapcar, Smapcar, 2, 2, 0,
   "Apply FUNCTION to each element of SEQUENCE, and make a list of the results.\n\
 The result is a list just as long as SEQUENCE.\n\
-SEQUENCE may be a list, a vector or a string.")
+SEQUENCE may be a list, a vector, a bool-vector, or a string.")
   (function, sequence)
      Lisp_Object function, sequence;
 {
@@ -1752,7 +2203,7 @@ Also accepts Space to mean yes, or Delete to mean no.")
 #endif /* HAVE_MENUS */
       cursor_in_echo_area = 1;
       choose_minibuf_frame ();
-      message_nolog ("%s(y or n) ", XSTRING (xprompt)->data);
+      message_with_string ("%s(y or n) ", xprompt, 0);
 
       if (minibuffer_auto_raise)
        {
@@ -1815,8 +2266,8 @@ Also accepts Space to mean yes, or Delete to mean no.")
   if (! noninteractive)
     {
       cursor_in_echo_area = -1;
-      message_nolog ("%s(y or n) %c",
-                    XSTRING (xprompt)->data, answer ? 'y' : 'n');
+      message_with_string (answer ? "%s(y or n) y" : "%s(y or n) n",
+                          xprompt, 0);
     }
 
   unbind_to (count, Qnil);
@@ -2089,6 +2540,9 @@ syms_of_fns ()
   Qwidget_type = intern ("widget-type");
   staticpro (&Qwidget_type);
 
+  staticpro (&string_char_byte_cache_string);
+  string_char_byte_cache_string = Qnil;
+
   Fset (Qyes_or_no_p_history, Qnil);
 
   DEFVAR_LISP ("features", &Vfeatures,
@@ -2106,12 +2560,17 @@ invoked by mouse clicks and mouse menu items.");
   defsubr (&Srandom);
   defsubr (&Slength);
   defsubr (&Ssafe_length);
+  defsubr (&Sstring_bytes);
   defsubr (&Sstring_equal);
   defsubr (&Sstring_lessp);
   defsubr (&Sappend);
   defsubr (&Sconcat);
   defsubr (&Svconcat);
   defsubr (&Scopy_sequence);
+  defsubr (&Sstring_make_multibyte);
+  defsubr (&Sstring_make_unibyte);
+  defsubr (&Sstring_as_multibyte);
+  defsubr (&Sstring_as_unibyte);
   defsubr (&Scopy_alist);
   defsubr (&Ssubstring);
   defsubr (&Snthcdr);