(adjust_frame_glyphs): Remove reference to
[bpt/emacs.git] / src / editfns.c
index dd5eb21..a12954a 100644 (file)
@@ -1,5 +1,5 @@
 /* Lisp functions pertaining to editing.
-   Copyright (C) 1985,86,87,89,93,94,95,96,97 Free Software Foundation, Inc.
+   Copyright (C) 1985,86,87,89,93,94,95,96,97,98 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -29,6 +29,14 @@ Boston, MA 02111-1307, USA.  */
 #include <pwd.h>
 #endif
 
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
 #include "lisp.h"
 #include "intervals.h"
 #include "buffer.h"
@@ -122,7 +130,7 @@ init_editfns ()
 }
 \f
 DEFUN ("char-to-string", Fchar_to_string, Schar_to_string, 1, 1, 0,
-  "Convert arg CHAR to a string containing multi-byte form of that character.")
+  "Convert arg CHAR to a string containing that character.")
   (character)
      Lisp_Object character;
 {
@@ -132,7 +140,7 @@ DEFUN ("char-to-string", Fchar_to_string, Schar_to_string, 1, 1, 0,
   CHECK_NUMBER (character, 0);
 
   len = CHAR_STRING (XFASTINT (character), workbuf, str);
-  return make_string (str, len);
+  return make_string_from_bytes (str, 1, len);
 }
 
 DEFUN ("string-to-char", Fstring_to_char, Sstring_to_char, 1, 1, 0,
@@ -146,53 +154,16 @@ A multibyte character is handled correctly.")
   CHECK_STRING (string, 0);
   p = XSTRING (string);
   if (p->size)
-    XSETFASTINT (val, STRING_CHAR (p->data, p->size));
-  else
-    XSETFASTINT (val, 0);
-  return val;
-}
-
-DEFUN ("sref", Fsref, Ssref, 2, 2, 0,
-  "Return the character in STRING at INDEX.  INDEX starts at 0.\n\
-A multibyte character is handled correctly.\n\
-INDEX not pointing at character boundary is an error.")
-  (str, idx)
-     Lisp_Object str, idx;
-{
-  register int idxval, len, i;
-  register unsigned char *p, *q;
-  register Lisp_Object val;
-
-  CHECK_STRING (str, 0);
-  CHECK_NUMBER (idx, 1);
-  idxval = XINT (idx);
-  if (idxval < 0 || idxval >= (len = XVECTOR (str)->size))
-    args_out_of_range (str, idx);
-
-  p = XSTRING (str)->data + idxval;
-  if (!NILP (current_buffer->enable_multibyte_characters)
-      && !CHAR_HEAD_P (*p)
-      && idxval > 0)
     {
-      /* We must check if P points to a tailing byte of a multibyte
-         form.  If so, we signal error.  */
-      i = idxval - 1;
-      q = p - 1;
-      while (i > 0 && *q >= 0xA0) i--, q--;
-
-      if (*q == LEADING_CODE_COMPOSITION)
-       i = multibyte_form_length (XSTRING (str)->data + i, len - i);
+      if (STRING_MULTIBYTE (string))
+       XSETFASTINT (val, STRING_CHAR (p->data, STRING_BYTES (p)));
       else
-       i = BYTES_BY_CHAR_HEAD (*q);
-      if (q + i > p)
-       error ("Not character boundary");
+       XSETFASTINT (val, p->data[0]);
     }
-
-  len = XSTRING (str)->size - idxval;
-  XSETFASTINT (val, STRING_CHAR (p, len));
+  else
+    XSETFASTINT (val, 0);
   return val;
 }
-
 \f
 static Lisp_Object
 buildmark (charpos, bytepos)
@@ -245,7 +216,8 @@ except in the case that `enable-multibyte-characters' is nil.")
   int pos;
   unsigned char *p;
 
-  if (MARKERP (position))
+  if (MARKERP (position)
+      && current_buffer == XMARKER (position)->buffer)
     {
       pos = marker_position (position);
       if (pos < BEGV)
@@ -422,7 +394,12 @@ DEFUN ("save-excursion", Fsave_excursion, Ssave_excursion, 0, UNEVALLED, 0,
 Executes BODY just like `progn'.\n\
 The values of point, mark and the current buffer are restored\n\
 even in case of abnormal exit (throw or error).\n\
-The state of activation of the mark is also restored.")
+The state of activation of the mark is also restored.\n\
+\n\
+This construct does not save `deactivate-mark', and therefore\n\
+functions that change the buffer will still cause deactivation\n\
+of the mark at the end of the command.  To prevent that, bind\n\
+`deactivate-mark' with `let'.")
   (args)
      Lisp_Object args;
 {
@@ -444,7 +421,7 @@ Executes BODY just like `progn'.")
   register Lisp_Object val;
   int count = specpdl_ptr - specpdl;
 
-  record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
+  record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
 
   val = Fprogn (args);
   return unbind_to (count, val);
@@ -497,6 +474,50 @@ is in effect, in which case it is less.")
   return buildmark (ZV, ZV_BYTE);
 }
 
+DEFUN ("gap-position", Fgap_position, Sgap_position, 0, 0, 0,
+  "Return the position of the gap, in the current buffer.\n\
+See also `gap-size'.")
+  ()
+{
+  Lisp_Object temp;
+  XSETFASTINT (temp, GPT);
+  return temp;
+}
+
+DEFUN ("gap-size", Fgap_size, Sgap_size, 0, 0, 0,
+  "Return the size of the current buffer's gap.\n\
+See also `gap-position'.")
+  ()
+{
+  Lisp_Object temp;
+  XSETFASTINT (temp, GAP_SIZE);
+  return temp;
+}
+
+DEFUN ("position-bytes", Fposition_bytes, Sposition_bytes, 1, 1, 0,
+  "Return the byte position for character position POSITION.\n\
+If POSITION is out of range, the value is nil.")
+  (position)
+     Lisp_Object position;
+{
+  CHECK_NUMBER_COERCE_MARKER (position, 1);
+  if (XINT (position) < BEG || XINT (position) > Z)
+    return Qnil;
+  return make_number (CHAR_TO_BYTE (XINT (position)));
+}
+
+DEFUN ("byte-to-position", Fbyte_to_position, Sbyte_to_position, 1, 1, 0,
+  "Return the character position for byte position BYTEPOS.\n\
+If BYTEPOS is out of range, the value is nil.")
+  (bytepos)
+     Lisp_Object bytepos;
+{
+  CHECK_NUMBER (bytepos, 1);
+  if (XINT (bytepos) < BEG_BYTE || XINT (bytepos) > Z_BYTE)
+    return Qnil;
+  return make_number (BYTE_TO_CHAR (XINT (bytepos)));
+}
+\f
 DEFUN ("following-char", Ffollowing_char, Sfollowing_char, 0, 0, 0,
   "Return the character following point, as a number.\n\
 At the end of the buffer or accessible region, return 0.\n\
@@ -577,10 +598,7 @@ DEFUN ("eolp", Feolp, Seolp, 0, 0, 0,
 DEFUN ("char-after", Fchar_after, Schar_after, 0, 1, 0,
   "Return character in current buffer at position POS.\n\
 POS is an integer or a buffer pointer.\n\
-If POS is out of range, the value is nil.\n\
-If `enable-multibyte-characters' is nil or POS is not at character boundary,\n\
- multi-byte form is ignored, and only one byte at POS\n\
- is returned as a character.")
+If POS is out of range, the value is nil.")
   (pos)
      Lisp_Object pos;
 {
@@ -588,30 +606,33 @@ If `enable-multibyte-characters' is nil or POS is not at character boundary,\n\
   register Lisp_Object val;
 
   if (NILP (pos))
-    return make_number (FETCH_CHAR (PT_BYTE));
+    {
+      pos_byte = PT_BYTE;
+      XSETFASTINT (pos, PT);
+    }
 
   if (MARKERP (pos))
-    pos_byte = marker_byte_position (pos);
+    {
+      pos_byte = marker_byte_position (pos);
+      if (pos_byte < BEGV_BYTE || pos_byte >= ZV_BYTE)
+       return Qnil;
+    }
   else
     {
       CHECK_NUMBER_COERCE_MARKER (pos, 0);
+      if (XINT (pos) < BEGV || XINT (pos) >= ZV)
+       return Qnil;
       
       pos_byte = CHAR_TO_BYTE (XINT (pos));
     }
 
-  if (n < BEGV_BYTE || n >= ZV_BYTE)
-    return Qnil;
-
   return make_number (FETCH_CHAR (pos_byte));
 }
 
 DEFUN ("char-before", Fchar_before, Schar_before, 0, 1, 0,
   "Return character in current buffer preceding position POS.\n\
 POS is an integer or a buffer pointer.\n\
-If POS is out of range, the value is nil.\n\
-If `enable-multibyte-characters' is nil or POS is not at character boundary,\n\
-multi-byte form is ignored, and only one byte preceding POS\n\
-is returned as a character.")
+If POS is out of range, the value is nil.")
   (pos)
      Lisp_Object pos;
 {
@@ -619,19 +640,28 @@ is returned as a character.")
   register int pos_byte;
 
   if (NILP (pos))
-    pos_byte = PT_BYTE;
-  else if (MARKERP (pos))
-    pos_byte = marker_byte_position (pos);
+    {
+      pos_byte = PT_BYTE;
+      XSETFASTINT (pos, PT);
+    }
+
+  if (MARKERP (pos))
+    {
+      pos_byte = marker_byte_position (pos);
+
+      if (pos_byte <= BEGV_BYTE || pos_byte > ZV_BYTE)
+       return Qnil;
+    }
   else
     {
       CHECK_NUMBER_COERCE_MARKER (pos, 0);
 
+      if (XINT (pos) <= BEGV || XINT (pos) > ZV)
+       return Qnil;
+
       pos_byte = CHAR_TO_BYTE (XINT (pos));
     }
 
-  if (pos_byte <= BEGV_BYTE || pos_byte > ZV_BYTE)
-    return Qnil;
-
   if (!NILP (current_buffer->enable_multibyte_characters))
     {
       DEC_POS (pos_byte);
@@ -702,10 +732,13 @@ DEFUN ("user-real-uid", Fuser_real_uid, Suser_real_uid, 0, 0, 0,
 
 DEFUN ("user-full-name", Fuser_full_name, Suser_full_name, 0, 1, 0,
   "Return the full name of the user logged in, as a string.\n\
+If the full name corresponding to Emacs's userid is not known,\n\
+return \"unknown\".\n\
+\n\
 If optional argument UID is an integer, return the full name of the user\n\
-with that uid, or \"unknown\" if there is no such user.\n\
+with that uid, or nil if there is no such user.\n\
 If UID is a string, return the full name of the user with that login\n\
-name, or \"unknown\" if no such user could be found.")
+name, or nil if there is no such user.")
   (uid)
      Lisp_Object uid;
 {
@@ -822,6 +855,58 @@ lisp_time_argument (specified_time, result)
     }
 }
 
+/* Write information into buffer S of size MAXSIZE, according to the
+   FORMAT of length FORMAT_LEN, using time information taken from *TP.
+   Return the number of bytes written, not including the terminating
+   '\0'.  If S is NULL, nothing will be written anywhere; so to
+   determine how many bytes would be written, use NULL for S and
+   ((size_t) -1) for MAXSIZE.
+
+   This function behaves like emacs_strftime, except it allows null
+   bytes in FORMAT.  */
+static size_t
+emacs_memftime (s, maxsize, format, format_len, tp)
+      char *s;
+      size_t maxsize;
+      const char *format;
+      size_t format_len;
+      const struct tm *tp;
+{
+  size_t total = 0;
+
+  /* Loop through all the null-terminated strings in the format
+     argument.  Normally there's just one null-terminated string, but
+     there can be arbitrarily many, concatenated together, if the
+     format contains '\0' bytes.  emacs_strftime stops at the first
+     '\0' byte so we must invoke it separately for each such string.  */
+  for (;;)
+    {
+      size_t len;
+      size_t result;
+
+      if (s)
+       s[0] = '\1';
+
+      result = emacs_strftime (s, maxsize, format, tp);
+
+      if (s)
+       {
+         if (result == 0 && s[0] != '\0')
+           return 0;
+         s += result + 1;
+       }
+
+      maxsize -= result + 1;
+      total += result;
+      len = strlen (format);
+      if (len == format_len)
+       return total;
+      total++;
+      format += len + 1;
+      format_len -= len + 1;
+    }
+}
+
 /*
 DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
   "Use FORMAT-STRING to format the time TIME, or now if omitted.\n\
@@ -880,6 +965,7 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
 {
   time_t value;
   int size;
+  struct tm *tm;
 
   CHECK_STRING (format_string, 1);
 
@@ -887,7 +973,11 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
     error ("Invalid time specification");
 
   /* This is probably enough.  */
-  size = XSTRING (format_string)->size * 6 + 50;
+  size = STRING_BYTES (XSTRING (format_string)) * 6 + 50;
+
+  tm = NILP (universal) ? localtime (&value) : gmtime (&value);
+  if (! tm)
+    error ("Specified time is not representable");
 
   while (1)
     {
@@ -895,16 +985,17 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
       int result;
 
       buf[0] = '\1';
-      result = emacs_strftime (buf, size, XSTRING (format_string)->data,
-                              (NILP (universal) ? localtime (&value)
-                               : gmtime (&value)));
+      result = emacs_memftime (buf, size, XSTRING (format_string)->data,
+                              STRING_BYTES (XSTRING (format_string)),
+                              tm);
       if ((result > 0 && result < size) || (result == 0 && buf[0] == '\0'))
-       return build_string (buf);
+       return make_string (buf, result);
 
       /* If buffer was too small, make it bigger and try again.  */
-      result = emacs_strftime (NULL, 0x7fffffff, XSTRING (format_string)->data,
-                              (NILP (universal) ? localtime (&value)
-                               : gmtime (&value)));
+      result = emacs_memftime (NULL, (size_t) -1,
+                              XSTRING (format_string)->data,
+                              STRING_BYTES (XSTRING (format_string)),
+                              tm);
       size = result + 1;
     }
 }
@@ -934,6 +1025,8 @@ ZONE is an integer indicating the number of seconds east of Greenwich.\n\
     error ("Invalid time specification");
 
   decoded_time = localtime (&time_spec);
+  if (! decoded_time)
+    error ("Specified time is not representable");
   XSETFASTINT (list_args[0], decoded_time->tm_sec);
   XSETFASTINT (list_args[1], decoded_time->tm_min);
   XSETFASTINT (list_args[2], decoded_time->tm_hour);
@@ -1118,18 +1211,15 @@ the data it can't find.")
 {
   time_t value;
   struct tm *t;
+  struct tm gmt;
 
   if (lisp_time_argument (specified_time, &value)
-      && (t = gmtime (&value)) != 0)
+      && (t = gmtime (&value)) != 0
+      && (gmt = *t, t = localtime (&value)) != 0)
     {
-      struct tm gmt;
-      int offset;
-      char *s, buf[6];
-
-      gmt = *t;                /* Make a copy, in case localtime modifies *t.  */
-      t = localtime (&value);
-      offset = tm_diff (t, &gmt);
-      s = 0;
+      int offset = tm_diff (t, &gmt);
+      char *s = 0;
+      char buf[6];
 #ifdef HAVE_TM_ZONE
       if (t->tm_zone)
        s = (char *)t->tm_zone;
@@ -1288,7 +1378,7 @@ void
 general_insert_function (insert_func, insert_from_string_func,
                         inherit, nargs, args)
      void (*insert_func) P_ ((unsigned char *, int));
-     void (*insert_from_string_func) P_ ((Lisp_Object, int, int, int));
+     void (*insert_from_string_func) P_ ((Lisp_Object, int, int, int, int, int));
      int inherit, nargs;
      register Lisp_Object *args;
 {
@@ -1307,12 +1397,21 @@ general_insert_function (insert_func, insert_from_string_func,
          if (!NILP (current_buffer->enable_multibyte_characters))
            len = CHAR_STRING (XFASTINT (val), workbuf, str);
          else
-           workbuf[0] = XINT (val), str = workbuf, len = 1;
+           {
+             workbuf[0] = (SINGLE_BYTE_CHAR_P (XINT (val))
+                           ? XINT (val)
+                           : multibyte_char_to_unibyte (XINT (val), Qnil));
+             str = workbuf;
+             len = 1;
+           }
          (*insert_func) (str, len);
        }
       else if (STRINGP (val))
        {
-         (*insert_from_string_func) (val, 0, XSTRING (val)->size, inherit);
+         (*insert_from_string_func) (val, 0, 0,
+                                     XSTRING (val)->size,
+                                     STRING_BYTES (XSTRING (val)),
+                                     inherit);
        }
       else
        {
@@ -1337,9 +1436,14 @@ insert1 (arg)
 
 DEFUN ("insert", Finsert, Sinsert, 0, MANY, 0,
   "Insert the arguments, either strings or characters, at point.\n\
-Point and before-insertion-markers move forward so that it ends up\n\
+Point and before-insertion markers move forward to end up\n\
  after the inserted text.\n\
-Any other markers at the point of insertion remain before the text.")
+Any other markers at the point of insertion remain before the text.\n\
+\n\
+If the current buffer is multibyte, unibyte strings are converted\n\
+to multibyte for insertion (see `unibyte-char-to-multibyte').\n\
+If the current buffer is unibyte, multibyte strings are converted\n\
+to unibyte for insertion.")
   (nargs, args)
      int nargs;
      register Lisp_Object *args;
@@ -1351,9 +1455,14 @@ Any other markers at the point of insertion remain before the text.")
 DEFUN ("insert-and-inherit", Finsert_and_inherit, Sinsert_and_inherit,
    0, MANY, 0,
   "Insert the arguments at point, inheriting properties from adjoining text.\n\
-Point and before-insertion-markers move forward so that it ends up\n\
+Point and before-insertion markers move forward to end up\n\
  after the inserted text.\n\
-Any other markers at the point of insertion remain before the text.")
+Any other markers at the point of insertion remain before the text.\n\
+\n\
+If the current buffer is multibyte, unibyte strings are converted\n\
+to multibyte for insertion (see `unibyte-char-to-multibyte').\n\
+If the current buffer is unibyte, multibyte strings are converted\n\
+to unibyte for insertion.")
   (nargs, args)
      int nargs;
      register Lisp_Object *args;
@@ -1365,9 +1474,12 @@ Any other markers at the point of insertion remain before the text.")
 
 DEFUN ("insert-before-markers", Finsert_before_markers, Sinsert_before_markers, 0, MANY, 0,
   "Insert strings or characters at point, relocating markers after the text.\n\
-Point and before-insertion-markers move forward so that it ends up\n\
- after the inserted text.\n\
-Any other markers at the point of insertion also end up after the text.")
+Point and markers move forward to end up after the inserted text.\n\
+\n\
+If the current buffer is multibyte, unibyte strings are converted\n\
+to multibyte for insertion (see `unibyte-char-to-multibyte').\n\
+If the current buffer is unibyte, multibyte strings are converted\n\
+to unibyte for insertion.")
   (nargs, args)
      int nargs;
      register Lisp_Object *args;
@@ -1381,8 +1493,12 @@ Any other markers at the point of insertion also end up after the text.")
 DEFUN ("insert-before-markers-and-inherit", Finsert_and_inherit_before_markers,
   Sinsert_and_inherit_before_markers, 0, MANY, 0,
   "Insert text at point, relocating markers and inheriting properties.\n\
-Point moves forward so that it ends up after the inserted text.\n\
-Any other markers at the point of insertion also end up after the text.")
+Point and markers move forward to end up after the inserted text.\n\
+\n\
+If the current buffer is multibyte, unibyte strings are converted\n\
+to multibyte for insertion (see `unibyte-char-to-multibyte').\n\
+If the current buffer is unibyte, multibyte strings are converted\n\
+to unibyte for insertion.")
   (nargs, args)
      int nargs;
      register Lisp_Object *args;
@@ -1395,8 +1511,8 @@ Any other markers at the point of insertion also end up after the text.")
 \f
 DEFUN ("insert-char", Finsert_char, Sinsert_char, 2, 3, 0,
   "Insert COUNT (second arg) copies of CHARACTER (first arg).\n\
-Point and before-insertion-markers are affected as in the function `insert'.\n\
 Both arguments are required.\n\
+Point, and before-insertion markers, are relocated as in the function `insert'.\n\
 The optional third arg INHERIT, if non-nil, says to inherit text properties\n\
 from adjoining text, if those properties are sticky.")
   (character, count, inherit)
@@ -1462,14 +1578,48 @@ make_buffer_string (start, end, props)
      int start, end;
      int props;
 {
-  Lisp_Object result, tem, tem1;
   int start_byte = CHAR_TO_BYTE (start);
   int end_byte = CHAR_TO_BYTE (end);
 
+  return make_buffer_string_both (start, start_byte, end, end_byte, props);
+}
+
+/* Return a Lisp_String containing the text of the current buffer from
+   START / START_BYTE to END / END_BYTE.
+
+   If text properties are in use and the current buffer
+   has properties in the range specified, the resulting string will also
+   have them, if PROPS is nonzero.
+
+   We don't want to use plain old make_string here, because it calls
+   make_uninit_string, which can cause the buffer arena to be
+   compacted.  make_string has no way of knowing that the data has
+   been moved, and thus copies the wrong data into the string.  This
+   doesn't effect most of the other users of make_string, so it should
+   be left as is.  But we should use this function when conjuring
+   buffer substrings.  */
+
+Lisp_Object
+make_buffer_string_both (start, start_byte, end, end_byte, props)
+     int start, start_byte, end, end_byte;
+     int props;
+{
+  Lisp_Object result, tem, tem1;
+
+  if (INTEGERP (current_buffer->minibuffer_prompt_length))
+    {
+      int len = XFASTINT (current_buffer->minibuffer_prompt_length);
+      start = min (end, max (len, start));
+      start_byte = CHAR_TO_BYTE (start);
+    }
+
   if (start < GPT && GPT < end)
     move_gap (start);
 
-  result = make_uninit_string (end_byte - start_byte);
+  if (! NILP (current_buffer->enable_multibyte_characters))
+    result = make_uninit_multibyte_string (end - start, end_byte - start_byte);
+  else
+    result = make_uninit_string (end - start);
   bcopy (BYTE_POS_ADDR (start_byte), XSTRING (result)->data,
         end_byte - start_byte);
 
@@ -1529,7 +1679,8 @@ update_buffer_properties (start, end)
 DEFUN ("buffer-substring", Fbuffer_substring, Sbuffer_substring, 2, 2, 0,
   "Return the contents of part of the current buffer as a string.\n\
 The two arguments START and END are character positions;\n\
-they can be in either order.")
+they can be in either order.\n\
+The string returned is multibyte if the buffer is multibyte.")
   (start, end)
      Lisp_Object start, end;
 {
@@ -1630,13 +1781,13 @@ determines whether case is significant or ignored.")
   (buffer1, start1, end1, buffer2, start2, end2)
      Lisp_Object buffer1, start1, end1, buffer2, start2, end2;
 {
-  register int begp1, endp1, begp2, endp2, temp, len1, len2, length, i;
+  register int begp1, endp1, begp2, endp2, temp;
   register struct buffer *bp1, *bp2;
   register Lisp_Object *trt
     = (!NILP (current_buffer->case_fold_search)
        ? XCHAR_TABLE (current_buffer->case_canon_table)->contents : 0);
   int chars = 0;
-  int beg1_byte, beg2_byte;
+  int i1, i2, i1_byte, i2_byte;
 
   /* Find the first buffer and its substring.  */
 
@@ -1714,26 +1865,42 @@ determines whether case is significant or ignored.")
         && endp2 <= BUF_ZV (bp2)))
     args_out_of_range (start2, end2);
 
-  beg1_byte = buf_charpos_to_bytepos (bp1, begp1);
-  beg2_byte = buf_charpos_to_bytepos (bp2, begp2);
-  len1 = buf_charpos_to_bytepos (bp1, endp1) - begp1;
-  len2 = buf_charpos_to_bytepos (bp2, endp2) - begp2;
-  length = len1;
-  if (len2 < length)
-    length = len2;
+  i1 = begp1;
+  i2 = begp2;
+  i1_byte = buf_charpos_to_bytepos (bp1, i1);
+  i2_byte = buf_charpos_to_bytepos (bp2, i2);
 
-  for (i = 0; i < length; i++)
+  while (i1 < endp1 && i2 < endp2)
     {
-      unsigned char *p1 = BUF_BYTE_ADDRESS (bp1, beg1_byte + i);
-      int c1 = *p1;
-      int c2 = *BUF_BYTE_ADDRESS (bp2, beg2_byte + i);
+      /* When we find a mismatch, we must compare the
+        characters, not just the bytes.  */
+      int c1, c2;
+
+      if (! NILP (bp1->enable_multibyte_characters))
+       {
+         c1 = BUF_FETCH_MULTIBYTE_CHAR (bp1, i1_byte);
+         BUF_INC_POS (bp1, i1_byte);
+         i1++;
+       }
+      else
+       {
+         c1 = BUF_FETCH_BYTE (bp1, i1);
+         c1 = unibyte_char_to_multibyte (c1);
+         i1++;
+       }
 
-      /* If a character begins here,
-        count the previous character now.  */
-      if (i > 0
-         && (NILP (current_buffer->enable_multibyte_characters)
-             || CHAR_HEAD_P (*p1)))
-       chars++;
+      if (! NILP (bp2->enable_multibyte_characters))
+       {
+         c2 = BUF_FETCH_MULTIBYTE_CHAR (bp2, i2_byte);
+         BUF_INC_POS (bp2, i2_byte);
+         i2++;
+       }
+      else
+       {
+         c2 = BUF_FETCH_BYTE (bp2, i2);
+         c2 = unibyte_char_to_multibyte (c2);
+         i2++;
+       }
 
       if (trt)
        {
@@ -1744,13 +1911,15 @@ determines whether case is significant or ignored.")
        return make_number (- 1 - chars);
       if (c1 > c2)
        return make_number (chars + 1);
+
+      chars++;
     }
 
   /* The strings match as far as they go.
      If one is shorter, that one is less.  */
-  if (length < len1)
+  if (chars < endp1 - begp1)
     return make_number (chars + 1);
-  else if (length < len2)
+  else if (chars < endp2 - begp2)
     return make_number (- chars - 1);
 
   /* Same length too => they are equal.  */
@@ -1780,10 +1949,15 @@ Both characters must have the same length of multi-byte form.")
   (start, end, fromchar, tochar, noundo)
      Lisp_Object start, end, fromchar, tochar, noundo;
 {
-  register int pos, stop, i, len, end_byte;
+  register int pos, pos_byte, stop, i, len, end_byte;
   int changed = 0;
   unsigned char fromwork[4], *fromstr, towork[4], *tostr, *p;
   int count = specpdl_ptr - specpdl;
+#define COMBINING_NO    0
+#define COMBINING_BEFORE 1
+#define COMBINING_AFTER  2
+#define COMBINING_BOTH (COMBINING_BEFORE | COMBINING_AFTER)
+  int maybe_byte_combining = COMBINING_NO;
 
   validate_region (&start, &end);
   CHECK_NUMBER (fromchar, 2);
@@ -1794,6 +1968,17 @@ Both characters must have the same length of multi-byte form.")
       len = CHAR_STRING (XFASTINT (fromchar), fromwork, fromstr);
       if (CHAR_STRING (XFASTINT (tochar), towork, tostr) != len)
        error ("Characters in subst-char-in-region have different byte-lengths");
+      if (!ASCII_BYTE_P (*tostr))
+       {
+         /* If *TOSTR is in the range 0x80..0x9F and TOCHAR is not a
+            complete multibyte character, it may be combined with the
+            after bytes.  If it is in the range 0xA0..0xFF, it may be
+            combined with the before and after bytes.  */
+         if (!CHAR_HEAD_P (*tostr))
+           maybe_byte_combining = COMBINING_BOTH;
+         else if (BYTES_BY_CHAR_HEAD (*tostr) > len)
+           maybe_byte_combining = COMBINING_AFTER;
+       }
     }
   else
     {
@@ -1802,7 +1987,8 @@ Both characters must have the same length of multi-byte form.")
       towork[0] = XFASTINT (tochar), tostr = towork;
     }
 
-  pos = CHAR_TO_BYTE (XINT (start));
+  pos = XINT (start);
+  pos_byte = CHAR_TO_BYTE (pos);
   stop = CHAR_TO_BYTE (XINT (end));
   end_byte = stop;
 
@@ -1821,18 +2007,21 @@ Both characters must have the same length of multi-byte form.")
       current_buffer->filename = Qnil;
     }
 
-  if (pos < GPT_BYTE)
+  if (pos_byte < GPT_BYTE)
     stop = min (stop, GPT_BYTE);
-  p = BYTE_POS_ADDR (pos);
   while (1)
     {
-      if (pos >= stop)
+      int pos_byte_next = pos_byte;
+
+      if (pos_byte >= stop)
        {
-         if (pos >= end_byte) break;
+         if (pos_byte >= end_byte) break;
          stop = end_byte;
-         p = BYTE_POS_ADDR (pos);
        }
-      if (p[0] == fromstr[0]
+      p = BYTE_POS_ADDR (pos_byte);
+      INC_POS (pos_byte_next);
+      if (pos_byte_next - pos_byte == len
+         && p[0] == fromstr[0]
          && (len == 1
              || (p[1] == fromstr[1]
                  && (len == 2 || (p[2] == fromstr[2]
@@ -1853,18 +2042,58 @@ Both characters must have the same length of multi-byte form.")
              changed = 1;
            }
 
-         if (NILP (noundo))
-           record_change (pos, len);
-         for (i = 0; i < len; i++) *p++ = tostr[i];
-         pos += len;
+         /* Take care of the case where the new character
+            combines with neighboring bytes.  */ 
+         if (maybe_byte_combining
+             && (maybe_byte_combining == COMBINING_AFTER
+                 ? (pos_byte_next < Z_BYTE
+                    && ! CHAR_HEAD_P (FETCH_BYTE (pos_byte_next)))
+                 : ((pos_byte_next < Z_BYTE
+                     && ! CHAR_HEAD_P (FETCH_BYTE (pos_byte_next)))
+                    || (pos_byte > BEG_BYTE
+                        && ! ASCII_BYTE_P (FETCH_BYTE (pos_byte - 1))))))
+           {
+             Lisp_Object tem, string;
+
+             struct gcpro gcpro1;
+
+             tem = current_buffer->undo_list;
+             GCPRO1 (tem);
+
+             /* Make a multibyte string containing this single character.  */
+             string = make_multibyte_string (tostr, 1, len);
+             /* replace_range is less efficient, because it moves the gap,
+                but it handles combining correctly.  */
+             replace_range (pos, pos + 1, string,
+                            0, 0, 1);
+             pos_byte_next = CHAR_TO_BYTE (pos);
+             if (pos_byte_next > pos_byte)
+               /* Before combining happened.  We should not increment
+                  POS.  So, to cancel the later increment of POS,
+                  decrease it now.  */
+               pos--;
+             else
+               INC_POS (pos_byte_next);
+               
+             if (! NILP (noundo))
+               current_buffer->undo_list = tem;
+
+             UNGCPRO;
+           }
+         else
+           {
+             if (NILP (noundo))
+               record_change (pos, 1);
+             for (i = 0; i < len; i++) *p++ = tostr[i];
+           }
        }
-      else
-       pos++, p++;
+      pos_byte = pos_byte_next;
+      pos++;
     }
 
   if (changed)
     signal_after_change (XINT (start),
-                        stop - XINT (start), stop - XINT (start));
+                        XINT (end) - XINT (start), XINT (end) - XINT (start));
 
   unbind_to (count, Qnil);
   return Qnil;
@@ -1873,7 +2102,9 @@ Both characters must have the same length of multi-byte form.")
 DEFUN ("translate-region", Ftranslate_region, Stranslate_region, 3, 3, 0,
   "From START to END, translate characters according to TABLE.\n\
 TABLE is a string; the Nth character in it is the mapping\n\
-for the character with code N.  Returns the number of characters changed.")
+for the character with code N.\n\
+This function does not alter multibyte characters.\n\
+It returns the number of characters changed.")
   (start, end, table)
      Lisp_Object start;
      Lisp_Object end;
@@ -1884,38 +2115,69 @@ for the character with code N.  Returns the number of characters changed.")
   register int nc;             /* New character. */
   int cnt;                     /* Number of changes made. */
   int size;                    /* Size of translate table. */
-  int charpos;
+  int pos;
 
   validate_region (&start, &end);
   CHECK_STRING (table, 2);
 
-  size = XSTRING (table)->size;
+  size = STRING_BYTES (XSTRING (table));
   tt = XSTRING (table)->data;
 
   pos_byte = CHAR_TO_BYTE (XINT (start));
   stop = CHAR_TO_BYTE (XINT (end));
   modify_region (current_buffer, XINT (start), XINT (end));
-  charpos = XINT (start);
+  pos = XINT (start);
 
   cnt = 0;
-  for (; pos_byte < stop; ++pos_byte)
+  for (; pos_byte < stop; )
     {
       register unsigned char *p = BYTE_POS_ADDR (pos_byte);
-      register int oc = *p;            /* Old character. */
-      if (CHAR_HEAD_P (*p))
-       charpos++;
+      int len;
+      int oc;
+      int pos_byte_next;
 
-      if (oc < size)
+      oc = STRING_CHAR_AND_LENGTH (p, stop - pos_byte, len);
+      pos_byte_next = pos_byte + len;
+      if (oc < size && len == 1)
        {
          nc = tt[oc];
          if (nc != oc)
            {
-             record_change (charpos, 1);
-             *p = nc;
-             signal_after_change (charpos, 1, 1);
+             /* Take care of the case where the new character
+                combines with neighboring bytes.  */ 
+             if (!ASCII_BYTE_P (nc)
+                 && (CHAR_HEAD_P (nc)
+                     ? ! CHAR_HEAD_P (FETCH_BYTE (pos_byte + 1))
+                     : (pos_byte > BEG_BYTE
+                        && ! ASCII_BYTE_P (FETCH_BYTE (pos_byte - 1)))))
+               {
+                 Lisp_Object string;
+
+                 string = make_multibyte_string (tt + oc, 1, 1);
+                 /* This is less efficient, because it moves the gap,
+                    but it handles combining correctly.  */
+                 replace_range (pos, pos + 1, string,
+                                1, 0, 1);
+                 pos_byte_next = CHAR_TO_BYTE (pos);
+                 if (pos_byte_next > pos_byte)
+                   /* Before combining happened.  We should not
+                      increment POS.  So, to cancel the later
+                      increment of POS, we decrease it now.  */
+                   pos--;
+                 else
+                   INC_POS (pos_byte_next);
+               }
+             else
+               {
+                 record_change (pos, 1);
+                 *p = nc;
+                 signal_after_change (pos, 1, 1);
+               }
              ++cnt;
            }
        }
+      pos_byte = pos_byte_next;
+      pos++;
     }
 
   return make_number (cnt);
@@ -2054,6 +2316,7 @@ The value returned is the value of the last form in BODY.\n\
 \n\
 `save-restriction' can get confused if, within the BODY, you widen\n\
 and then make changes outside the area within the saved restrictions.\n\
+See Info node `(elisp)Narrowing' for details and an appropriate technique.\n\
 \n\
 Note: if you are using both `save-excursion' and `save-restriction',\n\
 use `save-excursion' outermost:\n\
@@ -2095,19 +2358,7 @@ minibuffer contents show.")
     {
       register Lisp_Object val;
       val = Fformat (nargs, args);
-      /* Copy the data so that it won't move when we GC.  */
-      if (! message_text)
-       {
-         message_text = (char *)xmalloc (80);
-         message_length = 80;
-       }
-      if (XSTRING (val)->size > message_length)
-       {
-         message_length = XSTRING (val)->size;
-         message_text = (char *)xrealloc (message_text, message_length);
-       }
-      bcopy (XSTRING (val)->data, message_text, XSTRING (val)->size);
-      message2 (message_text, XSTRING (val)->size);
+      message3 (val, STRING_BYTES (XSTRING (val)), STRING_MULTIBYTE (val));
       return val;
     }
 }
@@ -2151,13 +2402,14 @@ minibuffer contents show.")
          message_text = (char *)xmalloc (80);
          message_length = 80;
        }
-      if (XSTRING (val)->size > message_length)
+      if (STRING_BYTES (XSTRING (val)) > message_length)
        {
-         message_length = XSTRING (val)->size;
+         message_length = STRING_BYTES (XSTRING (val));
          message_text = (char *)xrealloc (message_text, message_length);
        }
-      bcopy (XSTRING (val)->data, message_text, XSTRING (val)->size);
-      message2 (message_text, XSTRING (val)->size);
+      bcopy (XSTRING (val)->data, message_text, STRING_BYTES (XSTRING (val)));
+      message2 (message_text, STRING_BYTES (XSTRING (val)),
+               STRING_MULTIBYTE (val));
       return val;
 #endif /* not HAVE_MENUS */
     }
@@ -2190,11 +2442,18 @@ DEFUN ("current-message", Fcurrent_message, Scurrent_message, 0, 0, 0,
   "Return the string currently displayed in the echo area, or nil if none.")
   ()
 {
-  return (echo_area_glyphs
-         ? make_string (echo_area_glyphs, echo_area_glyphs_length)
-         : Qnil);
+  return current_message ();
 }
 
+/* Number of bytes that STRING will occupy when put into the result.
+   MULTIBYTE is nonzero if the result should be multibyte.  */
+
+#define CONVERTED_BYTE_SIZE(MULTIBYTE, STRING)                         \
+  (((MULTIBYTE) && ! STRING_MULTIBYTE (STRING))                                \
+   ? count_size_as_multibyte (XSTRING (STRING)->data,                  \
+                             STRING_BYTES (XSTRING (STRING)))          \
+   : STRING_BYTES (XSTRING (STRING)))
+
 DEFUN ("format", Fformat, Sformat, 1, MANY, 0,
   "Format a string out of a control-string and arguments.\n\
 The first argument is a control string.\n\
@@ -2207,7 +2466,7 @@ It may contain %-sequences meaning to substitute the next argument.\n\
 %g means print a number in exponential notation\n\
   or decimal-point notation, whichever uses fewer characters.\n\
 %c means print a number as a single character.\n\
-%S means print any object as an s-expression (using prin1).\n\
+%S means print any object as an s-expression (using `prin1').\n\
   The argument used for %d, %o, %x, %e, %f, %g or %c must be a number.\n\
 Use %% to put a single % into the output.")
   (nargs, args)
@@ -2215,23 +2474,60 @@ Use %% to put a single % into the output.")
      register Lisp_Object *args;
 {
   register int n;              /* The number of the next arg to substitute */
-  register int total = 5;      /* An estimate of the final length */
-  char *buf;
+  register int total;          /* An estimate of the final length */
+  char *buf, *p;
   register unsigned char *format, *end;
-  int length;
+  int length, nchars;
+  /* Nonzero if the output should be a multibyte string,
+     which is true if any of the inputs is one.  */
+  int multibyte = 0;
+  /* When we make a multibyte string, we must pay attention to the
+     byte combining problem, i.e., a byte may be combined with a
+     multibyte charcter of the previous string.  This flag tells if we
+     must consider such a situation or not.  */
+  int maybe_combine_byte;
+  unsigned char *this_format;
+  int longest_format;
+  Lisp_Object val;
+  struct info
+  {
+    int start, end;
+  } *info = 0;
+
   extern char *index ();
+
   /* It should not be necessary to GCPRO ARGS, because
      the caller in the interpreter should take care of that.  */
 
+  /* Try to determine whether the result should be multibyte.
+     This is not always right; sometimes the result needs to be multibyte
+     because of an object that we will pass through prin1,
+     and in that case, we won't know it here.  */
+  for (n = 0; n < nargs; n++)
+    if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n]))
+      multibyte = 1;
+
   CHECK_STRING (args[0], 0);
+
+  /* If we start out planning a unibyte result,
+     and later find it has to be multibyte, we jump back to retry.  */
+ retry:
+
   format = XSTRING (args[0])->data;
-  end = format + XSTRING (args[0])->size;
+  end = format + STRING_BYTES (XSTRING (args[0]));
+  longest_format = 0;
+
+  /* Make room in result for all the non-%-codes in the control string.  */
+  total = 5 + CONVERTED_BYTE_SIZE (multibyte, args[0]);
+
+  /* Add to TOTAL enough space to hold the converted arguments.  */
 
   n = 0;
   while (format != end)
     if (*format++ == '%')
       {
-       int minlen;
+       int minlen, thissize = 0;
+       unsigned char *this_format_start = format - 1;
 
        /* Process a numeric arg and skip it.  */
        minlen = atoi (format);
@@ -2242,6 +2538,11 @@ Use %% to put a single % into the output.")
               || *format == '-' || *format == ' ' || *format == '.')
          format++;
 
+       if (format - this_format_start + 1 > longest_format)
+         longest_format = format - this_format_start + 1;
+
+       if (format == end)
+         error ("Format string ends in middle of format specifier");
        if (*format == '%')
          format++;
        else if (++n >= nargs)
@@ -2251,24 +2552,30 @@ Use %% to put a single % into the output.")
            /* For `S', prin1 the argument and then treat like a string.  */
            register Lisp_Object tem;
            tem = Fprin1_to_string (args[n], Qnil);
+           if (STRING_MULTIBYTE (tem) && ! multibyte)
+             {
+               multibyte = 1;
+               goto retry;
+             }
            args[n] = tem;
            goto string;
          }
        else if (SYMBOLP (args[n]))
          {
            XSETSTRING (args[n], XSYMBOL (args[n])->name);
+           if (STRING_MULTIBYTE (args[n]) && ! multibyte)
+             {
+               multibyte = 1;
+               goto retry;
+             }
            goto string;
          }
        else if (STRINGP (args[n]))
          {
          string:
            if (*format != 's' && *format != 'S')
-             error ("format specifier doesn't match argument type");
-           total += XSTRING (args[n])->size;
-           /* We have to put an arbitrary limit on minlen
-              since otherwise it could make alloca fail.  */
-           if (minlen < XSTRING (args[n])->size + 1000)
-             total += minlen;
+             error ("Format specifier doesn't match argument type");
+           thissize = CONVERTED_BYTE_SIZE (multibyte, args[n]);
          }
        /* Would get MPV otherwise, since Lisp_Int's `point' to low memory.  */
        else if (INTEGERP (args[n]) && *format != 's')
@@ -2280,23 +2587,32 @@ Use %% to put a single % into the output.")
               be a double.  */
            if (*format == 'e' || *format == 'f' || *format == 'g')
              args[n] = Ffloat (args[n]);
+           else
 #endif
-           total += 30;
-           /* We have to put an arbitrary limit on minlen
-              since otherwise it could make alloca fail.  */
-           if (minlen < 1000)
-             total += minlen;
+             if (*format != 'd' && *format != 'o' && *format != 'x'
+                 && *format != 'i' && *format != 'X' && *format != 'c')
+               error ("Invalid format operation %%%c", *format);
+
+           thissize = 30;      
+           if (*format == 'c'
+               && (! SINGLE_BYTE_CHAR_P (XINT (args[n]))
+                   || XINT (args[n]) == 0))
+             {
+               if (! multibyte)
+                 {
+                   multibyte = 1;
+                   goto retry;
+                 }
+               args[n] = Fchar_to_string (args[n]);
+               thissize = STRING_BYTES (XSTRING (args[n]));
+             }
          }
 #ifdef LISP_FLOAT_TYPE
        else if (FLOATP (args[n]) && *format != 's')
          {
            if (! (*format == 'e' || *format == 'f' || *format == 'g'))
              args[n] = Ftruncate (args[n], Qnil);
-           total += 30;
-           /* We have to put an arbitrary limit on minlen
-              since otherwise it could make alloca fail.  */
-           if (minlen < 1000)
-             total += minlen;
+           thissize = 200;
          }
 #endif
        else
@@ -2304,67 +2620,212 @@ Use %% to put a single % into the output.")
            /* Anything but a string, convert to a string using princ.  */
            register Lisp_Object tem;
            tem = Fprin1_to_string (args[n], Qt);
+           if (STRING_MULTIBYTE (tem) & ! multibyte)
+             {
+               multibyte = 1;
+               goto retry;
+             }
            args[n] = tem;
            goto string;
          }
+       
+       if (thissize < minlen)
+         thissize = minlen;
+
+       total += thissize + 4;
       }
 
-  {
-    register int nstrings = n + 1;
+  /* Now we can no longer jump to retry.
+     TOTAL and LONGEST_FORMAT are known for certain.  */
 
-    /* Allocate twice as many strings as we have %-escapes; floats occupy
-       two slots, and we're not sure how many of those we have.  */
-    register unsigned char **strings
-      = (unsigned char **) alloca (2 * nstrings * sizeof (unsigned char *));
-    int i;
+  this_format = (unsigned char *) alloca (longest_format + 1);
 
-    i = 0;
-    for (n = 0; n < nstrings; n++)
-      {
-       if (n >= nargs)
-         strings[i++] = (unsigned char *) "";
-       else if (INTEGERP (args[n]))
-         /* We checked above that the corresponding format effector
-            isn't %s, which would cause MPV.  */
-         strings[i++] = (unsigned char *) XINT (args[n]);
-#ifdef LISP_FLOAT_TYPE
-       else if (FLOATP (args[n]))
-         {
-           union { double d; char *half[2]; } u;
+  /* Allocate the space for the result.
+     Note that TOTAL is an overestimate.  */
+  if (total < 1000)
+    buf = (char *) alloca (total + 1);
+  else
+    buf = (char *) xmalloc (total + 1);
 
-           u.d = XFLOAT (args[n])->data;
-           strings[i++] = (unsigned char *) u.half[0];
-           strings[i++] = (unsigned char *) u.half[1];
-         }
-#endif
-       else if (i == 0)
-         /* The first string is treated differently
-            because it is the format string.  */
-         strings[i++] = XSTRING (args[n])->data;
-       else
-         strings[i++] = (unsigned char *) XSTRING (args[n]);
-      }
+  p = buf;
+  nchars = 0;
+  n = 0;
 
-    /* Make room in result for all the non-%-codes in the control string.  */
-    total += XSTRING (args[0])->size;
+  /* Scan the format and store result in BUF.  */
+  format = XSTRING (args[0])->data;
+  maybe_combine_byte = 0;
+  while (format != end)
+    {
+      if (*format == '%')
+       {
+         int minlen;
+         int negative = 0;
+         unsigned char *this_format_start = format;
 
-    /* Format it in bigger and bigger buf's until it all fits. */
-    while (1)
-      {
-       buf = (char *) alloca (total + 1);
-       buf[total - 1] = 0;
+         format++;
 
-       length = doprnt_lisp (buf, total + 1, strings[0],
-                             end, i-1, (char **) strings + 1);
-       if (buf[total - 1] == 0)
-         break;
+         /* Process a numeric arg and skip it.  */
+         minlen = atoi (format);
+         if (minlen < 0)
+           minlen = - minlen, negative = 1;
 
-       total *= 2;
-      }
-  }
+         while ((*format >= '0' && *format <= '9')
+                || *format == '-' || *format == ' ' || *format == '.')
+           format++;
 
-  /*   UNGCPRO;  */
-  return make_string (buf, length);
+         if (*format++ == '%')
+           {
+             *p++ = '%';
+             nchars++;
+             continue;
+           }
+
+         ++n;
+
+         if (STRINGP (args[n]))
+           {
+             int padding, nbytes;
+             int width = strwidth (XSTRING (args[n])->data,
+                                   STRING_BYTES (XSTRING (args[n])));
+             int start = nchars;
+
+             /* If spec requires it, pad on right with spaces.  */
+             padding = minlen - width;
+             if (! negative)
+               while (padding-- > 0)
+                 {
+                   *p++ = ' ';
+                   nchars++;
+                 }
+
+             if (p > buf
+                 && multibyte
+                 && !ASCII_BYTE_P (*((unsigned char *) p - 1))
+                 && STRING_MULTIBYTE (args[n])
+                 && !CHAR_HEAD_P (XSTRING (args[n])->data[0]))
+               maybe_combine_byte = 1;
+             nbytes = copy_text (XSTRING (args[n])->data, p,
+                                 STRING_BYTES (XSTRING (args[n])),
+                                 STRING_MULTIBYTE (args[n]), multibyte);
+             p += nbytes;
+             nchars += XSTRING (args[n])->size;
+
+             if (negative)
+               while (padding-- > 0)
+                 {
+                   *p++ = ' ';
+                   nchars++;
+                 }
+
+             /* If this argument has text properties, record where
+                in the result string it appears.  */
+             if (XSTRING (args[n])->intervals)
+               {
+                 if (!info)
+                   {
+                     int nbytes = nargs * sizeof *info;
+                     info = (struct info *) alloca (nbytes);
+                     bzero (info, nbytes);
+                   }
+                 
+                 info[n].start = start;
+                 info[n].end = nchars;
+               }
+           }
+         else if (INTEGERP (args[n]) || FLOATP (args[n]))
+           {
+             int this_nchars;
+
+             bcopy (this_format_start, this_format,
+                    format - this_format_start);
+             this_format[format - this_format_start] = 0;
+
+             if (INTEGERP (args[n]))
+               sprintf (p, this_format, XINT (args[n]));
+             else
+               sprintf (p, this_format, XFLOAT (args[n])->data);
+
+             if (p > buf
+                 && multibyte
+                 && !ASCII_BYTE_P (*((unsigned char *) p - 1))
+                 && !CHAR_HEAD_P (*((unsigned char *) p)))
+               maybe_combine_byte = 1;
+             this_nchars = strlen (p);
+             p += this_nchars;
+             nchars += this_nchars;
+           }
+       }
+      else if (STRING_MULTIBYTE (args[0]))
+       {
+         /* Copy a whole multibyte character.  */
+         if (p > buf
+             && multibyte
+             && !ASCII_BYTE_P (*((unsigned char *) p - 1))
+             && !CHAR_HEAD_P (*format))
+           maybe_combine_byte = 1;
+         *p++ = *format++;
+         while (! CHAR_HEAD_P (*format)) *p++ = *format++;
+         nchars++;
+       }
+      else if (multibyte)
+       {
+         /* Convert a single-byte character to multibyte.  */
+         int len = copy_text (format, p, 1, 0, 1);
+
+         p += len;
+         format++;
+         nchars++;
+       }
+      else
+       *p++ = *format++, nchars++;
+    }
+
+  if (maybe_combine_byte)
+    nchars = multibyte_chars_in_text (buf, p - buf);
+  val = make_specified_string (buf, nchars, p - buf, multibyte);
+
+  /* If we allocated BUF with malloc, free it too.  */
+  if (total >= 1000)
+    xfree (buf);
+
+  /* If the format string has text properties, or any of the string
+     arguments has text properties, set up text properties of the
+     result string.  */
+  
+  if (XSTRING (args[0])->intervals || info)
+    {
+      Lisp_Object len, new_len, props;
+      struct gcpro gcpro1;
+      
+      /* Add text properties from the format string.  */
+      len = make_number (XSTRING (args[0])->size);
+      props = text_property_list (args[0], make_number (0), len, Qnil);
+      GCPRO1 (props);
+      
+      if (CONSP (props))
+       {
+         new_len = make_number (XSTRING (val)->size);
+         extend_property_ranges (props, len, new_len);
+         add_text_properties_from_list (val, props, make_number (0));
+       }
+
+      /* Add text properties from arguments.  */
+      if (info)
+       for (n = 1; n < nargs; ++n)
+         if (info[n].end)
+           {
+             len = make_number (XSTRING (args[n])->size);
+             new_len = make_number (info[n].end - info[n].start);
+             props = text_property_list (args[n], make_number (0), len, Qnil);
+             extend_property_ranges (props, len, new_len);
+             add_text_properties_from_list (val, props,
+                                            make_number (info[n].start));
+           }
+
+      UNGCPRO;
+    }
+
+  return val;
 }
 
 /* VARARGS 1 */
@@ -2385,7 +2846,7 @@ format1 (string1)
   args[2] = arg2;
   args[3] = arg3;
   args[4] = arg4;
-  doprnt (buf, sizeof buf, string1, (char *)0, 5, args);
+  doprnt (buf, sizeof buf, string1, (char *)0, 5, (char **) args);
 #else
   doprnt (buf, sizeof buf, string1, (char *)0, 5, &string1 + 1);
 #endif
@@ -2399,14 +2860,21 @@ Case is ignored if `case-fold-search' is non-nil in the current buffer.")
   (c1, c2)
      register Lisp_Object c1, c2;
 {
+  int i1, i2;
   CHECK_NUMBER (c1, 0);
   CHECK_NUMBER (c2, 1);
 
-  if (XINT (c1) == XINT (c2)
-      && (NILP (current_buffer->case_fold_search)
-         || DOWNCASE (XFASTINT (c1)) == DOWNCASE (XFASTINT (c2))))
+  if (XINT (c1) == XINT (c2))
     return Qt;
-  return Qnil;
+  if (NILP (current_buffer->case_fold_search))
+    return Qnil;
+
+  /* Do these in separate statements,
+     then compare the variables.
+     because of the way DOWNCASE uses temp variables.  */
+  i1 = DOWNCASE (XFASTINT (c1));
+  i2 = DOWNCASE (XFASTINT (c2));
+  return (i1 == i2 ? Qt :  Qnil);
 }
 \f
 /* Transpose the markers in two regions of the current buffer, and
@@ -2478,8 +2946,7 @@ transpose_markers (start1, end1, start2, end2,
            mpos += diff_byte;
          else
            mpos -= amt2_byte;
-         if (mpos > GPT_BYTE) mpos += GAP_SIZE;
-         XMARKER (marker)->bufpos = mpos;
+         XMARKER (marker)->bytepos = mpos;
        }
       mpos = XMARKER (marker)->charpos;
       if (mpos >= start1 && mpos < end2)
@@ -2511,6 +2978,9 @@ Transposing beyond buffer boundaries is an error.")
   int start1_byte, start2_byte, len1_byte, len2_byte;
   int gap, len1, len_mid, len2;
   unsigned char *start1_addr, *start2_addr, *temp;
+  int combined_before_bytes_1, combined_after_bytes_1;
+  int combined_before_bytes_2, combined_after_bytes_2;
+  struct gcpro gcpro1, gcpro2;
 
 #ifdef USE_TEXT_PROPERTIES
   INTERVAL cur_intv, tmp_interval1, tmp_interval_mid, tmp_interval2;
@@ -2541,9 +3011,9 @@ Transposing beyond buffer boundaries is an error.")
   len2 = end2 - start2;
 
   if (start2 < end1)
-    error ("Transposed regions not properly ordered");
+    error ("Transposed regions overlap");
   else if (start1 == end1 || start2 == end2)
-    error ("Transposed region may not be of length 0");
+    error ("Transposed region has length 0");
 
   /* The possibilities are:
      1. Adjacent (contiguous) regions, or separate but equal regions
@@ -2581,6 +3051,61 @@ Transposing beyond buffer boundaries is an error.")
   start2_byte = CHAR_TO_BYTE (start2);
   len1_byte = CHAR_TO_BYTE (end1) - start1_byte;
   len2_byte = CHAR_TO_BYTE (end2) - start2_byte;
+
+  if (end1 == start2)
+    {
+      combined_before_bytes_2
+       = count_combining_before (BYTE_POS_ADDR (start2_byte),
+                                 len2_byte, start1, start1_byte);
+      combined_before_bytes_1
+       = count_combining_before (BYTE_POS_ADDR (start1_byte),
+                                 len1_byte, end2, start2_byte + len2_byte);
+      combined_after_bytes_1
+       = count_combining_after (BYTE_POS_ADDR (start1_byte),
+                                len1_byte, end2, start2_byte + len2_byte);
+      combined_after_bytes_2 = 0;
+    }
+  else
+    {
+      combined_before_bytes_2
+       = count_combining_before (BYTE_POS_ADDR (start2_byte),
+                                 len2_byte, start1, start1_byte);
+      combined_before_bytes_1
+       = count_combining_before (BYTE_POS_ADDR (start1_byte),
+                                 len1_byte, start2, start2_byte);
+      combined_after_bytes_2
+       = count_combining_after (BYTE_POS_ADDR (start2_byte),
+                                len2_byte, end1, start1_byte + len1_byte);
+      combined_after_bytes_1
+       = count_combining_after (BYTE_POS_ADDR (start1_byte),
+                                len1_byte, end2, start2_byte + len2_byte);
+    }
+
+  /* If any combining is going to happen, do this the stupid way,
+     because replace handles combining properly.  */
+  if (combined_before_bytes_1 || combined_before_bytes_2
+      || combined_after_bytes_1 || combined_after_bytes_2)
+    {
+      Lisp_Object text1, text2;
+
+      text1 = text2 = Qnil;
+      GCPRO2 (text1, text2);
+
+      text1 = make_buffer_string_both (start1, start1_byte,
+                                      end1, start1_byte + len1_byte, 1);
+      text2 = make_buffer_string_both (start2, start2_byte,
+                                      end2, start2_byte + len2_byte, 1);
+
+      transpose_markers (start1, end1, start2, end2,
+                        start1_byte, start1_byte + len1_byte,
+                        start2_byte, start2_byte + len2_byte);
+
+      replace_range (start2, end2, text1, 1, 0, 0);
+      replace_range (start1, end1, text2, 1, 0, 0);
+
+      UNGCPRO;
+      return Qnil;
+    }
       
   /* Hmmm... how about checking to see if the gap is large
      enough to use as the temporary storage?  That would avoid an
@@ -2614,8 +3139,8 @@ Transposing beyond buffer boundaries is an error.")
          /* Don't precompute these addresses.  We have to compute them
             at the last minute, because the relocating allocator might
             have moved the buffer around during the xmalloc.  */
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
 
           bcopy (start2_addr, temp, len2_byte);
           bcopy (start1_addr, start1_addr + len2_byte, len1_byte);
@@ -2630,8 +3155,8 @@ Transposing beyond buffer boundaries is an error.")
            temp = (unsigned char *) xmalloc (len1_byte);
          else
            temp = (unsigned char *) alloca (len1_byte);
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
           bcopy (start1_addr, temp, len1_byte);
           bcopy (start2_addr, start1_addr, len2_byte);
           bcopy (temp, start1_addr + len2_byte, len1_byte);
@@ -2670,8 +3195,8 @@ Transposing beyond buffer boundaries is an error.")
            temp = (unsigned char *) xmalloc (len1_byte);
          else
            temp = (unsigned char *) alloca (len1_byte);
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
           bcopy (start1_addr, temp, len1_byte);
           bcopy (start2_addr, start1_addr, len2_byte);
           bcopy (temp, start2_addr, len1_byte);
@@ -2703,8 +3228,8 @@ Transposing beyond buffer boundaries is an error.")
            temp = (unsigned char *) xmalloc (len2_byte);
          else
            temp = (unsigned char *) alloca (len2_byte);
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
           bcopy (start2_addr, temp, len2_byte);
           bcopy (start1_addr, start1_addr + len_mid + len2_byte, len1_byte);
           safe_bcopy (start1_addr + len1_byte, start1_addr + len2_byte, len_mid);
@@ -2739,8 +3264,8 @@ Transposing beyond buffer boundaries is an error.")
            temp = (unsigned char *) xmalloc (len1_byte);
          else
            temp = (unsigned char *) alloca (len1_byte);
-         start1_addr = BUF_CHAR_ADDRESS (current_buffer, start1_byte);
-         start2_addr = BUF_CHAR_ADDRESS (current_buffer, start2_byte);
+         start1_addr = BYTE_POS_ADDR (start1_byte);
+         start2_addr = BYTE_POS_ADDR (start2_byte);
           bcopy (start1_addr, temp, len1_byte);
           bcopy (start2_addr, start1_addr, len2_byte);
           bcopy (start1_addr + len1_byte, start1_addr + len2_byte, len_mid);
@@ -2824,7 +3349,6 @@ functions if all the text being accessed has this property.");
   defsubr (&Sgoto_char);
   defsubr (&Sstring_to_char);
   defsubr (&Schar_to_string);
-  defsubr (&Ssref);
   defsubr (&Sbuffer_substring);
   defsubr (&Sbuffer_substring_no_properties);
   defsubr (&Sbuffer_string);
@@ -2834,6 +3358,10 @@ functions if all the text being accessed has this property.");
   defsubr (&Spoint);
   defsubr (&Sregion_beginning);
   defsubr (&Sregion_end);
+
+  defsubr (&Sline_beginning_position);
+  defsubr (&Sline_end_position);
+
 /*  defsubr (&Smark); */
 /*  defsubr (&Sset_mark); */
   defsubr (&Ssave_excursion);
@@ -2844,9 +3372,10 @@ functions if all the text being accessed has this property.");
   defsubr (&Spoint_min);
   defsubr (&Spoint_min_marker);
   defsubr (&Spoint_max_marker);
-
-  defsubr (&Sline_beginning_position);
-  defsubr (&Sline_end_position);
+  defsubr (&Sgap_position);
+  defsubr (&Sgap_size);
+  defsubr (&Sposition_bytes);
+  defsubr (&Sbyte_to_position);
 
   defsubr (&Sbobp);
   defsubr (&Seobp);