]> code.delx.au - gnu-emacs/blobdiff - src/editfns.c
Merge from trunk.
[gnu-emacs] / src / editfns.c
index 0c34a95b9492ebb6ab4a4b5e140dd3de3ebd833b..e3a7d1f7fa1014bfbeb04c432ea05c5687e1fb38 100644 (file)
@@ -45,9 +45,11 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #endif
 
 #include <ctype.h>
+#include <float.h>
 #include <limits.h>
 #include <intprops.h>
 #include <strftime.h>
+#include <verify.h>
 
 #include "intervals.h"
 #include "buffer.h"
@@ -57,13 +59,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "window.h"
 #include "blockinput.h"
 
-#ifdef STDC_HEADERS
-#include <float.h>
-#define MAX_10_EXP     DBL_MAX_10_EXP
-#else
-#define MAX_10_EXP     310
-#endif
-
 #ifndef NULL
 #define NULL 0
 #endif
@@ -91,24 +86,10 @@ extern Lisp_Object w32_get_internal_run_time (void);
 
 static void time_overflow (void) NO_RETURN;
 static int tm_diff (struct tm *, struct tm *);
-static void find_field (Lisp_Object, Lisp_Object, Lisp_Object,
-                       EMACS_INT *, Lisp_Object, EMACS_INT *);
 static void update_buffer_properties (EMACS_INT, EMACS_INT);
-static Lisp_Object region_limit (int);
-static size_t emacs_nmemftime (char *, size_t, const char *,
-                              size_t, const struct tm *, int, int);
-static void general_insert_function (void (*) (const char *, EMACS_INT),
-                                    void (*) (Lisp_Object, EMACS_INT,
-                                              EMACS_INT, EMACS_INT,
-                                              EMACS_INT, int),
-                                    int, size_t, Lisp_Object *);
-static Lisp_Object subst_char_in_region_unwind (Lisp_Object);
-static Lisp_Object subst_char_in_region_unwind_1 (Lisp_Object);
-static void transpose_markers (EMACS_INT, EMACS_INT, EMACS_INT, EMACS_INT,
-                              EMACS_INT, EMACS_INT, EMACS_INT, EMACS_INT);
-
-Lisp_Object Qbuffer_access_fontify_functions;
-INFUN (Fuser_full_name, 1);
+
+static Lisp_Object Qbuffer_access_fontify_functions;
+static Lisp_Object Fuser_full_name (Lisp_Object);
 
 /* Symbol for the text property used to mark fields.  */
 
@@ -116,7 +97,7 @@ Lisp_Object Qfield;
 
 /* A special value for Qfield properties.  */
 
-Lisp_Object Qboundary;
+static Lisp_Object Qboundary;
 
 
 void
@@ -185,17 +166,18 @@ init_editfns (void)
 #endif
 }
 \f
-DEFUE ("char-to-string", Fchar_to_string, Schar_to_string, 1, 1, 0,
+DEFUN ("char-to-string", Fchar_to_string, Schar_to_string, 1, 1, 0,
        doc: /* Convert arg CHAR to a string containing that character.
 usage: (char-to-string CHAR)  */)
   (Lisp_Object character)
 {
-  int len;
+  int c, len;
   unsigned char str[MAX_MULTIBYTE_LENGTH];
 
   CHECK_CHARACTER (character);
+  c = XFASTINT (character);
 
-  len = CHAR_STRING (XFASTINT (character), str);
+  len = CHAR_STRING (c, str);
   return make_string_from_bytes ((char *) str, 1, len);
 }
 
@@ -239,7 +221,7 @@ buildmark (EMACS_INT charpos, EMACS_INT bytepos)
   return mark;
 }
 
-DEFUE ("point", Fpoint, Spoint, 0, 0, 0,
+DEFUN ("point", Fpoint, Spoint, 0, 0, 0,
        doc: /* Return value of point, as an integer.
 Beginning of buffer is position (point-min).  */)
   (void)
@@ -249,7 +231,7 @@ Beginning of buffer is position (point-min).  */)
   return temp;
 }
 
-DEFUE ("point-marker", Fpoint_marker, Spoint_marker, 0, 0, 0,
+DEFUN ("point-marker", Fpoint_marker, Spoint_marker, 0, 0, 0,
        doc: /* Return value of point, as a marker object.  */)
   (void)
 {
@@ -267,7 +249,7 @@ clip_to_bounds (EMACS_INT lower, EMACS_INT num, EMACS_INT upper)
     return num;
 }
 
-DEFUE ("goto-char", Fgoto_char, Sgoto_char, 1, 1, "NGoto char: ",
+DEFUN ("goto-char", Fgoto_char, Sgoto_char, 1, 1, "NGoto char: ",
        doc: /* Set point to POSITION, a number or marker.
 Beginning of buffer is position (point-min), end is (point-max).
 
@@ -349,13 +331,13 @@ If you set the marker not to point anywhere, the buffer will have no mark.  */)
    Return the number found, and store them in a vector in VEC
    of length LEN.  */
 
-static int
-overlays_around (EMACS_INT pos, Lisp_Object *vec, int len)
+static ptrdiff_t
+overlays_around (EMACS_INT pos, Lisp_Object *vec, ptrdiff_t len)
 {
   Lisp_Object overlay, start, end;
   struct Lisp_Overlay *tail;
   EMACS_INT startpos, endpos;
-  int idx = 0;
+  ptrdiff_t idx = 0;
 
   for (tail = current_buffer->overlays_before; tail; tail = tail->next)
     {
@@ -423,7 +405,7 @@ get_pos_property (Lisp_Object position, register Lisp_Object prop, Lisp_Object o
   else
     {
       EMACS_INT posn = XINT (position);
-      int noverlays;
+      ptrdiff_t noverlays;
       Lisp_Object *overlay_vec, tem;
       struct buffer *obuf = current_buffer;
 
@@ -663,7 +645,7 @@ is before LIMIT, then LIMIT will be returned instead.  */)
   return make_number (beg);
 }
 
-DEFUE ("field-end", Ffield_end, Sfield_end, 0, 3, 0,
+DEFUN ("field-end", Ffield_end, Sfield_end, 0, 3, 0,
        doc: /* Return the end of the field surrounding POS.
 A field is a region of text with the same `field' property.
 If POS is nil, the value of point is used for POS.
@@ -678,7 +660,7 @@ is after LIMIT, then LIMIT will be returned instead.  */)
   return make_number (end);
 }
 
-DEFUE ("constrain-to-field", Fconstrain_to_field, Sconstrain_to_field, 2, 5, 0,
+DEFUN ("constrain-to-field", Fconstrain_to_field, Sconstrain_to_field, 2, 5, 0,
        doc: /* Return the position closest to NEW-POS that is in the same field as OLD-POS.
 
 A field is a region of text with the same `field' property.
@@ -787,7 +769,7 @@ Field boundaries are not noticed if `inhibit-field-text-motion' is non-nil.  */)
 }
 
 \f
-DEFUE ("line-beginning-position",
+DEFUN ("line-beginning-position",
        Fline_beginning_position, Sline_beginning_position, 0, 1, 0,
        doc: /* Return the character position of the first character on the current line.
 With argument N not nil or 1, move forward N - 1 lines first.
@@ -829,7 +811,7 @@ This function does not move point.  */)
                              Qt, Qnil);
 }
 
-DEFUE ("line-end-position", Fline_end_position, Sline_end_position, 0, 1, 0,
+DEFUN ("line-end-position", Fline_end_position, Sline_end_position, 0, 1, 0,
        doc: /* Return the character position of the last character on the current line.
 With argument N not nil or 1, move forward N - 1 lines first.
 If scan reaches end of buffer, return that position.
@@ -1048,7 +1030,7 @@ is in effect, in which case it is less.  */)
   return temp;
 }
 
-DEFUE ("point-max-marker", Fpoint_max_marker, Spoint_max_marker, 0, 0, 0,
+DEFUN ("point-max-marker", Fpoint_max_marker, Spoint_max_marker, 0, 0, 0,
        doc: /* Return a marker to the maximum permissible value of point in this buffer.
 This is (1+ (buffer-size)), unless narrowing (a buffer restriction)
 is in effect, in which case it is less.  */)
@@ -1099,7 +1081,7 @@ If BYTEPOS is out of range, the value is nil.  */)
   return make_number (BYTE_TO_CHAR (XINT (bytepos)));
 }
 \f
-DEFUE ("following-char", Ffollowing_char, Sfollowing_char, 0, 0, 0,
+DEFUN ("following-char", Ffollowing_char, Sfollowing_char, 0, 0, 0,
        doc: /* Return the character following point, as a number.
 At the end of the buffer or accessible region, return 0.  */)
   (void)
@@ -1112,7 +1094,7 @@ At the end of the buffer or accessible region, return 0.  */)
   return temp;
 }
 
-DEFUE ("preceding-char", Fprevious_char, Sprevious_char, 0, 0, 0,
+DEFUN ("preceding-char", Fprevious_char, Sprevious_char, 0, 0, 0,
        doc: /* Return the character preceding point, as a number.
 At the beginning of the buffer or accessible region, return 0.  */)
   (void)
@@ -1131,7 +1113,7 @@ At the beginning of the buffer or accessible region, return 0.  */)
   return temp;
 }
 
-DEFUE ("bobp", Fbobp, Sbobp, 0, 0, 0,
+DEFUN ("bobp", Fbobp, Sbobp, 0, 0, 0,
        doc: /* Return t if point is at the beginning of the buffer.
 If the buffer is narrowed, this means the beginning of the narrowed part.  */)
   (void)
@@ -1141,7 +1123,7 @@ If the buffer is narrowed, this means the beginning of the narrowed part.  */)
   return Qnil;
 }
 
-DEFUE ("eobp", Feobp, Seobp, 0, 0, 0,
+DEFUN ("eobp", Feobp, Seobp, 0, 0, 0,
        doc: /* Return t if point is at the end of the buffer.
 If the buffer is narrowed, this means the end of the narrowed part.  */)
   (void)
@@ -1151,7 +1133,7 @@ If the buffer is narrowed, this means the end of the narrowed part.  */)
   return Qnil;
 }
 
-DEFUE ("bolp", Fbolp, Sbolp, 0, 0, 0,
+DEFUN ("bolp", Fbolp, Sbolp, 0, 0, 0,
        doc: /* Return t if point is at the beginning of a line.  */)
   (void)
 {
@@ -1160,7 +1142,7 @@ DEFUE ("bolp", Fbolp, Sbolp, 0, 0, 0,
   return Qnil;
 }
 
-DEFUE ("eolp", Feolp, Seolp, 0, 0, 0,
+DEFUN ("eolp", Feolp, Seolp, 0, 0, 0,
        doc: /* Return t if point is at the end of a line.
 `End of a line' includes point being at the end of the buffer.  */)
   (void)
@@ -1170,7 +1152,7 @@ DEFUE ("eolp", Feolp, Seolp, 0, 0, 0,
   return Qnil;
 }
 
-DEFUE ("char-after", Fchar_after, Schar_after, 0, 1, 0,
+DEFUN ("char-after", Fchar_after, Schar_after, 0, 1, 0,
        doc: /* Return character in current buffer at position POS.
 POS is an integer or a marker and defaults to point.
 If POS is out of range, the value is nil.  */)
@@ -1247,7 +1229,7 @@ If POS is out of range, the value is nil.  */)
    return val;
 }
 \f
-DEFUE ("user-login-name", Fuser_login_name, Suser_login_name, 0, 1, 0,
+DEFUN ("user-login-name", Fuser_login_name, Suser_login_name, 0, 1, 0,
        doc: /* Return the name under which the user logged in, as a string.
 This is based on the effective uid, not the real uid.
 Also, if the environment variables LOGNAME or USER are set,
@@ -1387,7 +1369,7 @@ name, or nil if there is no such user.  */)
   return full;
 }
 
-DEFUE ("system-name", Fsystem_name, Ssystem_name, 0, 0, 0,
+DEFUN ("system-name", Fsystem_name, Ssystem_name, 0, 0, 0,
        doc: /* Return the host name of the machine you are running on, as a string.  */)
   (void)
 {
@@ -1403,15 +1385,6 @@ get_system_name (void)
     return "";
 }
 
-const char *
-get_operating_system_release (void)
-{
-  if (STRINGP (Voperating_system_release))
-    return SSDATA (Voperating_system_release);
-  else
-    return "";
-}
-
 DEFUN ("emacs-pid", Femacs_pid, Semacs_pid, 0, 0, 0,
        doc: /* Return the process ID of Emacs, as an integer.  */)
   (void)
@@ -1462,7 +1435,7 @@ lo_time (time_t t)
   return t & ((1 << 16) - 1);
 }
 
-DEFUE ("current-time", Fcurrent_time, Scurrent_time, 0, 0, 0,
+DEFUN ("current-time", Fcurrent_time, Scurrent_time, 0, 0, 0,
        doc: /* Return the current time, as the number of seconds since 1970-01-01 00:00:00.
 The time is returned as a list of three integers.  The first has the
 most significant 16 bits of the seconds, while the second has the
@@ -1481,7 +1454,7 @@ resolution finer than a second.  */)
                make_number (EMACS_USECS (t)));
 }
 
-DEFUE ("get-internal-run-time", Fget_internal_run_time, Sget_internal_run_time,
+DEFUN ("get-internal-run-time", Fget_internal_run_time, Sget_internal_run_time,
        0, 0, 0,
        doc: /* Return the current run time used by Emacs.
 The time is returned as a list of three integers.  The first has the
@@ -1727,7 +1700,7 @@ For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z".  */)
   (Lisp_Object format_string, Lisp_Object timeval, Lisp_Object universal)
 {
   time_t value;
-  int size;
+  ptrdiff_t size;
   int usec;
   int ns;
   struct tm *tm;
@@ -1744,7 +1717,9 @@ For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z".  */)
                                                Vlocale_coding_system, 1);
 
   /* This is probably enough.  */
-  size = SBYTES (format_string) * 6 + 50;
+  size = SBYTES (format_string);
+  if (size <= (STRING_BYTES_BOUND - 50) / 6)
+    size = size * 6 + 50;
 
   BLOCK_INPUT;
   tm = ut ? gmtime (&value) : localtime (&value);
@@ -1757,7 +1732,7 @@ For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z".  */)
   while (1)
     {
       char *buf = (char *) alloca (size + 1);
-      int result;
+      size_t result;
 
       buf[0] = '\1';
       BLOCK_INPUT;
@@ -1776,6 +1751,8 @@ For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z".  */)
                                SBYTES (format_string),
                                tm, ut, ns);
       UNBLOCK_INPUT;
+      if (STRING_BYTES_BOUND <= result)
+       string_overflow ();
       size = result + 1;
     }
 }
@@ -1871,7 +1848,7 @@ Years before 1970 are not guaranteed to work.  On some systems,
 year values as low as 1901 do work.
 
 usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE)  */)
-  (size_t nargs, register Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   time_t value;
   struct tm tm;
@@ -2207,9 +2184,9 @@ general_insert_function (void (*insert_func)
                         void (*insert_from_string_func)
                              (Lisp_Object, EMACS_INT, EMACS_INT,
                               EMACS_INT, EMACS_INT, int),
-                        int inherit, size_t nargs, Lisp_Object *args)
+                        int inherit, ptrdiff_t nargs, Lisp_Object *args)
 {
-  register size_t argnum;
+  ptrdiff_t argnum;
   register Lisp_Object val;
 
   for (argnum = 0; argnum < nargs; argnum++)
@@ -2217,16 +2194,15 @@ general_insert_function (void (*insert_func)
       val = args[argnum];
       if (CHARACTERP (val))
        {
+         int c = XFASTINT (val);
          unsigned char str[MAX_MULTIBYTE_LENGTH];
          int len;
 
          if (!NILP (BVAR (current_buffer, enable_multibyte_characters)))
-           len = CHAR_STRING (XFASTINT (val), str);
+           len = CHAR_STRING (c, str);
          else
            {
-             str[0] = (ASCII_CHAR_P (XINT (val))
-                       ? XINT (val)
-                       : multibyte_char_to_unibyte (XINT (val)));
+             str[0] = ASCII_CHAR_P (c) ? c : multibyte_char_to_unibyte (c);
              len = 1;
            }
          (*insert_func) ((char *) str, len);
@@ -2255,7 +2231,7 @@ insert1 (Lisp_Object arg)
    not be used after calling insert or insert_from_string, so
    we don't care if it gets trashed.  */
 
-DEFUE ("insert", Finsert, Sinsert, 0, MANY, 0,
+DEFUN ("insert", Finsert, Sinsert, 0, MANY, 0,
        doc: /* Insert the arguments, either strings or characters, at point.
 Point and before-insertion markers move forward to end up
  after the inserted text.
@@ -2272,7 +2248,7 @@ buffer; to accomplish this, apply `string-as-multibyte' to the string
 and insert the result.
 
 usage: (insert &rest ARGS)  */)
-  (size_t nargs, register Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   general_insert_function (insert, insert_from_string, 0, nargs, args);
   return Qnil;
@@ -2291,7 +2267,7 @@ If the current buffer is unibyte, multibyte strings are converted
 to unibyte for insertion.
 
 usage: (insert-and-inherit &rest ARGS)  */)
-  (size_t nargs, register Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   general_insert_function (insert_and_inherit, insert_from_string, 1,
                           nargs, args);
@@ -2308,7 +2284,7 @@ If the current buffer is unibyte, multibyte strings are converted
 to unibyte for insertion.
 
 usage: (insert-before-markers &rest ARGS)  */)
-  (size_t nargs, register Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   general_insert_function (insert_before_markers,
                           insert_from_string_before_markers, 0,
@@ -2327,7 +2303,7 @@ If the current buffer is unibyte, multibyte strings are converted
 to unibyte for insertion.
 
 usage: (insert-before-markers-and-inherit &rest ARGS)  */)
-  (size_t nargs, register Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   general_insert_function (insert_before_markers_and_inherit,
                           insert_from_string_before_markers, 1,
@@ -2335,37 +2311,36 @@ usage: (insert-before-markers-and-inherit &rest ARGS)  */)
   return Qnil;
 }
 \f
-DEFUE ("insert-char", Finsert_char, Sinsert_char, 2, 3, 0,
+DEFUN ("insert-char", Finsert_char, Sinsert_char, 2, 3, 0,
        doc: /* Insert COUNT copies of CHARACTER.
 Point, and before-insertion markers, are relocated as in the function `insert'.
 The optional third arg INHERIT, if non-nil, says to inherit text properties
 from adjoining text, if those properties are sticky.  */)
   (Lisp_Object character, Lisp_Object count, Lisp_Object inherit)
 {
-  register char *string;
-  register EMACS_INT stringlen;
-  register int i;
+  int i, stringlen;
   register EMACS_INT n;
-  int len;
+  int c, len;
   unsigned char str[MAX_MULTIBYTE_LENGTH];
+  char string[4000];
 
-  CHECK_NUMBER (character);
+  CHECK_CHARACTER (character);
   CHECK_NUMBER (count);
+  c = XFASTINT (character);
 
   if (!NILP (BVAR (current_buffer, enable_multibyte_characters)))
-    len = CHAR_STRING (XFASTINT (character), str);
+    len = CHAR_STRING (c, str);
   else
-    str[0] = XFASTINT (character), len = 1;
-  if (MOST_POSITIVE_FIXNUM / len < XINT (count))
-    error ("Maximum buffer size would be exceeded");
-  n = XINT (count) * len;
-  if (n <= 0)
+    str[0] = c, len = 1;
+  if (XINT (count) <= 0)
     return Qnil;
-  stringlen = min (n, 256 * len);
-  string = (char *) alloca (stringlen);
+  if (BUF_BYTES_MAX / len < XINT (count))
+    buffer_overflow ();
+  n = XINT (count) * len;
+  stringlen = min (n, sizeof string - sizeof string % len);
   for (i = 0; i < stringlen; i++)
     string[i] = str[i % len];
-  while (n >= stringlen)
+  while (n > stringlen)
     {
       QUIT;
       if (!NILP (inherit))
@@ -2374,13 +2349,10 @@ from adjoining text, if those properties are sticky.  */)
        insert (string, stringlen);
       n -= stringlen;
     }
-  if (n > 0)
-    {
-      if (!NILP (inherit))
-       insert_and_inherit (string, n);
-      else
-       insert (string, n);
-    }
+  if (!NILP (inherit))
+    insert_and_inherit (string, n);
+  else
+    insert (string, n);
   return Qnil;
 }
 
@@ -2509,7 +2481,7 @@ update_buffer_properties (EMACS_INT start, EMACS_INT end)
     }
 }
 
-DEFUE ("buffer-substring", Fbuffer_substring, Sbuffer_substring, 2, 2, 0,
+DEFUN ("buffer-substring", Fbuffer_substring, Sbuffer_substring, 2, 2, 0,
        doc: /* Return the contents of part of the current buffer as a string.
 The two arguments START and END are character positions;
 they can be in either order.
@@ -2545,7 +2517,7 @@ they can be in either order.  */)
   return make_buffer_string (b, e, 0);
 }
 
-DEFUE ("buffer-string", Fbuffer_string, Sbuffer_string, 0, 0, 0,
+DEFUN ("buffer-string", Fbuffer_string, Sbuffer_string, 0, 0, 0,
        doc: /* Return the contents of the current buffer as a string.
 If narrowing is in effect, this function returns only the visible part
 of the buffer.  */)
@@ -2798,17 +2770,20 @@ Both characters must have the same length of multi-byte form.  */)
   int maybe_byte_combining = COMBINING_NO;
   EMACS_INT last_changed = 0;
   int multibyte_p = !NILP (BVAR (current_buffer, enable_multibyte_characters));
+  int fromc, toc;
 
  restart:
 
   validate_region (&start, &end);
-  CHECK_NUMBER (fromchar);
-  CHECK_NUMBER (tochar);
+  CHECK_CHARACTER (fromchar);
+  CHECK_CHARACTER (tochar);
+  fromc = XFASTINT (fromchar);
+  toc = XFASTINT (tochar);
 
   if (multibyte_p)
     {
-      len = CHAR_STRING (XFASTINT (fromchar), fromstr);
-      if (CHAR_STRING (XFASTINT (tochar), tostr) != len)
+      len = CHAR_STRING (fromc, fromstr);
+      if (CHAR_STRING (toc, tostr) != len)
        error ("Characters in `subst-char-in-region' have different byte-lengths");
       if (!ASCII_BYTE_P (*tostr))
        {
@@ -2825,8 +2800,8 @@ Both characters must have the same length of multi-byte form.  */)
   else
     {
       len = 1;
-      fromstr[0] = XFASTINT (fromchar);
-      tostr[0] = XFASTINT (tochar);
+      fromstr[0] = fromc;
+      tostr[0] = toc;
     }
 
   pos = XINT (start);
@@ -3098,14 +3073,11 @@ It returns the number of characters changed.  */)
            }
          else
            {
-             EMACS_INT c;
-
              nc = oc;
              val = CHAR_TABLE_REF (table, oc);
-             if (CHARACTERP (val)
-                 && (c = XINT (val), CHAR_VALID_P (c, 0)))
+             if (CHARACTERP (val))
                {
-                 nc = c;
+                 nc = XFASTINT (val);
                  str_len = CHAR_STRING (nc, buf);
                  str = buf;
                }
@@ -3183,7 +3155,7 @@ It returns the number of characters changed.  */)
   return make_number (cnt);
 }
 
-DEFUE ("delete-region", Fdelete_region, Sdelete_region, 2, 2, "r",
+DEFUN ("delete-region", Fdelete_region, Sdelete_region, 2, 2, "r",
        doc: /* Delete the text between point and mark.
 
 When called from a program, expects two arguments,
@@ -3206,7 +3178,7 @@ DEFUN ("delete-and-extract-region", Fdelete_and_extract_region,
   return del_range_1 (XINT (start), XINT (end), 1, 1);
 }
 \f
-DEFUE ("widen", Fwiden, Swiden, 0, 0, "",
+DEFUN ("widen", Fwiden, Swiden, 0, 0, "",
        doc: /* Remove restrictions (narrowing) from current buffer.
 This allows the buffer's full text to be seen and edited.  */)
   (void)
@@ -3221,7 +3193,7 @@ This allows the buffer's full text to be seen and edited.  */)
   return Qnil;
 }
 
-DEFUE ("narrow-to-region", Fnarrow_to_region, Snarrow_to_region, 2, 2, "r",
+DEFUN ("narrow-to-region", Fnarrow_to_region, Snarrow_to_region, 2, 2, "r",
        doc: /* Restrict editing in this buffer to the current region.
 The rest of the text becomes temporarily invisible and untouchable
 but is not deleted; if you save the buffer in a file, the invisible
@@ -3382,7 +3354,7 @@ static char *message_text;
 /* Allocated length of that buffer.  */
 static int message_length;
 
-DEFUE ("message", Fmessage, Smessage, 1, MANY, 0,
+DEFUN ("message", Fmessage, Smessage, 1, MANY, 0,
        doc: /* Display a message at the bottom of the screen.
 The message also goes into the `*Messages*' buffer.
 \(In keyboard macros, that's all it does.)
@@ -3399,7 +3371,7 @@ any existing message; this lets the minibuffer contents show.  See
 also `current-message'.
 
 usage: (message FORMAT-STRING &rest ARGS)  */)
-  (size_t nargs, Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   if (NILP (args[0])
       || (STRINGP (args[0])
@@ -3427,7 +3399,7 @@ If the first argument is nil or the empty string, clear any existing
 message; let the minibuffer contents show.
 
 usage: (message-box FORMAT-STRING &rest ARGS)  */)
-  (size_t nargs, Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   if (NILP (args[0]))
     {
@@ -3484,7 +3456,7 @@ If the first argument is nil or the empty string, clear any existing
 message; let the minibuffer contents show.
 
 usage: (message-or-box FORMAT-STRING &rest ARGS)  */)
-  (size_t nargs, Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
 #ifdef HAVE_MENUS
   if ((NILP (last_nonmenu_event) || CONSP (last_nonmenu_event))
@@ -3494,7 +3466,7 @@ usage: (message-or-box FORMAT-STRING &rest ARGS)  */)
   return Fmessage (nargs, args);
 }
 
-DEFUE ("current-message", Fcurrent_message, Scurrent_message, 0, 0, 0,
+DEFUN ("current-message", Fcurrent_message, Scurrent_message, 0, 0, 0,
        doc: /* Return the string currently displayed in the echo area, or nil if none.  */)
   (void)
 {
@@ -3508,11 +3480,11 @@ First argument is the string to copy.
 Remaining arguments form a sequence of PROPERTY VALUE pairs for text
 properties to add to the result.
 usage: (propertize STRING &rest PROPERTIES)  */)
-  (size_t nargs, Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
   Lisp_Object properties, string;
   struct gcpro gcpro1, gcpro2;
-  size_t i;
+  ptrdiff_t i;
 
   /* Number of args must be odd.  */
   if ((nargs & 1) == 0)
@@ -3534,16 +3506,7 @@ usage: (propertize STRING &rest PROPERTIES)  */)
   RETURN_UNGCPRO (string);
 }
 
-
-/* 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 (SDATA (STRING), SBYTES (STRING))         \
-   : SBYTES (STRING))
-
-DEFUE ("format", Fformat, Sformat, 1, MANY, 0,
+DEFUN ("format", Fformat, Sformat, 1, MANY, 0,
        doc: /* Format a string out of a format-string and arguments.
 The first argument is a format control string.
 The other arguments are substituted into it to make the result, a string.
@@ -3582,7 +3545,8 @@ The width specifier supplies a lower limit for the length of the
 printed representation.  The padding, if any, normally goes on the
 left, but it goes on the right if the - flag is present.  The padding
 character is normally a space, but it is 0 if the 0 flag is present.
-The - flag takes precedence over the 0 flag.
+The 0 flag is ignored if the - flag is present, or the format sequence
+is something other than %d, %e, %f, and %g.
 
 For %e, %f, and %g sequences, the number after the "." in the
 precision specifier says how many decimal places to show; if zero, the
@@ -3590,13 +3554,19 @@ decimal point itself is omitted.  For %s and %S, the precision
 specifier truncates the string to the given width.
 
 usage: (format STRING &rest OBJECTS)  */)
-  (size_t nargs, register Lisp_Object *args)
+  (ptrdiff_t nargs, Lisp_Object *args)
 {
-  register size_t n;           /* The number of the next arg to substitute */
-  register size_t total;       /* An estimate of the final length */
-  char *buf, *p;
+  ptrdiff_t n;         /* The number of the next arg to substitute */
+  char initial_buffer[4000];
+  char *buf = initial_buffer;
+  EMACS_INT bufsize = sizeof initial_buffer;
+  EMACS_INT max_bufsize = STRING_BYTES_BOUND + 1;
+  char *p;
+  Lisp_Object buf_save_value IF_LINT (= {0});
   register char *format, *end, *format_start;
-  int nchars;
+  EMACS_INT formatlen, nchars;
+  /* Nonzero if the format is multibyte.  */
+  int multibyte_format = 0;
   /* Nonzero if the output should be a multibyte string,
      which is true if any of the inputs is one.  */
   int multibyte = 0;
@@ -3605,14 +3575,6 @@ usage: (format STRING &rest OBJECTS)  */)
      multibyte character of the previous string.  This flag tells if we
      must consider such a situation or not.  */
   int maybe_combine_byte;
-  char *this_format;
-  /* Precision for each spec, or -1, a flag value meaning no precision
-     was given in that spec.  Element 0, corresponding to the format
-     string itself, will not be used.  Element NARGS, corresponding to
-     no argument, *will* be assigned to in the case that a `%' and `.'
-     occur after the final format specifier.  */
-  int *precision = (int *) (alloca ((nargs + 1) * sizeof (int)));
-  int longest_format;
   Lisp_Object val;
   int arg_intervals = 0;
   USE_SAFE_ALLOCA;
@@ -3620,458 +3582,610 @@ usage: (format STRING &rest OBJECTS)  */)
   /* discarded[I] is 1 if byte I of the format
      string was not copied into the output.
      It is 2 if byte I was not the first byte of its character.  */
-  char *discarded = 0;
+  char *discarded;
 
   /* Each element records, for one argument,
      the start and end bytepos in the output string,
+     whether the argument has been converted to string (e.g., due to "%S"),
      and whether the argument is a string with intervals.
      info[0] is unused.  Unused elements have -1 for start.  */
   struct info
   {
-    int start, end, intervals;
+    EMACS_INT start, end;
+    int converted_to_string;
+    int intervals;
   } *info = 0;
 
   /* It should not be necessary to GCPRO ARGS, because
      the caller in the interpreter should take care of that.  */
 
+  CHECK_STRING (args[0]);
+  format_start = SSDATA (args[0]);
+  formatlen = SBYTES (args[0]);
+
+  /* Allocate the info and discarded tables.  */
+  {
+    ptrdiff_t i;
+    if ((SIZE_MAX - formatlen) / sizeof (struct info) <= nargs)
+      memory_full (SIZE_MAX);
+    SAFE_ALLOCA (info, struct info *, (nargs + 1) * sizeof *info + formatlen);
+    discarded = (char *) &info[nargs + 1];
+    for (i = 0; i < nargs + 1; i++)
+      {
+       info[i].start = -1;
+       info[i].intervals = info[i].converted_to_string = 0;
+      }
+    memset (discarded, 0, formatlen);
+  }
+
   /* 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;
-      /* Piggyback on this loop to initialize precision[N]. */
-      precision[n] = -1;
-    }
-  precision[nargs] = -1;
-
-  CHECK_STRING (args[0]);
-  /* We may have to change "%S" to "%s". */
-  args[0] = Fcopy_sequence (args[0]);
-
-  /* GC should never happen here, so abort if it does.  */
-  abort_on_gc++;
+  multibyte_format = STRING_MULTIBYTE (args[0]);
+  multibyte = multibyte_format;
+  for (n = 1; !multibyte && n < nargs; n++)
+    if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n]))
+      multibyte = 1;
 
   /* If we start out planning a unibyte result,
-     then discover it has to be multibyte, we jump back to retry.
-     That can only happen from the first large while loop below.  */
+     then discover it has to be multibyte, we jump back to retry.  */
  retry:
 
-  format = SSDATA (args[0]);
-  format_start = format;
-  end = format + SBYTES (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]) + 1;
-
-  /* Allocate the info and discarded tables.  */
-  {
-    size_t nbytes = (nargs+1) * sizeof *info;
-    size_t i;
-    if (!info)
-      info = (struct info *) alloca (nbytes);
-    memset (info, 0, nbytes);
-    for (i = 0; i < nargs + 1; i++)
-      info[i].start = -1;
-    if (!discarded)
-      SAFE_ALLOCA (discarded, char *, SBYTES (args[0]));
-    memset (discarded, 0, SBYTES (args[0]));
-  }
+  p = buf;
+  nchars = 0;
+  n = 0;
 
-  /* Add to TOTAL enough space to hold the converted arguments.  */
+  /* Scan the format and store result in BUF.  */
+  format = format_start;
+  end = format + formatlen;
+  maybe_combine_byte = 0;
 
-  n = 0;
   while (format != end)
-    if (*format++ == '%')
-      {
-       EMACS_INT thissize = 0;
-       EMACS_INT actual_width = 0;
-       char *this_format_start = format - 1;
-       int field_width = 0;
-
-       /* General format specifications look like
+    {
+      /* The values of N and FORMAT when the loop body is entered.  */
+      ptrdiff_t n0 = n;
+      char *format0 = format;
 
-          '%' [flags] [field-width] [precision] format
+      /* Bytes needed to represent the output of this conversion.  */
+      EMACS_INT convbytes;
 
-          where
+      if (*format == '%')
+       {
+         /* General format specifications look like
 
-          flags        ::= [-+ #0]+
-          field-width  ::= [0-9]+
-          precision    ::= '.' [0-9]*
+            '%' [flags] [field-width] [precision] format
 
-          If a field-width is specified, it specifies to which width
-          the output should be padded with blanks, if the output
-          string is shorter than field-width.
+            where
 
-          If precision is specified, it specifies the number of
-          digits to print after the '.' for floats, or the max.
-          number of chars to print from a string.  */
+            flags ::= [-+0# ]+
+            field-width ::= [0-9]+
+            precision ::= '.' [0-9]*
 
-       while (format != end
-              && (*format == '-' || *format == '0' || *format == '#'
-                  || * format == ' ' || *format == '+'))
-         ++format;
+            If a field-width is specified, it specifies to which width
+            the output should be padded with blanks, if the output
+            string is shorter than field-width.
 
-       if (*format >= '0' && *format <= '9')
-         {
-           for (field_width = 0; *format >= '0' && *format <= '9'; ++format)
-             field_width = 10 * field_width + *format - '0';
-         }
+            If precision is specified, it specifies the number of
+            digits to print after the '.' for floats, or the max.
+            number of chars to print from a string.  */
 
-       /* N is not incremented for another few lines below, so refer to
-          element N+1 (which might be precision[NARGS]). */
-       if (*format == '.')
-         {
-           ++format;
-           for (precision[n+1] = 0; *format >= '0' && *format <= '9'; ++format)
-             precision[n+1] = 10 * precision[n+1] + *format - '0';
-         }
+         int minus_flag = 0;
+         int  plus_flag = 0;
+         int space_flag = 0;
+         int sharp_flag = 0;
+         int  zero_flag = 0;
+         EMACS_INT field_width;
+         int precision_given;
+         uintmax_t precision = UINTMAX_MAX;
+         char *num_end;
+         char conversion;
 
-       /* Extra +1 for 'l' that we may need to insert into the
-          format.  */
-       if (format - this_format_start + 2 > longest_format)
-         longest_format = format - this_format_start + 2;
+         while (1)
+           {
+             switch (*++format)
+               {
+               case '-': minus_flag = 1; continue;
+               case '+':  plus_flag = 1; continue;
+               case ' ': space_flag = 1; continue;
+               case '#': sharp_flag = 1; continue;
+               case '0':  zero_flag = 1; continue;
+               }
+             break;
+           }
 
-       if (format == end)
-         error ("Format string ends in middle of format specifier");
-       if (*format == '%')
-         format++;
-       else if (++n >= nargs)
-         error ("Not enough arguments for format string");
-       else if (*format == 'S')
-         {
-           /* 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;
-           /* If we restart the loop, we should not come here again
-              because args[n] is now a string and calling
-              Fprin1_to_string on it produces superflous double
-              quotes.  So, change "%S" to "%s" now.  */
-           *format = 's';
-           goto string;
-         }
-       else if (SYMBOLP (args[n]))
-         {
-           args[n] = SYMBOL_NAME (args[n]);
-           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");
-           /* In the case (PRECISION[N] > 0), THISSIZE may not need
-              to be as large as is calculated here.  Easy check for
-              the case PRECISION = 0. */
-           thissize = precision[n] ? CONVERTED_BYTE_SIZE (multibyte, args[n]) : 0;
-           /* The precision also constrains how much of the argument
-              string will finally appear (Bug#5710). */
-           actual_width = lisp_string_width (args[n], -1, NULL, NULL);
-           if (precision[n] != -1)
-             actual_width = min (actual_width, precision[n]);
-         }
-       /* Would get MPV otherwise, since Lisp_Int's `point' to low memory.  */
-       else if (INTEGERP (args[n]) && *format != 's')
-         {
-           /* The following loop assumes the Lisp type indicates
-              the proper way to pass the argument.
-              So make sure we have a flonum if the argument should
-              be a double.  */
-           if (*format == 'e' || *format == 'f' || *format == 'g')
-             args[n] = Ffloat (args[n]);
-           else
-             if (*format != 'd' && *format != 'o' && *format != 'x'
-                 && *format != 'i' && *format != 'X' && *format != 'c')
-               error ("Invalid format operation %%%c", *format);
-
-           thissize = 30 + (precision[n] > 0 ? precision[n] : 0);
-           if (*format == 'c')
-             {
-               if (! ASCII_CHAR_P (XINT (args[n]))
-                   /* Note: No one can remember why we have to treat
-                      the character 0 as a multibyte character here.
-                      But, until it causes a real problem, let's
-                      don't change it.  */
-                   || XINT (args[n]) == 0)
-                 {
-                   if (! multibyte)
-                     {
-                       multibyte = 1;
-                       goto retry;
-                     }
-                   args[n] = Fchar_to_string (args[n]);
-                   thissize = SBYTES (args[n]);
-                 }
-             }
-         }
-       else if (FLOATP (args[n]) && *format != 's')
-         {
-           if (! (*format == 'e' || *format == 'f' || *format == 'g'))
-             {
-               if (*format != 'd' && *format != 'o' && *format != 'x'
-                   && *format != 'i' && *format != 'X' && *format != 'c')
-                 error ("Invalid format operation %%%c", *format);
-               /* This fails unnecessarily if args[n] is bigger than
-                  most-positive-fixnum but smaller than MAXINT.
-                  These cases are important because we sometimes use floats
-                  to represent such integer values (typically such values
-                  come from UIDs or PIDs).  */
-               /* args[n] = Ftruncate (args[n], Qnil); */
-             }
+         /* Ignore flags when sprintf ignores them.  */
+         space_flag &= ~ plus_flag;
+         zero_flag &= ~ minus_flag;
 
-           /* Note that we're using sprintf to print floats,
-              so we have to take into account what that function
-              prints.  */
-           /* Filter out flag value of -1.  */
-           thissize = (MAX_10_EXP + 100
-                       + (precision[n] > 0 ? precision[n] : 0));
-         }
-       else
          {
-           /* 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;
+           uintmax_t w = strtoumax (format, &num_end, 10);
+           if (max_bufsize <= w)
+             string_overflow ();
+           field_width = w;
          }
-
-       thissize += max (0, field_width - actual_width);
-       total += thissize + 4;
-      }
-
-  abort_on_gc--;
-
-  /* Now we can no longer jump to retry.
-     TOTAL and LONGEST_FORMAT are known for certain.  */
-
-  this_format = (char *) alloca (longest_format + 1);
-
-  /* Allocate the space for the result.
-     Note that TOTAL is an overestimate.  */
-  SAFE_ALLOCA (buf, char *, total);
-
-  p = buf;
-  nchars = 0;
-  n = 0;
-
-  /* Scan the format and store result in BUF.  */
-  format = SSDATA (args[0]);
-  format_start = format;
-  end = format + SBYTES (args[0]);
-  maybe_combine_byte = 0;
-  while (format != end)
-    {
-      if (*format == '%')
-       {
-         int minlen;
-         int negative = 0;
-         char *this_format_start = format;
-
+         precision_given = *num_end == '.';
+         if (precision_given)
+           precision = strtoumax (num_end + 1, &num_end, 10);
+         format = num_end;
+
+         if (format == end)
+           error ("Format string ends in middle of format specifier");
+
+         memset (&discarded[format0 - format_start], 1, format - format0);
+         conversion = *format;
+         if (conversion == '%')
+           goto copy_char;
          discarded[format - format_start] = 1;
          format++;
 
-         while (strchr ("-+0# ", *format))
+         ++n;
+         if (! (n < nargs))
+           error ("Not enough arguments for format string");
+
+         /* For 'S', prin1 the argument, and then treat like 's'.
+            For 's', princ any argument that is not a string or
+            symbol.  But don't do this conversion twice, which might
+            happen after retrying.  */
+         if ((conversion == 'S'
+              || (conversion == 's'
+                  && ! STRINGP (args[n]) && ! SYMBOLP (args[n]))))
            {
-             if (*format == '-')
+             if (! info[n].converted_to_string)
                {
-                 negative = 1;
+                 Lisp_Object noescape = conversion == 'S' ? Qnil : Qt;
+                 args[n] = Fprin1_to_string (args[n], noescape);
+                 info[n].converted_to_string = 1;
+                 if (STRING_MULTIBYTE (args[n]) && ! multibyte)
+                   {
+                     multibyte = 1;
+                     goto retry;
+                   }
                }
-             discarded[format - format_start] = 1;
-             ++format;
+             conversion = 's';
            }
+         else if (conversion == 'c')
+           {
+             if (FLOATP (args[n]))
+               {
+                 double d = XFLOAT_DATA (args[n]);
+                 args[n] = make_number (FIXNUM_OVERFLOW_P (d) ? -1 : d);
+               }
 
-         minlen = atoi (format);
+             if (INTEGERP (args[n]) && ! ASCII_CHAR_P (XINT (args[n])))
+               {
+                 if (!multibyte)
+                   {
+                     multibyte = 1;
+                     goto retry;
+                   }
+                 args[n] = Fchar_to_string (args[n]);
+                 info[n].converted_to_string = 1;
+               }
 
-         while ((*format >= '0' && *format <= '9') || *format == '.')
-           {
-             discarded[format - format_start] = 1;
-             format++;
+             if (info[n].converted_to_string)
+               conversion = 's';
+             zero_flag = 0;
            }
 
-         if (*format++ == '%')
+         if (SYMBOLP (args[n]))
            {
-             *p++ = '%';
-             nchars++;
-             continue;
+             args[n] = SYMBOL_NAME (args[n]);
+             if (STRING_MULTIBYTE (args[n]) && ! multibyte)
+               {
+                 multibyte = 1;
+                 goto retry;
+               }
            }
 
-         ++n;
-
-         discarded[format - format_start - 1] = 1;
-         info[n].start = nchars;
-
-         if (STRINGP (args[n]))
+         if (conversion == 's')
            {
              /* handle case (precision[n] >= 0) */
 
-             int width, padding;
-             EMACS_INT nbytes, start;
+             EMACS_INT width, padding, nbytes;
              EMACS_INT nchars_string;
 
+             EMACS_INT prec = -1;
+             if (precision_given && precision <= TYPE_MAXIMUM (EMACS_INT))
+               prec = precision;
+
              /* lisp_string_width ignores a precision of 0, but GNU
                 libc functions print 0 characters when the precision
                 is 0.  Imitate libc behavior here.  Changing
                 lisp_string_width is the right thing, and will be
                 done, but meanwhile we work with it. */
 
-             if (precision[n] == 0)
+             if (prec == 0)
                width = nchars_string = nbytes = 0;
-             else if (precision[n] > 0)
-               width = lisp_string_width (args[n], precision[n],
-                                          &nchars_string, &nbytes);
              else
-               {               /* no precision spec given for this argument */
-                 width = lisp_string_width (args[n], -1, NULL, NULL);
-                 nbytes = SBYTES (args[n]);
-                 nchars_string = SCHARS (args[n]);
+               {
+                 EMACS_INT nch, nby;
+                 width = lisp_string_width (args[n], prec, &nch, &nby);
+                 if (prec < 0)
+                   {
+                     nchars_string = SCHARS (args[n]);
+                     nbytes = SBYTES (args[n]);
+                   }
+                 else
+                   {
+                     nchars_string = nch;
+                     nbytes = nby;
+                   }
                }
 
-             /* If spec requires it, pad on right with spaces.  */
-             padding = minlen - width;
-             if (! negative)
-               while (padding-- > 0)
-                 {
-                   *p++ = ' ';
-                   ++nchars;
-                 }
+             convbytes = nbytes;
+             if (convbytes && multibyte && ! STRING_MULTIBYTE (args[n]))
+               convbytes = count_size_as_multibyte (SDATA (args[n]), nbytes);
 
-             info[n].start = start = nchars;
-             nchars += nchars_string;
+             padding = width < field_width ? field_width - width : 0;
 
-             if (p > buf
-                 && multibyte
-                 && !ASCII_BYTE_P (*((unsigned char *) p - 1))
-                 && STRING_MULTIBYTE (args[n])
-                 && !CHAR_HEAD_P (SREF (args[n], 0)))
-               maybe_combine_byte = 1;
+             if (max_bufsize - padding <= convbytes)
+               string_overflow ();
+             convbytes += padding;
+             if (convbytes <= buf + bufsize - p)
+               {
+                 if (! minus_flag)
+                   {
+                     memset (p, ' ', padding);
+                     p += padding;
+                     nchars += padding;
+                   }
 
-             p += copy_text (SDATA (args[n]), (unsigned char *) p,
-                             nbytes,
-                             STRING_MULTIBYTE (args[n]), multibyte);
+                 if (p > buf
+                     && multibyte
+                     && !ASCII_BYTE_P (*((unsigned char *) p - 1))
+                     && STRING_MULTIBYTE (args[n])
+                     && !CHAR_HEAD_P (SREF (args[n], 0)))
+                   maybe_combine_byte = 1;
 
-             info[n].end = nchars;
+                 p += copy_text (SDATA (args[n]), (unsigned char *) p,
+                                 nbytes,
+                                 STRING_MULTIBYTE (args[n]), multibyte);
 
-             if (negative)
-               while (padding-- > 0)
-                 {
-                   *p++ = ' ';
-                   nchars++;
-                 }
+                  info[n].start = nchars;
+                 nchars += nchars_string;
+                 info[n].end = nchars;
 
-             /* If this argument has text properties, record where
-                in the result string it appears.  */
-             if (STRING_INTERVALS (args[n]))
-               info[n].intervals = arg_intervals = 1;
+                 if (minus_flag)
+                   {
+                     memset (p, ' ', padding);
+                     p += padding;
+                     nchars += padding;
+                   }
+
+                 /* If this argument has text properties, record where
+                    in the result string it appears.  */
+                 if (STRING_INTERVALS (args[n]))
+                   info[n].intervals = arg_intervals = 1;
+
+                 continue;
+               }
            }
-         else if (INTEGERP (args[n]) || FLOATP (args[n]))
+         else if (! (conversion == 'c' || conversion == 'd'
+                     || conversion == 'e' || conversion == 'f'
+                     || conversion == 'g' || conversion == 'i'
+                     || conversion == 'o' || conversion == 'x'
+                     || conversion == 'X'))
+           error ("Invalid format operation %%%c",
+                  STRING_CHAR ((unsigned char *) format - 1));
+         else if (! (INTEGERP (args[n]) || FLOATP (args[n])))
+           error ("Format specifier doesn't match argument type");
+         else
            {
-             int this_nchars;
-
-             memcpy (this_format, this_format_start,
-                     format - this_format_start);
-             this_format[format - this_format_start] = 0;
+             enum
+             {
+               /* Maximum precision for a %f conversion such that the
+                  trailing output digit might be nonzero.  Any precisions
+                  larger than this will not yield useful information.  */
+               USEFUL_PRECISION_MAX =
+                 ((1 - DBL_MIN_EXP)
+                  * (FLT_RADIX == 2 || FLT_RADIX == 10 ? 1
+                     : FLT_RADIX == 16 ? 4
+                     : -1)),
+
+               /* Maximum number of bytes generated by any format, if
+                  precision is no more than DBL_USEFUL_PRECISION_MAX.
+                  On all practical hosts, %f is the worst case.  */
+               SPRINTF_BUFSIZE =
+                 sizeof "-." + (DBL_MAX_10_EXP + 1) + USEFUL_PRECISION_MAX,
+
+               /* Length of pM (that is, of pMd without the
+                  trailing "d").  */
+               pMlen = sizeof pMd - 2
+             };
+             verify (0 < USEFUL_PRECISION_MAX);
+
+             int prec;
+             EMACS_INT padding, sprintf_bytes;
+             uintmax_t excess_precision, numwidth;
+             uintmax_t leading_zeros = 0, trailing_zeros = 0;
+
+             char sprintf_buf[SPRINTF_BUFSIZE];
+
+             /* Copy of conversion specification, modified somewhat.
+                At most three flags F can be specified at once.  */
+             char convspec[sizeof "%FFF.*d" + pMlen];
+
+             /* Avoid undefined behavior in underlying sprintf.  */
+             if (conversion == 'd' || conversion == 'i')
+               sharp_flag = 0;
+
+             /* Create the copy of the conversion specification, with
+                any width and precision removed, with ".*" inserted,
+                and with pM inserted for integer formats.  */
+             {
+               char *f = convspec;
+               *f++ = '%';
+               *f = '-'; f += minus_flag;
+               *f = '+'; f +=  plus_flag;
+               *f = ' '; f += space_flag;
+               *f = '#'; f += sharp_flag;
+               *f = '0'; f +=  zero_flag;
+                *f++ = '.';
+                *f++ = '*';
+               if (conversion == 'd' || conversion == 'i'
+                   || conversion == 'o' || conversion == 'x'
+                   || conversion == 'X')
+                 {
+                   memcpy (f, pMd, pMlen);
+                   f += pMlen;
+                   zero_flag &= ~ precision_given;
+                 }
+               *f++ = conversion;
+               *f = '\0';
+             }
 
-             if (format[-1] == 'e' || format[-1] == 'f' || format[-1] == 'g')
-               sprintf (p, this_format, XFLOAT_DATA (args[n]));
+             prec = -1;
+             if (precision_given)
+               prec = min (precision, USEFUL_PRECISION_MAX);
+
+             /* Use sprintf to format this number into sprintf_buf.  Omit
+                padding and excess precision, though, because sprintf limits
+                output length to INT_MAX.
+
+                There are four types of conversion: double, unsigned
+                char (passed as int), wide signed int, and wide
+                unsigned int.  Treat them separately because the
+                sprintf ABI is sensitive to which type is passed.  Be
+                careful about integer overflow, NaNs, infinities, and
+                conversions; for example, the min and max macros are
+                not suitable here.  */
+             if (conversion == 'e' || conversion == 'f' || conversion == 'g')
+               {
+                 double x = (INTEGERP (args[n])
+                             ? XINT (args[n])
+                             : XFLOAT_DATA (args[n]));
+                 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
+               }
+             else if (conversion == 'c')
+               {
+                 /* Don't use sprintf here, as it might mishandle prec.  */
+                 sprintf_buf[0] = XINT (args[n]);
+                 sprintf_bytes = prec != 0;
+               }
+             else if (conversion == 'd')
+               {
+                 /* For float, maybe we should use "%1.0f"
+                    instead so it also works for values outside
+                    the integer range.  */
+                 printmax_t x;
+                 if (INTEGERP (args[n]))
+                   x = XINT (args[n]);
+                 else
+                   {
+                     double d = XFLOAT_DATA (args[n]);
+                     if (d < 0)
+                       {
+                         x = TYPE_MINIMUM (printmax_t);
+                         if (x < d)
+                           x = d;
+                       }
+                     else
+                       {
+                         x = TYPE_MAXIMUM (printmax_t);
+                         if (d < x)
+                           x = d;
+                       }
+                   }
+                 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
+               }
              else
                {
-                 if (sizeof (EMACS_INT) > sizeof (int)
-                     && format[-1] != 'c')
+                 /* Don't sign-extend for octal or hex printing.  */
+                 uprintmax_t x;
+                 if (INTEGERP (args[n]))
+                   x = XUINT (args[n]);
+                 else
                    {
-                     /* Insert 'l' before format spec.  */
-                     this_format[format - this_format_start]
-                       = this_format[format - this_format_start - 1];
-                     this_format[format - this_format_start - 1] = 'l';
-                     this_format[format - this_format_start + 1] = 0;
+                     double d = XFLOAT_DATA (args[n]);
+                     if (d < 0)
+                       x = 0;
+                     else
+                       {
+                         x = TYPE_MAXIMUM (uprintmax_t);
+                         if (d < x)
+                           x = d;
+                       }
                    }
+                 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
+               }
 
-                 if (INTEGERP (args[n]))
+             /* Now the length of the formatted item is known, except it omits
+                padding and excess precision.  Deal with excess precision
+                first.  This happens only when the format specifies
+                ridiculously large precision.  */
+             excess_precision = precision - prec;
+             if (excess_precision)
+               {
+                 if (conversion == 'e' || conversion == 'f'
+                     || conversion == 'g')
                    {
-                     if (format[-1] == 'c')
-                       sprintf (p, this_format, (int) XINT (args[n]));
-                     else if (format[-1] == 'd')
-                       sprintf (p, this_format, XINT (args[n]));
-                     /* Don't sign-extend for octal or hex printing.  */
+                     if ((conversion == 'g' && ! sharp_flag)
+                         || ! ('0' <= sprintf_buf[sprintf_bytes - 1]
+                               && sprintf_buf[sprintf_bytes - 1] <= '9'))
+                       excess_precision = 0;
                      else
-                       sprintf (p, this_format, XUINT (args[n]));
+                       {
+                         if (conversion == 'g')
+                           {
+                             char *dot = strchr (sprintf_buf, '.');
+                             if (!dot)
+                               excess_precision = 0;
+                           }
+                       }
+                     trailing_zeros = excess_precision;
                    }
-                 else if (format[-1] == 'c')
-                   sprintf (p, this_format, (int) XFLOAT_DATA (args[n]));
-                 else if (format[-1] == 'd')
-                   /* Maybe we should use "%1.0f" instead so it also works
-                      for values larger than MAXINT.  */
-                   sprintf (p, this_format, (EMACS_INT) XFLOAT_DATA (args[n]));
                  else
-                   /* Don't sign-extend for octal or hex printing.  */
-                   sprintf (p, this_format, (EMACS_UINT) XFLOAT_DATA (args[n]));
+                   leading_zeros = excess_precision;
+               }
+
+             /* Compute the total bytes needed for this item, including
+                excess precision and padding.  */
+             numwidth = sprintf_bytes + excess_precision;
+             padding = numwidth < field_width ? field_width - numwidth : 0;
+             if (max_bufsize - sprintf_bytes <= excess_precision
+                 || max_bufsize - padding <= numwidth)
+               string_overflow ();
+             convbytes = numwidth + padding;
+
+             if (convbytes <= buf + bufsize - p)
+               {
+                 /* Copy the formatted item from sprintf_buf into buf,
+                    inserting padding and excess-precision zeros.  */
+
+                  char *src = sprintf_buf;
+                 char src0 = src[0];
+                 int exponent_bytes = 0;
+                 int signedp = src0 == '-' || src0 == '+' || src0 == ' ';
+                 int significand_bytes;
+                 if (zero_flag
+                     && ((src[signedp] >= '0' && src[signedp] <= '9')
+                         || (src[signedp] >= 'a' && src[signedp] <= 'f')
+                         || (src[signedp] >= 'A' && src[signedp] <= 'F')))
+                   {
+                     leading_zeros += padding;
+                     padding = 0;
+                   }
+
+                 if (excess_precision
+                     && (conversion == 'e' || conversion == 'g'))
+                   {
+                     char *e = strchr (src, 'e');
+                     if (e)
+                       exponent_bytes = src + sprintf_bytes - e;
+                   }
+
+                 if (! minus_flag)
+                   {
+                     memset (p, ' ', padding);
+                     p += padding;
+                     nchars += padding;
+                   }
+
+                 *p = src0;
+                 src += signedp;
+                 p += signedp;
+                 memset (p, '0', leading_zeros);
+                 p += leading_zeros;
+                 significand_bytes = sprintf_bytes - signedp - exponent_bytes;
+                 memcpy (p, src, significand_bytes);
+                  p += significand_bytes;
+                 src += significand_bytes;
+                 memset (p, '0', trailing_zeros);
+                 p += trailing_zeros;
+                 memcpy (p, src, exponent_bytes);
+                 p += exponent_bytes;
+
+                  info[n].start = nchars;
+                 nchars += leading_zeros + sprintf_bytes + trailing_zeros;
+                 info[n].end = nchars;
+
+                 if (minus_flag)
+                   {
+                     memset (p, ' ', padding);
+                     p += padding;
+                     nchars += padding;
+                   }
+
+                 continue;
                }
+           }
+       }
+      else
+      copy_char:
+       {
+         /* Copy a single character from format to buf.  */
 
+         char *src = format;
+         unsigned char str[MAX_MULTIBYTE_LENGTH];
+
+         if (multibyte_format)
+           {
+             /* Copy a whole multibyte character.  */
              if (p > buf
-                 && multibyte
                  && !ASCII_BYTE_P (*((unsigned char *) p - 1))
-                 && !CHAR_HEAD_P (*((unsigned char *) p)))
+                 && !CHAR_HEAD_P (*format))
                maybe_combine_byte = 1;
-             this_nchars = strlen (p);
-             if (multibyte)
-               p += str_to_multibyte ((unsigned char *) p,
-                                      buf + total - 1 - p, this_nchars);
+
+             do
+               format++;
+             while (! CHAR_HEAD_P (*format));
+
+             convbytes = format - format0;
+             memset (&discarded[format0 + 1 - format_start], 2, convbytes - 1);
+           }
+         else
+           {
+             unsigned char uc = *format++;
+             if (! multibyte || ASCII_BYTE_P (uc))
+               convbytes = 1;
              else
-               p += this_nchars;
-             nchars += this_nchars;
-             info[n].end = nchars;
+               {
+                 int c = BYTE8_TO_CHAR (uc);
+                 convbytes = CHAR_STRING (c, str);
+                 src = (char *) str;
+               }
            }
 
-       }
-      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))
+         if (convbytes <= buf + bufsize - p)
            {
-             discarded[format - format_start] = 2;
-             *p++ = *format++;
+             memcpy (p, src, convbytes);
+             p += convbytes;
+             nchars++;
+             continue;
            }
-         nchars++;
        }
-      else if (multibyte)
-       {
-         /* Convert a single-byte character to multibyte.  */
-         int len = copy_text ((unsigned char *) format, (unsigned char *) p,
-                              1, 0, 1);
 
-         p += len;
-         format++;
-         nchars++;
-       }
-      else
-       *p++ = *format++, nchars++;
+      /* There wasn't enough room to store this conversion or single
+        character.  CONVBYTES says how much room is needed.  Allocate
+        enough room (and then some) and do it again.  */
+      {
+       EMACS_INT used = p - buf;
+
+       if (max_bufsize - used < convbytes)
+         string_overflow ();
+       bufsize = used + convbytes;
+       bufsize = bufsize < max_bufsize / 2 ? bufsize * 2 : max_bufsize;
+
+       if (buf == initial_buffer)
+         {
+           buf = xmalloc (bufsize);
+           sa_must_free = 1;
+           buf_save_value = make_save_value (buf, 0);
+           record_unwind_protect (safe_alloca_unwind, buf_save_value);
+           memcpy (buf, initial_buffer, used);
+         }
+       else
+         XSAVE_VALUE (buf_save_value)->pointer = buf = xrealloc (buf, bufsize);
+
+       p = buf + used;
+      }
+
+      format = format0;
+      n = n0;
     }
 
-  if (p > buf + total)
+  if (bufsize < p - buf)
     abort ();
 
   if (maybe_combine_byte)
@@ -4098,7 +4212,7 @@ usage: (format STRING &rest OBJECTS)  */)
       if (CONSP (props))
        {
          EMACS_INT bytepos = 0, position = 0, translated = 0;
-         int argn = 1;
+         EMACS_INT argn = 1;
          Lisp_Object list;
 
          /* Adjust the bounds of each text property
@@ -4362,8 +4476,9 @@ Transposing beyond buffer boundaries is an error.  */)
 
   if (start2 < end1)
     error ("Transposed regions overlap");
-  else if (start1 == end1 || start2 == end2)
-    error ("Transposed region has length 0");
+  /* Nothing to change for adjacent regions with one being empty */
+  else if ((start1 == end1 || start2 == end2) && end1 == start2)
+    return Qnil;
 
   /* The possibilities are:
      1. Adjacent (contiguous) regions, or separate but equal regions
@@ -4616,9 +4731,7 @@ syms_of_editfns (void)
   environbuf = 0;
   initial_tz = 0;
 
-  Qbuffer_access_fontify_functions
-    = intern_c_string ("buffer-access-fontify-functions");
-  staticpro (&Qbuffer_access_fontify_functions);
+  DEFSYM (Qbuffer_access_fontify_functions, "buffer-access-fontify-functions");
 
   DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion,
               doc: /* Non-nil means text motion commands don't notice fields.  */);
@@ -4680,10 +4793,8 @@ functions if all the text being accessed has this property.  */);
   defsubr (&Sregion_beginning);
   defsubr (&Sregion_end);
 
-  staticpro (&Qfield);
-  Qfield = intern_c_string ("field");
-  staticpro (&Qboundary);
-  Qboundary = intern_c_string ("boundary");
+  DEFSYM (Qfield, "field");
+  DEFSYM (Qboundary, "boundary");
   defsubr (&Sfield_beginning);
   defsubr (&Sfield_end);
   defsubr (&Sfield_string);