Merge from emacs-24; up to 2012-12-27T08:21:08Z!rgm@gnu.org
[bpt/emacs.git] / src / editfns.c
index c5d4ed2..e0b0347 100644 (file)
@@ -1,6 +1,6 @@
 /* Lisp functions pertaining to editing.
 
-Copyright (C) 1985-1987, 1989, 1993-2012 Free Software Foundation, Inc.
+Copyright (C) 1985-1987, 1989, 1993-2013 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -24,6 +24,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #ifdef HAVE_PWD_H
 #include <pwd.h>
+#include <grp.h>
 #endif
 
 #include <unistd.h>
@@ -78,6 +79,15 @@ Lisp_Object Qfield;
 
 static Lisp_Object Qboundary;
 
+/* The startup value of the TZ environment variable so it can be
+   restored if the user calls set-time-zone-rule with a nil
+   argument.  If null, the TZ environment variable was unset.  */
+static char const *initial_tz;
+
+/* True if the static variable tzvalbuf (defined in
+   set_time_zone_rule) is part of 'environ'.  */
+static bool tzvalbuf_in_environ;
+
 
 void
 init_editfns (void)
@@ -96,6 +106,9 @@ init_editfns (void)
     return;
 #endif /* not CANNOT_DUMP */
 
+  initial_tz = getenv ("TZ");
+  tzvalbuf_in_environ = 0;
+
   pw = getpwuid (getuid ());
 #ifdef MSDOS
   /* We let the real user name default to "root" because that's quite
@@ -360,7 +373,7 @@ get_pos_property (Lisp_Object position, register Lisp_Object prop, Lisp_Object o
   if (NILP (object))
     XSETBUFFER (object, current_buffer);
   else if (WINDOWP (object))
-    object = XWINDOW (object)->buffer;
+    object = XWINDOW (object)->contents;
 
   if (!BUFFERP (object))
     /* pos-property only makes sense in buffers right now, since strings
@@ -373,6 +386,7 @@ get_pos_property (Lisp_Object position, register Lisp_Object prop, Lisp_Object o
       ptrdiff_t noverlays;
       Lisp_Object *overlay_vec, tem;
       struct buffer *obuf = current_buffer;
+      USE_SAFE_ALLOCA;
 
       set_buffer_temp (XBUFFER (object));
 
@@ -385,7 +399,7 @@ get_pos_property (Lisp_Object position, register Lisp_Object prop, Lisp_Object o
         make enough space for all, and try again.  */
       if (noverlays > 40)
        {
-         overlay_vec = alloca (noverlays * sizeof *overlay_vec);
+         SAFE_ALLOCA_LISP (overlay_vec, noverlays);
          noverlays = overlays_around (posn, overlay_vec, noverlays);
        }
       noverlays = sort_overlays (overlay_vec, noverlays, NULL);
@@ -408,10 +422,12 @@ get_pos_property (Lisp_Object position, register Lisp_Object prop, Lisp_Object o
                ; /* The overlay will not cover a char inserted at point.  */
              else
                {
+                 SAFE_FREE ();
                  return tem;
                }
            }
        }
+      SAFE_FREE ();
 
       { /* Now check the text properties.  */
        int stickiness = text_property_stickiness (prop, position, object);
@@ -653,7 +669,8 @@ If the optional argument INHIBIT-CAPTURE-PROPERTY is non-nil, and OLD-POS has
 a non-nil property of that name, then any field boundaries are ignored.
 
 Field boundaries are not noticed if `inhibit-field-text-motion' is non-nil.  */)
-  (Lisp_Object new_pos, Lisp_Object old_pos, Lisp_Object escape_from_edge, Lisp_Object only_in_line, Lisp_Object inhibit_capture_property)
+  (Lisp_Object new_pos, Lisp_Object old_pos, Lisp_Object escape_from_edge,
+   Lisp_Object only_in_line, Lisp_Object inhibit_capture_property)
 {
   /* If non-zero, then the original point, before re-positioning.  */
   ptrdiff_t orig_point = 0;
@@ -719,9 +736,9 @@ Field boundaries are not noticed if `inhibit-field-text-motion' is non-nil.  */)
              /* This is the ONLY_IN_LINE case, check that NEW_POS and
                 FIELD_BOUND are on the same line by seeing whether
                 there's an intervening newline or not.  */
-             || (scan_buffer ('\n',
-                              XFASTINT (new_pos), XFASTINT (field_bound),
-                              fwd ? -1 : 1, &shortage, 1),
+             || (find_newline (XFASTINT (new_pos), -1,
+                               XFASTINT (field_bound), -1,
+                               fwd ? -1 : 1, &shortage, NULL, 1),
                  shortage != 0)))
        /* Constrain NEW_POS to FIELD_BOUND.  */
        new_pos = field_bound;
@@ -806,45 +823,47 @@ This function does not move point.  */)
     CHECK_NUMBER (n);
 
   clipped_n = clip_to_bounds (PTRDIFF_MIN + 1, XINT (n), PTRDIFF_MAX);
-  end_pos = find_before_next_newline (orig, 0, clipped_n - (clipped_n <= 0));
+  end_pos = find_before_next_newline (orig, 0, clipped_n - (clipped_n <= 0),
+                                     NULL);
 
   /* Return END_POS constrained to the current input field.  */
   return Fconstrain_to_field (make_number (end_pos), make_number (orig),
                              Qnil, Qt, Qnil);
 }
 
-\f
+/* Save current buffer state for `save-excursion' special form.
+   We (ab)use Lisp_Misc_Save_Value to allow explicit free and so
+   offload some work from GC.  */
+
 Lisp_Object
 save_excursion_save (void)
 {
-  bool visible = (XBUFFER (XWINDOW (selected_window)->buffer)
-                 == current_buffer);
-  /* Do not copy the mark if it points to nowhere.  */
-  Lisp_Object mark = (XMARKER (BVAR (current_buffer, mark))->buffer
-                     ? Fcopy_marker (BVAR (current_buffer, mark), Qnil)
-                     : Qnil);
-
-  return Fcons (Fpoint_marker (),
-               Fcons (mark,
-                      Fcons (visible ? Qt : Qnil,
-                             Fcons (BVAR (current_buffer, mark_active),
-                                    selected_window))));
+  return make_save_value
+    (SAVE_TYPE_OBJ_OBJ_OBJ_OBJ,
+     Fpoint_marker (),
+     /* Do not copy the mark if it points to nowhere.  */
+     (XMARKER (BVAR (current_buffer, mark))->buffer
+      ? Fcopy_marker (BVAR (current_buffer, mark), Qnil)
+      : Qnil),
+     /* Selected window if current buffer is shown in it, nil otherwise.  */
+     (EQ (XWINDOW (selected_window)->contents, Fcurrent_buffer ())
+      ? selected_window : Qnil),
+     BVAR (current_buffer, mark_active));
 }
 
+/* Restore saved buffer before leaving `save-excursion' special form.  */
+
 Lisp_Object
 save_excursion_restore (Lisp_Object info)
 {
   Lisp_Object tem, tem1, omark, nmark;
   struct gcpro gcpro1, gcpro2, gcpro3;
-  bool visible_p;
 
-  tem = Fmarker_buffer (XCAR (info));
-  /* If buffer being returned to is now deleted, avoid error */
-  /* Otherwise could get error here while unwinding to top level
-     and crash */
-  /* In that case, Fmarker_buffer returns nil now.  */
+  tem = Fmarker_buffer (XSAVE_OBJECT (info, 0));
+  /* If we're unwinding to top level, saved buffer may be deleted.  This
+     means that all of its markers are unchained and so tem is nil.  */
   if (NILP (tem))
-    return Qnil;
+    goto out;
 
   omark = nmark = Qnil;
   GCPRO3 (info, omark, nmark);
@@ -852,13 +871,12 @@ save_excursion_restore (Lisp_Object info)
   Fset_buffer (tem);
 
   /* Point marker.  */
-  tem = XCAR (info);
+  tem = XSAVE_OBJECT (info, 0);
   Fgoto_char (tem);
   unchain_marker (XMARKER (tem));
 
   /* Mark marker.  */
-  info = XCDR (info);
-  tem = XCAR (info);
+  tem = XSAVE_OBJECT (info, 1);
   omark = Fmarker_position (BVAR (current_buffer, mark));
   if (NILP (tem))
     unchain_marker (XMARKER (BVAR (current_buffer, mark)));
@@ -869,23 +887,8 @@ save_excursion_restore (Lisp_Object info)
       unchain_marker (XMARKER (tem));
     }
 
-  /* visible */
-  info = XCDR (info);
-  visible_p = !NILP (XCAR (info));
-
-#if 0 /* We used to make the current buffer visible in the selected window
-        if that was true previously.  That avoids some anomalies.
-        But it creates others, and it wasn't documented, and it is simpler
-        and cleaner never to alter the window/buffer connections.  */
-  tem1 = Fcar (tem);
-  if (!NILP (tem1)
-      && current_buffer != XBUFFER (XWINDOW (selected_window)->buffer))
-    Fswitch_to_buffer (Fcurrent_buffer (), Qnil);
-#endif /* 0 */
-
-  /* Mark active */
-  info = XCDR (info);
-  tem = XCAR (info);
+  /* Mark active.  */
+  tem = XSAVE_OBJECT (info, 3);
   tem1 = BVAR (current_buffer, mark_active);
   bset_mark_active (current_buffer, tem);
 
@@ -909,10 +912,10 @@ save_excursion_restore (Lisp_Object info)
   /* If buffer was visible in a window, and a different window was
      selected, and the old selected window is still showing this
      buffer, restore point in that window.  */
-  tem = XCDR (info);
-  if (visible_p
+  tem = XSAVE_OBJECT (info, 2);
+  if (WINDOWP (tem)
       && !EQ (tem, selected_window)
-      && (tem1 = XWINDOW (tem)->buffer,
+      && (tem1 = XWINDOW (tem)->contents,
          (/* Window is live...  */
           BUFFERP (tem1)
           /* ...and it shows the current buffer.  */
@@ -920,6 +923,10 @@ save_excursion_restore (Lisp_Object info)
     Fset_window_point (tem, make_number (PT));
 
   UNGCPRO;
+
+ out:
+
+  free_misc (info);
   return Qnil;
 }
 
@@ -962,7 +969,7 @@ usage: (save-current-buffer &rest BODY)  */)
   return unbind_to (count, Fprogn (args));
 }
 \f
-DEFUN ("buffer-size", Fbufsize, Sbufsize, 0, 1, 0,
+DEFUN ("buffer-size", Fbuffer_size, Sbuffer_size, 0, 1, 0,
        doc: /* Return the number of characters in the current buffer.
 If BUFFER, return the number of characters in that buffer instead.  */)
   (Lisp_Object buffer)
@@ -1267,6 +1274,24 @@ Value is an integer or a float, depending on the value.  */)
   return make_fixnum_or_float (uid);
 }
 
+DEFUN ("group-gid", Fgroup_gid, Sgroup_gid, 0, 0, 0,
+       doc: /* Return the effective gid of Emacs.
+Value is an integer or a float, depending on the value.  */)
+  (void)
+{
+  gid_t egid = getegid ();
+  return make_fixnum_or_float (egid);
+}
+
+DEFUN ("group-real-gid", Fgroup_real_gid, Sgroup_real_gid, 0, 0, 0,
+       doc: /* Return the real gid of Emacs.
+Value is an integer or a float, depending on the value.  */)
+  (void)
+{
+  gid_t gid = getgid ();
+  return make_fixnum_or_float (gid);
+}
+
 DEFUN ("user-full-name", Fuser_full_name, Suser_full_name, 0, 1, 0,
        doc: /* Return the full name of the user logged in, as a string.
 If the full name corresponding to Emacs's userid is not known,
@@ -1461,9 +1486,7 @@ Lisp_Object
 make_lisp_time (EMACS_TIME t)
 {
   int ns = EMACS_NSECS (t);
-  return make_time_tail (EMACS_SECS (t),
-                        list2 (make_number (ns / 1000),
-                               make_number (ns % 1000 * 1000)));
+  return make_time_tail (EMACS_SECS (t), list2i (ns / 1000, ns % 1000 * 1000));
 }
 
 /* Decode a Lisp list SPECIFIED_TIME that represents a time.
@@ -1907,9 +1930,11 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE)  */)
     }
   else
     {
-      char tzbuf[100];
+      static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d";
+      char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)];
+      char *old_tzstring;
       const char *tzstring;
-      char **oldenv = environ, **newenv;
+      USE_SAFE_ALLOCA;
 
       if (EQ (zone, Qt))
        tzstring = "UTC0";
@@ -1921,13 +1946,20 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE)  */)
          EMACS_INT zone_hr = abszone / (60*60);
          int zone_min = (abszone/60) % 60;
          int zone_sec = abszone % 60;
-         sprintf (tzbuf, "XXX%s%"pI"d:%02d:%02d", "-" + (XINT (zone) < 0),
+         sprintf (tzbuf, tzbuf_format, "-" + (XINT (zone) < 0),
                   zone_hr, zone_min, zone_sec);
          tzstring = tzbuf;
        }
       else
        error ("Invalid time zone specification");
 
+      old_tzstring = getenv ("TZ");
+      if (old_tzstring)
+       {
+         char *buf = SAFE_ALLOCA (strlen (old_tzstring) + 1);
+         old_tzstring = strcpy (buf, old_tzstring);
+       }
+
       block_input ();
 
       /* Set TZ before calling mktime; merely adjusting mktime's returned
@@ -1936,15 +1968,12 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE)  */)
 
       value = mktime (&tm);
 
-      /* Restore TZ to previous value.  */
-      newenv = environ;
-      environ = oldenv;
+      set_time_zone_rule (old_tzstring);
 #ifdef LOCALTIME_CACHE
       tzset ();
 #endif
       unblock_input ();
-
-      xfree (newenv);
+      SAFE_FREE ();
     }
 
   if (value == (time_t) -1)
@@ -2074,16 +2103,6 @@ the data it can't find.  */)
   return list2 (zone_offset, zone_name);
 }
 
-/* This holds the value of `environ' produced by the previous
-   call to Fset_time_zone_rule, or 0 if Fset_time_zone_rule
-   has never been called.  */
-static char **environbuf;
-
-/* This holds the startup value of the TZ environment variable so it
-   can be restored if the user calls set-time-zone-rule with a nil
-   argument.  */
-static char *initial_tz;
-
 DEFUN ("set-time-zone-rule", Fset_time_zone_rule, Sset_time_zone_rule, 1, 1, 0,
        doc: /* Set the local time zone using TZ, a string specifying a time zone rule.
 If TZ is nil, use implementation-defined default time zone information.
@@ -2096,18 +2115,10 @@ only the former.  */)
   (Lisp_Object tz)
 {
   const char *tzstring;
-  char **old_environbuf;
 
   if (! (NILP (tz) || EQ (tz, Qt)))
     CHECK_STRING (tz);
 
-  block_input ();
-
-  /* When called for the first time, save the original TZ.  */
-  old_environbuf = environbuf;
-  if (!old_environbuf)
-    initial_tz = (char *) getenv ("TZ");
-
   if (NILP (tz))
     tzstring = initial_tz;
   else if (EQ (tz, Qt))
@@ -2115,106 +2126,98 @@ only the former.  */)
   else
     tzstring = SSDATA (tz);
 
+  block_input ();
   set_time_zone_rule (tzstring);
-  environbuf = environ;
-
   unblock_input ();
 
-  xfree (old_environbuf);
   return Qnil;
 }
 
-#ifdef LOCALTIME_CACHE
-
-/* These two values are known to load tz files in buggy implementations,
-   i.e. Solaris 1 executables running under either Solaris 1 or Solaris 2.
-   Their values shouldn't matter in non-buggy implementations.
-   We don't use string literals for these strings,
-   since if a string in the environment is in readonly
-   storage, it runs afoul of bugs in SVR4 and Solaris 2.3.
-   See Sun bugs 1113095 and 1114114, ``Timezone routines
-   improperly modify environment''.  */
-
-static char set_time_zone_rule_tz1[] = "TZ=GMT+0";
-static char set_time_zone_rule_tz2[] = "TZ=GMT+1";
-
-#endif
-
 /* Set the local time zone rule to TZSTRING.
-   This allocates memory into `environ', which it is the caller's
-   responsibility to free.  */
+
+   This function is not thread-safe, partly because putenv, unsetenv
+   and tzset are not, and partly because of the static storage it
+   updates.  Other threads that invoke localtime etc. may be adversely
+   affected while this function is executing.  */
 
 void
 set_time_zone_rule (const char *tzstring)
 {
-  ptrdiff_t envptrs;
-  char **from, **to, **newenv;
+  /* A buffer holding a string of the form "TZ=value", intended
+     to be part of the environment.  */
+  static char *tzvalbuf;
+  static ptrdiff_t tzvalbufsize;
 
-  /* Make the ENVIRON vector longer with room for TZSTRING.  */
-  for (from = environ; *from; from++)
-    continue;
-  envptrs = from - environ + 2;
-  newenv = to = xmalloc (envptrs * sizeof *newenv
-                        + (tzstring ? strlen (tzstring) + 4 : 0));
+  int tzeqlen = sizeof "TZ=" - 1;
+
+#ifdef LOCALTIME_CACHE
+  /* These two values are known to load tz files in buggy implementations,
+     i.e., Solaris 1 executables running under either Solaris 1 or Solaris 2.
+     Their values shouldn't matter in non-buggy implementations.
+     We don't use string literals for these strings,
+     since if a string in the environment is in readonly
+     storage, it runs afoul of bugs in SVR4 and Solaris 2.3.
+     See Sun bugs 1113095 and 1114114, ``Timezone routines
+     improperly modify environment''.  */
+
+  static char set_time_zone_rule_tz[][sizeof "TZ=GMT+0"]
+    = { "TZ=GMT+0", "TZ=GMT+1" };
+
+  /* In SunOS 4.1.3_U1 and 4.1.4, if TZ has a value like
+     "US/Pacific" that loads a tz file, then changes to a value like
+     "XXX0" that does not load a tz file, and then changes back to
+     its original value, the last change is (incorrectly) ignored.
+     Also, if TZ changes twice in succession to values that do
+     not load a tz file, tzset can dump core (see Sun bug#1225179).
+     The following code works around these bugs.  */
 
-  /* Add TZSTRING to the end of environ, as a value for TZ.  */
   if (tzstring)
     {
-      char *t = (char *) (to + envptrs);
-      strcpy (t, "TZ=");
-      strcat (t, tzstring);
-      *to++ = t;
+      /* Temporarily set TZ to a value that loads a tz file
+        and that differs from tzstring.  */
+      bool eq0 = strcmp (tzstring, set_time_zone_rule_tz[0] + tzeqlen) == 0;
+      xputenv (set_time_zone_rule_tz[eq0]);
+    }
+  else
+    {
+      /* The implied tzstring is unknown, so temporarily set TZ to
+        two different values that each load a tz file.  */
+      xputenv (set_time_zone_rule_tz[0]);
+      tzset ();
+      xputenv (set_time_zone_rule_tz[1]);
     }
+  tzset ();
+  tzvalbuf_in_environ = 0;
+#endif
 
-  /* Copy the old environ vector elements into NEWENV,
-     but don't copy the TZ variable.
-     So we have only one definition of TZ, which came from TZSTRING.  */
-  for (from = environ; *from; from++)
-    if (strncmp (*from, "TZ=", 3) != 0)
-      *to++ = *from;
-  *to = 0;
+  if (!tzstring)
+    {
+      unsetenv ("TZ");
+      tzvalbuf_in_environ = 0;
+    }
+  else
+    {
+      ptrdiff_t tzstringlen = strlen (tzstring);
 
-  environ = newenv;
+      if (tzvalbufsize <= tzeqlen + tzstringlen)
+       {
+         unsetenv ("TZ");
+         tzvalbuf_in_environ = 0;
+         tzvalbuf = xpalloc (tzvalbuf, &tzvalbufsize,
+                             tzeqlen + tzstringlen - tzvalbufsize + 1, -1, 1);
+         memcpy (tzvalbuf, "TZ=", tzeqlen);
+       }
 
-  /* If we do have a TZSTRING, NEWENV points to the vector slot where
-     the TZ variable is stored.  If we do not have a TZSTRING,
-     TO points to the vector slot which has the terminating null.  */
+      strcpy (tzvalbuf + tzeqlen, tzstring);
 
-#ifdef LOCALTIME_CACHE
-  {
-    /* In SunOS 4.1.3_U1 and 4.1.4, if TZ has a value like
-       "US/Pacific" that loads a tz file, then changes to a value like
-       "XXX0" that does not load a tz file, and then changes back to
-       its original value, the last change is (incorrectly) ignored.
-       Also, if TZ changes twice in succession to values that do
-       not load a tz file, tzset can dump core (see Sun bug#1225179).
-       The following code works around these bugs.  */
-
-    if (tzstring)
-      {
-       /* Temporarily set TZ to a value that loads a tz file
-          and that differs from tzstring.  */
-       char *tz = *newenv;
-       *newenv = (strcmp (tzstring, set_time_zone_rule_tz1 + 3) == 0
-                  ? set_time_zone_rule_tz2 : set_time_zone_rule_tz1);
-       tzset ();
-       *newenv = tz;
-      }
-    else
-      {
-       /* The implied tzstring is unknown, so temporarily set TZ to
-          two different values that each load a tz file.  */
-       *to = set_time_zone_rule_tz1;
-       to[1] = 0;
-       tzset ();
-       *to = set_time_zone_rule_tz2;
-       tzset ();
-       *to = 0;
-      }
-
-    /* Now TZ has the desired value, and tzset can be invoked safely.  */
-  }
+      if (!tzvalbuf_in_environ)
+       {
+         xputenv (tzvalbuf);
+         tzvalbuf_in_environ = 1;
+       }
+    }
 
+#ifdef LOCALTIME_CACHE
   tzset ();
 #endif
 }
@@ -2359,8 +2362,8 @@ usage: (insert-before-markers-and-inherit &rest ARGS)  */)
 \f
 DEFUN ("insert-char", Finsert_char, Sinsert_char, 1, 3,
        "(list (read-char-by-name \"Insert character (Unicode name or hex): \")\
-        (prefix-numeric-value current-prefix-arg)\
-        t))",
+              (prefix-numeric-value current-prefix-arg)\
+              t))",
        doc: /* Insert COUNT copies of CHARACTER.
 Interactively, prompt for CHARACTER.  You can specify CHARACTER in one
 of these ways:
@@ -2497,7 +2500,7 @@ make_buffer_string_both (ptrdiff_t start, ptrdiff_t start_byte,
   Lisp_Object result, tem, tem1;
 
   if (start < GPT && GPT < end)
-    move_gap (start);
+    move_gap_both (start, start_byte);
 
   if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
     result = make_uninit_multibyte_string (end - start, end_byte - start_byte);
@@ -2595,7 +2598,7 @@ If narrowing is in effect, this function returns only the visible part
 of the buffer.  */)
   (void)
 {
-  return make_buffer_string (BEGV, ZV, 1);
+  return make_buffer_string_both (BEGV, BEGV_BYTE, ZV, ZV_BYTE, 1);
 }
 
 DEFUN ("insert-buffer-substring", Finsert_buffer_substring, Sinsert_buffer_substring,
@@ -2650,10 +2653,10 @@ They default to the values of (point-min) and (point-max) in BUFFER.  */)
 DEFUN ("compare-buffer-substrings", Fcompare_buffer_substrings, Scompare_buffer_substrings,
        6, 6, 0,
        doc: /* Compare two substrings of two buffers; return result as number.
-the value is -N if first string is less after N-1 chars,
-+N if first string is greater after N-1 chars, or 0 if strings match.
-Each substring is represented as three arguments: BUFFER, START and END.
-That makes six args in all, three for each substring.
+Return -N if first string is less after N-1 chars, +N if first string is
+greater after N-1 chars, or 0 if strings match.  Each substring is
+represented as three arguments: BUFFER, START and END.  That makes six
+args in all, three for each substring.
 
 The value of `case-fold-search' in the current buffer
 determines whether case is significant or ignored.  */)
@@ -2929,7 +2932,7 @@ Both characters must have the same length of multi-byte form.  */)
          else if (!changed)
            {
              changed = -1;
-             modify_region (current_buffer, pos, XINT (end), 0);
+             modify_region_1 (pos, XINT (end), false);
 
              if (! NILP (noundo))
                {
@@ -3105,7 +3108,7 @@ It returns the number of characters changed.  */)
   pos = XINT (start);
   pos_byte = CHAR_TO_BYTE (pos);
   end_pos = XINT (end);
-  modify_region (current_buffer, pos, end_pos, 0);
+  modify_region_1 (pos, end_pos, false);
 
   cnt = 0;
   for (; pos < end_pos; )
@@ -3426,16 +3429,10 @@ usage: (save-restriction &rest BODY)  */)
   return unbind_to (count, val);
 }
 \f
-/* Buffer for the most recent text displayed by Fmessage_box.  */
-static char *message_text;
-
-/* Allocated length of that buffer.  */
-static ptrdiff_t message_length;
-
 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.)
+The message also goes into the `*Messages*' buffer, if `message-log-max'
+is non-nil.  (In keyboard macros, that's all it does.)
 Return the message.
 
 The first argument is a format control string, and the rest are data
@@ -3455,14 +3452,14 @@ usage: (message FORMAT-STRING &rest ARGS)  */)
       || (STRINGP (args[0])
          && SBYTES (args[0]) == 0))
     {
-      message (0);
+      message1 (0);
       return args[0];
     }
   else
     {
       register Lisp_Object val;
       val = Fformat (nargs, args);
-      message3 (val, SBYTES (val), STRING_MULTIBYTE (val));
+      message3 (val);
       return val;
     }
 }
@@ -3481,13 +3478,12 @@ usage: (message-box FORMAT-STRING &rest ARGS)  */)
 {
   if (NILP (args[0]))
     {
-      message (0);
+      message1 (0);
       return Qnil;
     }
   else
     {
-      register Lisp_Object val;
-      val = Fformat (nargs, args);
+      Lisp_Object val = Fformat (nargs, args);
 #ifdef HAVE_MENUS
       /* The MS-DOS frames support popup menus even though they are
         not FRAME_WINDOW_P.  */
@@ -3504,16 +3500,7 @@ usage: (message-box FORMAT-STRING &rest ARGS)  */)
        return val;
       }
 #endif /* HAVE_MENUS */
-      /* Copy the data so that it won't move when we GC.  */
-      if (SBYTES (val) > message_length)
-       {
-         ptrdiff_t new_length = SBYTES (val) + 80;
-         message_text = xrealloc (message_text, new_length);
-         message_length = new_length;
-       }
-      memcpy (message_text, SDATA (val), SBYTES (val));
-      message2 (message_text, SBYTES (val),
-               STRING_MULTIBYTE (val));
+      message3 (val);
       return val;
     }
 }
@@ -3971,7 +3958,7 @@ usage: (format STRING &rest OBJECTS)  */)
                   trailing "d").  */
                pMlen = sizeof pMd - 2
              };
-             verify (0 < USEFUL_PRECISION_MAX);
+             verify (USEFUL_PRECISION_MAX > 0);
 
              int prec;
              ptrdiff_t padding, sprintf_bytes;
@@ -4249,12 +4236,15 @@ usage: (format STRING &rest OBJECTS)  */)
          {
            buf = xmalloc (bufsize);
            sa_must_free = 1;
-           buf_save_value = make_save_value (buf, 0);
+           buf_save_value = make_save_pointer (buf);
            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);
+         {
+           buf = xrealloc (buf, bufsize);
+           set_save_pointer (buf_save_value, 0, buf);
+         }
 
        p = buf + used;
       }
@@ -4519,7 +4509,7 @@ Transposing beyond buffer boundaries is an error.  */)
   (Lisp_Object startr1, Lisp_Object endr1, Lisp_Object startr2, Lisp_Object endr2, Lisp_Object leave_markers)
 {
   register ptrdiff_t start1, end1, start2, end2;
-  ptrdiff_t start1_byte, start2_byte, len1_byte, len2_byte;
+  ptrdiff_t start1_byte, start2_byte, len1_byte, len2_byte, end2_byte;
   ptrdiff_t gap, len1, len_mid, len2;
   unsigned char *start1_addr, *start2_addr, *temp;
 
@@ -4580,20 +4570,22 @@ Transposing beyond buffer boundaries is an error.  */)
      the gap the minimum distance to get it out of the way, and then
      deal with an unbroken array.  */
 
+  start1_byte = CHAR_TO_BYTE (start1);
+  end2_byte = CHAR_TO_BYTE (end2);
+
   /* Make sure the gap won't interfere, by moving it out of the text
      we will operate on.  */
   if (start1 < gap && gap < end2)
     {
       if (gap - start1 < end2 - gap)
-       move_gap (start1);
+       move_gap_both (start1, start1_byte);
       else
-       move_gap (end2);
+       move_gap_both (end2, end2_byte);
     }
 
-  start1_byte = CHAR_TO_BYTE (start1);
   start2_byte = CHAR_TO_BYTE (start2);
   len1_byte = CHAR_TO_BYTE (end1) - start1_byte;
-  len2_byte = CHAR_TO_BYTE (end2) - start2_byte;
+  len2_byte = end2_byte - start2_byte;
 
 #ifdef BYTE_COMBINING_DEBUG
   if (end1 == start2)
@@ -4629,7 +4621,7 @@ Transposing beyond buffer boundaries is an error.  */)
 
   if (end1 == start2)          /* adjacent regions */
     {
-      modify_region (current_buffer, start1, end2, 0);
+      modify_region_1 (start1, end2, false);
       record_change (start1, len1 + len2);
 
       tmp_interval1 = copy_intervals (cur_intv, start1, len1);
@@ -4688,8 +4680,8 @@ Transposing beyond buffer boundaries is an error.  */)
         {
          USE_SAFE_ALLOCA;
 
-          modify_region (current_buffer, start1, end1, 0);
-          modify_region (current_buffer, start2, end2, 0);
+          modify_region_1 (start1, end1, false);
+          modify_region_1 (start2, end2, false);
           record_change (start1, len1);
           record_change (start2, len2);
           tmp_interval1 = copy_intervals (cur_intv, start1, len1);
@@ -4722,7 +4714,7 @@ Transposing beyond buffer boundaries is an error.  */)
         {
          USE_SAFE_ALLOCA;
 
-          modify_region (current_buffer, start1, end2, 0);
+          modify_region_1 (start1, end2, false);
           record_change (start1, (end2 - start1));
           tmp_interval1 = copy_intervals (cur_intv, start1, len1);
           tmp_interval_mid = copy_intervals (cur_intv, end1, len_mid);
@@ -4755,7 +4747,7 @@ Transposing beyond buffer boundaries is an error.  */)
          USE_SAFE_ALLOCA;
 
           record_change (start1, (end2 - start1));
-          modify_region (current_buffer, start1, end2, 0);
+          modify_region_1 (start1, end2, false);
 
           tmp_interval1 = copy_intervals (cur_intv, start1, len1);
           tmp_interval_mid = copy_intervals (cur_intv, end1, len_mid);
@@ -4806,9 +4798,6 @@ Transposing beyond buffer boundaries is an error.  */)
 void
 syms_of_editfns (void)
 {
-  environbuf = 0;
-  initial_tz = 0;
-
   DEFSYM (Qbuffer_access_fontify_functions, "buffer-access-fontify-functions");
 
   DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion,
@@ -4883,12 +4872,10 @@ functions if all the text being accessed has this property.  */);
   defsubr (&Sline_beginning_position);
   defsubr (&Sline_end_position);
 
-/*  defsubr (&Smark); */
-/*  defsubr (&Sset_mark); */
   defsubr (&Ssave_excursion);
   defsubr (&Ssave_current_buffer);
 
-  defsubr (&Sbufsize);
+  defsubr (&Sbuffer_size);
   defsubr (&Spoint_max);
   defsubr (&Spoint_min);
   defsubr (&Spoint_min_marker);
@@ -4917,6 +4904,8 @@ functions if all the text being accessed has this property.  */);
   defsubr (&Suser_real_login_name);
   defsubr (&Suser_uid);
   defsubr (&Suser_real_uid);
+  defsubr (&Sgroup_gid);
+  defsubr (&Sgroup_real_gid);
   defsubr (&Suser_full_name);
   defsubr (&Semacs_pid);
   defsubr (&Scurrent_time);